Merge changes from topic 'ClosebySystem' into oc-mr1-dev
* changes:
Allow to close session from wrong context.
Prevent closing by prev owner after transfer.
Revert "Revert session-transfer change"
diff --git a/api/current.txt b/api/current.txt
index ec62158..434e9da1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10172,6 +10172,7 @@
method public int describeContents();
method public void dump(android.util.Printer, java.lang.String);
method public static java.lang.CharSequence getCategoryTitle(android.content.Context, int);
+ method public boolean isVirtualPreload();
method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager);
field public static final int CATEGORY_AUDIO = 1; // 0x1
field public static final int CATEGORY_GAME = 0; // 0x0
diff --git a/api/system-current.txt b/api/system-current.txt
index f2d2b34..604354bf 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -10760,6 +10760,7 @@
method public int describeContents();
method public void dump(android.util.Printer, java.lang.String);
method public static java.lang.CharSequence getCategoryTitle(android.content.Context, int);
+ method public boolean isVirtualPreload();
method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager);
field public static final int CATEGORY_AUDIO = 1; // 0x1
field public static final int CATEGORY_GAME = 0; // 0x0
diff --git a/api/test-current.txt b/api/test-current.txt
index c80c364..be1e314 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10209,6 +10209,7 @@
method public static java.lang.CharSequence getCategoryTitle(android.content.Context, int);
method public boolean isPrivilegedApp();
method public boolean isSystemApp();
+ method public boolean isVirtualPreload();
method public java.lang.CharSequence loadDescription(android.content.pm.PackageManager);
field public static final int CATEGORY_AUDIO = 1; // 0x1
field public static final int CATEGORY_GAME = 0; // 0x0
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 0bfe567..72075a5 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -572,7 +572,7 @@
public static final int PRIVATE_FLAG_STATIC_SHARED_LIBRARY = 1 << 14;
/**
- * Value for {@linl #privateFlags}: When set, the application will only have its splits loaded
+ * Value for {@link #privateFlags}: When set, the application will only have its splits loaded
* if they are required to load a component. Splits can be loaded on demand using the
* {@link Context#createContextForSplit(String)} API.
* @hide
@@ -580,10 +580,40 @@
public static final int PRIVATE_FLAG_ISOLATED_SPLIT_LOADING = 1 << 15;
/**
- * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
- * {@hide}
+ * Value for {@link #privateFlags}: When set, the application was installed as
+ * a virtual preload.
+ * @hide
*/
- public int privateFlags;
+ public static final int PRIVATE_FLAG_VIRTUAL_PRELOAD = 1 << 16;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "PRIVATE_FLAG_" }, value = {
+ PRIVATE_FLAG_HIDDEN,
+ PRIVATE_FLAG_CANT_SAVE_STATE,
+ PRIVATE_FLAG_FORWARD_LOCK,
+ PRIVATE_FLAG_PRIVILEGED,
+ PRIVATE_FLAG_HAS_DOMAIN_URLS,
+ PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE,
+ PRIVATE_FLAG_DIRECT_BOOT_AWARE,
+ PRIVATE_FLAG_INSTANT,
+ PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE,
+ PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER,
+ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE,
+ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE,
+ PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION,
+ PRIVATE_FLAG_BACKUP_IN_FOREGROUND,
+ PRIVATE_FLAG_STATIC_SHARED_LIBRARY,
+ PRIVATE_FLAG_ISOLATED_SPLIT_LOADING,
+ PRIVATE_FLAG_VIRTUAL_PRELOAD,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ApplicationInfoPrivateFlags {}
+
+ /**
+ * Private/hidden flags. See {@code PRIVATE_FLAG_...} constants.
+ * @hide
+ */
+ public @ApplicationInfoPrivateFlags int privateFlags;
/**
* @hide
@@ -1509,6 +1539,13 @@
}
/**
+ * Returns whether or not this application was installed as a virtual preload.
+ */
+ public boolean isVirtualPreload() {
+ return (privateFlags & PRIVATE_FLAG_VIRTUAL_PRELOAD) != 0;
+ }
+
+ /**
* @hide
*/
@Override protected ApplicationInfo getApplicationInfo() {
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4ae1aaf..85f11d6 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6930,6 +6930,11 @@
} else {
ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_INSTANT;
}
+ if (state.virtualPreload) {
+ ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD;
+ } else {
+ ai.privateFlags &= ~ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD;
+ }
if (state.hidden) {
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
} else {
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 470336c..069b2d4 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -21,10 +21,10 @@
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
-import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
-import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
+import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import android.util.ArraySet;
@@ -45,6 +45,7 @@
public boolean hidden; // Is the app restricted by owner / admin
public boolean suspended;
public boolean instantApp;
+ public boolean virtualPreload;
public int enabled;
public String lastDisableAppCaller;
public int domainVerificationStatus;
@@ -75,6 +76,7 @@
hidden = o.hidden;
suspended = o.suspended;
instantApp = o.instantApp;
+ virtualPreload = o.virtualPreload;
enabled = o.enabled;
lastDisableAppCaller = o.lastDisableAppCaller;
domainVerificationStatus = o.domainVerificationStatus;
@@ -194,6 +196,9 @@
if (instantApp != oldState.instantApp) {
return false;
}
+ if (virtualPreload != oldState.virtualPreload) {
+ return false;
+ }
if (enabled != oldState.enabled) {
return false;
}
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 233c4d4..8b6f9c1 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -106,6 +106,7 @@
/**
* The operation was canceled because the API is locked out due to too many attempts.
+ * This occurs after 5 failed attempts, and lasts for 30 seconds.
*/
public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 3a6de96..82cff7c 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1390,17 +1390,15 @@
* Sets whether Safe Browsing is enabled. Safe browsing allows WebView to
* protect against malware and phishing attacks by verifying the links.
*
- * Safe browsing is disabled by default. The recommended way to enable
- * Safe browsing is using a manifest tag to change the default value to
- * enabled for all WebViews.
* <p>
- * <pre>
- * <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
- * android:value="true" />
- * </pre>
+ * Safe browsing is disabled by default. The recommended way to enable Safe browsing is using a
+ * manifest tag to change the default value to enabled for all WebViews (read <a
+ * href="{@docRoot}reference/android/webkit/WebView.html">general Safe Browsing info</a>).
* </p>
*
+ * <p>
* This API overrides the manifest tag value for this WebView.
+ * </p>
*
* @param enabled Whether Safe browsing is enabled.
*/
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index da77640..dd716eb 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -319,6 +319,22 @@
* out.
* </p>
*
+ * <h3>Safe Browsing</h3>
+ *
+ * <p>
+ * If Safe Browsing is enabled, WebView will block malicious URLs and present a warning UI to the
+ * user to allow them to navigate back safely or proceed to the malicious page.
+ * </p>
+ * <p>
+ * The recommended way for apps to enable the feature is putting the following tag in the manifest:
+ * </p>
+ * <p>
+ * <pre>
+ * <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
+ * android:value="true" />
+ * </pre>
+ * </p>
+ *
*/
// Implementation notes.
// The WebView is a thin API class that delegates its public API to a backend WebViewProvider
@@ -1622,8 +1638,24 @@
}
/**
- * Starts Safe Browsing initialization. This should only be called once. This does not require
- * an Activity Context, and will always use the application Context to do its work.
+ * Starts Safe Browsing initialization. This should only be called once.
+ *
+ * <p>
+ * Because the Safe Browsing feature takes time to initialize, WebView may temporarily whitelist
+ * URLs until the feature is ready. The callback will be invoked with true once initialization
+ * is complete.
+ * </p>
+ *
+ * <p>
+ * This does not enable the Safe Browsing feature itself, and should only be used if the feature
+ * is otherwise enabled.
+ * </p>
+ *
+ * <p>
+ * This does not require an Activity Context, and will always use the application Context to do
+ * its work.
+ * </p>
+ *
* @param context Application Context.
* @param callback will be called with the value true if initialization is
* successful. The callback will be run on the UI thread.
@@ -1633,7 +1665,9 @@
}
/**
- * Shuts down Safe Browsing. This should only be called once.
+ * Shuts down Safe Browsing. This should only be called once. This does not disable the feature,
+ * it only frees resources used by Safe Browsing code. To disable Safe Browsing on an individual
+ * WebView, see {@link WebSettings#setSafeBrowsingEnabled}
*/
public static void shutdownSafeBrowsing() {
getFactory().getStatics().shutdownSafeBrowsing();
diff --git a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
index f9dc0e4..00f32b2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
+++ b/packages/SettingsLib/src/com/android/settingslib/suggestions/SuggestionParser.java
@@ -91,28 +91,25 @@
// Shared prefs keys for storing dismissed state.
// Index into current dismissed state.
- @VisibleForTesting
- static final String DISMISS_INDEX = "_dismiss_index";
public static final String SETUP_TIME = "_setup_time";
private static final String IS_DISMISSED = "_is_dismissed";
// Default dismiss control for smart suggestions.
- private static final String DEFAULT_SMART_DISMISS_CONTROL = "0,10";
+ private static final String DEFAULT_SMART_DISMISS_CONTROL = "0";
private final Context mContext;
private final List<SuggestionCategory> mSuggestionList;
private final ArrayMap<Pair<String, String>, Tile> mAddCache = new ArrayMap<>();
private final SharedPreferences mSharedPrefs;
- private final String mSmartDismissControl;
-
+ private final String mDefaultDismissControl;
public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml,
- String smartDismissControl) {
+ String defaultDismissControl) {
this(
context,
sharedPrefs,
(List<SuggestionCategory>) new SuggestionOrderInflater(context).parse(orderXml),
- smartDismissControl);
+ defaultDismissControl);
}
public SuggestionParser(Context context, SharedPreferences sharedPrefs, int orderXml) {
@@ -124,11 +121,11 @@
Context context,
SharedPreferences sharedPrefs,
List<SuggestionCategory> suggestionList,
- String smartDismissControl) {
+ String defaultDismissControl) {
mContext = context;
mSuggestionList = suggestionList;
mSharedPrefs = sharedPrefs;
- mSmartDismissControl = smartDismissControl;
+ mDefaultDismissControl = defaultDismissControl;
}
public SuggestionList getSuggestions(boolean isSmartSuggestionEnabled) {
@@ -161,25 +158,16 @@
return suggestionList;
}
- public boolean dismissSuggestion(Tile suggestion) {
- return dismissSuggestion(suggestion, false);
- }
-
/**
* Dismisses a suggestion, returns true if the suggestion has no more dismisses left and should
* be disabled.
*/
- public boolean dismissSuggestion(Tile suggestion, boolean isSmartSuggestionEnabled) {
- String keyBase = suggestion.intent.getComponent().flattenToShortString();
- int index = mSharedPrefs.getInt(keyBase + DISMISS_INDEX, 0);
- String dismissControl = getDismissControl(suggestion, isSmartSuggestionEnabled);
- if (dismissControl == null || parseDismissString(dismissControl).length == index) {
- return true;
- }
+ public boolean dismissSuggestion(Tile suggestion) {
+ final String keyBase = suggestion.intent.getComponent().flattenToShortString();
mSharedPrefs.edit()
.putBoolean(keyBase + IS_DISMISSED, true)
.commit();
- return false;
+ return true;
}
@VisibleForTesting
@@ -357,32 +345,29 @@
@VisibleForTesting
boolean isDismissed(Tile suggestion, boolean isSmartSuggestionEnabled) {
String dismissControl = getDismissControl(suggestion, isSmartSuggestionEnabled);
- if (dismissControl == null) {
- return false;
- }
String keyBase = suggestion.intent.getComponent().flattenToShortString();
if (!mSharedPrefs.contains(keyBase + SETUP_TIME)) {
mSharedPrefs.edit()
.putLong(keyBase + SETUP_TIME, System.currentTimeMillis())
.commit();
}
- // Default to dismissed, so that we can have suggestions that only first appear after
- // some number of days.
- if (!mSharedPrefs.getBoolean(keyBase + IS_DISMISSED, true)) {
- return false;
- }
- int index = mSharedPrefs.getInt(keyBase + DISMISS_INDEX, 0);
- int[] dismissRules = parseDismissString(dismissControl);
- if (dismissRules.length <= index) {
+ // Check if it's already manually dismissed
+ final boolean isDismissed = mSharedPrefs.getBoolean(keyBase + IS_DISMISSED, false);
+ if (isDismissed) {
return true;
}
- int currentDismiss = dismissRules[index];
- long time = getEndTime(mSharedPrefs.getLong(keyBase + SETUP_TIME, 0), currentDismiss);
- if (System.currentTimeMillis() >= time) {
+ if (dismissControl == null) {
+ return false;
+ }
+ // Parse when suggestion should first appear. return true to artificially hide suggestion
+ // before then.
+ int firstAppearDay = parseDismissString(dismissControl);
+ long firstAppearDayInMs = getEndTime(mSharedPrefs.getLong(keyBase + SETUP_TIME, 0),
+ firstAppearDay);
+ if (System.currentTimeMillis() >= firstAppearDayInMs) {
// Dismiss timeout has passed, undismiss it.
mSharedPrefs.edit()
.putBoolean(keyBase + IS_DISMISSED, false)
- .putInt(keyBase + DISMISS_INDEX, index + 1)
.commit();
return false;
}
@@ -394,18 +379,18 @@
return startTime + days;
}
- private int[] parseDismissString(String dismissControl) {
- String[] dismissStrs = dismissControl.split(",");
- int[] dismisses = new int[dismissStrs.length];
- for (int i = 0; i < dismissStrs.length; i++) {
- dismisses[i] = Integer.parseInt(dismissStrs[i]);
- }
- return dismisses;
+ /**
+ * Parse the first int from a string formatted as "0,1,2..."
+ * The value means suggestion should first appear on Day X.
+ */
+ private int parseDismissString(String dismissControl) {
+ final String[] dismissStrs = dismissControl.split(",");
+ return Integer.parseInt(dismissStrs[0]);
}
private String getDismissControl(Tile suggestion, boolean isSmartSuggestionEnabled) {
if (isSmartSuggestionEnabled) {
- return mSmartDismissControl;
+ return mDefaultDismissControl;
} else {
return suggestion.metaData.getString(META_DATA_DISMISS_CONTROL);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
index 8391136..f6404a2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/suggestions/SuggestionParserTest.java
@@ -83,7 +83,7 @@
mSuggestionParser = new SuggestionParser(mContext, mPrefs,
Arrays.asList(mMultipleCategory, mExclusiveCategory, mExpiredExclusiveCategory),
- "0,0");
+ "0");
ResolveInfo info1 = TileUtilsTest.newInfo(true, null);
info1.activityInfo.packageName = "pkg";
@@ -109,17 +109,12 @@
}
@Test
- public void testDismissSuggestion_withoutSmartSuggestion() {
- assertThat(mSuggestionParser.dismissSuggestion(mSuggestion, false)).isTrue();
+ public void dismissSuggestion_shouldDismiss() {
+ assertThat(mSuggestionParser.dismissSuggestion(mSuggestion)).isTrue();
}
@Test
- public void testDismissSuggestion_withSmartSuggestion() {
- assertThat(mSuggestionParser.dismissSuggestion(mSuggestion, true)).isFalse();
- }
-
- @Test
- public void testGetSuggestions_withoutSmartSuggestions() {
+ public void testGetSuggestions_withoutSmartSuggestions_shouldDismiss() {
readAndDismissSuggestion(false);
mSuggestionParser.readSuggestions(mMultipleCategory, mSuggestionsAfterDismiss, false);
assertThat(mSuggestionsBeforeDismiss).hasSize(2);
@@ -128,11 +123,10 @@
}
@Test
- public void testGetSuggestions_withSmartSuggestions() {
+ public void testGetSuggestions_withSmartSuggestions_shouldDismiss() {
readAndDismissSuggestion(true);
assertThat(mSuggestionsBeforeDismiss).hasSize(2);
- assertThat(mSuggestionsAfterDismiss).hasSize(2);
- assertThat(mSuggestionsBeforeDismiss).isEqualTo(mSuggestionsAfterDismiss);
+ assertThat(mSuggestionsAfterDismiss).hasSize(1);
}
@Test
@@ -191,19 +185,15 @@
}
@Test
- public void isSuggestionDismissed_mismatchRule_shouldDismiss() {
+ public void isSuggestionDismissed_dismissedSuggestion_shouldReturnTrue() {
final Tile suggestion = new Tile();
suggestion.metaData = new Bundle();
suggestion.metaData.putString(SuggestionParser.META_DATA_DISMISS_CONTROL, "1,2,3");
suggestion.intent = new Intent().setComponent(new ComponentName("pkg", "cls"));
// Dismiss suggestion when smart suggestion is not enabled.
- mSuggestionParser.dismissSuggestion(suggestion, false /* isSmartSuggestionEnabled */);
- final String suggestionKey = suggestion.intent.getComponent().flattenToShortString();
- // And point to last rule in dismiss control
- mPrefs.edit().putInt(suggestionKey + SuggestionParser.DISMISS_INDEX, 2).apply();
+ mSuggestionParser.dismissSuggestion(suggestion);
- // Turn on smart suggestion, and check if suggestion is enabled.
assertThat(mSuggestionParser.isDismissed(suggestion, true /* isSmartSuggestionEnabled */))
.isTrue();
}
@@ -215,7 +205,7 @@
mMultipleCategory, mSuggestionsBeforeDismiss, isSmartSuggestionEnabled);
final Tile suggestion = mSuggestionsBeforeDismiss.get(0);
- if (mSuggestionParser.dismissSuggestion(suggestion, isSmartSuggestionEnabled)) {
+ if (mSuggestionParser.dismissSuggestion(suggestion)) {
RuntimeEnvironment.getRobolectricPackageManager().removeResolveInfosForIntent(
new Intent(Intent.ACTION_MAIN).addCategory(mMultipleCategory.category),
suggestion.intent.getComponent().getPackageName());
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index d47ca1c..cb4becc 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1448,52 +1448,23 @@
// rebooted in the middle of an operation that was removing something from
// this log, we sanity-check its contents here and reconstruct it.
mEverStored = new File(mBaseStateDir, "processed");
- File tempProcessedFile = new File(mBaseStateDir, "processed.new");
-
- // If we were in the middle of removing something from the ever-backed-up
- // file, there might be a transient "processed.new" file still present.
- // Ignore it -- we'll validate "processed" against the current package set.
- if (tempProcessedFile.exists()) {
- tempProcessedFile.delete();
- }
// If there are previous contents, parse them out then start a new
// file to continue the recordkeeping.
if (mEverStored.exists()) {
- DataOutputStream temp = null;
- DataInputStream in = null;
-
- try {
- temp = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(
- tempProcessedFile)));
- in = new DataInputStream(new BufferedInputStream(new FileInputStream(mEverStored)));
+ try (DataInputStream in = new DataInputStream(
+ new BufferedInputStream(new FileInputStream(mEverStored)))) {
// Loop until we hit EOF
while (true) {
String pkg = in.readUTF();
- try {
- // is this package still present?
- mPackageManager.getPackageInfo(pkg, 0);
- // if we get here then yes it is; remember it
- mEverStoredApps.add(pkg);
- temp.writeUTF(pkg);
- if (MORE_DEBUG) Slog.v(TAG, " + " + pkg);
- } catch (NameNotFoundException e) {
- // nope, this package was uninstalled; don't include it
- if (MORE_DEBUG) Slog.v(TAG, " - " + pkg);
- }
+ mEverStoredApps.add(pkg);
+ if (MORE_DEBUG) Slog.v(TAG, " + " + pkg);
}
} catch (EOFException e) {
- // Once we've rewritten the backup history log, atomically replace the
- // old one with the new one then reopen the file for continuing use.
- if (!tempProcessedFile.renameTo(mEverStored)) {
- Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
- }
+ // Done
} catch (IOException e) {
Slog.e(TAG, "Error in processed file", e);
- } finally {
- try { if (temp != null) temp.close(); } catch (IOException e) {}
- try { if (in != null) in.close(); } catch (IOException e) {}
}
}
@@ -2240,44 +2211,6 @@
}
}
- // Remove our awareness of having ever backed up the given package
- void removeEverBackedUp(String packageName) {
- if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName);
- if (MORE_DEBUG) Slog.v(TAG, "New set:");
-
- synchronized (mEverStoredApps) {
- // Rewrite the file and rename to overwrite. If we reboot in the middle,
- // we'll recognize on initialization time that the package no longer
- // exists and fix it up then.
- File tempKnownFile = new File(mBaseStateDir, "processed.new");
- RandomAccessFile known = null;
- try {
- known = new RandomAccessFile(tempKnownFile, "rws");
- mEverStoredApps.remove(packageName);
- for (String s : mEverStoredApps) {
- known.writeUTF(s);
- if (MORE_DEBUG) Slog.v(TAG, " " + s);
- }
- known.close();
- known = null;
- if (!tempKnownFile.renameTo(mEverStored)) {
- throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
- }
- } catch (IOException e) {
- // Bad: we couldn't create the new copy. For safety's sake we
- // abandon the whole process and remove all what's-backed-up
- // state entirely, meaning we'll force a backup pass for every
- // participant on the next boot or [re]install.
- Slog.w(TAG, "Error rewriting " + mEverStored, e);
- mEverStoredApps.clear();
- tempKnownFile.delete();
- mEverStored.delete();
- } finally {
- try { if (known != null) known.close(); } catch (IOException e) {}
- }
- }
- }
-
// Persistently record the current and ancestral backup tokens as well
// as the set of packages with data [supposedly] available in the
// ancestral dataset.
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index 13e3ae6..c297010 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -370,7 +370,7 @@
private void unbindServiceLocked() {
for (SpellCheckerBindGroup scbg : mSpellCheckerBindGroups.values()) {
- scbg.removeAll();
+ scbg.removeAllLocked();
}
mSpellCheckerBindGroups.clear();
}
@@ -788,29 +788,41 @@
mListeners = new InternalDeathRecipients(this);
}
- public void onServiceConnected(ISpellCheckerService spellChecker) {
+ public void onServiceConnectedLocked(ISpellCheckerService spellChecker) {
if (DBG) {
- Slog.d(TAG, "onServiceConnected");
+ Slog.d(TAG, "onServiceConnectedLocked");
}
- synchronized (mLock) {
- mSpellChecker = spellChecker;
- mConnected = true;
- // Dispatch pending getISpellCheckerSession requests.
- mPendingSessionRequests.forEach(this::getISpellCheckerSessionLocked);
- mPendingSessionRequests.clear();
+ if (mUnbindCalled) {
+ return;
}
+ mSpellChecker = spellChecker;
+ mConnected = true;
+ // Dispatch pending getISpellCheckerSession requests.
+ try {
+ final int size = mPendingSessionRequests.size();
+ for (int i = 0; i < size; ++i) {
+ final SessionRequest request = mPendingSessionRequests.get(i);
+ mSpellChecker.getISpellCheckerSession(
+ request.mLocale, request.mScListener, request.mBundle,
+ new ISpellCheckerServiceCallbackBinder(this, request));
+ mOnGoingSessionRequests.add(request);
+ }
+ mPendingSessionRequests.clear();
+ } catch(RemoteException e) {
+ // The target spell checker service is not available. Better to reset the state.
+ removeAllLocked();
+ }
+ cleanLocked();
}
- public void onServiceDisconnected() {
+ public void onServiceDisconnectedLocked() {
if (DBG) {
- Slog.d(TAG, "onServiceDisconnected");
+ Slog.d(TAG, "onServiceDisconnectedLocked");
}
- synchronized (mLock) {
- mSpellChecker = null;
- mConnected = false;
- }
+ mSpellChecker = null;
+ mConnected = false;
}
public void removeListener(ISpellCheckerSessionListener listener) {
@@ -853,17 +865,15 @@
mUnbindCalled = true;
}
- public void removeAll() {
+ public void removeAllLocked() {
Slog.e(TAG, "Remove the spell checker bind unexpectedly.");
- synchronized (mLock) {
- final int size = mListeners.getRegisteredCallbackCount();
- for (int i = size - 1; i >= 0; --i) {
- mListeners.unregister(mListeners.getRegisteredCallbackItem(i));
- }
- mPendingSessionRequests.clear();
- mOnGoingSessionRequests.clear();
- cleanLocked();
+ final int size = mListeners.getRegisteredCallbackCount();
+ for (int i = size - 1; i >= 0; --i) {
+ mListeners.unregister(mListeners.getRegisteredCallbackItem(i));
}
+ mPendingSessionRequests.clear();
+ mOnGoingSessionRequests.clear();
+ cleanLocked();
}
public void getISpellCheckerSessionOrQueueLocked(@NonNull SessionRequest request) {
@@ -874,13 +884,6 @@
mPendingSessionRequests.add(request);
return;
}
- getISpellCheckerSessionLocked(request);
- }
-
- private void getISpellCheckerSessionLocked(@NonNull SessionRequest request) {
- if (mUnbindCalled) {
- return;
- }
try {
mSpellChecker.getISpellCheckerSession(
request.mLocale, request.mScListener, request.mBundle,
@@ -888,7 +891,7 @@
mOnGoingSessionRequests.add(request);
} catch(RemoteException e) {
// The target spell checker service is not available. Better to reset the state.
- removeAll();
+ removeAllLocked();
}
cleanLocked();
}
@@ -930,13 +933,13 @@
private void onServiceConnectedInnerLocked(ComponentName name, IBinder service) {
if (DBG) {
- Slog.w(TAG, "onServiceConnected: " + name);
+ Slog.w(TAG, "onServiceConnectedInnerLocked: " + name);
}
final ISpellCheckerService spellChecker =
ISpellCheckerService.Stub.asInterface(service);
final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId);
if (group != null && this == group.mInternalConnection) {
- group.onServiceConnected(spellChecker);
+ group.onServiceConnectedLocked(spellChecker);
}
}
@@ -949,11 +952,11 @@
private void onServiceDisconnectedInnerLocked(ComponentName name) {
if (DBG) {
- Slog.w(TAG, "onServiceDisconnected: " + name);
+ Slog.w(TAG, "onServiceDisconnectedInnerLocked: " + name);
}
final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId);
if (group != null && this == group.mInternalConnection) {
- group.onServiceDisconnected();
+ group.onServiceDisconnectedLocked();
}
}
}
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index e56b09d..74c4826 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -679,102 +679,111 @@
}
writeTaskIdsFiles();
- // If mNextWriteTime, then don't delay between each call to saveToXml().
- final WriteQueueItem item;
- synchronized (TaskPersister.this) {
- if (mNextWriteTime != FLUSH_QUEUE) {
- // The next write we don't have to wait so long.
- mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
- if (DEBUG) Slog.d(TAG, "Next write time may be in " +
- INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
- }
+ processNextItem();
+ }
+ }
- while (mWriteQueue.isEmpty()) {
- if (mNextWriteTime != 0) {
- mNextWriteTime = 0; // idle.
- TaskPersister.this.notifyAll(); // wake up flush() if needed.
- }
- try {
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
- TaskPersister.this.wait();
- } catch (InterruptedException e) {
- }
- // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
- // from now.
- }
- item = mWriteQueue.remove(0);
+ private void processNextItem() {
+ // This part is extracted into a method so that the GC can clearly see the end of the
+ // scope of the variable 'item'. If this part was in the loop above, the last item
+ // it processed would always "leak".
+ // See https://b.corp.google.com/issues/64438652#comment7
- long now = SystemClock.uptimeMillis();
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
- mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
- while (now < mNextWriteTime) {
- try {
- if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
- (mNextWriteTime - now));
- TaskPersister.this.wait(mNextWriteTime - now);
- } catch (InterruptedException e) {
- }
- now = SystemClock.uptimeMillis();
- }
-
- // Got something to do.
+ // If mNextWriteTime, then don't delay between each call to saveToXml().
+ final WriteQueueItem item;
+ synchronized (TaskPersister.this) {
+ if (mNextWriteTime != FLUSH_QUEUE) {
+ // The next write we don't have to wait so long.
+ mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
+ if (DEBUG) Slog.d(TAG, "Next write time may be in " +
+ INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
}
- if (item instanceof ImageWriteQueueItem) {
- ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
- final String filePath = imageWriteQueueItem.mFilePath;
- if (!createParentDirectory(filePath)) {
- Slog.e(TAG, "Error while creating images directory for file: " + filePath);
- continue;
+ while (mWriteQueue.isEmpty()) {
+ if (mNextWriteTime != 0) {
+ mNextWriteTime = 0; // idle.
+ TaskPersister.this.notifyAll(); // wake up flush() if needed.
}
- final Bitmap bitmap = imageWriteQueueItem.mImage;
- if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filePath);
- FileOutputStream imageFile = null;
try {
- imageFile = new FileOutputStream(new File(filePath));
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
- } catch (Exception e) {
- Slog.e(TAG, "saveImage: unable to save " + filePath, e);
- } finally {
- IoUtils.closeQuietly(imageFile);
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
+ TaskPersister.this.wait();
+ } catch (InterruptedException e) {
}
- } else if (item instanceof TaskWriteQueueItem) {
- // Write out one task.
- StringWriter stringWriter = null;
- TaskRecord task = ((TaskWriteQueueItem) item).mTask;
- if (DEBUG) Slog.d(TAG, "Writing task=" + task);
- synchronized (mService) {
- if (task.inRecents) {
- // Still there.
- try {
- if (DEBUG) Slog.d(TAG, "Saving task=" + task);
- stringWriter = saveToXml(task);
- } catch (IOException e) {
- } catch (XmlPullParserException e) {
- }
- }
+ // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
+ // from now.
+ }
+ item = mWriteQueue.remove(0);
+
+ long now = SystemClock.uptimeMillis();
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
+ mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
+ while (now < mNextWriteTime) {
+ try {
+ if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
+ (mNextWriteTime - now));
+ TaskPersister.this.wait(mNextWriteTime - now);
+ } catch (InterruptedException e) {
}
- if (stringWriter != null) {
- // Write out xml file while not holding mService lock.
- FileOutputStream file = null;
- AtomicFile atomicFile = null;
+ now = SystemClock.uptimeMillis();
+ }
+
+ // Got something to do.
+ }
+
+ if (item instanceof ImageWriteQueueItem) {
+ ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
+ final String filePath = imageWriteQueueItem.mFilePath;
+ if (!createParentDirectory(filePath)) {
+ Slog.e(TAG, "Error while creating images directory for file: " + filePath);
+ return;
+ }
+ final Bitmap bitmap = imageWriteQueueItem.mImage;
+ if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filePath);
+ FileOutputStream imageFile = null;
+ try {
+ imageFile = new FileOutputStream(new File(filePath));
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
+ } catch (Exception e) {
+ Slog.e(TAG, "saveImage: unable to save " + filePath, e);
+ } finally {
+ IoUtils.closeQuietly(imageFile);
+ }
+ } else if (item instanceof TaskWriteQueueItem) {
+ // Write out one task.
+ StringWriter stringWriter = null;
+ TaskRecord task = ((TaskWriteQueueItem) item).mTask;
+ if (DEBUG) Slog.d(TAG, "Writing task=" + task);
+ synchronized (mService) {
+ if (task.inRecents) {
+ // Still there.
try {
- atomicFile = new AtomicFile(new File(
- getUserTasksDir(task.userId),
- String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
- file = atomicFile.startWrite();
- file.write(stringWriter.toString().getBytes());
- file.write('\n');
- atomicFile.finishWrite(file);
+ if (DEBUG) Slog.d(TAG, "Saving task=" + task);
+ stringWriter = saveToXml(task);
} catch (IOException e) {
- if (file != null) {
- atomicFile.failWrite(file);
- }
- Slog.e(TAG,
- "Unable to open " + atomicFile + " for persisting. " + e);
+ } catch (XmlPullParserException e) {
}
}
}
+ if (stringWriter != null) {
+ // Write out xml file while not holding mService lock.
+ FileOutputStream file = null;
+ AtomicFile atomicFile = null;
+ try {
+ atomicFile = new AtomicFile(new File(
+ getUserTasksDir(task.userId),
+ String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
+ file = atomicFile.startWrite();
+ file.write(stringWriter.toString().getBytes());
+ file.write('\n');
+ atomicFile.finishWrite(file);
+ } catch (IOException e) {
+ if (file != null) {
+ atomicFile.failWrite(file);
+ }
+ Slog.e(TAG,
+ "Unable to open " + atomicFile + " for persisting. " + e);
+ }
+ }
}
}
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index a105c84..2f166e9 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1223,6 +1223,7 @@
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.setUserAuthenticationValidityDurationSeconds(30)
+ .setCriticalToDeviceEncryption(true)
.build());
// Key imported, obtain a reference to it.
SecretKey keyStoreEncryptionKey = (SecretKey) keyStore.getKey(
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 241d76f..da6e26e 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -318,7 +318,7 @@
optimizer.performDexOpt(pkg, libraryDependencies,
null /* ISAs */,
null /* CompilerStats.PackageStats */,
- mPackageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName),
+ mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(pkg.packageName),
new DexoptOptions(pkg.packageName, compilationReason,
DexoptOptions.DEXOPT_BOOT_COMPLETE));
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index dabd35c..b72abc7 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -31,6 +31,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.DexoptUtils;
import com.android.server.pm.dex.PackageDexUsage;
@@ -112,7 +113,7 @@
*/
int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
String[] instructionSets, CompilerStats.PackageStats packageStats,
- boolean isUsedByOtherApps, DexoptOptions options) {
+ PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
if (!canOptimizePackage(pkg)) {
return DEX_OPT_SKIPPED;
}
@@ -120,7 +121,7 @@
final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
try {
return performDexOptLI(pkg, sharedLibraries, instructionSets,
- packageStats, isUsedByOtherApps, options);
+ packageStats, packageUseInfo, options);
} finally {
releaseWakeLockLI(acquireTime);
}
@@ -134,21 +135,13 @@
@GuardedBy("mInstallLock")
private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
- boolean isUsedByOtherApps, DexoptOptions options) {
+ PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
final List<String> paths = pkg.getAllCodePaths();
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
- final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
- options.getCompilerFilter(), isUsedByOtherApps);
- final boolean profileUpdated = options.isCheckForProfileUpdates() &&
- isProfileUpdated(pkg, sharedGid, compilerFilter);
-
- // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
- final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete());
-
// Get the class loader context dependencies.
// For each code path in the package, this array contains the class loader context that
// needs to be passed to dexopt in order to ensure correct optimizations.
@@ -172,6 +165,17 @@
}
}
+ final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
+ || packageUseInfo.isUsedByOtherApps(path);
+ final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
+ options.getCompilerFilter(), isUsedByOtherApps);
+ final boolean profileUpdated = options.isCheckForProfileUpdates() &&
+ isProfileUpdated(pkg, sharedGid, compilerFilter);
+
+ // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
+ // flags.
+ final int dexoptFlags = getDexFlags(pkg, compilerFilter, options.isBootComplete());
+
for (String dexCodeIsa : dexCodeInstructionSets) {
int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b66f009..c3ec94e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -449,6 +449,7 @@
static final int SCAN_FIRST_BOOT_OR_UPGRADE = 1<<16;
static final int SCAN_AS_INSTANT_APP = 1<<17;
static final int SCAN_AS_FULL_APP = 1<<18;
+ static final int SCAN_AS_VIRTUAL_PRELOAD = 1<<19;
/** Should not be with the scan flags */
static final int FLAGS_REMOVE_CHATTY = 1<<31;
@@ -9404,6 +9405,9 @@
if (ps != null && ps.getInstantApp(userId)) {
scanFlags |= SCAN_AS_INSTANT_APP;
}
+ if (ps != null && ps.getVirtulalPreload(userId)) {
+ scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
+ }
// Note that we invoke the following method only if we are about to unpack an application
PackageParser.Package scannedPkg = scanPackageLI(pkg, policyFlags, scanFlags
@@ -9887,17 +9891,19 @@
Collection<PackageParser.Package> deps = findSharedNonSystemLibraries(p);
final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
if (!deps.isEmpty()) {
+ DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
+ options.getCompilerFilter(), options.getSplitName(),
+ options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
for (PackageParser.Package depPackage : deps) {
// TODO: Analyze and investigate if we (should) profile libraries.
pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets,
getOrCreateCompilerPackageStats(depPackage),
- true /* isUsedByOtherApps */,
- options);
+ mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions);
}
}
return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets,
getOrCreateCompilerPackageStats(p),
- mDexManager.isUsedByOtherApps(p.packageName), options);
+ mDexManager.getPackageUseInfoOrDefault(p.packageName), options);
}
/**
@@ -10023,7 +10029,7 @@
public void shutdown() {
mPackageUsage.writeNow(mPackages);
mCompilerStats.writeNow();
- mDexManager.savePackageDexUsageNow();
+ mDexManager.writePackageDexUsageNow();
}
@Override
@@ -10654,15 +10660,17 @@
final String parentPackageName = (pkg.parentPackage != null)
? pkg.parentPackage.packageName : null;
final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+ final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
// REMOVE SharedUserSetting from method; update in a separate call
pkgSetting = Settings.createNewSetting(pkg.packageName, origPackage,
disabledPkgSetting, realName, suid, destCodeFile, destResourceFile,
pkg.applicationInfo.nativeLibraryRootDir, pkg.applicationInfo.primaryCpuAbi,
pkg.applicationInfo.secondaryCpuAbi, pkg.mVersionCode,
pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags, user,
- true /*allowInstall*/, instantApp, parentPackageName,
- pkg.getChildPackageNames(), UserManagerService.getInstance(),
- usesStaticLibraries, pkg.usesStaticLibrariesVersions);
+ true /*allowInstall*/, instantApp, virtualPreload,
+ parentPackageName, pkg.getChildPackageNames(),
+ UserManagerService.getInstance(), usesStaticLibraries,
+ pkg.usesStaticLibrariesVersions);
// SIDE EFFECTS; updates system state; move elsewhere
if (origPackage != null) {
mSettings.addRenamedPackageLPw(pkg.packageName, origPackage.name);
@@ -18178,6 +18186,8 @@
final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
final boolean fullApp = ((installFlags & PackageManager.INSTALL_FULL_APP) != 0);
final boolean forceSdk = ((installFlags & PackageManager.INSTALL_FORCE_SDK) != 0);
+ final boolean virtualPreload =
+ ((installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
boolean replace = false;
int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
if (args.move != null) {
@@ -18193,6 +18203,9 @@
if (fullApp) {
scanFlags |= SCAN_AS_FULL_APP;
}
+ if (virtualPreload) {
+ scanFlags |= SCAN_AS_VIRTUAL_PRELOAD;
+ }
// Result object to be returned
res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
@@ -18572,7 +18585,7 @@
mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
null /* instructionSets */,
getOrCreateCompilerPackageStats(pkg),
- mDexManager.isUsedByOtherApps(pkg.packageName),
+ mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
dexoptOptions);
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -19983,6 +19996,7 @@
false /*hidden*/,
false /*suspended*/,
false /*instantApp*/,
+ false /*virtualPreload*/,
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
@@ -25409,8 +25423,8 @@
if (ps == null) {
continue;
}
- PackageDexUsage.PackageUseInfo packageUseInfo = getDexManager().getPackageUseInfo(
- pkg.packageName);
+ PackageDexUsage.PackageUseInfo packageUseInfo =
+ getDexManager().getPackageUseInfoOrDefault(pkg.packageName);
if (PackageManagerServiceUtils
.isUnusedSinceTimeInMillis(ps.firstInstallTime, currentTimeInMillis,
downgradeTimeThresholdMillis, packageUseInfo,
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index a7031c9..25fef0a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -25,7 +25,6 @@
import android.annotation.NonNull;
import android.app.AppGlobals;
import android.content.Intent;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageParser;
import android.content.pm.ResolveInfo;
import android.os.Build;
@@ -137,9 +136,11 @@
sortTemp, packageManagerService);
// Give priority to apps used by other apps.
+ DexManager dexManager = packageManagerService.getDexManager();
applyPackageFilter((pkg) ->
- packageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName), result,
- remainingPkgs, sortTemp, packageManagerService);
+ dexManager.getPackageUseInfoOrDefault(pkg.packageName)
+ .isAnyCodePathUsedByOtherApps(),
+ result, remainingPkgs, sortTemp, packageManagerService);
// Filter out packages that aren't recently used, add all remaining apps.
// TODO: add a property to control this?
@@ -209,12 +210,10 @@
// If the app was active in background during the threshold period and was used
// by other packages.
- // If packageUseInfo is null, it can be said that the package was not used by other
- // packages.
boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis
- latestPackageUseTimeInMillis)
< thresholdTimeinMillis)
- && (packageUseInfo != null && packageUseInfo.isUsedByOtherApps());
+ && packageUseInfo.isAnyCodePathUsedByOtherApps();
return !isActiveInBackgroundAndUsedByOtherPackages;
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index f685127..d3ca1fd 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -420,11 +420,19 @@
modifyUserState(userId).instantApp = instantApp;
}
+ boolean getVirtulalPreload(int userId) {
+ return readUserState(userId).virtualPreload;
+ }
+
+ void setVirtualPreload(boolean virtualPreload, int userId) {
+ modifyUserState(userId).virtualPreload = virtualPreload;
+ }
+
void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
boolean notLaunched, boolean hidden, boolean suspended, boolean instantApp,
- String lastDisableAppCaller, ArraySet<String> enabledComponents,
- ArraySet<String> disabledComponents, int domainVerifState,
- int linkGeneration, int installReason) {
+ boolean virtualPreload, String lastDisableAppCaller,
+ ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
+ int domainVerifState, int linkGeneration, int installReason) {
PackageUserState state = modifyUserState(userId);
state.ceDataInode = ceDataInode;
state.enabled = enabled;
@@ -440,6 +448,7 @@
state.appLinkGeneration = linkGeneration;
state.installReason = installReason;
state.instantApp = instantApp;
+ state.virtualPreload = virtualPreload;
}
ArraySet<String> getEnabledComponents(int userId) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 45d0c58..d99408f 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -226,6 +226,7 @@
private static final String ATTR_APP_LINK_GENERATION = "app-link-generation";
private static final String ATTR_INSTALL_REASON = "install-reason";
private static final String ATTR_INSTANT_APP = "instant-app";
+ private static final String ATTR_VIRTUAL_PRELOAD = "virtual-preload";
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_FINGERPRINT = "fingerprint";
@@ -697,8 +698,9 @@
PackageSetting disabledPkg, String realPkgName, SharedUserSetting sharedUser,
File codePath, File resourcePath, String legacyNativeLibraryPath, String primaryCpuAbi,
String secondaryCpuAbi, int versionCode, int pkgFlags, int pkgPrivateFlags,
- UserHandle installUser, boolean allowInstall, boolean instantApp, String parentPkgName,
- List<String> childPkgNames, UserManagerService userManager,
+ UserHandle installUser, boolean allowInstall, boolean instantApp,
+ boolean virtualPreload, String parentPkgName, List<String> childPkgNames,
+ UserManagerService userManager,
String[] usesStaticLibraries, int[] usesStaticLibrariesVersions) {
final PackageSetting pkgSetting;
if (originalPkg != null) {
@@ -760,6 +762,7 @@
false /*hidden*/,
false /*suspended*/,
instantApp,
+ virtualPreload,
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
@@ -1693,6 +1696,7 @@
false /*hidden*/,
false /*suspended*/,
false /*instantApp*/,
+ false /*virtualPreload*/,
null /*lastDisableAppCaller*/,
null /*enabledComponents*/,
null /*disabledComponents*/,
@@ -1766,6 +1770,8 @@
ATTR_BLOCK_UNINSTALL, false);
final boolean instantApp = XmlUtils.readBooleanAttribute(parser,
ATTR_INSTANT_APP, false);
+ final boolean virtualPreload = XmlUtils.readBooleanAttribute(parser,
+ ATTR_VIRTUAL_PRELOAD, false);
final int enabled = XmlUtils.readIntAttribute(parser, ATTR_ENABLED,
COMPONENT_ENABLED_STATE_DEFAULT);
final String enabledCaller = parser.getAttributeValue(null,
@@ -1805,8 +1811,8 @@
setBlockUninstallLPw(userId, name, true);
}
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
- hidden, suspended, instantApp, enabledCaller, enabledComponents,
- disabledComponents, verifState, linkGeneration,
+ hidden, suspended, instantApp, virtualPreload, enabledCaller,
+ enabledComponents, disabledComponents, verifState, linkGeneration,
installReason);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
@@ -2117,6 +2123,9 @@
if (ustate.instantApp) {
serializer.attribute(null, ATTR_INSTANT_APP, "true");
}
+ if (ustate.virtualPreload) {
+ serializer.attribute(null, ATTR_VIRTUAL_PRELOAD, "true");
+ }
if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
serializer.attribute(null, ATTR_ENABLED,
Integer.toString(ustate.enabled));
@@ -4603,6 +4612,7 @@
pw.print(ps.getStopped(user.id) ? "S" : "s");
pw.print(ps.getNotLaunched(user.id) ? "l" : "L");
pw.print(ps.getInstantApp(user.id) ? "IA" : "ia");
+ pw.print(ps.getVirtulalPreload(user.id) ? "VPI" : "vpi");
pw.print(",");
pw.print(ps.getEnabled(user.id));
String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
@@ -4858,6 +4868,8 @@
pw.print(ps.getEnabled(user.id));
pw.print(" instant=");
pw.println(ps.getInstantApp(user.id));
+ pw.print(" virtual=");
+ pw.println(ps.getVirtulalPreload(user.id));
String[] overlayPaths = ps.getOverlayPaths(user.id);
if (overlayPaths != null && overlayPaths.length > 0) {
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 947e01c4..6274754 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -36,6 +36,8 @@
import java.io.File;
import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.HashMap;
import java.util.HashSet;
@@ -81,6 +83,19 @@
private static int DEX_SEARCH_FOUND_SPLIT = 2; // dex file is a split apk
private static int DEX_SEARCH_FOUND_SECONDARY = 3; // dex file is a secondary dex
+ /**
+ * We do not record packages that have no secondary dex files or that are not used by other
+ * apps. This is an optimization to reduce the amount of data that needs to be written to
+ * disk (apps will not usually be shared so this trims quite a bit the number we record).
+ *
+ * To make this behaviour transparent to the callers which need use information on packages,
+ * DexManager will return this DEFAULT instance from
+ * {@link DexManager#getPackageUseInfoOrDefault}. It has no data about secondary dex files and
+ * is marked as not being used by other apps. This reflects the intended behaviour when we don't
+ * find the package in the underlying data file.
+ */
+ private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();
+
public DexManager(IPackageManager pms, PackageDexOptimizer pdo,
Installer installer, Object installLock) {
mPackageCodeLocationsCache = new HashMap<>();
@@ -297,6 +312,8 @@
private void loadInternal(Map<Integer, List<PackageInfo>> existingPackages) {
Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
+ Map<String, Set<String>> packageToCodePaths = new HashMap<>();
+
// Cache the code locations for the installed packages. This allows for
// faster lookups (no locks) when finding what package owns the dex file.
for (Map.Entry<Integer, List<PackageInfo>> entry : existingPackages.entrySet()) {
@@ -306,25 +323,53 @@
// Cache the code locations.
cachePackageInfo(pi, userId);
- // Cache a map from package name to the set of user ids who installed the package.
+ // Cache two maps:
+ // - from package name to the set of user ids who installed the package.
+ // - from package name to the set of code paths.
// We will use it to sync the data and remove obsolete entries from
// mPackageDexUsage.
Set<Integer> users = putIfAbsent(
packageToUsersMap, pi.packageName, new HashSet<>());
users.add(userId);
+
+ Set<String> codePaths = putIfAbsent(
+ packageToCodePaths, pi.packageName, new HashSet<>());
+ codePaths.add(pi.applicationInfo.sourceDir);
+ if (pi.applicationInfo.splitSourceDirs != null) {
+ Collections.addAll(codePaths, pi.applicationInfo.splitSourceDirs);
+ }
}
}
mPackageDexUsage.read();
- mPackageDexUsage.syncData(packageToUsersMap);
+ mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
}
/**
* Get the package dex usage for the given package name.
- * @return the package data or null if there is no data available for this package.
+ * If there is no usage info the method will return a default {@code PackageUseInfo} with
+ * no data about secondary dex files and marked as not being used by other apps.
+ *
+ * Note that no use info means the package was not used or it was used but not by other apps.
+ * Also, note that right now we might prune packages which are not used by other apps.
+ * TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
+ * to access the package use.
*/
- public PackageUseInfo getPackageUseInfo(String packageName) {
- return mPackageDexUsage.getPackageUseInfo(packageName);
+ public PackageUseInfo getPackageUseInfoOrDefault(String packageName) {
+ PackageUseInfo useInfo = mPackageDexUsage.getPackageUseInfo(packageName);
+ return useInfo == null ? DEFAULT_USE_INFO : useInfo;
+ }
+
+ /**
+ * Return whether or not the manager has usage information on the give package.
+ *
+ * Note that no use info means the package was not used or it was used but not by other apps.
+ * Also, note that right now we might prune packages which are not used by other apps.
+ * TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
+ * to access the package use.
+ */
+ /*package*/ boolean hasInfoOnPackage(String packageName) {
+ return mPackageDexUsage.getPackageUseInfo(packageName) != null;
}
/**
@@ -343,7 +388,7 @@
? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
: mPackageDexOptimizer;
String packageName = options.getPackageName();
- PackageUseInfo useInfo = getPackageUseInfo(packageName);
+ PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "No secondary dex use for package:" + packageName);
@@ -387,7 +432,7 @@
* deleted, update the internal records and delete any generated oat files.
*/
public void reconcileSecondaryDexFiles(String packageName) {
- PackageUseInfo useInfo = getPackageUseInfo(packageName);
+ PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "No secondary dex use for package:" + packageName);
@@ -519,23 +564,6 @@
}
/**
- * Return true if the profiling data collected for the given app indicate
- * that the apps's APK has been loaded by another app.
- * Note that this returns false for all apps without any collected profiling data.
- */
- public boolean isUsedByOtherApps(String packageName) {
- PackageUseInfo useInfo = getPackageUseInfo(packageName);
- if (useInfo == null) {
- // No use info, means the package was not used or it was used but not by other apps.
- // Note that right now we might prune packages which are not used by other apps.
- // TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
- // to access the package use.
- return false;
- }
- return useInfo.isUsedByOtherApps();
- }
-
- /**
* Retrieves the package which owns the given dexPath.
*/
private DexSearchResult getDexPackage(
@@ -593,9 +621,9 @@
}
/**
- * Saves the in-memory package dex usage to disk right away.
+ * Writes the in-memory package dex usage to disk right away.
*/
- public void savePackageDexUsageNow() {
+ public void writePackageDexUsageNow() {
mPackageDexUsage.writeNow();
}
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
index f57cf5e..4fa47b5 100644
--- a/services/core/java/com/android/server/pm/dex/DexoptOptions.java
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -50,6 +50,12 @@
// save disk space.
public static final int DEXOPT_DOWNGRADE = 1 << 5;
+ // When set, dexopt will compile the dex file as a shared library even if it is not actually
+ // used by other apps. This is used to force the compilation or shared libraries declared
+ // with in the manifest with ''uses-library' before we have a chance to detect they are
+ // actually shared at runtime.
+ public static final int DEXOPT_AS_SHARED_LIBRARY = 1 << 6;
+
// The name of package to optimize.
private final String mPackageName;
@@ -79,7 +85,8 @@
DEXOPT_BOOT_COMPLETE |
DEXOPT_ONLY_SECONDARY_DEX |
DEXOPT_ONLY_SHARED_DEX |
- DEXOPT_DOWNGRADE;
+ DEXOPT_DOWNGRADE |
+ DEXOPT_AS_SHARED_LIBRARY;
if ((flags & (~validityMask)) != 0) {
throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags));
}
@@ -122,7 +129,15 @@
return (mFlags & DEXOPT_DOWNGRADE) != 0;
}
+ public boolean isDexoptAsSharedLibrary() {
+ return (mFlags & DEXOPT_AS_SHARED_LIBRARY) != 0;
+ }
+
public String getSplitName() {
return mSplitName;
}
+
+ public int getFlags() {
+ return mFlags;
+ }
}
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 6ee26d3..a4a0a54 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -35,6 +35,7 @@
import java.io.StringWriter;
import java.io.Writer;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.HashMap;
@@ -53,17 +54,21 @@
public class PackageDexUsage extends AbstractStatsBase<Void> {
private final static String TAG = "PackageDexUsage";
- // The last version update: add class loader contexts for secondary dex files.
- private final static int PACKAGE_DEX_USAGE_VERSION = 3;
// We support previous version to ensure that the usage list remains valid cross OTAs.
private final static int PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1 = 1;
- // Version 2 added the list of packages that load the dex files.
+ // Version 2 added:
+ // - the list of packages that load the dex files
+ // - class loader contexts for secondary dex files
+ // - usage for all code paths (including splits)
private final static int PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2 = 2;
+ private final static int PACKAGE_DEX_USAGE_VERSION = PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2;
+
private final static String PACKAGE_DEX_USAGE_VERSION_HEADER =
"PACKAGE_MANAGER__PACKAGE_DEX_USAGE__";
private final static String SPLIT_CHAR = ",";
+ private final static String CODE_PATH_LINE_CHAR = "+";
private final static String DEX_LINE_CHAR = "#";
private final static String LOADING_PACKAGE_CHAR = "@";
@@ -130,9 +135,8 @@
// If we have a primary or a split apk, set isUsedByOtherApps.
// We do not need to record the loaderIsa or the owner because we compile
// primaries for all users and all ISAs.
- packageUseInfo.mIsUsedByOtherApps = isUsedByOtherApps;
- maybeAddLoadingPackage(owningPackageName, loadingPackageName,
- packageUseInfo.mLoadingPackages);
+ packageUseInfo.mergeCodePathUsedByOtherApps(dexPath, isUsedByOtherApps,
+ owningPackageName, loadingPackageName);
} else {
// For secondary dex files record the loaderISA and the owner. We'll need
// to know under which user to compile and for what ISA.
@@ -149,9 +153,8 @@
if (primaryOrSplit) {
// We have a possible update on the primary apk usage. Merge
// isUsedByOtherApps information and return if there was an update.
- boolean updateLoadingPackages = maybeAddLoadingPackage(owningPackageName,
- loadingPackageName, packageUseInfo.mLoadingPackages);
- return packageUseInfo.merge(isUsedByOtherApps) || updateLoadingPackages;
+ return packageUseInfo.mergeCodePathUsedByOtherApps(
+ dexPath, isUsedByOtherApps, owningPackageName, loadingPackageName);
} else {
DexUseInfo newData = new DexUseInfo(
isUsedByOtherApps, ownerUserId, classLoaderContext, loaderIsa);
@@ -230,22 +233,18 @@
*
* file_magic_version
* package_name_1
+ * +code_path1
* @ loading_package_1_1, loading_package_1_2...
+ * +code_path2
+ * @ loading_package_2_1, loading_package_2_2...
* #dex_file_path_1_1
- * @ loading_package_1_1_1, loading_package_1_1_2...
* user_1_1, used_by_other_app_1_1, user_isa_1_1_1, user_isa_1_1_2
+ * @ loading_package_1_1_1, loading_package_1_1_2...
+ * class_loader_context_1_1
* #dex_file_path_1_2
- * @ loading_package_1_2_1, loading_package_1_2_2...
* user_1_2, used_by_other_app_1_2, user_isa_1_2_1, user_isa_1_2_2
- * ...
- * package_name_2
- * @ loading_package_2_1, loading_package_2_1_2...
- * #dex_file_path_2_1
- * @ loading_package_2_1_1, loading_package_2_1_2...
- * user_2_1, used_by_other_app_2_1, user_isa_2_1_1, user_isa_2_1_2
- * #dex_file_path_2_2,
- * @ loading_package_2_2_1, loading_package_2_2_2...
- * user_2_2, used_by_other_app_2_2, user_isa_2_2_1, user_isa_2_2_2
+ * @ loading_package_1_2_1, loading_package_1_2_2...
+ * class_loader_context_1_2
* ...
*/
/* package */ void write(Writer out) {
@@ -262,27 +261,31 @@
// Write the package line.
String packageName = pEntry.getKey();
PackageUseInfo packageUseInfo = pEntry.getValue();
+ fpw.println(packageName);
- fpw.println(String.join(SPLIT_CHAR, packageName,
- writeBoolean(packageUseInfo.mIsUsedByOtherApps)));
- fpw.println(LOADING_PACKAGE_CHAR +
- String.join(SPLIT_CHAR, packageUseInfo.mLoadingPackages));
+ // Write the code paths used by other apps.
+ for (Map.Entry<String, Set<String>> codeEntry :
+ packageUseInfo.mCodePathsUsedByOtherApps.entrySet()) {
+ String codePath = codeEntry.getKey();
+ Set<String> loadingPackages = codeEntry.getValue();
+ fpw.println(CODE_PATH_LINE_CHAR + codePath);
+ fpw.println(LOADING_PACKAGE_CHAR + String.join(SPLIT_CHAR, loadingPackages));
+ }
// Write dex file lines.
for (Map.Entry<String, DexUseInfo> dEntry : packageUseInfo.mDexUseInfoMap.entrySet()) {
String dexPath = dEntry.getKey();
DexUseInfo dexUseInfo = dEntry.getValue();
fpw.println(DEX_LINE_CHAR + dexPath);
- fpw.println(LOADING_PACKAGE_CHAR +
- String.join(SPLIT_CHAR, dexUseInfo.mLoadingPackages));
- fpw.println(dexUseInfo.getClassLoaderContext());
-
fpw.print(String.join(SPLIT_CHAR, Integer.toString(dexUseInfo.mOwnerUserId),
- writeBoolean(dexUseInfo.mIsUsedByOtherApps)));
+ writeBoolean(dexUseInfo.mIsUsedByOtherApps)));
for (String isa : dexUseInfo.mLoaderIsas) {
fpw.print(SPLIT_CHAR + isa);
}
fpw.println();
+ fpw.println(LOADING_PACKAGE_CHAR
+ + String.join(SPLIT_CHAR, dexUseInfo.mLoadingPackages));
+ fpw.println(dexUseInfo.getClassLoaderContext());
}
}
fpw.flush();
@@ -324,7 +327,7 @@
}
}
- String s;
+ String line;
String currentPackage = null;
PackageUseInfo currentPackageData = null;
@@ -332,8 +335,8 @@
for (String abi : Build.SUPPORTED_ABIS) {
supportedIsas.add(VMRuntime.getInstructionSet(abi));
}
- while ((s = in.readLine()) != null) {
- if (s.startsWith(DEX_LINE_CHAR)) {
+ while ((line = in.readLine()) != null) {
+ if (line.startsWith(DEX_LINE_CHAR)) {
// This is the start of the the dex lines.
// We expect 4 lines for each dex entry:
// #dexPaths
@@ -345,25 +348,23 @@
"Malformed PackageDexUsage file. Expected package line before dex line.");
}
- // First line is the dex path.
- String dexPath = s.substring(DEX_LINE_CHAR.length());
+ // Line 1 is the dex path.
+ String dexPath = line.substring(DEX_LINE_CHAR.length());
- // In version 2 the second line contains the list of packages that loaded the file.
- List<String> loadingPackages = maybeReadLoadingPackages(in, version);
- // In version 3 the third line contains the class loader context.
- String classLoaderContext = maybeReadClassLoaderContext(in, version);
-
- // Next line is the dex data.
- s = in.readLine();
- if (s == null) {
+ // Line 2 is the dex data: (userId, isUsedByOtherApps, isa).
+ line = in.readLine();
+ if (line == null) {
throw new IllegalStateException("Could not find dexUseInfo line");
}
-
- // We expect at least 3 elements (isUsedByOtherApps, userId, isa).
- String[] elems = s.split(SPLIT_CHAR);
+ String[] elems = line.split(SPLIT_CHAR);
if (elems.length < 3) {
- throw new IllegalStateException("Invalid PackageDexUsage line: " + s);
+ throw new IllegalStateException("Invalid PackageDexUsage line: " + line);
}
+
+ // In version 2 we added the loading packages and class loader context.
+ Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
+ String classLoaderContext = maybeReadClassLoaderContext(in, version);
+
int ownerUserId = Integer.parseInt(elems[0]);
boolean isUsedByOtherApps = readBoolean(elems[1]);
DexUseInfo dexUseInfo = new DexUseInfo(isUsedByOtherApps, ownerUserId,
@@ -386,17 +387,35 @@
continue;
}
currentPackageData.mDexUseInfoMap.put(dexPath, dexUseInfo);
+ } else if (line.startsWith(CODE_PATH_LINE_CHAR)) {
+ // This is a code path used by other apps line.
+ if (version < PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
+ throw new IllegalArgumentException("Unexpected code path line when parsing " +
+ "PackageDexUseData: " + line);
+ }
+
+ // Expects 2 lines:
+ // +code_paths
+ // @loading_packages
+ String codePath = line.substring(CODE_PATH_LINE_CHAR.length());
+ Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
+ currentPackageData.mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
} else {
// This is a package line.
- // We expect it to be: `packageName,isUsedByOtherApps`.
- String[] elems = s.split(SPLIT_CHAR);
- if (elems.length != 2) {
- throw new IllegalStateException("Invalid PackageDexUsage line: " + s);
+ if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
+ currentPackage = line;
+ currentPackageData = new PackageUseInfo();
+ } else {
+ // Old version (<2)
+ // We expect it to be: `packageName,isUsedByOtherApps`.
+ String[] elems = line.split(SPLIT_CHAR);
+ if (elems.length != 2) {
+ throw new IllegalStateException("Invalid PackageDexUsage line: " + line);
+ }
+ currentPackage = elems[0];
+ currentPackageData = new PackageUseInfo();
+ currentPackageData.mUsedByOtherAppsBeforeUpgrade = readBoolean(elems[1]);
}
- currentPackage = elems[0];
- currentPackageData = new PackageUseInfo();
- currentPackageData.mIsUsedByOtherApps = readBoolean(elems[1]);
- currentPackageData.mLoadingPackages.addAll(maybeReadLoadingPackages(in, version));
data.put(currentPackage, currentPackageData);
}
}
@@ -413,7 +432,7 @@
*/
private String maybeReadClassLoaderContext(BufferedReader in, int version) throws IOException {
String context = null;
- if (version == PACKAGE_DEX_USAGE_VERSION) {
+ if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
context = in.readLine();
if (context == null) {
throw new IllegalStateException("Could not find the classLoaderContext line.");
@@ -429,7 +448,7 @@
* Reads the list of loading packages from the buffer {@code in} if
* {@code version} is at least {PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2}.
*/
- private List<String> maybeReadLoadingPackages(BufferedReader in, int version)
+ private Set<String> maybeReadLoadingPackages(BufferedReader in, int version)
throws IOException {
if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
String line = in.readLine();
@@ -438,13 +457,15 @@
}
// We expect that most of the times the list of loading packages will be empty.
if (line.length() == LOADING_PACKAGE_CHAR.length()) {
- return Collections.emptyList();
+ return Collections.emptySet();
} else {
- return Arrays.asList(
+ Set<String> result = new HashSet<>();
+ Collections.addAll(result,
line.substring(LOADING_PACKAGE_CHAR.length()).split(SPLIT_CHAR));
+ return result;
}
} else {
- return Collections.emptyList();
+ return Collections.emptySet();
}
}
@@ -458,14 +479,15 @@
}
private boolean isSupportedVersion(int version) {
- return version == PACKAGE_DEX_USAGE_VERSION ||
- version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1;
+ return version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1
+ || version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2;
}
/**
* Syncs the existing data with the set of available packages by removing obsolete entries.
*/
- public void syncData(Map<String, Set<Integer>> packageToUsersMap) {
+ /*package*/ void syncData(Map<String, Set<Integer>> packageToUsersMap,
+ Map<String, Set<String>> packageToCodePaths) {
synchronized (mPackageUseInfoMap) {
Iterator<Map.Entry<String, PackageUseInfo>> pIt =
mPackageUseInfoMap.entrySet().iterator();
@@ -489,8 +511,26 @@
dIt.remove();
}
}
- if (!packageUseInfo.mIsUsedByOtherApps
- && packageUseInfo.mDexUseInfoMap.isEmpty()) {
+
+ // Sync the code paths.
+ Set<String> codePaths = packageToCodePaths.get(packageName);
+ Iterator<Map.Entry<String, Set<String>>> codeIt =
+ packageUseInfo.mCodePathsUsedByOtherApps.entrySet().iterator();
+ while (codeIt.hasNext()) {
+ if (!codePaths.contains(codeIt.next().getKey())) {
+ codeIt.remove();
+ }
+ }
+
+ // In case the package was marked as used by other apps in a previous version
+ // propagate the flag to all the code paths.
+ // See mUsedByOtherAppsBeforeUpgrade docs on why it is important to do it.
+ if (packageUseInfo.mUsedByOtherAppsBeforeUpgrade) {
+ for (String codePath : codePaths) {
+ packageUseInfo.mergeCodePathUsedByOtherApps(codePath, true, null, null);
+ }
+ } else if (!packageUseInfo.isAnyCodePathUsedByOtherApps()
+ && packageUseInfo.mDexUseInfoMap.isEmpty()) {
// The package is not used by other apps and we removed all its dex files
// records. Remove the entire package record as well.
pIt.remove();
@@ -504,14 +544,13 @@
* Clears the {@code usesByOtherApps} marker for the package {@code packageName}.
* @return true if the package usage info was updated.
*/
- public boolean clearUsedByOtherApps(String packageName) {
+ /*package*/ boolean clearUsedByOtherApps(String packageName) {
synchronized (mPackageUseInfoMap) {
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
- if (packageUseInfo == null || !packageUseInfo.mIsUsedByOtherApps) {
+ if (packageUseInfo == null) {
return false;
}
- packageUseInfo.mIsUsedByOtherApps = false;
- return true;
+ return packageUseInfo.clearCodePathUsedByOtherApps();
}
}
@@ -532,7 +571,7 @@
* @return true if the record was found and actually deleted,
* false if the record doesn't exist
*/
- public boolean removeUserPackage(String packageName, int userId) {
+ /*package*/ boolean removeUserPackage(String packageName, int userId) {
synchronized (mPackageUseInfoMap) {
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
if (packageUseInfo == null) {
@@ -550,7 +589,8 @@
}
// If no secondary dex info is left and the package is not used by other apps
// remove the data since it is now useless.
- if (packageUseInfo.mDexUseInfoMap.isEmpty() && !packageUseInfo.mIsUsedByOtherApps) {
+ if (packageUseInfo.mDexUseInfoMap.isEmpty()
+ && !packageUseInfo.isAnyCodePathUsedByOtherApps()) {
mPackageUseInfoMap.remove(packageName);
updated = true;
}
@@ -564,7 +604,7 @@
* @return true if the record was found and actually deleted,
* false if the record doesn't exist
*/
- public boolean removeDexFile(String packageName, String dexFile, int userId) {
+ /*package*/ boolean removeDexFile(String packageName, String dexFile, int userId) {
synchronized (mPackageUseInfoMap) {
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
if (packageUseInfo == null) {
@@ -586,7 +626,7 @@
return false;
}
- public PackageUseInfo getPackageUseInfo(String packageName) {
+ /*package*/ PackageUseInfo getPackageUseInfo(String packageName) {
synchronized (mPackageUseInfoMap) {
PackageUseInfo useInfo = mPackageUseInfoMap.get(packageName);
// The useInfo contains a map for secondary dex files which could be modified
@@ -601,7 +641,7 @@
/**
* Return all packages that contain records of secondary dex files.
*/
- public Set<String> getAllPackagesWithSecondaryDexFiles() {
+ /*package*/ Set<String> getAllPackagesWithSecondaryDexFiles() {
Set<String> packages = new HashSet<>();
synchronized (mPackageUseInfoMap) {
for (Map.Entry<String, PackageUseInfo> entry : mPackageUseInfoMap.entrySet()) {
@@ -639,15 +679,6 @@
throw new IllegalArgumentException("Unknown bool encoding: " + bool);
}
- private boolean contains(int[] array, int elem) {
- for (int i = 0; i < array.length; i++) {
- if (elem == array[i]) {
- return true;
- }
- }
- return false;
- }
-
public String dump() {
StringWriter sw = new StringWriter();
write(sw);
@@ -658,46 +689,94 @@
* Stores data on how a package and its dex files are used.
*/
public static class PackageUseInfo {
- // This flag is for the primary and split apks. It is set to true whenever one of them
- // is loaded by another app.
- private boolean mIsUsedByOtherApps;
+ // The app's code paths that are used by other apps.
+ // The key is the code path and the value is the set of loading packages.
+ private final Map<String, Set<String>> mCodePathsUsedByOtherApps;
// Map dex paths to their data (isUsedByOtherApps, owner id, loader isa).
private final Map<String, DexUseInfo> mDexUseInfoMap;
- // Packages who load this dex file.
- private final Set<String> mLoadingPackages;
+
+ // Keeps track of whether or not this package was used by other apps before
+ // we upgraded to VERSION 4 which records the info for each code path separately.
+ // This is unwanted complexity but without it we risk to profile guide compile
+ // something that supposed to be shared. For example:
+ // 1) we determine that chrome is used by another app
+ // 2) we take an OTA which upgrades the way we keep track of usage data
+ // 3) chrome doesn't get used until the background job executes
+ // 4) as part of the backgound job we now think that chrome is not used by others
+ // and we speed-profile.
+ // 5) as a result the next time someone uses chrome it will extract from apk since
+ // the compiled code will be private.
+ private boolean mUsedByOtherAppsBeforeUpgrade;
public PackageUseInfo() {
- mIsUsedByOtherApps = false;
+ mCodePathsUsedByOtherApps = new HashMap<>();
mDexUseInfoMap = new HashMap<>();
- mLoadingPackages = new HashSet<>();
}
// Creates a deep copy of the `other`.
public PackageUseInfo(PackageUseInfo other) {
- mIsUsedByOtherApps = other.mIsUsedByOtherApps;
+ mCodePathsUsedByOtherApps = new HashMap<>();
+ for (Map.Entry<String, Set<String>> e : other.mCodePathsUsedByOtherApps.entrySet()) {
+ mCodePathsUsedByOtherApps.put(e.getKey(), new HashSet<>(e.getValue()));
+ }
+
mDexUseInfoMap = new HashMap<>();
for (Map.Entry<String, DexUseInfo> e : other.mDexUseInfoMap.entrySet()) {
mDexUseInfoMap.put(e.getKey(), new DexUseInfo(e.getValue()));
}
- mLoadingPackages = new HashSet<>(other.mLoadingPackages);
}
- private boolean merge(boolean isUsedByOtherApps) {
- boolean oldIsUsedByOtherApps = mIsUsedByOtherApps;
- mIsUsedByOtherApps = mIsUsedByOtherApps || isUsedByOtherApps;
- return oldIsUsedByOtherApps != this.mIsUsedByOtherApps;
+ private boolean mergeCodePathUsedByOtherApps(String codePath, boolean isUsedByOtherApps,
+ String owningPackageName, String loadingPackage) {
+ if (!isUsedByOtherApps) {
+ // Nothing to update if the the code path is not used by other apps.
+ return false;
+ }
+
+ boolean newCodePath = false;
+ Set<String> loadingPackages = mCodePathsUsedByOtherApps.get(codePath);
+ if (loadingPackages == null) {
+ loadingPackages = new HashSet<>();
+ mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
+ newCodePath = true;
+ }
+ boolean newLoadingPackage = loadingPackage != null
+ && !loadingPackage.equals(owningPackageName)
+ && loadingPackages.add(loadingPackage);
+ return newCodePath || newLoadingPackage;
}
- public boolean isUsedByOtherApps() {
- return mIsUsedByOtherApps;
+ public boolean isUsedByOtherApps(String codePath) {
+ return mCodePathsUsedByOtherApps.containsKey(codePath);
}
public Map<String, DexUseInfo> getDexUseInfoMap() {
return mDexUseInfoMap;
}
- public Set<String> getLoadingPackages() {
- return mLoadingPackages;
+ public Set<String> getLoadingPackages(String codePath) {
+ return mCodePathsUsedByOtherApps.getOrDefault(codePath, null);
+ }
+
+ public boolean isAnyCodePathUsedByOtherApps() {
+ return !mCodePathsUsedByOtherApps.isEmpty();
+ }
+
+ /**
+ * Clears the usedByOtherApps markers from all code paths.
+ * Returns whether or not there was an update.
+ */
+ /*package*/ boolean clearCodePathUsedByOtherApps() {
+ // Update mUsedByOtherAppsBeforeUpgrade as well to be consistent with
+ // the new data. This is not saved to disk so we don't need to return it.
+ mUsedByOtherAppsBeforeUpgrade = true;
+
+ if (mCodePathsUsedByOtherApps.isEmpty()) {
+ return false;
+ } else {
+ mCodePathsUsedByOtherApps.clear();
+ return true;
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 25ba66e..3cd24b8 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -382,6 +382,7 @@
null /*installUser*/,
false /*allowInstall*/,
false /*instantApp*/,
+ false /*virtualPreload*/,
null /*parentPkgName*/,
null /*childPkgNames*/,
UserManagerService.getInstance(),
@@ -423,6 +424,7 @@
UserHandle.SYSTEM /*installUser*/,
true /*allowInstall*/,
false /*instantApp*/,
+ false /*virtualPreload*/,
null /*parentPkgName*/,
null /*childPkgNames*/,
UserManagerService.getInstance(),
@@ -467,6 +469,7 @@
null /*installUser*/,
false /*allowInstall*/,
false /*instantApp*/,
+ false /*virtualPreload*/,
null /*parentPkgName*/,
null /*childPkgNames*/,
UserManagerService.getInstance(),
@@ -511,6 +514,7 @@
null /*installUser*/,
false /*allowInstall*/,
false /*instantApp*/,
+ false /*virtualPreload*/,
null /*parentPkgName*/,
null /*childPkgNames*/,
UserManagerService.getInstance(),
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index e2dfb29..4db9a30 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -106,7 +106,7 @@
notifyDexLoad(mFooUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser0);
// Package is not used by others, so we should get nothing back.
- assertNull(getPackageUseInfo(mFooUser0));
+ assertNoUseInfo(mFooUser0);
}
@Test
@@ -116,8 +116,7 @@
// Bar is used by others now and should be in our records
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNotNull(pui);
- assertTrue(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mBarUser0, pui, true);
assertTrue(pui.getDexUseInfoMap().isEmpty());
}
@@ -128,8 +127,7 @@
notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mFooUser0, pui, false);
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
}
@@ -141,8 +139,7 @@
notifyDexLoad(mFooUser0, barSecondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mBarUser0, pui, false);
assertEquals(barSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
}
@@ -165,8 +162,7 @@
// Check bar usage. Should be used by other app (for primary and barSecondaries).
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNotNull(pui);
- assertTrue(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mBarUser0, pui, true);
assertEquals(barSecondaries.size() + barSecondariesForOwnUse.size(),
pui.getDexUseInfoMap().size());
@@ -176,8 +172,7 @@
// Check foo usage. Should not be used by other app.
pui = getPackageUseInfo(mFooUser0);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mFooUser0, pui, false);
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
}
@@ -185,22 +180,22 @@
@Test
public void testPackageUseInfoNotFound() {
// Assert we don't get back data we did not previously record.
- assertNull(getPackageUseInfo(mFooUser0));
+ assertNoUseInfo(mFooUser0);
}
@Test
public void testInvalidIsa() {
// Notifying with an invalid ISA should be ignored.
notifyDexLoad(mInvalidIsa, mInvalidIsa.getSecondaryDexPaths(), mUser0);
- assertNull(getPackageUseInfo(mInvalidIsa));
+ assertNoUseInfo(mInvalidIsa);
}
@Test
- public void testNotExistingPackate() {
+ public void testNotExistingPackage() {
// Notifying about the load of a package which was previously not
// register in DexManager#load should be ignored.
notifyDexLoad(mDoesNotExist, mDoesNotExist.getBaseAndSplitDexPaths(), mUser0);
- assertNull(getPackageUseInfo(mDoesNotExist));
+ assertNoUseInfo(mDoesNotExist);
}
@Test
@@ -208,7 +203,7 @@
// Bar from User1 tries to load secondary dex files from User0 Bar.
// Request should be ignored.
notifyDexLoad(mBarUser1, mBarUser0.getSecondaryDexPaths(), mUser1);
- assertNull(getPackageUseInfo(mBarUser1));
+ assertNoUseInfo(mBarUser1);
}
@Test
@@ -217,7 +212,7 @@
// Note that the PackageManagerService already filters this out but we
// still check that nothing goes unexpected in DexManager.
notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser1);
- assertNull(getPackageUseInfo(mBarUser1));
+ assertNoUseInfo(mBarUser1);
}
@Test
@@ -229,7 +224,7 @@
// Before we notify about the installation of the newPackage if mFoo
// is trying to load something from it we should not find it.
notifyDexLoad(mFooUser0, newSecondaries, mUser0);
- assertNull(getPackageUseInfo(newPackage));
+ assertNoUseInfo(newPackage);
// Notify about newPackage install and let mFoo load its dexes.
mDexManager.notifyPackageInstalled(newPackage.mPackageInfo, mUser0);
@@ -237,8 +232,7 @@
// We should get back the right info.
PackageUseInfo pui = getPackageUseInfo(newPackage);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(newPackage, pui, false);
assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/true, mUser0);
}
@@ -254,8 +248,7 @@
notifyDexLoad(newPackage, newSecondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(newPackage);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(newPackage, pui, false);
assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
}
@@ -267,8 +260,7 @@
// Bar is used by others now and should be in our records.
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNotNull(pui);
- assertTrue(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mBarUser0, pui, true);
assertTrue(pui.getDexUseInfoMap().isEmpty());
// Notify that bar is updated.
@@ -278,8 +270,7 @@
// The usedByOtherApps flag should be clear now.
pui = getPackageUseInfo(mBarUser0);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mBarUser0, pui, false);
}
@Test
@@ -291,8 +282,7 @@
// We shouldn't find yet the new split as we didn't notify the package update.
notifyDexLoad(mFooUser0, newSplits, mUser0);
- PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNull(pui);
+ assertNoUseInfo(mBarUser0);
// Notify that bar is updated. splitSourceDirs will contain the updated path.
mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
@@ -301,9 +291,9 @@
// Now, when the split is loaded we will find it and we should mark Bar as usedByOthers.
notifyDexLoad(mFooUser0, newSplits, mUser0);
- pui = getPackageUseInfo(mBarUser0);
+ PackageUseInfo pui = getPackageUseInfo(mBarUser0);
assertNotNull(pui);
- assertTrue(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(newSplits, pui, true);
}
@Test
@@ -335,8 +325,7 @@
// Foo should still be around since it's used by other apps but with no
// secondary dex info.
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
- assertNotNull(pui);
- assertTrue(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mFooUser0, pui, true);
assertTrue(pui.getDexUseInfoMap().isEmpty());
}
@@ -350,8 +339,7 @@
// Foo should not be around since all its secondary dex info were deleted
// and it is not used by other apps.
- PackageUseInfo pui = getPackageUseInfo(mFooUser0);
- assertNull(pui);
+ assertNoUseInfo(mFooUser0);
}
@Test
@@ -363,8 +351,7 @@
mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), UserHandle.USER_ALL);
// Bar should not be around since it was removed for all users.
- PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNull(pui);
+ assertNoUseInfo(mBarUser0);
}
@Test
@@ -373,7 +360,7 @@
// Load a dex file from framework.
notifyDexLoad(mFooUser0, Arrays.asList(frameworkDex), mUser0);
// The dex file should not be recognized as a package.
- assertNull(mDexManager.getPackageUseInfo(frameworkDex));
+ assertFalse(mDexManager.hasInfoOnPackage(frameworkDex));
}
@Test
@@ -383,8 +370,7 @@
notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mFooUser0, pui, false);
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
}
@@ -395,8 +381,7 @@
notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
// We expect that all the contexts are unsupported.
String[] expectedContexts =
@@ -413,8 +398,7 @@
notifyDexLoad(mBarUser0, secondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mBarUser0, pui, false);
assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
@@ -422,8 +406,7 @@
notifyDexLoad(mBarUser0DelegateLastClassLoader, secondaries, mUser0);
pui = getPackageUseInfo(mBarUser0);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mBarUser0, pui, false);
assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
// We expect that all the contexts to be changed to variable now.
String[] expectedContexts =
@@ -439,8 +422,7 @@
notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
// We expect that all the contexts are unsupported.
String[] expectedContexts =
@@ -484,6 +466,17 @@
expectedContexts);
}
+ private void assertIsUsedByOtherApps(TestData testData, PackageUseInfo pui,
+ boolean isUsedByOtherApps) {
+ assertIsUsedByOtherApps(testData.getBaseAndSplitDexPaths(), pui, isUsedByOtherApps);
+ }
+
+ private void assertIsUsedByOtherApps(List<String> codePaths, PackageUseInfo pui,
+ boolean isUsedByOtherApps) {
+ for (String codePath : codePaths) {
+ assertEquals(codePath, isUsedByOtherApps, pui.isUsedByOtherApps(codePath));
+ }
+ }
private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId) {
// By default, assume a single class loader in the chain.
// This makes writing tests much easier.
@@ -499,7 +492,12 @@
}
private PackageUseInfo getPackageUseInfo(TestData testData) {
- return mDexManager.getPackageUseInfo(testData.mPackageInfo.packageName);
+ assertTrue(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
+ return mDexManager.getPackageUseInfoOrDefault(testData.mPackageInfo.packageName);
+ }
+
+ private void assertNoUseInfo(TestData testData) {
+ assertFalse(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
}
private static PackageInfo getMockPackageInfo(String packageName, int userId) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
index 1eb5552..b64716c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -62,7 +62,8 @@
DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES |
DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
DexoptOptions.DEXOPT_ONLY_SHARED_DEX |
- DexoptOptions.DEXOPT_DOWNGRADE;
+ DexoptOptions.DEXOPT_DOWNGRADE |
+ DexoptOptions.DEXOPT_AS_SHARED_LIBRARY;
DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags);
assertEquals(mPackageName, opt.getPackageName());
@@ -74,6 +75,7 @@
assertTrue(opt.isDexoptOnlySharedDex());
assertTrue(opt.isDowngrade());
assertTrue(opt.isForce());
+ assertTrue(opt.isDexoptAsSharedLibrary());
}
@Test
@@ -89,7 +91,7 @@
PackageManagerService.REASON_INSTALL,
PackageManagerService.REASON_BACKGROUND_DEXOPT,
PackageManagerService.REASON_AB_OTA,
- PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE};
+ PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE,};
for (int reason : reasons) {
DexoptOptions opt = new DexoptOptions(mPackageName, reason, flags);
@@ -102,6 +104,7 @@
assertFalse(opt.isDexoptOnlySharedDex());
assertFalse(opt.isDowngrade());
assertTrue(opt.isForce());
+ assertFalse(opt.isDexoptAsSharedLibrary());
}
}
@@ -119,6 +122,7 @@
assertFalse(opt.isDexoptOnlySharedDex());
assertFalse(opt.isDowngrade());
assertTrue(opt.isForce());
+ assertFalse(opt.isDexoptAsSharedLibrary());
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 3fc12b4..69a148d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -21,6 +21,7 @@
import android.support.test.runner.AndroidJUnit4;
import dalvik.system.VMRuntime;
+import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -249,7 +250,10 @@
Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
packageToUsersMap.put(mBarSecondary2User1.mPackageName,
new HashSet<>(Arrays.asList(mBarSecondary2User1.mOwnerUserId)));
- mPackageDexUsage.syncData(packageToUsersMap);
+ Map<String, Set<String>> packageToCodePaths = new HashMap<>();
+ packageToCodePaths.put(mBarBaseUser0.mPackageName,
+ new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
+ mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
// Assert that only user 1 files are there.
assertPackageDexUsage(mBarBaseUser0, mBarSecondary2User1);
@@ -341,8 +345,8 @@
Set<String> usersExtra = new HashSet<>(Arrays.asList(
new String[] {"another.package.2", "another.package.3"}));
- assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, users));
- assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, usersExtra));
+ assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, users));
+ assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, usersExtra));
assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, users));
assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, usersExtra));
@@ -351,7 +355,7 @@
// Verify that the users were recorded.
Set<String> userAll = new HashSet<>(users);
userAll.addAll(usersExtra);
- assertPackageDexUsage(packageDexUsageRecordUsers, userAll, mFooBaseUser0,
+ assertPackageDexUsage(packageDexUsageRecordUsers, userAll, mFooSplit2UsedByOtherApps0,
mFooSecondary1User0);
}
@@ -359,19 +363,19 @@
public void testRecordDexFileUsersNotTheOwningPackage() {
PackageDexUsage packageDexUsageRecordUsers = new PackageDexUsage();
Set<String> users = new HashSet<>(Arrays.asList(
- new String[] {mFooBaseUser0.mPackageName}));
+ new String[] {mFooSplit2UsedByOtherApps0.mPackageName}));
Set<String> usersExtra = new HashSet<>(Arrays.asList(
new String[] {"another.package.2", "another.package.3"}));
- assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, users));
- assertTrue(record(packageDexUsageRecordUsers, mFooBaseUser0, usersExtra));
+ assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, users));
+ assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, usersExtra));
assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, users));
assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, usersExtra));
packageDexUsageRecordUsers = writeAndReadBack(packageDexUsageRecordUsers);
// Verify that only the non owning packages were recorded.
- assertPackageDexUsage(packageDexUsageRecordUsers, usersExtra, mFooBaseUser0,
+ assertPackageDexUsage(packageDexUsageRecordUsers, usersExtra, mFooSplit2UsedByOtherApps0,
mFooSecondary1User0);
}
@@ -428,7 +432,6 @@
assertPackageDexUsage(null, mFooSecondary1User0);
}
-
@Test
public void testDexUsageClassLoaderContext() {
final boolean isUsedByOtherApps = false;
@@ -458,6 +461,80 @@
assertFalse(unknownContext.isVariableClassLoaderContext());
}
+ @Test
+ public void testReadVersion1() {
+ String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
+ // Equivalent to
+ // record(mFooSplit2UsedByOtherApps0);
+ // record(mFooSecondary1User0);
+ // record(mFooSecondary2UsedByOtherApps0);
+ // record(mBarBaseUser0);
+ // record(mBarSecondary1User0);
+ String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__1\n"
+ + "com.google.foo,1\n"
+ + "#/data/user/0/com.google.foo/sec-1.dex\n"
+ + "0,0," + isa + "\n"
+ + "#/data/user/0/com.google.foo/sec-2.dex\n"
+ + "0,1," + isa + "\n"
+ + "com.google.bar,0\n"
+ + "#/data/user/0/com.google.bar/sec-1.dex\n"
+ + "0,0," + isa + "\n";
+
+ PackageDexUsage packageDexUsage = new PackageDexUsage();
+ try {
+ packageDexUsage.read(new StringReader(content));
+ } catch (IOException e) {
+ fail();
+ }
+
+ // After the read we must sync the data to fill the missing information on the code paths.
+ Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
+ Map<String, Set<String>> packageToCodePaths = new HashMap<>();
+
+ // Handle foo package.
+ packageToUsersMap.put(mFooSplit2UsedByOtherApps0.mPackageName,
+ new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mOwnerUserId)));
+ packageToCodePaths.put(mFooSplit2UsedByOtherApps0.mPackageName,
+ new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mDexFile,
+ mFooSplit1User0.mDexFile, mFooBaseUser0.mDexFile)));
+ // Handle bar package.
+ packageToUsersMap.put(mBarBaseUser0.mPackageName,
+ new HashSet<>(Arrays.asList(mBarBaseUser0.mOwnerUserId)));
+ packageToCodePaths.put(mBarBaseUser0.mPackageName,
+ new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
+
+ // Sync the data.
+ packageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
+
+ // Update the class loaders to unknown before asserting if needed. Before version 2 we
+ // didn't have any.
+ String unknown = PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT;
+ TestData fooBaseUser0 = mFooBaseUser0.updateClassLoaderContext(unknown);
+ TestData fooSplit1User0 = mFooSplit1User0.updateClassLoaderContext(unknown);
+ TestData fooSplit2UsedByOtherApps0 =
+ mFooSplit2UsedByOtherApps0.updateClassLoaderContext(unknown);
+ TestData fooSecondary1User0 = mFooSecondary1User0.updateClassLoaderContext(unknown);
+ TestData fooSecondary2UsedByOtherApps0 =
+ mFooSecondary2UsedByOtherApps0.updateClassLoaderContext(unknown);
+ TestData barBaseUser0 = mBarBaseUser0.updateClassLoaderContext(unknown);
+ TestData barSecondary1User0 = mBarSecondary1User0.updateClassLoaderContext(unknown);
+
+ // Assert foo code paths. Note that we ignore the users during upgrade.
+ final Set<String> ignoredUsers = null;
+ assertPackageDexUsage(packageDexUsage, ignoredUsers,
+ fooSplit2UsedByOtherApps0, fooSecondary1User0, fooSecondary2UsedByOtherApps0);
+ // Because fooSplit2UsedByOtherApps0 is used by others, all the other code paths must
+ // share the same data.
+ assertPackageDexUsage(packageDexUsage, ignoredUsers,
+ fooSplit1User0.updateUseByOthers(true),
+ fooSecondary1User0, fooSecondary2UsedByOtherApps0);
+ assertPackageDexUsage(packageDexUsage, ignoredUsers, fooBaseUser0.updateUseByOthers(true),
+ fooSecondary1User0, fooSecondary2UsedByOtherApps0);
+
+ // Assert bar code paths. Note that we ignore the users during upgrade.
+ assertPackageDexUsage(packageDexUsage, ignoredUsers, barBaseUser0, barSecondary1User0);
+ }
+
private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
assertPackageDexUsage(mPackageDexUsage, null, primary, secondaries);
}
@@ -470,9 +547,11 @@
// Check package use info
assertNotNull(pInfo);
- assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps());
- if (users != null) {
- assertEquals(pInfo.getLoadingPackages(), users);
+ if (primary != null) {
+ assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps(primary.mDexFile));
+ if (users != null) {
+ assertEquals(pInfo.getLoadingPackages(primary.mDexFile), users);
+ }
}
Map<String, DexUseInfo> dexUseInfoMap = pInfo.getDexUseInfoMap();
@@ -560,5 +639,10 @@
return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, mUsedByOtherApps,
mPrimaryOrSplit, mUsedBy, newContext);
}
+
+ private TestData updateUseByOthers(boolean newUsedByOthers) {
+ return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, newUsedByOthers,
+ mPrimaryOrSplit, mUsedBy, mClassLoaderContext);
+ }
}
}