Merge "Doze: Fix NPE found when pulsing and !dozing." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index 90d63b0..2cd1708 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -27278,7 +27278,7 @@
method public boolean isSpeaking();
method public int playEarcon(java.lang.String, int, android.os.Bundle, java.lang.String);
method public deprecated int playEarcon(java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
- method public int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>, java.lang.String);
+ method public int playSilence(long, int, java.lang.String);
method public deprecated int playSilence(long, int, java.util.HashMap<java.lang.String, java.lang.String>);
method public int setAudioAttributes(android.media.AudioAttributes);
method public deprecated int setEngineByPackageName(java.lang.String);
@@ -27366,7 +27366,7 @@
public abstract class TextToSpeechService extends android.app.Service {
ctor public TextToSpeechService();
method public android.os.IBinder onBind(android.content.Intent);
- method protected java.lang.String onGetDefaultVoiceNameFor(java.lang.String, java.lang.String, java.lang.String);
+ method public java.lang.String onGetDefaultVoiceNameFor(java.lang.String, java.lang.String, java.lang.String);
method protected java.util.Set<java.lang.String> onGetFeaturesForLanguage(java.lang.String, java.lang.String, java.lang.String);
method protected abstract java.lang.String[] onGetLanguage();
method public java.util.List<android.speech.tts.Voice> onGetVoices();
@@ -27396,6 +27396,7 @@
method public int getQuality();
method public boolean isNetworkConnectionRequired();
method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator CREATOR;
field public static final int LATENCY_HIGH = 400; // 0x190
field public static final int LATENCY_LOW = 200; // 0xc8
field public static final int LATENCY_NORMAL = 300; // 0x12c
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 9be220e..120c9e3 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -1216,17 +1216,12 @@
*
* @param durationInMs The duration of the silence.
* @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
- * @param params Parameters for the request. Can be null.
- * Engine specific parameters may be passed in but the parameter keys
- * must be prefixed by the name of the engine they are intended for. For example
- * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
- * engine named "com.svox.pico" if it is being used.
* @param utteranceId An unique identifier for this request.
*
* @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation.
*/
public int playSilence(final long durationInMs, final int queueMode,
- final HashMap<String, String> params, final String utteranceId) {
+ final String utteranceId) {
return runAction(new Action<Integer>() {
@Override
public Integer run(ITextToSpeechService service) throws RemoteException {
@@ -1258,12 +1253,12 @@
*
* @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation.
* @deprecated As of API level 20, replaced by
- * {@link #playSilence(long, int, HashMap, String)}.
+ * {@link #playSilence(long, int, String)}.
*/
@Deprecated
public int playSilence(final long durationInMs, final int queueMode,
final HashMap<String, String> params) {
- return playSilence(durationInMs, queueMode, params,
+ return playSilence(durationInMs, queueMode,
params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
}
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index d00a433..079467a 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -317,7 +317,7 @@
* @return A name of the default voice for a given locale.
*/
- protected String onGetDefaultVoiceNameFor(String lang, String country, String variant) {
+ public String onGetDefaultVoiceNameFor(String lang, String country, String variant) {
int localeStatus = onIsLanguageAvailable(lang, country, variant);
Locale iso3Locale = null;
switch (localeStatus) {
diff --git a/core/java/android/speech/tts/Voice.java b/core/java/android/speech/tts/Voice.java
index a1fa51d..dcf5980 100644
--- a/core/java/android/speech/tts/Voice.java
+++ b/core/java/android/speech/tts/Voice.java
@@ -91,9 +91,6 @@
Collections.addAll(this.mFeatures, in.readStringArray());
}
- /**
- * @hide
- */
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mName);
@@ -104,17 +101,11 @@
dest.writeStringList(new ArrayList<String>(mFeatures));
}
- /**
- * @hide
- */
@Override
public int describeContents() {
return 0;
}
- /**
- * @hide
- */
public static final Parcelable.Creator<Voice> CREATOR = new Parcelable.Creator<Voice>() {
@Override
public Voice createFromParcel(Parcel in) {
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index 8f8ce95..ac915d1 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -16,6 +16,7 @@
package com.android.internal.inputmethod;
+import android.app.AppOpsManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -637,6 +638,25 @@
}
/**
+ * Returns true if a package name belongs to a UID.
+ *
+ * <p>This is a simple wrapper of {@link AppOpsManager#checkPackage(int, String)}.</p>
+ * @param appOpsManager the {@link AppOpsManager} object to be used for the validation.
+ * @param uid the UID to be validated.
+ * @param packageName the package name.
+ * @return {@code true} if the package name belongs to the UID.
+ */
+ public static boolean checkIfPackageBelongsToUid(final AppOpsManager appOpsManager,
+ final int uid, final String packageName) {
+ try {
+ appOpsManager.checkPackage(uid, packageName);
+ return true;
+ } catch (SecurityException e) {
+ return false;
+ }
+ }
+
+ /**
* Utility class for putting and getting settings for InputMethod
* TODO: Move all putters and getters of settings to this class.
*/
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 40c9ed2..4dde217 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -458,7 +458,7 @@
Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
}
if (id != 0) {
- if (mResources.getDrawable(id) == null) {
+ if (mResources.getDrawable(id, null) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded drawable resource #0x"
+ Integer.toHexString(id)
diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml
index bfeac9d..d0fba20 100644
--- a/packages/SystemUI/res/layout/zen_mode_panel.xml
+++ b/packages/SystemUI/res/layout/zen_mode_panel.xml
@@ -76,7 +76,7 @@
android:layout_alignParentEnd="true"
android:background="@drawable/btn_borderless_rect"
android:clickable="true"
- android:contentDescription="@null"
+ android:contentDescription="@string/accessibility_desc_settings"
android:scaleType="center"
android:src="@drawable/ic_settings" />
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index cb9abfd..072fb29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -748,7 +748,9 @@
transition(mQsDetailHeader, showingDetail);
mShowingDetail = showingDetail;
if (showingDetail) {
- mQsDetailHeaderTitle.setText(detail.getTitle());
+ String title = mContext.getString(detail.getTitle());
+ mQsDetailHeaderTitle.setText(title);
+ announceForAccessibility(title);
final Boolean toggleState = detail.getToggleState();
if (toggleState == null) {
mQsDetailHeaderSwitch.setVisibility(INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
index eb0be05..e344954 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CastControllerImpl.java
@@ -55,6 +55,7 @@
private final Object mProjectionLock = new Object();
private boolean mDiscovering;
+ private boolean mCallbackRegistered;
private MediaProjectionInfo mProjection;
public CastControllerImpl(Context context) {
@@ -70,6 +71,7 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("CastController state:");
pw.print(" mDiscovering="); pw.println(mDiscovering);
+ pw.print(" mCallbackRegistered="); pw.println(mCallbackRegistered);
pw.print(" mCallbacks.size="); pw.println(mCallbacks.size());
pw.print(" mRoutes.size="); pw.println(mRoutes.size());
for (int i = 0; i < mRoutes.size(); i++) {
@@ -83,11 +85,17 @@
public void addCallback(Callback callback) {
mCallbacks.add(callback);
fireOnCastDevicesChanged(callback);
+ synchronized (mDiscoveringLock) {
+ handleDiscoveryChangeLocked();
+ }
}
@Override
public void removeCallback(Callback callback) {
mCallbacks.remove(callback);
+ synchronized (mDiscoveringLock) {
+ handleDiscoveryChangeLocked();
+ }
}
@Override
@@ -96,12 +104,23 @@
if (mDiscovering == request) return;
mDiscovering = request;
if (DEBUG) Log.d(TAG, "setDiscovering: " + request);
- if (request) {
- mMediaRouter.addCallback(ROUTE_TYPE_REMOTE_DISPLAY, mMediaCallback,
- MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
- } else {
- mMediaRouter.removeCallback(mMediaCallback);
- }
+ handleDiscoveryChangeLocked();
+ }
+ }
+
+ private void handleDiscoveryChangeLocked() {
+ if (mCallbackRegistered) {
+ mMediaRouter.removeCallback(mMediaCallback);
+ mCallbackRegistered = false;
+ }
+ if (mDiscovering) {
+ mMediaRouter.addCallback(ROUTE_TYPE_REMOTE_DISPLAY, mMediaCallback,
+ MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY);
+ mCallbackRegistered = true;
+ } else if (mCallbacks.size() != 0) {
+ mMediaRouter.addCallback(ROUTE_TYPE_REMOTE_DISPLAY, mMediaCallback,
+ MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
+ mCallbackRegistered = true;
}
}
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 060c8e3..1623eac 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -40,6 +40,7 @@
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.app.AlertDialog;
+import android.app.AppOpsManager;
import android.app.IUserSwitchObserver;
import android.app.KeyguardManager;
import android.app.Notification;
@@ -175,6 +176,7 @@
private InputMethodFileManager mFileManager;
private final HardKeyboardListener mHardKeyboardListener;
private final WindowManagerService mWindowManagerService;
+ private final AppOpsManager mAppOpsManager;
final InputBindResult mNoBinding = new InputBindResult(null, null, null, -1, -1);
@@ -643,6 +645,7 @@
}
}, true /*asyncHandler*/);
mWindowManagerService = windowManager;
+ mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mHardKeyboardListener = new HardKeyboardListener();
mHasFeature = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_INPUT_METHODS);
@@ -1746,6 +1749,20 @@
throw new IllegalArgumentException("Unknown id: " + id);
}
+ if (mCurClient != null && mCurAttribute != null) {
+ final int uid = mCurClient.uid;
+ final String packageName = mCurAttribute.packageName;
+ if (SystemConfig.getInstance().getFixedImeApps().contains(packageName)) {
+ if (InputMethodUtils.checkIfPackageBelongsToUid(mAppOpsManager, uid, packageName)) {
+ return;
+ }
+ // TODO: Do we need to lock the input method when the application reported an
+ // incorrect package name?
+ Slog.e(TAG, "Ignoring FixedImeApps due to the validation failure. uid=" + uid
+ + " package=" + packageName);
+ }
+ }
+
// See if we need to notify a subtype change within the same IME.
if (id.equals(mCurMethodId)) {
final int subtypeCount = info.getSubtypeCount();
diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h
index 3fc9f81..d809c5b 100644
--- a/tools/aapt/AaptAssets.h
+++ b/tools/aapt/AaptAssets.h
@@ -104,6 +104,9 @@
struct AaptGroupEntry
{
public:
+ AaptGroupEntry() {}
+ AaptGroupEntry(const ConfigDescription& config) : mParams(config) {}
+
bool initFromDirName(const char* dir, String8* resType);
inline const ConfigDescription& toParams() const { return mParams; }
diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp
index 137c85c..56d1650 100644
--- a/tools/aapt/Images.cpp
+++ b/tools/aapt/Images.cpp
@@ -1483,7 +1483,7 @@
return NO_ERROR;
}
-status_t postProcessImage(const sp<AaptAssets>& assets,
+status_t postProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets,
ResourceTable* table, const sp<AaptFile>& file)
{
String8 ext(file->getPath().getPathExtension());
@@ -1491,7 +1491,8 @@
// At this point, now that we have all the resource data, all we need to
// do is compile XML files.
if (strcmp(ext.string(), ".xml") == 0) {
- return compileXmlFile(assets, file, table);
+ String16 resourceName(parseResourceName(file->getPath().getPathLeaf()));
+ return compileXmlFile(bundle, assets, resourceName, file, table);
}
return NO_ERROR;
diff --git a/tools/aapt/Images.h b/tools/aapt/Images.h
index 91b6554..a0a94f8 100644
--- a/tools/aapt/Images.h
+++ b/tools/aapt/Images.h
@@ -20,7 +20,7 @@
status_t preProcessImageToCache(const Bundle* bundle, const String8& source, const String8& dest);
-status_t postProcessImage(const sp<AaptAssets>& assets,
+status_t postProcessImage(const Bundle* bundle, const sp<AaptAssets>& assets,
ResourceTable* table, const sp<AaptFile>& file);
#endif
diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h
index f24a023b..089dde5 100644
--- a/tools/aapt/Main.h
+++ b/tools/aapt/Main.h
@@ -62,4 +62,7 @@
status_t writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets,
FILE* fp, bool includeRaw);
+
+android::String8 parseResourceName(const String8& pathLeaf);
+
#endif // __MAIN_H
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index d605202..a4c9dab 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -50,7 +50,7 @@
// ==========================================================================
// ==========================================================================
-static String8 parseResourceName(const String8& leaf)
+String8 parseResourceName(const String8& leaf)
{
const char* firstDot = strchr(leaf.string(), '.');
const char* str = leaf.string();
@@ -1088,7 +1088,7 @@
manifest->addChild(app);
root->addChild(manifest);
- int err = compileXmlFile(assets, root, outFile, table);
+ int err = compileXmlFile(bundle, assets, String16(), root, outFile, table);
if (err < NO_ERROR) {
return err;
}
@@ -1336,7 +1336,8 @@
ResourceDirIterator it(layouts, String8("layout"));
while ((err=it.next()) == NO_ERROR) {
String8 src = it.getFile()->getPrintableSource();
- err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
+ it.getFile(), &table, xmlFlags);
if (err == NO_ERROR) {
ResXMLTree block;
block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
@@ -1355,7 +1356,8 @@
if (anims != NULL) {
ResourceDirIterator it(anims, String8("anim"));
while ((err=it.next()) == NO_ERROR) {
- err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
+ it.getFile(), &table, xmlFlags);
if (err != NO_ERROR) {
hasErrors = true;
}
@@ -1370,7 +1372,8 @@
if (animators != NULL) {
ResourceDirIterator it(animators, String8("animator"));
while ((err=it.next()) == NO_ERROR) {
- err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
+ it.getFile(), &table, xmlFlags);
if (err != NO_ERROR) {
hasErrors = true;
}
@@ -1385,7 +1388,8 @@
if (interpolators != NULL) {
ResourceDirIterator it(interpolators, String8("interpolator"));
while ((err=it.next()) == NO_ERROR) {
- err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
+ it.getFile(), &table, xmlFlags);
if (err != NO_ERROR) {
hasErrors = true;
}
@@ -1400,7 +1404,8 @@
if (transitions != NULL) {
ResourceDirIterator it(transitions, String8("transition"));
while ((err=it.next()) == NO_ERROR) {
- err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
+ it.getFile(), &table, xmlFlags);
if (err != NO_ERROR) {
hasErrors = true;
}
@@ -1415,7 +1420,8 @@
if (xmls != NULL) {
ResourceDirIterator it(xmls, String8("xml"));
while ((err=it.next()) == NO_ERROR) {
- err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
+ it.getFile(), &table, xmlFlags);
if (err != NO_ERROR) {
hasErrors = true;
}
@@ -1430,7 +1436,7 @@
if (drawables != NULL) {
ResourceDirIterator it(drawables, String8("drawable"));
while ((err=it.next()) == NO_ERROR) {
- err = postProcessImage(assets, &table, it.getFile());
+ err = postProcessImage(bundle, assets, &table, it.getFile());
if (err != NO_ERROR) {
hasErrors = true;
}
@@ -1445,7 +1451,8 @@
if (colors != NULL) {
ResourceDirIterator it(colors, String8("color"));
while ((err=it.next()) == NO_ERROR) {
- err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
+ it.getFile(), &table, xmlFlags);
if (err != NO_ERROR) {
hasErrors = true;
}
@@ -1461,7 +1468,8 @@
ResourceDirIterator it(menus, String8("menu"));
while ((err=it.next()) == NO_ERROR) {
String8 src = it.getFile()->getPrintableSource();
- err = compileXmlFile(assets, it.getFile(), &table, xmlFlags);
+ err = compileXmlFile(bundle, assets, String16(it.getBaseName()),
+ it.getFile(), &table, xmlFlags);
if (err == NO_ERROR) {
ResXMLTree block;
block.setTo(it.getFile()->getData(), it.getFile()->getSize(), true);
@@ -1477,6 +1485,22 @@
err = NO_ERROR;
}
+ // Now compile any generated resources.
+ std::queue<CompileResourceWorkItem>& workQueue = table.getWorkQueue();
+ while (!workQueue.empty()) {
+ CompileResourceWorkItem& workItem = workQueue.front();
+ err = compileXmlFile(bundle, assets, workItem.resourceName, workItem.file, &table, xmlFlags);
+ if (err == NO_ERROR) {
+ assets->addResource(workItem.resPath.getPathLeaf(),
+ workItem.resPath,
+ workItem.file,
+ workItem.file->getResourceType());
+ } else {
+ hasErrors = true;
+ }
+ workQueue.pop();
+ }
+
if (table.validateLocalizations()) {
hasErrors = true;
}
@@ -1509,7 +1533,7 @@
if (err < NO_ERROR) {
return err;
}
- err = compileXmlFile(assets, manifestTree, manifestFile, &table);
+ err = compileXmlFile(bundle, assets, String16(), manifestTree, manifestFile, &table);
if (err < NO_ERROR) {
return err;
}
@@ -1599,7 +1623,7 @@
sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(),
manifestFile->getGroupEntry(),
manifestFile->getResourceType());
- err = compileXmlFile(assets, manifestFile,
+ err = compileXmlFile(bundle, assets, String16(), manifestFile,
outManifestFile, &table,
XML_COMPILE_ASSIGN_ATTRIBUTE_IDS
| XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES);
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index 8c9efc9..b8c34543 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -17,7 +17,9 @@
#define NOISY(x) //x
-status_t compileXmlFile(const sp<AaptAssets>& assets,
+status_t compileXmlFile(const Bundle* bundle,
+ const sp<AaptAssets>& assets,
+ const String16& resourceName,
const sp<AaptFile>& target,
ResourceTable* table,
int options)
@@ -27,10 +29,12 @@
return UNKNOWN_ERROR;
}
- return compileXmlFile(assets, root, target, table, options);
+ return compileXmlFile(bundle, assets, resourceName, root, target, table, options);
}
-status_t compileXmlFile(const sp<AaptAssets>& assets,
+status_t compileXmlFile(const Bundle* bundle,
+ const sp<AaptAssets>& assets,
+ const String16& resourceName,
const sp<AaptFile>& target,
const sp<AaptFile>& outTarget,
ResourceTable* table,
@@ -41,10 +45,12 @@
return UNKNOWN_ERROR;
}
- return compileXmlFile(assets, root, outTarget, table, options);
+ return compileXmlFile(bundle, assets, resourceName, root, outTarget, table, options);
}
-status_t compileXmlFile(const sp<AaptAssets>& assets,
+status_t compileXmlFile(const Bundle* bundle,
+ const sp<AaptAssets>& assets,
+ const String16& resourceName,
const sp<XMLNode>& root,
const sp<AaptFile>& target,
ResourceTable* table,
@@ -77,6 +83,10 @@
if (hasErrors) {
return UNKNOWN_ERROR;
}
+
+ if (table->modifyForCompat(bundle, resourceName, target, root) != NO_ERROR) {
+ return UNKNOWN_ERROR;
+ }
NOISY(printf("Input XML Resource:\n"));
NOISY(root->print());
@@ -4028,6 +4038,39 @@
return t->getEntry(name, sourcePos, config, doSetIndex, overlay, mBundle->getAutoAddOverlay());
}
+sp<ResourceTable::ConfigList> ResourceTable::getConfigList(const String16& package,
+ const String16& type, const String16& name) const
+{
+ const size_t packageCount = mOrderedPackages.size();
+ for (size_t pi = 0; pi < packageCount; pi++) {
+ const sp<Package>& p = mOrderedPackages[pi];
+ if (p == NULL || p->getName() != package) {
+ continue;
+ }
+
+ const Vector<sp<Type> >& types = p->getOrderedTypes();
+ const size_t typeCount = types.size();
+ for (size_t ti = 0; ti < typeCount; ti++) {
+ const sp<Type>& t = types[ti];
+ if (t == NULL || t->getName() != type) {
+ continue;
+ }
+
+ const Vector<sp<ConfigList> >& configs = t->getOrderedConfigs();
+ const size_t configCount = configs.size();
+ for (size_t ci = 0; ci < configCount; ci++) {
+ const sp<ConfigList>& cl = configs[ci];
+ if (cl == NULL || cl->getName() != name) {
+ continue;
+ }
+
+ return cl;
+ }
+ }
+ }
+ return NULL;
+}
+
sp<const ResourceTable::Entry> ResourceTable::getEntry(uint32_t resID,
const ResTable_config* config) const
{
@@ -4157,6 +4200,22 @@
(attrId & 0x0000ffff) >= (baseAttrId & 0x0000ffff);
}
+static bool isMinSdkVersionLOrAbove(const Bundle* bundle) {
+ if (bundle->getMinSdkVersion() != NULL && strlen(bundle->getMinSdkVersion()) > 0) {
+ const char firstChar = bundle->getMinSdkVersion()[0];
+ if (firstChar >= 'L' && firstChar <= 'Z') {
+ // L is the code-name for the v21 release.
+ return true;
+ }
+
+ const int minSdk = atoi(bundle->getMinSdkVersion());
+ if (minSdk >= SDK_L) {
+ return true;
+ }
+ }
+ return false;
+}
+
/**
* Modifies the entries in the resource table to account for compatibility
* issues with older versions of Android.
@@ -4200,19 +4259,10 @@
* attribute will be respected.
*/
status_t ResourceTable::modifyForCompat(const Bundle* bundle) {
- if (bundle->getMinSdkVersion() != NULL) {
+ if (isMinSdkVersionLOrAbove(bundle)) {
// If this app will only ever run on L+ devices,
// we don't need to do any compatibility work.
-
- if (String8("L") == bundle->getMinSdkVersion()) {
- // Code-name for the v21 release.
- return NO_ERROR;
- }
-
- const int minSdk = atoi(bundle->getMinSdkVersion());
- if (minSdk >= SDK_L) {
- return NO_ERROR;
- }
+ return NO_ERROR;
}
const String16 attr16("attr");
@@ -4309,3 +4359,104 @@
}
return NO_ERROR;
}
+
+status_t ResourceTable::modifyForCompat(const Bundle* bundle,
+ const String16& resourceName,
+ const sp<AaptFile>& target,
+ const sp<XMLNode>& root) {
+ if (isMinSdkVersionLOrAbove(bundle)) {
+ return NO_ERROR;
+ }
+
+ if (target->getResourceType() == "" || target->getGroupEntry().toParams().sdkVersion >= SDK_L) {
+ // Skip resources that have no type (AndroidManifest.xml) or are already version qualified with v21
+ // or higher.
+ return NO_ERROR;
+ }
+
+ Vector<key_value_pair_t<sp<XMLNode>, size_t> > attrsToRemove;
+
+ Vector<sp<XMLNode> > nodesToVisit;
+ nodesToVisit.push(root);
+ while (!nodesToVisit.isEmpty()) {
+ sp<XMLNode> node = nodesToVisit.top();
+ nodesToVisit.pop();
+
+ const Vector<XMLNode::attribute_entry>& attrs = node->getAttributes();
+ const size_t attrCount = attrs.size();
+ for (size_t i = 0; i < attrCount; i++) {
+ const XMLNode::attribute_entry& attr = attrs[i];
+ if (isAttributeFromL(attr.nameResId)) {
+ attrsToRemove.add(key_value_pair_t<sp<XMLNode>, size_t>(node, i));
+ }
+ }
+
+ // Schedule a visit to the children.
+ const Vector<sp<XMLNode> >& children = node->getChildren();
+ const size_t childCount = children.size();
+ for (size_t i = 0; i < childCount; i++) {
+ nodesToVisit.push(children[i]);
+ }
+ }
+
+ if (attrsToRemove.isEmpty()) {
+ return NO_ERROR;
+ }
+
+ ConfigDescription newConfig(target->getGroupEntry().toParams());
+ newConfig.sdkVersion = SDK_L;
+
+ // Look to see if we already have an overriding v21 configuration.
+ sp<ConfigList> cl = getConfigList(String16(mAssets->getPackage()),
+ String16(target->getResourceType()), resourceName);
+ if (cl->getEntries().indexOfKey(newConfig) < 0) {
+ // We don't have an overriding entry for v21, so we must duplicate this one.
+ sp<XMLNode> newRoot = root->clone();
+ sp<AaptFile> newFile = new AaptFile(target->getSourceFile(),
+ AaptGroupEntry(newConfig), target->getResourceType());
+ String8 resPath = String8::format("res/%s/%s",
+ newFile->getGroupEntry().toDirName(target->getResourceType()).string(),
+ target->getPath().getPathLeaf().string());
+ resPath.convertToResPath();
+
+ // Add a resource table entry.
+ SourcePos(target->getSourceFile(), -1).printf(
+ "using v%d attributes; synthesizing resource %s:%s/%s for configuration %s.",
+ SDK_L,
+ mAssets->getPackage().string(),
+ newFile->getResourceType().string(),
+ String8(resourceName).string(),
+ newConfig.toString().string());
+
+ addEntry(SourcePos(),
+ String16(mAssets->getPackage()),
+ String16(target->getResourceType()),
+ resourceName,
+ String16(resPath),
+ NULL,
+ &newConfig);
+
+ // Schedule this to be compiled.
+ CompileResourceWorkItem item;
+ item.resourceName = resourceName;
+ item.resPath = resPath;
+ item.file = newFile;
+ mWorkQueue.push(item);
+ }
+
+ const size_t removeCount = attrsToRemove.size();
+ for (size_t i = 0; i < removeCount; i++) {
+ sp<XMLNode> node = attrsToRemove[i].key;
+ size_t attrIndex = attrsToRemove[i].value;
+ const XMLNode::attribute_entry& ae = node->getAttributes()[attrIndex];
+ SourcePos(node->getFilename(), node->getStartLineNumber()).printf(
+ "removing attribute %s%s%s from <%s>",
+ String8(ae.ns).string(),
+ (ae.ns.size() == 0 ? "" : ":"),
+ String8(ae.name).string(),
+ String8(node->getElementName()).string());
+ node->removeAttribute(attrIndex);
+ }
+
+ return NO_ERROR;
+}
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index 025a868..c548a85 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -12,8 +12,9 @@
#include "SourcePos.h"
#include "ResourceFilter.h"
-#include <set>
#include <map>
+#include <queue>
+#include <set>
using namespace std;
@@ -33,18 +34,24 @@
| XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES
};
-status_t compileXmlFile(const sp<AaptAssets>& assets,
+status_t compileXmlFile(const Bundle* bundle,
+ const sp<AaptAssets>& assets,
+ const String16& resourceName,
const sp<AaptFile>& target,
ResourceTable* table,
int options = XML_COMPILE_STANDARD_RESOURCE);
-status_t compileXmlFile(const sp<AaptAssets>& assets,
+status_t compileXmlFile(const Bundle* bundle,
+ const sp<AaptAssets>& assets,
+ const String16& resourceName,
const sp<AaptFile>& target,
const sp<AaptFile>& outTarget,
ResourceTable* table,
int options = XML_COMPILE_STANDARD_RESOURCE);
-status_t compileXmlFile(const sp<AaptAssets>& assets,
+status_t compileXmlFile(const Bundle* bundle,
+ const sp<AaptAssets>& assets,
+ const String16& resourceName,
const sp<XMLNode>& xmlTree,
const sp<AaptFile>& target,
ResourceTable* table,
@@ -71,6 +78,14 @@
}
};
+// Holds the necessary information to compile the
+// resource.
+struct CompileResourceWorkItem {
+ String16 resourceName;
+ String8 resPath;
+ sp<AaptFile> file;
+};
+
class ResourceTable : public ResTable::Accessor
{
public:
@@ -92,6 +107,18 @@
return mAssetsPackage;
}
+ /**
+ * Returns the queue of resources that need to be compiled.
+ * This is only used for resources that have been generated
+ * during the compilation phase. If they were just added
+ * to the AaptAssets, then they may be skipped over
+ * and would mess up iteration order for the existing
+ * resources.
+ */
+ queue<CompileResourceWorkItem>& getWorkQueue() {
+ return mWorkQueue;
+ }
+
status_t addIncludedResources(Bundle* bundle, const sp<AaptAssets>& assets);
status_t addPublic(const SourcePos& pos,
@@ -166,6 +193,10 @@
bool hasResources() const;
status_t modifyForCompat(const Bundle* bundle);
+ status_t modifyForCompat(const Bundle* bundle,
+ const String16& resourceName,
+ const sp<AaptFile>& file,
+ const sp<XMLNode>& root);
sp<AaptFile> flatten(Bundle* bundle, const sp<const ResourceFilter>& filter,
const bool isBase);
@@ -527,6 +558,9 @@
bool doSetIndex = false);
sp<const Entry> getEntry(uint32_t resID,
const ResTable_config* config = NULL) const;
+ sp<ConfigList> getConfigList(const String16& package,
+ const String16& type,
+ const String16& name) const;
const Item* getItem(uint32_t resID, uint32_t attrID) const;
bool getItemValue(uint32_t resID, uint32_t attrID,
Res_value* outValue);
@@ -545,6 +579,7 @@
// key = string resource name, value = set of locales in which that name is defined
map<String16, map<String8, SourcePos> > mLocalizations;
+ queue<CompileResourceWorkItem> mWorkQueue;
};
#endif
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 607d419..51a4154 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -621,6 +621,12 @@
return state.root;
}
+XMLNode::XMLNode()
+ : mNextAttributeIndex(0x80000000)
+ , mStartLineNumber(0)
+ , mEndLineNumber(0)
+ , mUTF8(false) {}
+
XMLNode::XMLNode(const String8& filename, const String16& s1, const String16& s2, bool isNamespace)
: mNextAttributeIndex(0x80000000)
, mFilename(filename)
@@ -810,6 +816,32 @@
return NO_ERROR;
}
+status_t XMLNode::removeAttribute(size_t index)
+{
+ if (getType() == TYPE_CDATA) {
+ return UNKNOWN_ERROR;
+ }
+
+ if (index >= mAttributes.size()) {
+ return UNKNOWN_ERROR;
+ }
+
+ const attribute_entry& e = mAttributes[index];
+ const uint32_t key = e.nameResId ? e.nameResId : e.index;
+ mAttributeOrder.removeItem(key);
+ mAttributes.removeAt(index);
+
+ // Shift all the indices.
+ const size_t attrCount = mAttributeOrder.size();
+ for (size_t i = 0; i < attrCount; i++) {
+ size_t attrIdx = mAttributeOrder[i];
+ if (attrIdx > index) {
+ mAttributeOrder.replaceValueAt(i, attrIdx - 1);
+ }
+ }
+ return NO_ERROR;
+}
+
void XMLNode::setAttributeResID(size_t attrIdx, uint32_t resId)
{
attribute_entry& e = mAttributes.editItemAt(attrIdx);
@@ -999,6 +1031,30 @@
return hasErrors ? UNKNOWN_ERROR : NO_ERROR;
}
+sp<XMLNode> XMLNode::clone() const {
+ sp<XMLNode> copy = new XMLNode();
+ copy->mNamespacePrefix = mNamespacePrefix;
+ copy->mNamespaceUri = mNamespaceUri;
+ copy->mElementName = mElementName;
+
+ const size_t childCount = mChildren.size();
+ for (size_t i = 0; i < childCount; i++) {
+ copy->mChildren.add(mChildren[i]->clone());
+ }
+
+ copy->mAttributes = mAttributes;
+ copy->mAttributeOrder = mAttributeOrder;
+ copy->mNextAttributeIndex = mNextAttributeIndex;
+ copy->mChars = mChars;
+ memcpy(©->mCharsValue, &mCharsValue, sizeof(mCharsValue));
+ copy->mComment = mComment;
+ copy->mFilename = mFilename;
+ copy->mStartLineNumber = mStartLineNumber;
+ copy->mEndLineNumber = mEndLineNumber;
+ copy->mUTF8 = mUTF8;
+ return copy;
+}
+
status_t XMLNode::flatten(const sp<AaptFile>& dest,
bool stripComments, bool stripRawValues) const
{
diff --git a/tools/aapt/XMLNode.h b/tools/aapt/XMLNode.h
index ccbf9f4..3161f65 100644
--- a/tools/aapt/XMLNode.h
+++ b/tools/aapt/XMLNode.h
@@ -116,6 +116,8 @@
status_t addAttribute(const String16& ns, const String16& name,
const String16& value);
+ status_t removeAttribute(size_t index);
+
void setAttributeResID(size_t attrIdx, uint32_t resId);
status_t appendChars(const String16& chars);
@@ -137,6 +139,8 @@
status_t flatten(const sp<AaptFile>& dest, bool stripComments,
bool stripRawValues) const;
+ sp<XMLNode> clone() const;
+
void print(int indent=0);
private:
@@ -163,6 +167,9 @@
static void XMLCALL
commentData(void *userData, const char *comment);
+ // For cloning
+ XMLNode();
+
// Creating an element node.
XMLNode(const String8& filename, const String16& s1, const String16& s2, bool isNamespace);