Merge "Update Emoji.java to use ICU"
diff --git a/Android.mk b/Android.mk
index 0e5dfed..03b2533 100644
--- a/Android.mk
+++ b/Android.mk
@@ -944,27 +944,8 @@
-werror -hide 111 -hide 113 \
-overview $(LOCAL_PATH)/core/java/overview.html
-SUPPORT_API_DIR := ./frameworks/support/api
-
-# More API Level information for the Support Library, which is currently
-# included as part of the core framework docs build.
-framework_docs_LOCAL_DROIDDOC_OPTIONS += \
- -since $(SUPPORT_API_DIR)/22.0.0.txt 22.0.0 \
- -since $(SUPPORT_API_DIR)/22.0.0.txt 22.0.0 \
- -since $(SUPPORT_API_DIR)/22.1.0.txt 22.1.0 \
- -since $(SUPPORT_API_DIR)/22.2.0.txt 22.2.0 \
- -since $(SUPPORT_API_DIR)/22.2.1.txt 22.2.1 \
- -since $(SUPPORT_API_DIR)/23.0.0.txt 23.0.0 \
- -since $(SUPPORT_API_DIR)/23.1.0.txt 23.1.0 \
- -since $(SUPPORT_API_DIR)/23.1.1.txt 23.1.1 \
- -since $(SUPPORT_API_DIR)/23.2.0.txt 23.2.0 \
- -since $(SUPPORT_API_DIR)/23.2.1.txt 23.2.1 \
- -since $(SUPPORT_API_DIR)/23.4.0.txt 23.4.0 \
- -since $(SUPPORT_API_DIR)/24.0.0.txt 24.0.0 \
- -since $(SUPPORT_API_DIR)/24.1.0.txt 24.1.0 \
- -since $(SUPPORT_API_DIR)/24.2.0.txt 24.2.0 \
- -since $(SUPPORT_API_DIR)/25.0.0.txt 25.0.0 \
- -since $(SUPPORT_API_DIR)/25.1.0.txt 25.1.0
+# Allow the support library to add its own droiddoc options.
+include $(LOCAL_PATH)/../support/droiddoc.mk
framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
$(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
@@ -1472,10 +1453,6 @@
LOCAL_SRC_FILES := \
$(call all-proto-files-under, core/proto) \
$(call all-proto-files-under, libs/incident/proto)
-LOCAL_C_INCLUDES := \
- $(call generated-sources-dir-for,STATIC_LIBRARIES,libplatformprotos,)/proto
-LOCAL_EXPORT_C_INCLUDES := \
- $(call generated-sources-dir-for,STATIC_LIBRARIES,libplatformprotos,)/proto
include $(BUILD_STATIC_LIBRARY)
# ==== c++ proto host library ==============================
@@ -1490,10 +1467,6 @@
LOCAL_SRC_FILES := \
$(call all-proto-files-under, core/proto) \
$(call all-proto-files-under, libs/incident/proto)
-LOCAL_C_INCLUDES := \
- $(call generated-sources-dir-for,STATIC_LIBRARIES,libplatformprotos,)/proto
-LOCAL_EXPORT_C_INCLUDES := \
- $(call generated-sources-dir-for,STATIC_LIBRARIES,libplatformprotos,)/proto
include $(BUILD_HOST_SHARED_LIBRARY)
diff --git a/api/current.txt b/api/current.txt
index 2276012..aab29be 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -746,6 +746,7 @@
field public static final int isAsciiCapable = 16843753; // 0x10103e9
field public static final int isAuxiliary = 16843647; // 0x101037f
field public static final int isDefault = 16843297; // 0x1010221
+ field public static final int isFeatureSplit = 16844126; // 0x101055e
field public static final int isGame = 16843764; // 0x10103f4
field public static final int isIndicator = 16843079; // 0x1010147
field public static final int isModifier = 16843334; // 0x1010246
@@ -7937,7 +7938,7 @@
}
public final class AdvertisingSet {
- method public void enableAdvertising(boolean);
+ method public void enableAdvertising(boolean, int);
method public void periodicAdvertisingEnable(boolean);
method public void setAdvertisingData(android.bluetooth.le.AdvertiseData);
method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters);
@@ -7970,7 +7971,6 @@
method public int getInterval();
method public int getPrimaryPhy();
method public int getSecondaryPhy();
- method public int getTimeout();
method public int getTxPowerLevel();
method public boolean includeTxPower();
method public boolean isAnonymous();
@@ -8004,7 +8004,6 @@
method public android.bluetooth.le.AdvertisingSetParameters.Builder setLegacyMode(boolean);
method public android.bluetooth.le.AdvertisingSetParameters.Builder setPrimaryPhy(int);
method public android.bluetooth.le.AdvertisingSetParameters.Builder setSecondaryPhy(int);
- method public android.bluetooth.le.AdvertisingSetParameters.Builder setTimeout(int);
method public android.bluetooth.le.AdvertisingSetParameters.Builder setTxPowerLevel(int);
}
@@ -8013,6 +8012,8 @@
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback);
method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, android.bluetooth.le.AdvertisingSetCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
method public void stopAdvertisingSet(android.bluetooth.le.AdvertisingSetCallback);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 00fe0b6..0f42a54 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -859,6 +859,7 @@
field public static final int isAsciiCapable = 16843753; // 0x10103e9
field public static final int isAuxiliary = 16843647; // 0x101037f
field public static final int isDefault = 16843297; // 0x1010221
+ field public static final int isFeatureSplit = 16844126; // 0x101055e
field public static final int isGame = 16843764; // 0x10103f4
field public static final int isIndicator = 16843079; // 0x1010147
field public static final int isModifier = 16843334; // 0x1010246
@@ -8412,7 +8413,7 @@
}
public final class AdvertisingSet {
- method public void enableAdvertising(boolean);
+ method public void enableAdvertising(boolean, int);
method public void periodicAdvertisingEnable(boolean);
method public void setAdvertisingData(android.bluetooth.le.AdvertiseData);
method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters);
@@ -8445,7 +8446,6 @@
method public int getInterval();
method public int getPrimaryPhy();
method public int getSecondaryPhy();
- method public int getTimeout();
method public int getTxPowerLevel();
method public boolean includeTxPower();
method public boolean isAnonymous();
@@ -8479,7 +8479,6 @@
method public android.bluetooth.le.AdvertisingSetParameters.Builder setLegacyMode(boolean);
method public android.bluetooth.le.AdvertisingSetParameters.Builder setPrimaryPhy(int);
method public android.bluetooth.le.AdvertisingSetParameters.Builder setSecondaryPhy(int);
- method public android.bluetooth.le.AdvertisingSetParameters.Builder setTimeout(int);
method public android.bluetooth.le.AdvertisingSetParameters.Builder setTxPowerLevel(int);
}
@@ -8488,6 +8487,8 @@
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback);
method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, android.bluetooth.le.AdvertisingSetCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
method public void stopAdvertisingSet(android.bluetooth.le.AdvertisingSetCallback);
}
@@ -34433,6 +34434,7 @@
method public deprecated void setUserRestrictions(android.os.Bundle);
method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
method public static boolean supportsMultipleUsers();
+ field public static final java.lang.String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
field public static final java.lang.String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
field public static final java.lang.String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
diff --git a/api/test-current.txt b/api/test-current.txt
index dd25eb7..f91bbb9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -746,6 +746,7 @@
field public static final int isAsciiCapable = 16843753; // 0x10103e9
field public static final int isAuxiliary = 16843647; // 0x101037f
field public static final int isDefault = 16843297; // 0x1010221
+ field public static final int isFeatureSplit = 16844126; // 0x101055e
field public static final int isGame = 16843764; // 0x10103f4
field public static final int isIndicator = 16843079; // 0x1010147
field public static final int isModifier = 16843334; // 0x1010246
@@ -7964,7 +7965,7 @@
}
public final class AdvertisingSet {
- method public void enableAdvertising(boolean);
+ method public void enableAdvertising(boolean, int);
method public void periodicAdvertisingEnable(boolean);
method public void setAdvertisingData(android.bluetooth.le.AdvertiseData);
method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters);
@@ -7997,7 +7998,6 @@
method public int getInterval();
method public int getPrimaryPhy();
method public int getSecondaryPhy();
- method public int getTimeout();
method public int getTxPowerLevel();
method public boolean includeTxPower();
method public boolean isAnonymous();
@@ -8031,7 +8031,6 @@
method public android.bluetooth.le.AdvertisingSetParameters.Builder setLegacyMode(boolean);
method public android.bluetooth.le.AdvertisingSetParameters.Builder setPrimaryPhy(int);
method public android.bluetooth.le.AdvertisingSetParameters.Builder setSecondaryPhy(int);
- method public android.bluetooth.le.AdvertisingSetParameters.Builder setTimeout(int);
method public android.bluetooth.le.AdvertisingSetParameters.Builder setTxPowerLevel(int);
}
@@ -8040,6 +8039,8 @@
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback);
method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, android.bluetooth.le.AdvertisingSetCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, int, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
method public void stopAdvertisingSet(android.bluetooth.le.AdvertisingSetCallback);
}
@@ -31735,6 +31736,7 @@
method public deprecated void setUserRestrictions(android.os.Bundle);
method public deprecated void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
method public static boolean supportsMultipleUsers();
+ field public static final java.lang.String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED";
field public static final java.lang.String ALLOW_PARENT_PROFILE_APP_LINKING = "allow_parent_profile_app_linking";
field public static final java.lang.String DISALLOW_ADD_MANAGED_PROFILE = "no_add_managed_profile";
field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
@@ -35019,6 +35021,7 @@
field public static final java.lang.String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
field public static final java.lang.String SETTINGS_CLASSNAME = "settings_classname";
field public static final java.lang.String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
+ field public static final java.lang.String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
field public static final java.lang.String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
field public static final deprecated java.lang.String TTS_DEFAULT_COUNTRY = "tts_default_country";
field public static final deprecated java.lang.String TTS_DEFAULT_LANG = "tts_default_lang";
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 91520f1..827a77f 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -411,7 +411,8 @@
if (file.isFile()) {
try {
ApkLite baseApk = PackageParser.parseApkLite(file, 0);
- PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null);
+ PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
+ null, null);
params.sessionParams.setSize(
PackageHelper.calculateInstalledSize(pkgLite, false,
params.sessionParams.abiOverride));
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index ede9281..8a3d9b1 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -156,13 +156,13 @@
@GuardedBy("ContextImpl.class")
private ArrayMap<String, File> mSharedPrefsPaths;
- final ActivityThread mMainThread;
- final LoadedApk mPackageInfo;
- private ClassLoader mClassLoader;
+ final @NonNull ActivityThread mMainThread;
+ final @NonNull LoadedApk mPackageInfo;
+ private @Nullable ClassLoader mClassLoader;
- private final IBinder mActivityToken;
+ private final @Nullable IBinder mActivityToken;
- private final UserHandle mUser;
+ private final @Nullable UserHandle mUser;
private final ApplicationContentResolver mContentResolver;
@@ -181,6 +181,9 @@
private PackageManager mPackageManager;
private Context mReceiverRestrictedContext = null;
+ // The name of the split this Context is representing. May be null.
+ private @Nullable String mSplitName = null;
+
private final Object mSync = new Object();
@GuardedBy("mSync")
@@ -1914,17 +1917,25 @@
}
}
- private static Resources createResources(IBinder activityToken, LoadedApk pi, int displayId,
- Configuration overrideConfig, CompatibilityInfo compatInfo) {
+ private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
+ int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
+ final String[] splitResDirs;
+ final ClassLoader classLoader;
+ try {
+ splitResDirs = pi.getSplitPaths(splitName);
+ classLoader = pi.getSplitClassLoader(splitName);
+ } catch (NameNotFoundException e) {
+ throw new RuntimeException(e);
+ }
return ResourcesManager.getInstance().getResources(activityToken,
pi.getResDir(),
- pi.getSplitResDirs(),
+ splitResDirs,
pi.getOverlayDirs(),
pi.getApplicationInfo().sharedLibraryFiles,
displayId,
overrideConfig,
compatInfo,
- pi.getClassLoader());
+ classLoader);
}
@Override
@@ -1933,14 +1944,13 @@
LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE);
if (pi != null) {
- ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
- new UserHandle(UserHandle.getUserId(application.uid)), flags,
- null);
+ ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
+ new UserHandle(UserHandle.getUserId(application.uid)), flags, null);
final int displayId = mDisplay != null
? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
- c.setResources(createResources(mActivityToken, pi, displayId, null,
+ c.setResources(createResources(mActivityToken, pi, null, displayId, null,
getDisplayAdjustments(displayId).getCompatibilityInfo()));
if (c.mResources != null) {
return c;
@@ -1964,20 +1974,20 @@
if (packageName.equals("system") || packageName.equals("android")) {
// The system resources are loaded in every application, so we can safely copy
// the context without reloading Resources.
- return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, user, flags,
- null);
+ return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user,
+ flags, null);
}
LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
if (pi != null) {
- ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken, user, flags,
- null);
+ ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,
+ flags, null);
final int displayId = mDisplay != null
? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
- c.setResources(createResources(mActivityToken, pi, displayId, null,
+ c.setResources(createResources(mActivityToken, pi, null, displayId, null,
getDisplayAdjustments(displayId).getCompatibilityInfo()));
if (c.mResources != null) {
return c;
@@ -1999,7 +2009,7 @@
final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName);
final String[] paths = mPackageInfo.getSplitPaths(splitName);
- final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo,
+ final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName,
mActivityToken, mUser, mFlags, classLoader);
final int displayId = mDisplay != null
@@ -2024,11 +2034,11 @@
throw new IllegalArgumentException("overrideConfiguration must not be null");
}
- ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
- mUser, mFlags, mClassLoader);
+ ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
+ mActivityToken, mUser, mFlags, mClassLoader);
final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
- context.setResources(createResources(mActivityToken, mPackageInfo, displayId,
+ context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
return context;
}
@@ -2039,12 +2049,12 @@
throw new IllegalArgumentException("display must not be null");
}
- ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
- mUser, mFlags, mClassLoader);
+ ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
+ mActivityToken, mUser, mFlags, mClassLoader);
final int displayId = display.getDisplayId();
- context.setResources(createResources(mActivityToken, mPackageInfo, displayId, null,
- getDisplayAdjustments(displayId).getCompatibilityInfo()));
+ context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
+ null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
context.mDisplay = display;
return context;
}
@@ -2053,16 +2063,16 @@
public Context createDeviceProtectedStorageContext() {
final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
| Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
- return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, mUser, flags,
- mClassLoader);
+ return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
+ flags, mClassLoader);
}
@Override
public Context createCredentialProtectedStorageContext() {
final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
| Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
- return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken, mUser, flags,
- mClassLoader);
+ return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
+ flags, mClassLoader);
}
@Override
@@ -2149,7 +2159,7 @@
static ContextImpl createSystemContext(ActivityThread mainThread) {
LoadedApk packageInfo = new LoadedApk(mainThread);
- ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, 0,
+ ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
null);
context.setResources(packageInfo.getResources());
context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
@@ -2159,7 +2169,7 @@
static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
- ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, 0,
+ ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
null);
context.setResources(packageInfo.getResources());
return context;
@@ -2186,8 +2196,8 @@
}
}
- ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityToken, null,
- 0, classLoader);
+ ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
+ activityToken, null, 0, classLoader);
// Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
@@ -2214,9 +2224,10 @@
return context;
}
- private ContextImpl(ContextImpl container, ActivityThread mainThread,
- LoadedApk packageInfo, IBinder activityToken, UserHandle user, int flags,
- ClassLoader classLoader) {
+ private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
+ @NonNull LoadedApk packageInfo, @Nullable String splitName,
+ @Nullable IBinder activityToken, @Nullable UserHandle user, int flags,
+ @Nullable ClassLoader classLoader) {
mOuterContext = this;
// If creator didn't specify which storage to use, use the default
@@ -2241,6 +2252,7 @@
mUser = user;
mPackageInfo = packageInfo;
+ mSplitName = splitName;
mClassLoader = classLoader;
mResourcesManager = ResourcesManager.getInstance();
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index cf41e4e..dbed1be 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -27,7 +27,8 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.split.SplitDependencyLoaderHelper;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.split.SplitDependencyLoader;
import android.content.res.AssetManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Resources;
@@ -49,7 +50,6 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
-import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayAdjustments;
@@ -304,7 +304,7 @@
final String[] splitPaths;
try {
splitPaths = getSplitPaths(null);
- } catch (PackageManager.NameNotFoundException e) {
+ } catch (NameNotFoundException e) {
// This should NEVER fail.
throw new AssertionError("null split not found");
}
@@ -336,7 +336,7 @@
mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;
if (aInfo.requestsIsolatedSplitLoading() && !ArrayUtils.isEmpty(mSplitNames)) {
- mSplitLoader = new SplitDependencyLoader(aInfo.splitDependencies);
+ mSplitLoader = new SplitDependencyLoaderImpl(aInfo.splitDependencies);
}
}
@@ -465,110 +465,88 @@
}
}
- private class SplitDependencyLoader
- extends SplitDependencyLoaderHelper<PackageManager.NameNotFoundException> {
- private String[] mCachedBaseResourcePath;
+ /*
+ * All indices received by the super class should be shifted by 1 when accessing mSplitNames,
+ * etc. The super class assumes the base APK is index 0, while the PackageManager APIs don't
+ * include the base APK in the list of splits.
+ */
+ private class SplitDependencyLoaderImpl extends SplitDependencyLoader<NameNotFoundException> {
private final String[][] mCachedResourcePaths;
- private final ClassLoader[] mCachedSplitClassLoaders;
+ private final ClassLoader[] mCachedClassLoaders;
- SplitDependencyLoader(SparseIntArray dependencies) {
+ SplitDependencyLoaderImpl(@NonNull SparseArray<int[]> dependencies) {
super(dependencies);
- mCachedResourcePaths = new String[mSplitNames.length][];
- mCachedSplitClassLoaders = new ClassLoader[mSplitNames.length];
+ mCachedResourcePaths = new String[mSplitNames.length + 1][];
+ mCachedClassLoaders = new ClassLoader[mSplitNames.length + 1];
}
@Override
protected boolean isSplitCached(int splitIdx) {
- if (splitIdx != -1) {
- return mCachedSplitClassLoaders[splitIdx] != null;
- }
- return mClassLoader != null && mCachedBaseResourcePath != null;
- }
-
- private void addAllConfigSplits(String splitName, ArrayList<String> outAssetPaths) {
- for (int i = 0; i < mSplitNames.length; i++) {
- if (isConfigurationSplitOf(mSplitNames[i], splitName)) {
- outAssetPaths.add(mSplitResDirs[i]);
- }
- }
+ return mCachedClassLoaders[splitIdx] != null;
}
@Override
- protected void constructSplit(int splitIdx, int parentSplitIdx) throws
- PackageManager.NameNotFoundException {
+ protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
+ int parentSplitIdx) throws NameNotFoundException {
final ArrayList<String> splitPaths = new ArrayList<>();
- if (splitIdx == -1) {
+ if (splitIdx == 0) {
createOrUpdateClassLoaderLocked(null);
- addAllConfigSplits(null, splitPaths);
- mCachedBaseResourcePath = splitPaths.toArray(new String[splitPaths.size()]);
+ mCachedClassLoaders[0] = mClassLoader;
+
+ // Never add the base resources here, they always get added no matter what.
+ for (int configSplitIdx : configSplitIndices) {
+ splitPaths.add(mSplitResDirs[configSplitIdx - 1]);
+ }
+ mCachedResourcePaths[0] = splitPaths.toArray(new String[splitPaths.size()]);
return;
}
- final ClassLoader parent;
- if (parentSplitIdx == -1) {
- // The parent is the base APK, so use its ClassLoader as parent
- // and its configuration splits as part of our own too.
- parent = mClassLoader;
- Collections.addAll(splitPaths, mCachedBaseResourcePath);
- } else {
- parent = mCachedSplitClassLoaders[parentSplitIdx];
- Collections.addAll(splitPaths, mCachedResourcePaths[parentSplitIdx]);
+ // Since we handled the special base case above, parentSplitIdx is always valid.
+ final ClassLoader parent = mCachedClassLoaders[parentSplitIdx];
+ mCachedClassLoaders[splitIdx] = ApplicationLoaders.getDefault().getClassLoader(
+ mSplitAppDirs[splitIdx - 1], getTargetSdkVersion(), false, null, null, parent);
+
+ Collections.addAll(splitPaths, mCachedResourcePaths[parentSplitIdx]);
+ splitPaths.add(mSplitResDirs[splitIdx - 1]);
+ for (int configSplitIdx : configSplitIndices) {
+ splitPaths.add(mSplitResDirs[configSplitIdx - 1]);
}
-
- mCachedSplitClassLoaders[splitIdx] = ApplicationLoaders.getDefault().getClassLoader(
- mSplitAppDirs[splitIdx], getTargetSdkVersion(), false, null, null, parent);
-
- splitPaths.add(mSplitResDirs[splitIdx]);
- addAllConfigSplits(mSplitNames[splitIdx], splitPaths);
mCachedResourcePaths[splitIdx] = splitPaths.toArray(new String[splitPaths.size()]);
}
- private int ensureSplitLoaded(String splitName)
- throws PackageManager.NameNotFoundException {
- final int idx;
- if (splitName == null) {
- idx = -1;
- } else {
+ private int ensureSplitLoaded(String splitName) throws NameNotFoundException {
+ int idx = 0;
+ if (splitName != null) {
idx = Arrays.binarySearch(mSplitNames, splitName);
if (idx < 0) {
throw new PackageManager.NameNotFoundException(
"Split name '" + splitName + "' is not installed");
}
+ idx += 1;
}
-
loadDependenciesForSplit(idx);
return idx;
}
- ClassLoader getClassLoaderForSplit(String splitName)
- throws PackageManager.NameNotFoundException {
- final int idx = ensureSplitLoaded(splitName);
- if (idx < 0) {
- return mClassLoader;
- }
- return mCachedSplitClassLoaders[idx];
+ ClassLoader getClassLoaderForSplit(String splitName) throws NameNotFoundException {
+ return mCachedClassLoaders[ensureSplitLoaded(splitName)];
}
- String[] getSplitPathsForSplit(String splitName)
- throws PackageManager.NameNotFoundException {
- final int idx = ensureSplitLoaded(splitName);
- if (idx < 0) {
- return mCachedBaseResourcePath;
- }
- return mCachedResourcePaths[idx];
+ String[] getSplitPathsForSplit(String splitName) throws NameNotFoundException {
+ return mCachedResourcePaths[ensureSplitLoaded(splitName)];
}
}
- private SplitDependencyLoader mSplitLoader;
+ private SplitDependencyLoaderImpl mSplitLoader;
- ClassLoader getSplitClassLoader(String splitName) throws PackageManager.NameNotFoundException {
+ ClassLoader getSplitClassLoader(String splitName) throws NameNotFoundException {
if (mSplitLoader == null) {
return mClassLoader;
}
return mSplitLoader.getClassLoaderForSplit(splitName);
}
- String[] getSplitPaths(String splitName) throws PackageManager.NameNotFoundException {
+ String[] getSplitPaths(String splitName) throws NameNotFoundException {
if (mSplitLoader == null) {
return mSplitResDirs;
}
@@ -925,7 +903,7 @@
final String[] splitPaths;
try {
splitPaths = getSplitPaths(null);
- } catch (PackageManager.NameNotFoundException e) {
+ } catch (NameNotFoundException e) {
// This should never fail.
throw new AssertionError("null split not found");
}
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 29f29e7..c281c7f 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -52,10 +52,10 @@
void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData,
in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters,
- in AdvertiseData periodicData, in IAdvertisingSetCallback callback);
+ in AdvertiseData periodicData, in int timeout, in IAdvertisingSetCallback callback);
void stopAdvertisingSet(in IAdvertisingSetCallback callback);
- void enableAdverisingSet(in int advertiserId, in boolean enable);
+ void enableAdverisingSet(in int advertiserId, in boolean enable, in int timeout);
void setAdvertisingData(in int advertiserId, in AdvertiseData data);
void setScanResponseData(in int advertiserId, in AdvertiseData data);
void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters);
diff --git a/core/java/android/bluetooth/le/AdvertisingSet.java b/core/java/android/bluetooth/le/AdvertisingSet.java
index 1524022..5524a2b 100644
--- a/core/java/android/bluetooth/le/AdvertisingSet.java
+++ b/core/java/android/bluetooth/le/AdvertisingSet.java
@@ -63,9 +63,9 @@
* Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
*
*/
- public void enableAdvertising(boolean enable) {
+ public void enableAdvertising(boolean enable, int timeout) {
try {
- gatt.enableAdverisingSet(this.advertiserId, enable);
+ gatt.enableAdverisingSet(this.advertiserId, enable, timeout);
} catch (RemoteException e) {
Log.e(TAG, "remote exception - ", e);
}
diff --git a/core/java/android/bluetooth/le/AdvertisingSetParameters.java b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
index 453dd70..59fef8d 100644
--- a/core/java/android/bluetooth/le/AdvertisingSetParameters.java
+++ b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
@@ -118,13 +118,11 @@
private final boolean connectable;
private final int interval;
private final int txPowerLevel;
- private final int timeoutMillis;
private AdvertisingSetParameters(boolean connectable, boolean isLegacy,
boolean isAnonymous, boolean includeTxPower,
int primaryPhy, int secondaryPhy,
- int interval, int txPowerLevel,
- int timeoutMillis) {
+ int interval, int txPowerLevel) {
this.connectable = connectable;
this.isLegacy = isLegacy;
this.isAnonymous = isAnonymous;
@@ -133,7 +131,6 @@
this.secondaryPhy = secondaryPhy;
this.interval = interval;
this.txPowerLevel = txPowerLevel;
- this.timeoutMillis = timeoutMillis;
}
private AdvertisingSetParameters(Parcel in) {
@@ -145,7 +142,6 @@
secondaryPhy = in.readInt();
interval = in.readInt();
txPowerLevel = in.readInt();
- timeoutMillis = in.readInt();
}
/**
@@ -188,11 +184,6 @@
*/
public int getTxPowerLevel() { return txPowerLevel; }
- /**
- * Returns the advertising time limit in milliseconds.
- */
- public int getTimeout() { return timeoutMillis; }
-
@Override
public String toString() {
return "AdvertisingSetParameters [connectable=" + connectable
@@ -202,8 +193,7 @@
+ ", primaryPhy=" + primaryPhy
+ ", secondaryPhy=" + secondaryPhy
+ ", interval=" + interval
- + ", txPowerLevel=" + txPowerLevel
- + ", timeoutMillis=" + timeoutMillis + "]";
+ + ", txPowerLevel=" + txPowerLevel + "]";
}
@Override
@@ -221,7 +211,6 @@
dest.writeInt(secondaryPhy);
dest.writeInt(interval);
dest.writeInt(txPowerLevel);
- dest.writeInt(timeoutMillis);
}
public static final Parcelable.Creator<AdvertisingSetParameters> CREATOR =
@@ -250,7 +239,6 @@
private int secondaryPhy = PHY_LE_1M;
private int interval = INTERVAL_LOW;
private int txPowerLevel = TX_POWER_MEDIUM;
- private int timeoutMillis = 0;
/**
* Set whether the advertisement type should be connectable or
@@ -380,30 +368,12 @@
}
/**
- * Limit advertising to a given amount of time.
- * @param timeoutMillis Advertising time limit. May not exceed 180000
- * milliseconds. A value of 0 will disable the time limit.
- * @throws IllegalArgumentException If the provided timeout is over 180000
- * ms.
- */
- public Builder setTimeout(int timeoutMillis) {
- if (timeoutMillis < 0 || timeoutMillis > LIMITED_ADVERTISING_MAX_MILLIS) {
- throw new IllegalArgumentException("timeoutMillis invalid (must be 0-" +
- LIMITED_ADVERTISING_MAX_MILLIS +
- " milliseconds)");
- }
- this.timeoutMillis = timeoutMillis;
- return this;
- }
-
- /**
* Build the {@link AdvertisingSetParameters} object.
*/
public AdvertisingSetParameters build() {
return new AdvertisingSetParameters(connectable, isLegacy, isAnonymous,
includeTxPower, primaryPhy,
- secondaryPhy, interval, txPowerLevel,
- timeoutMillis);
+ secondaryPhy, interval, txPowerLevel);
}
}
}
\ No newline at end of file
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index c9f1d7a..67fd1c8 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -130,7 +130,6 @@
AdvertisingSetParameters.Builder parameters = new AdvertisingSetParameters.Builder();
parameters.setLegacyMode(true);
parameters.setConnectable(isConnectable);
- parameters.setTimeout(settings.getTimeout());
if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) {
parameters.setInterval(1600); // 1s
} else if (settings.getMode() == AdvertiseSettings.ADVERTISE_MODE_BALANCED) {
@@ -152,7 +151,7 @@
AdvertisingSetCallback wrapped = wrapOldCallback(callback, settings);
mLegacyAdvertisers.put(callback, wrapped);
startAdvertisingSet(parameters.build(), advertiseData, scanResponse, null, null,
- wrapped);
+ settings.getTimeout(), wrapped);
}
}
@@ -216,8 +215,8 @@
AdvertiseData advertiseData, AdvertiseData scanResponse,
PeriodicAdvertisingParameters periodicParameters,
AdvertiseData periodicData, AdvertisingSetCallback callback) {
- startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
- periodicData, callback, new Handler(Looper.getMainLooper()));
+ startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
+ periodicData, 0, callback, new Handler(Looper.getMainLooper()));
}
/**
@@ -237,6 +236,49 @@
PeriodicAdvertisingParameters periodicParameters,
AdvertiseData periodicData, AdvertisingSetCallback callback,
Handler handler) {
+ startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
+ periodicData, 0, callback, handler);
+ }
+
+ /**
+ * Creates a new advertising set. If operation succeed, device will start advertising. This
+ * method returns immediately, the operation status is delivered through
+ * {@code callback.onAdvertisingSetStarted()}.
+ * <p>
+ * @param parameters advertising set parameters.
+ * @param advertiseData Advertisement data to be broadcasted.
+ * @param scanResponse Scan response associated with the advertisement data.
+ * @param periodicData Periodic advertising data.
+ * @param timeoutMillis Advertising time limit. May not exceed 180000
+ * @param callback Callback for advertising set.
+ */
+ public void startAdvertisingSet(AdvertisingSetParameters parameters,
+ AdvertiseData advertiseData, AdvertiseData scanResponse,
+ PeriodicAdvertisingParameters periodicParameters,
+ AdvertiseData periodicData, int timeoutMillis,
+ AdvertisingSetCallback callback) {
+ startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
+ periodicData, timeoutMillis, callback, new Handler(Looper.getMainLooper()));
+ }
+
+ /**
+ * Creates a new advertising set. If operation succeed, device will start advertising. This
+ * method returns immediately, the operation status is delivered through
+ * {@code callback.onAdvertisingSetStarted()}.
+ * <p>
+ * @param parameters advertising set parameters.
+ * @param advertiseData Advertisement data to be broadcasted.
+ * @param scanResponse Scan response associated with the advertisement data.
+ * @param periodicData Periodic advertising data.
+ * @param timeoutMillis Advertising time limit. May not exceed 180000
+ * @param callback Callback for advertising set.
+ * @param handler thread upon which the callbacks will be invoked.
+ */
+ public void startAdvertisingSet(AdvertisingSetParameters parameters,
+ AdvertiseData advertiseData, AdvertiseData scanResponse,
+ PeriodicAdvertisingParameters periodicParameters,
+ AdvertiseData periodicData, int timeoutMillis,
+ AdvertisingSetCallback callback, Handler handler) {
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
if (callback == null) {
@@ -259,7 +301,7 @@
try {
gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
- periodicData, wrapped);
+ periodicData, timeoutMillis, wrapped);
} catch (RemoteException e) {
Log.e(TAG, "Failed to start advertising set - ", e);
throw new IllegalStateException("Failed to start advertising set");
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 4dc6fd2..bd31b03 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1104,18 +1104,7 @@
* @hide
*/
public static final String ACTION_CALL_PRIVILEGED = "android.intent.action.CALL_PRIVILEGED";
- /**
- * Activity action: Activate the current SIM card. If SIM cards do not require activation,
- * sending this intent is a no-op.
- * <p>Input: No data should be specified. get*Extra may have an optional
- * {@link #EXTRA_SIM_ACTIVATION_RESPONSE} field containing a PendingIntent through which to
- * send the activation result.
- * <p>Output: nothing.
- * @hide
- */
- @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_SIM_ACTIVATION_REQUEST =
- "android.intent.action.SIM_ACTIVATION_REQUEST";
+
/**
* Activity Action: Main entry point for carrier setup apps.
* <p>Carrier apps that provide an implementation for this action may be invoked to configure
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index b4d77a0..0b3742f 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -31,7 +31,7 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Printer;
-import android.util.SparseIntArray;
+import android.util.SparseArray;
import com.android.internal.util.ArrayUtils;
@@ -656,13 +656,15 @@
*
* The keys and values are all indices into the {@link #splitNames}, {@link #splitSourceDirs},
* and {@link #splitPublicSourceDirs} arrays.
- * Each key represents a split and its value is its parent split.
+ * Each key represents a split and its value is an array of splits. The first element of this
+ * array is the parent split, and the rest are configuration splits. These configuration splits
+ * have no dependencies themselves.
* Cycles do not exist because they are illegal and screened for during installation.
*
* May be null if no splits are installed, or if no dependencies exist between them.
* @hide
*/
- public SparseIntArray splitDependencies;
+ public SparseArray<int[]> splitDependencies;
/**
* Full paths to the locations of extra resource packages (runtime overlays)
@@ -1182,7 +1184,7 @@
dest.writeStringArray(splitNames);
dest.writeStringArray(splitSourceDirs);
dest.writeStringArray(splitPublicSourceDirs);
- dest.writeSparseIntArray(splitDependencies);
+ dest.writeSparseArray((SparseArray) splitDependencies);
dest.writeString(nativeLibraryDir);
dest.writeString(secondaryNativeLibraryDir);
dest.writeString(nativeLibraryRootDir);
@@ -1244,7 +1246,7 @@
splitNames = source.readStringArray();
splitSourceDirs = source.readStringArray();
splitPublicSourceDirs = source.readStringArray();
- splitDependencies = source.readSparseIntArray();
+ splitDependencies = source.readSparseArray(null);
nativeLibraryDir = source.readString();
secondaryNativeLibraryDir = source.readString();
nativeLibraryRootDir = source.readString();
diff --git a/core/java/android/content/pm/InstrumentationInfo.java b/core/java/android/content/pm/InstrumentationInfo.java
index 59c5307..f6f1be6 100644
--- a/core/java/android/content/pm/InstrumentationInfo.java
+++ b/core/java/android/content/pm/InstrumentationInfo.java
@@ -82,13 +82,15 @@
*
* The keys and values are all indices into the {@link #splitNames}, {@link #splitSourceDirs},
* and {@link #splitPublicSourceDirs} arrays.
- * Each key represents a split and its value is its parent split.
+ * Each key represents a split and its value is an array of splits. The first element of this
+ * array is the parent split, and the rest are configuration splits. These configuration splits
+ * have no dependencies themselves.
* Cycles do not exist because they are illegal and screened for during installation.
*
* May be null if no splits are installed, or if no dependencies exist between them.
* @hide
*/
- public SparseIntArray splitDependencies;
+ public SparseArray<int[]> splitDependencies;
/**
* Full path to a directory assigned to the package for its persistent data.
@@ -155,7 +157,7 @@
dest.writeStringArray(splitNames);
dest.writeStringArray(splitSourceDirs);
dest.writeStringArray(splitPublicSourceDirs);
- dest.writeSparseIntArray(splitDependencies);
+ dest.writeSparseArray((SparseArray) splitDependencies);
dest.writeString(dataDir);
dest.writeString(deviceProtectedDataDir);
dest.writeString(credentialProtectedDataDir);
@@ -185,7 +187,7 @@
splitNames = source.readStringArray();
splitSourceDirs = source.readStringArray();
splitPublicSourceDirs = source.readStringArray();
- splitDependencies = source.readSparseIntArray();
+ splitDependencies = source.readSparseArray(null);
dataDir = source.readString();
deviceProtectedDataDir = source.readString();
credentialProtectedDataDir = source.readString();
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index be6dc05..99aa1bc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -76,7 +76,7 @@
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
-import android.util.SparseIntArray;
+import android.util.SparseArray;
import android.util.TypedValue;
import android.util.apk.ApkSignatureSchemeV2Verifier;
import android.util.jar.StrictJarFile;
@@ -109,7 +109,6 @@
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
@@ -368,8 +367,12 @@
/** Names of any split APKs, ordered by parsed splitName */
public final String[] splitNames;
+ /** Names of any split APKs that are features. Ordered by splitName */
+ public final boolean[] isFeatureSplits;
+
/** Dependencies of any split APKs, ordered by parsed splitName */
public final String[] usesSplitNames;
+ public final String[] configForSplit;
/**
* Path where this package was found on disk. For monolithic packages
@@ -396,14 +399,16 @@
public final boolean isolatedSplits;
public PackageLite(String codePath, ApkLite baseApk, String[] splitNames,
- String[] usesSplitNames, String[] splitCodePaths,
- int[] splitRevisionCodes) {
+ boolean[] isFeatureSplits, String[] usesSplitNames, String[] configForSplit,
+ String[] splitCodePaths, int[] splitRevisionCodes) {
this.packageName = baseApk.packageName;
this.versionCode = baseApk.versionCode;
this.installLocation = baseApk.installLocation;
this.verifiers = baseApk.verifiers;
this.splitNames = splitNames;
+ this.isFeatureSplits = isFeatureSplits;
this.usesSplitNames = usesSplitNames;
+ this.configForSplit = configForSplit;
this.codePath = codePath;
this.baseCodePath = baseApk.codePath;
this.splitCodePaths = splitCodePaths;
@@ -434,6 +439,8 @@
public final String codePath;
public final String packageName;
public final String splitName;
+ public boolean isFeatureSplit;
+ public final String configForSplit;
public final String usesSplitName;
public final int versionCode;
public final int revisionCode;
@@ -448,14 +455,17 @@
public final boolean extractNativeLibs;
public final boolean isolatedSplits;
- public ApkLite(String codePath, String packageName, String splitName, String usesSplitName,
- int versionCode, int revisionCode, int installLocation, List<VerifierInfo> verifiers,
- Signature[] signatures, Certificate[][] certificates, boolean coreApp,
- boolean debuggable, boolean multiArch, boolean use32bitAbi,
- boolean extractNativeLibs, boolean isolatedSplits) {
+ public ApkLite(String codePath, String packageName, String splitName, boolean isFeatureSplit,
+ String configForSplit, String usesSplitName, int versionCode, int revisionCode,
+ int installLocation, List<VerifierInfo> verifiers, Signature[] signatures,
+ Certificate[][] certificates, boolean coreApp, boolean debuggable,
+ boolean multiArch, boolean use32bitAbi, boolean extractNativeLibs,
+ boolean isolatedSplits) {
this.codePath = codePath;
this.packageName = packageName;
this.splitName = splitName;
+ this.isFeatureSplit = isFeatureSplit;
+ this.configForSplit = configForSplit;
this.usesSplitName = usesSplitName;
this.versionCode = versionCode;
this.revisionCode = revisionCode;
@@ -811,10 +821,10 @@
final ApkLite baseApk = parseApkLite(packageFile, flags);
final String packagePath = packageFile.getAbsolutePath();
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- return new PackageLite(packagePath, baseApk, null, null, null, null);
+ return new PackageLite(packagePath, baseApk, null, null, null, null, null, null);
}
- private static PackageLite parseClusterPackageLite(File packageDir, int flags)
+ static PackageLite parseClusterPackageLite(File packageDir, int flags)
throws PackageParserException {
final File[] files = packageDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
@@ -869,12 +879,16 @@
final int size = apks.size();
String[] splitNames = null;
+ boolean[] isFeatureSplits = null;
String[] usesSplitNames = null;
+ String[] configForSplits = null;
String[] splitCodePaths = null;
int[] splitRevisionCodes = null;
if (size > 0) {
splitNames = new String[size];
+ isFeatureSplits = new boolean[size];
usesSplitNames = new String[size];
+ configForSplits = new String[size];
splitCodePaths = new String[size];
splitRevisionCodes = new int[size];
@@ -884,14 +898,16 @@
for (int i = 0; i < size; i++) {
final ApkLite apk = apks.get(splitNames[i]);
usesSplitNames[i] = apk.usesSplitName;
+ isFeatureSplits[i] = apk.isFeatureSplit;
+ configForSplits[i] = apk.configForSplit;
splitCodePaths[i] = apk.codePath;
splitRevisionCodes[i] = apk.revisionCode;
}
}
final String codePath = packageDir.getAbsolutePath();
- return new PackageLite(codePath, baseApk, splitNames, usesSplitNames, splitCodePaths,
- splitRevisionCodes);
+ return new PackageLite(codePath, baseApk, splitNames, isFeatureSplits, usesSplitNames,
+ configForSplits, splitCodePaths, splitRevisionCodes);
}
/**
@@ -1069,42 +1085,6 @@
}
}
- private static SparseIntArray buildSplitDependencyTree(PackageLite pkg)
- throws PackageParserException {
- SparseIntArray splitDependencies = new SparseIntArray();
- for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
- final String splitDependency = pkg.usesSplitNames[splitIdx];
- if (splitDependency != null) {
- final int depIdx = Arrays.binarySearch(pkg.splitNames, splitDependency);
- if (depIdx < 0) {
- throw new PackageParserException(
- PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Split '" + pkg.splitNames[splitIdx] + "' requires split '"
- + splitDependency + "', which is missing.");
- }
- splitDependencies.put(splitIdx, depIdx);
- }
- }
-
- // Verify that there are no cycles.
- final BitSet bitset = new BitSet();
- for (int i = 0; i < splitDependencies.size(); i++) {
- int splitIdx = splitDependencies.keyAt(i);
-
- bitset.clear();
- while (splitIdx != -1) {
- if (bitset.get(splitIdx)) {
- throw new PackageParserException(
- PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Cycle detected in split dependencies.");
- }
- bitset.set(splitIdx);
- splitIdx = splitDependencies.get(splitIdx, -1);
- }
- }
- return splitDependencies.size() != 0 ? splitDependencies : null;
- }
-
/**
* Parse all APKs contained in the given directory, treating them as a
* single package. This also performs sanity checking, such as requiring
@@ -1122,11 +1102,15 @@
}
// Build the split dependency tree.
- SparseIntArray splitDependencies = null;
+ SparseArray<int[]> splitDependencies = null;
final SplitAssetLoader assetLoader;
if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
- splitDependencies = buildSplitDependencyTree(lite);
- assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
+ try {
+ splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
+ assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
+ } catch (SplitAssetDependencyLoader.IllegalDependencyException e) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST, e.getMessage());
+ }
} else {
assetLoader = new DefaultSplitAssetLoader(lite, flags);
}
@@ -1762,6 +1746,8 @@
boolean use32bitAbi = false;
boolean extractNativeLibs = true;
boolean isolatedSplits = false;
+ boolean isFeatureSplit = false;
+ String configForSplit = null;
String usesSplitName = null;
for (int i = 0; i < attrs.getAttributeCount(); i++) {
@@ -1777,6 +1763,10 @@
coreApp = attrs.getAttributeBooleanValue(i, false);
} else if (attr.equals("isolatedSplits")) {
isolatedSplits = attrs.getAttributeBooleanValue(i, false);
+ } else if (attr.equals("configForSplit")) {
+ configForSplit = attrs.getAttributeValue(i);
+ } else if (attr.equals("isFeatureSplit")) {
+ isFeatureSplit = attrs.getAttributeBooleanValue(i, false);
}
}
@@ -1831,10 +1821,10 @@
}
}
- return new ApkLite(codePath, packageSplit.first, packageSplit.second, usesSplitName,
- versionCode, revisionCode, installLocation, verifiers, signatures,
- certificates, coreApp, debuggable, multiArch, use32bitAbi, extractNativeLibs,
- isolatedSplits);
+ return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
+ configForSplit, usesSplitName, versionCode, revisionCode, installLocation,
+ verifiers, signatures, certificates, coreApp, debuggable, multiArch, use32bitAbi,
+ extractNativeLibs, isolatedSplits);
}
/**
diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
index 4df90eb..16023f0 100644
--- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
+++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -18,10 +18,11 @@
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
+import android.annotation.NonNull;
import android.content.pm.PackageParser;
import android.content.res.AssetManager;
import android.os.Build;
-import android.util.SparseIntArray;
+import android.util.SparseArray;
import libcore.io.IoUtils;
@@ -34,49 +35,31 @@
* @hide
*/
public class SplitAssetDependencyLoader
- extends SplitDependencyLoaderHelper<PackageParser.PackageParserException>
+ extends SplitDependencyLoader<PackageParser.PackageParserException>
implements SplitAssetLoader {
- private static final int BASE_ASSET_PATH_IDX = -1;
- private final String mBasePath;
- private final String[] mSplitNames;
private final String[] mSplitPaths;
private final int mFlags;
- private String[] mCachedBasePaths;
- private AssetManager mCachedBaseAssetManager;
-
- private String[][] mCachedSplitPaths;
+ private String[][] mCachedPaths;
private AssetManager[] mCachedAssetManagers;
- public SplitAssetDependencyLoader(PackageParser.PackageLite pkg, SparseIntArray dependencies,
- int flags) {
+ public SplitAssetDependencyLoader(PackageParser.PackageLite pkg,
+ SparseArray<int[]> dependencies, int flags) {
super(dependencies);
- mBasePath = pkg.baseCodePath;
- mSplitNames = pkg.splitNames;
- mSplitPaths = pkg.splitCodePaths;
+
+ // The base is inserted into index 0, so we need to shift all the splits by 1.
+ mSplitPaths = new String[pkg.splitCodePaths.length + 1];
+ mSplitPaths[0] = pkg.baseCodePath;
+ System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length);
+
mFlags = flags;
- mCachedBasePaths = null;
- mCachedBaseAssetManager = null;
- mCachedSplitPaths = new String[mSplitNames.length][];
- mCachedAssetManagers = new AssetManager[mSplitNames.length];
+ mCachedPaths = new String[mSplitPaths.length][];
+ mCachedAssetManagers = new AssetManager[mSplitPaths.length];
}
@Override
protected boolean isSplitCached(int splitIdx) {
- if (splitIdx != -1) {
- return mCachedAssetManagers[splitIdx] != null;
- }
- return mCachedBaseAssetManager != null;
- }
-
- // Adds all non-code configuration splits for this split name. The split name is expected
- // to represent a feature split.
- private void addAllConfigSplits(String splitName, ArrayList<String> outAssetPaths) {
- for (int i = 0; i < mSplitNames.length; i++) {
- if (isConfigurationSplitOf(mSplitNames[i], splitName)) {
- outAssetPaths.add(mSplitPaths[i]);
- }
- }
+ return mCachedAssetManagers[splitIdx] != null;
}
private static AssetManager createAssetManagerWithPaths(String[] assetPaths, int flags)
@@ -107,45 +90,38 @@
}
@Override
- protected void constructSplit(int splitIdx, int parentSplitIdx) throws
- PackageParser.PackageParserException {
+ protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
+ int parentSplitIdx) throws PackageParser.PackageParserException {
final ArrayList<String> assetPaths = new ArrayList<>();
- if (splitIdx == BASE_ASSET_PATH_IDX) {
- assetPaths.add(mBasePath);
- addAllConfigSplits(null, assetPaths);
- mCachedBasePaths = assetPaths.toArray(new String[assetPaths.size()]);
- mCachedBaseAssetManager = createAssetManagerWithPaths(mCachedBasePaths, mFlags);
- return;
- }
-
- if (parentSplitIdx == BASE_ASSET_PATH_IDX) {
- Collections.addAll(assetPaths, mCachedBasePaths);
- } else {
- Collections.addAll(assetPaths, mCachedSplitPaths[parentSplitIdx]);
+ if (parentSplitIdx >= 0) {
+ Collections.addAll(assetPaths, mCachedPaths[parentSplitIdx]);
}
assetPaths.add(mSplitPaths[splitIdx]);
- addAllConfigSplits(mSplitNames[splitIdx], assetPaths);
- mCachedSplitPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]);
- mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedSplitPaths[splitIdx],
+ for (int configSplitIdx : configSplitIndices) {
+ assetPaths.add(mSplitPaths[configSplitIdx]);
+ }
+ mCachedPaths[splitIdx] = assetPaths.toArray(new String[assetPaths.size()]);
+ mCachedAssetManagers[splitIdx] = createAssetManagerWithPaths(mCachedPaths[splitIdx],
mFlags);
}
@Override
public AssetManager getBaseAssetManager() throws PackageParser.PackageParserException {
- loadDependenciesForSplit(BASE_ASSET_PATH_IDX);
- return mCachedBaseAssetManager;
+ loadDependenciesForSplit(0);
+ return mCachedAssetManagers[0];
}
@Override
public AssetManager getSplitAssetManager(int idx) throws PackageParser.PackageParserException {
- loadDependenciesForSplit(idx);
- return mCachedAssetManagers[idx];
+ // Since we insert the base at position 0, and PackageParser keeps splits separate from
+ // the base, we need to adjust the index.
+ loadDependenciesForSplit(idx + 1);
+ return mCachedAssetManagers[idx + 1];
}
@Override
public void close() throws Exception {
- IoUtils.closeQuietly(mCachedBaseAssetManager);
for (AssetManager assets : mCachedAssetManagers) {
IoUtils.closeQuietly(assets);
}
diff --git a/core/java/android/content/pm/split/SplitDependencyLoader.java b/core/java/android/content/pm/split/SplitDependencyLoader.java
new file mode 100644
index 0000000..3586546
--- /dev/null
+++ b/core/java/android/content/pm/split/SplitDependencyLoader.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm.split;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.content.pm.PackageParser;
+import android.util.IntArray;
+import android.util.SparseArray;
+
+import libcore.util.EmptyArray;
+
+import java.util.Arrays;
+import java.util.BitSet;
+
+/**
+ * A helper class that implements the dependency tree traversal for splits. Callbacks
+ * are implemented by subclasses to notify whether a split has already been constructed
+ * and is cached, and to actually create the split requested.
+ *
+ * This helper is meant to be subclassed so as to reduce the number of allocations
+ * needed to make use of it.
+ *
+ * All inputs and outputs are assumed to be indices into an array of splits.
+ *
+ * @hide
+ */
+public abstract class SplitDependencyLoader<E extends Exception> {
+ private final @NonNull SparseArray<int[]> mDependencies;
+
+ /**
+ * Construct a new SplitDependencyLoader. Meant to be called from the
+ * subclass constructor.
+ * @param dependencies The dependency tree of splits.
+ */
+ protected SplitDependencyLoader(@NonNull SparseArray<int[]> dependencies) {
+ mDependencies = dependencies;
+ }
+
+ /**
+ * Traverses the dependency tree and constructs any splits that are not already
+ * cached. This routine short-circuits and skips the creation of splits closer to the
+ * root if they are cached, as reported by the subclass implementation of
+ * {@link #isSplitCached(int)}. The construction of splits is delegated to the subclass
+ * implementation of {@link #constructSplit(int, int[], int)}.
+ * @param splitIdx The index of the split to load. 0 represents the base Application.
+ */
+ protected void loadDependenciesForSplit(@IntRange(from = 0) int splitIdx) throws E {
+ // Quick check before any allocations are done.
+ if (isSplitCached(splitIdx)) {
+ return;
+ }
+
+ // Special case the base, since it has no dependencies.
+ if (splitIdx == 0) {
+ final int[] configSplitIndices = collectConfigSplitIndices(0);
+ constructSplit(0, configSplitIndices, -1);
+ return;
+ }
+
+ // Build up the dependency hierarchy.
+ final IntArray linearDependencies = new IntArray();
+ linearDependencies.add(splitIdx);
+
+ // Collect all the dependencies that need to be constructed.
+ // They will be listed from leaf to root.
+ while (true) {
+ // Only follow the first index into the array. The others are config splits and
+ // get loaded with the split.
+ final int[] deps = mDependencies.get(splitIdx);
+ if (deps != null && deps.length > 0) {
+ splitIdx = deps[0];
+ } else {
+ splitIdx = -1;
+ }
+
+ if (splitIdx < 0 || isSplitCached(splitIdx)) {
+ break;
+ }
+
+ linearDependencies.add(splitIdx);
+ }
+
+ // Visit each index, from right to left (root to leaf).
+ int parentIdx = splitIdx;
+ for (int i = linearDependencies.size() - 1; i >= 0; i--) {
+ final int idx = linearDependencies.get(i);
+ final int[] configSplitIndices = collectConfigSplitIndices(idx);
+ constructSplit(idx, configSplitIndices, parentIdx);
+ parentIdx = idx;
+ }
+ }
+
+ private @NonNull int[] collectConfigSplitIndices(int splitIdx) {
+ // The config splits appear after the first element.
+ final int[] deps = mDependencies.get(splitIdx);
+ if (deps == null || deps.length <= 1) {
+ return EmptyArray.INT;
+ }
+ return Arrays.copyOfRange(deps, 1, deps.length);
+ }
+
+ /**
+ * Subclass to report whether the split at `splitIdx` is cached and need not be constructed.
+ * It is assumed that if `splitIdx` is cached, any parent of `splitIdx` is also cached.
+ * @param splitIdx The index of the split to check for in the cache.
+ * @return true if the split is cached and does not need to be constructed.
+ */
+ protected abstract boolean isSplitCached(@IntRange(from = 0) int splitIdx);
+
+ /**
+ * Subclass to construct a split at index `splitIdx` with parent split `parentSplitIdx`.
+ * The result is expected to be cached by the subclass in its own structures.
+ * @param splitIdx The index of the split to construct. 0 represents the base Application.
+ * @param configSplitIndices The array of configuration splits to load along with this split.
+ * May be empty (length == 0) but never null.
+ * @param parentSplitIdx The index of the parent split. -1 if there is no parent.
+ * @throws E Subclass defined exception representing failure to construct a split.
+ */
+ protected abstract void constructSplit(@IntRange(from = 0) int splitIdx,
+ @NonNull @IntRange(from = 1) int[] configSplitIndices,
+ @IntRange(from = -1) int parentSplitIdx) throws E;
+
+ public static class IllegalDependencyException extends Exception {
+ private IllegalDependencyException(String message) {
+ super(message);
+ }
+ }
+
+ private static int[] append(int[] src, int elem) {
+ if (src == null) {
+ return new int[] { elem };
+ }
+ int[] dst = Arrays.copyOf(src, src.length + 1);
+ dst[src.length] = elem;
+ return dst;
+ }
+
+ public static @NonNull SparseArray<int[]> createDependenciesFromPackage(
+ PackageParser.PackageLite pkg) throws IllegalDependencyException {
+ // The data structure that holds the dependencies. In PackageParser, splits are stored
+ // in their own array, separate from the base. We treat all paths as equals, so
+ // we need to insert the base as index 0, and shift all other splits.
+ final SparseArray<int[]> splitDependencies = new SparseArray<>();
+
+ // The base depends on nothing.
+ splitDependencies.put(0, new int[] {-1});
+
+ // First write out the <uses-split> dependencies. These must appear first in the
+ // array of ints, as is convention in this class.
+ for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
+ if (!pkg.isFeatureSplits[splitIdx]) {
+ // Non-feature splits don't have dependencies.
+ continue;
+ }
+
+ // Implicit dependency on the base.
+ final int targetIdx;
+ final String splitDependency = pkg.usesSplitNames[splitIdx];
+ if (splitDependency != null) {
+ final int depIdx = Arrays.binarySearch(pkg.splitNames, splitDependency);
+ if (depIdx < 0) {
+ throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+ + "' requires split '" + splitDependency + "', which is missing.");
+ }
+ targetIdx = depIdx + 1;
+ } else {
+ // Implicitly depend on the base.
+ targetIdx = 0;
+ }
+ splitDependencies.put(splitIdx + 1, new int[] {targetIdx});
+ }
+
+ // Write out the configForSplit reverse-dependencies. These appear after the <uses-split>
+ // dependencies and are considered leaves.
+ //
+ // At this point, all splits in splitDependencies have the first element in their array set.
+ for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
+ if (pkg.isFeatureSplits[splitIdx]) {
+ // Feature splits are not configForSplits.
+ continue;
+ }
+
+ // Implicit feature for the base.
+ final int targetSplitIdx;
+ final String configForSplit = pkg.configForSplit[splitIdx];
+ if (configForSplit != null) {
+ final int depIdx = Arrays.binarySearch(pkg.splitNames, configForSplit);
+ if (depIdx < 0) {
+ throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+ + "' targets split '" + configForSplit + "', which is missing.");
+ }
+
+ if (!pkg.isFeatureSplits[depIdx]) {
+ throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+ + "' declares itself as configuration split for a non-feature split '"
+ + pkg.splitNames[depIdx] + "'");
+ }
+ targetSplitIdx = depIdx + 1;
+ } else {
+ targetSplitIdx = 0;
+ }
+ splitDependencies.put(targetSplitIdx,
+ append(splitDependencies.get(targetSplitIdx), splitIdx + 1));
+ }
+
+ // Verify that there are no cycles.
+ final BitSet bitset = new BitSet();
+ for (int i = 0, size = splitDependencies.size(); i < size; i++) {
+ int splitIdx = splitDependencies.keyAt(i);
+
+ bitset.clear();
+ while (splitIdx != -1) {
+ // Check if this split has been visited yet.
+ if (bitset.get(splitIdx)) {
+ throw new IllegalDependencyException("Cycle detected in split dependencies.");
+ }
+
+ // Mark the split so that if we visit it again, we no there is a cycle.
+ bitset.set(splitIdx);
+
+ // Follow the first dependency only, the others are leaves by definition.
+ final int[] deps = splitDependencies.get(splitIdx);
+ splitIdx = deps != null ? deps[0] : -1;
+ }
+ }
+ return splitDependencies;
+ }
+}
diff --git a/core/java/android/content/pm/split/SplitDependencyLoaderHelper.java b/core/java/android/content/pm/split/SplitDependencyLoaderHelper.java
deleted file mode 100644
index b493480..0000000
--- a/core/java/android/content/pm/split/SplitDependencyLoaderHelper.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.content.pm.split;
-
-import android.annotation.Nullable;
-import android.util.IntArray;
-import android.util.SparseIntArray;
-
-/**
- * A helper class that implements the dependency tree traversal for splits. Callbacks
- * are implemented by subclasses to notify whether a split has already been constructed
- * and is cached, and to actually create the split requested.
- *
- * This helper is meant to be subclassed so as to reduce the number of allocations
- * needed to make use of it.
- *
- * All inputs and outputs are assumed to be indices into an array of splits.
- *
- * @hide
- */
-public abstract class SplitDependencyLoaderHelper<E extends Exception> {
- @Nullable private final SparseIntArray mDependencies;
-
- /**
- * Construct a new SplitDependencyLoaderHelper. Meant to be called from the
- * subclass constructor.
- * @param dependencies The dependency tree of splits. Can be null, which leads to
- * just the implicit dependency of all splits on the base.
- */
- protected SplitDependencyLoaderHelper(@Nullable SparseIntArray dependencies) {
- mDependencies = dependencies;
- }
-
- /**
- * Traverses the dependency tree and constructs any splits that are not already
- * cached. This routine short-circuits and skips the creation of splits closer to the
- * root if they are cached, as reported by the subclass implementation of
- * {@link #isSplitCached(int)}. The construction of splits is delegated to the subclass
- * implementation of {@link #constructSplit(int, int)}.
- * @param splitIdx The index of the split to load. Can be -1, which represents the
- * base Application.
- */
- protected void loadDependenciesForSplit(int splitIdx) throws E {
- // Quick check before any allocations are done.
- if (isSplitCached(splitIdx)) {
- return;
- }
-
- final IntArray linearDependencies = new IntArray();
- linearDependencies.add(splitIdx);
-
- // Collect all the dependencies that need to be constructed.
- // They will be listed from leaf to root.
- while (splitIdx >= 0) {
- splitIdx = mDependencies != null ? mDependencies.get(splitIdx, -1) : -1;
- if (isSplitCached(splitIdx)) {
- break;
- }
- linearDependencies.add(splitIdx);
- }
-
- // Visit each index, from right to left (root to leaf).
- int parentIdx = splitIdx;
- for (int i = linearDependencies.size() - 1; i >= 0; i--) {
- final int idx = linearDependencies.get(i);
- constructSplit(idx, parentIdx);
- parentIdx = idx;
- }
- }
-
- /**
- * Subclass to report whether the split at `splitIdx` is cached and need not be constructed.
- * It is assumed that if `splitIdx` is cached, any parent of `splitIdx` is also cached.
- * @param splitIdx The index of the split to check for in the cache.
- * @return true if the split is cached and does not need to be constructed.
- */
- protected abstract boolean isSplitCached(int splitIdx);
-
- /**
- * Subclass to construct a split at index `splitIdx` with parent split `parentSplitIdx`.
- * The result is expected to be cached by the subclass in its own structures.
- * @param splitIdx The index of the split to construct. Can be -1, which represents the
- * base Application.
- * @param parentSplitIdx The index of the parent split. Can be -1, which represents the
- * base Application.
- * @throws E
- */
- protected abstract void constructSplit(int splitIdx, int parentSplitIdx) throws E;
-
- /**
- * Returns true if `splitName` represents a Configuration split of `featureSplitName`.
- *
- * A Configuration split's name is prefixed with the associated Feature split's name
- * or the empty string if the split is for the base Application APK. It is then followed by the
- * dollar sign character "$" and some unique string that should represent the configurations
- * the split contains.
- *
- * Example:
- * <table>
- * <tr>
- * <th>Feature split name</th>
- * <th>Configuration split name: xhdpi</th>
- * <th>Configuration split name: fr-rFR</th>
- * </tr>
- * <tr>
- * <td>(base APK)</td>
- * <td><code>$xhdpi</code></td>
- * <td><code>$fr-rFR</code></td>
- * </tr>
- * <tr>
- * <td><code>Extras</code></td>
- * <td><code>Extras$xhdpi</code></td>
- * <td><code>Extras$fr-rFR</code></td>
- * </tr>
- * </table>
- *
- * @param splitName The name of the split to check.
- * @param featureSplitName The name of the Feature split. May be null or "" if checking
- * the base Application APK.
- * @return true if the splitName represents a Configuration split of featureSplitName.
- */
- protected static boolean isConfigurationSplitOf(String splitName, String featureSplitName) {
- if (featureSplitName == null || featureSplitName.length() == 0) {
- // We are looking for configuration splits of the base, which have some legacy support.
- if (splitName.startsWith("config_")) {
- return true;
- } else if (splitName.startsWith("$")) {
- return true;
- } else {
- return false;
- }
- } else {
- return splitName.startsWith(featureSplitName + "$");
- }
- }
-}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index a638cd4..f6712f8 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -22,6 +22,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.Activity;
@@ -763,6 +764,16 @@
public static final int PIN_VERIFICATION_SUCCESS = -1;
/**
+ * Sent when user restrictions have changed.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi // To allow seeing it from CTS.
+ public static final String ACTION_USER_RESTRICTIONS_CHANGED =
+ "android.os.action.USER_RESTRICTIONS_CHANGED";
+
+ /**
* Error result indicating that this user is not allowed to add other users on this device.
* This is a result code returned from the activity created by the intent
* {@link #createUserCreationIntent(String, String, String, PersistableBundle)}.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 391ee83..7b84f689 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6630,6 +6630,8 @@
* This value is only used for managed profiles.
* @hide
*/
+ @TestApi
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
/** @hide */
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 3822138..519a7dd 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -58,6 +58,7 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
@@ -159,20 +160,6 @@
private SearchableInfo mSearchable;
private Bundle mAppSearchData;
- /*
- * SearchView can be set expanded before the IME is ready to be shown during
- * initial UI setup. The show operation is asynchronous to account for this.
- */
- private Runnable mShowImeRunnable = new Runnable() {
- public void run() {
- InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
-
- if (imm != null) {
- imm.showSoftInputUnchecked(0, null);
- }
- }
- };
-
private Runnable mUpdateDrawableStateRunnable = new Runnable() {
public void run() {
updateFocusedState();
@@ -497,9 +484,9 @@
@Override
public void clearFocus() {
mClearingFocus = true;
- setImeVisibility(false);
super.clearFocus();
mSearchSrcTextView.clearFocus();
+ mSearchSrcTextView.setImeVisibility(false);
mClearingFocus = false;
}
@@ -967,19 +954,6 @@
super.onDetachedFromWindow();
}
- private void setImeVisibility(final boolean visible) {
- if (visible) {
- post(mShowImeRunnable);
- } else {
- removeCallbacks(mShowImeRunnable);
- InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
-
- if (imm != null) {
- imm.hideSoftInputFromWindow(getWindowToken(), 0);
- }
- }
- }
-
/**
* Called by the SuggestionsAdapter
* @hide
@@ -1286,7 +1260,7 @@
if (mSearchable != null) {
launchQuerySearch(KeyEvent.KEYCODE_UNKNOWN, null, query.toString());
}
- setImeVisibility(false);
+ mSearchSrcTextView.setImeVisibility(false);
dismissSuggestions();
}
}
@@ -1311,7 +1285,7 @@
} else {
mSearchSrcTextView.setText("");
mSearchSrcTextView.requestFocus();
- setImeVisibility(true);
+ mSearchSrcTextView.setImeVisibility(true);
}
}
@@ -1319,7 +1293,7 @@
private void onSearchClicked() {
updateViewsVisibility(false);
mSearchSrcTextView.requestFocus();
- setImeVisibility(true);
+ mSearchSrcTextView.setImeVisibility(true);
if (mOnSearchClickListener != null) {
mOnSearchClickListener.onClick(this);
}
@@ -1477,7 +1451,7 @@
if (mOnSuggestionListener == null
|| !mOnSuggestionListener.onSuggestionClick(position)) {
launchSuggestion(position, KeyEvent.KEYCODE_UNKNOWN, null);
- setImeVisibility(false);
+ mSearchSrcTextView.setImeVisibility(false);
dismissSuggestions();
return true;
}
@@ -1910,6 +1884,9 @@
private int mThreshold;
private SearchView mSearchView;
+ private boolean mHasPendingShowSoftInputRequest;
+ final Runnable mRunShowSoftInputIfNecessary = () -> showSoftInputIfNecessary();
+
public SearchAutoComplete(Context context) {
super(context);
mThreshold = getThreshold();
@@ -1983,11 +1960,13 @@
super.onWindowFocusChanged(hasWindowFocus);
if (hasWindowFocus && mSearchView.hasFocus() && getVisibility() == VISIBLE) {
- InputMethodManager inputManager =
- getContext().getSystemService(InputMethodManager.class);
- inputManager.showSoftInput(this, 0);
- // If in landscape mode, then make sure that
- // the ime is in front of the dropdown.
+ // Since InputMethodManager#onPostWindowFocus() will be called after this callback,
+ // it is a bit too early to call InputMethodManager#showSoftInput() here. We still
+ // need to wait until the system calls back onCreateInputConnection() to call
+ // InputMethodManager#showSoftInput().
+ mHasPendingShowSoftInputRequest = true;
+
+ // If in landscape mode, then make sure that the ime is in front of the dropdown.
if (isLandscapeMode(getContext())) {
ensureImeVisible(true);
}
@@ -2027,7 +2006,7 @@
}
if (event.isTracking() && !event.isCanceled()) {
mSearchView.clearFocus();
- mSearchView.setImeVisibility(false);
+ setImeVisibility(false);
return true;
}
}
@@ -2051,5 +2030,51 @@
};
return 160;
}
+
+ /**
+ * We override {@link View#onCreateInputConnection(EditorInfo)} as a signal to schedule a
+ * pending {@link InputMethodManager#showSoftInput(View, int)} request (if any).
+ */
+ @Override
+ public InputConnection onCreateInputConnection(EditorInfo editorInfo) {
+ final InputConnection ic = super.onCreateInputConnection(editorInfo);
+ if (mHasPendingShowSoftInputRequest) {
+ removeCallbacks(mRunShowSoftInputIfNecessary);
+ post(mRunShowSoftInputIfNecessary);
+ }
+ return ic;
+ }
+
+ private void showSoftInputIfNecessary() {
+ if (mHasPendingShowSoftInputRequest) {
+ final InputMethodManager imm =
+ getContext().getSystemService(InputMethodManager.class);
+ imm.showSoftInput(this, 0);
+ mHasPendingShowSoftInputRequest = false;
+ }
+ }
+
+ private void setImeVisibility(final boolean visible) {
+ final InputMethodManager imm = getContext().getSystemService(InputMethodManager.class);
+ if (!visible) {
+ mHasPendingShowSoftInputRequest = false;
+ removeCallbacks(mRunShowSoftInputIfNecessary);
+ imm.hideSoftInputFromWindow(getWindowToken(), 0);
+ return;
+ }
+
+ if (imm.isActive(this)) {
+ // This means that SearchAutoComplete is already connected to the IME.
+ // InputMethodManager#showSoftInput() is guaranteed to pass client-side focus check.
+ mHasPendingShowSoftInputRequest = false;
+ removeCallbacks(mRunShowSoftInputIfNecessary);
+ imm.showSoftInput(this, 0);
+ return;
+ }
+
+ // Otherwise, InputMethodManager#showSoftInput() should be deferred after
+ // onCreateInputConnection().
+ mHasPendingShowSoftInputRequest = true;
+ }
}
}
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 67050f7..cf6bd9e 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1014,6 +1014,15 @@
<p>The default value of this attribute is <code>false</code>. -->
<attr name="isolatedSplits" format="boolean" />
+ <!-- If set to <code>true</code>, indicates to the platform that this APK is
+ a 'feature' split and that it implicitly depends on the base APK. This distinguishes
+ this split APK from a 'configuration' split, which provides resource overrides
+ for a particular 'feature' split. Only useful when the base APK specifies
+ <code>android:isolatedSplits="true"</code>.
+
+ <p>The default value of this attribute is <code>false</code>. -->
+ <attr name="isFeatureSplit" format="boolean" />
+
<!-- Extra options for an activity's UI. Applies to either the {@code <activity>} or
{@code <application>} tag. If specified on the {@code <application>}
tag these will be considered defaults for all activities in the
@@ -1286,6 +1295,7 @@
<attr name="sharedUserLabel" />
<attr name="installLocation" />
<attr name="isolatedSplits" />
+ <attr name="isFeatureSplit" />
<attr name="targetSandboxVersion" />
</declare-styleable>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2897c62..f965c69 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2807,6 +2807,7 @@
<public name="importantForAutofill" />
<public name="recycleEnabled"/>
<public name="isStatic" />
+ <public name="isFeatureSplit" />
</public-group>
<public-group type="style" first-id="0x010302e0">
diff --git a/docs/html/topic/libraries/support-library/revisions.jd b/docs/html/topic/libraries/support-library/revisions.jd
index 9a24d15..adb1af5 100644
--- a/docs/html/topic/libraries/support-library/revisions.jd
+++ b/docs/html/topic/libraries/support-library/revisions.jd
@@ -316,14 +316,13 @@
implementations, as well as any calls to this method, should be removed.
</li>
- <li>{@link android.support.v4.media.session.MediaSessionCompat#obtain
+ <li>{@code
MediaSessionCompat.obtain()} has been deprecated and replaced with the more
appropriately-named method
<a href="/reference/android/support/v4/media/session/MediaSessionCompat.html#fromMediaSession"><code>fromMediaSession()</code></a>.
</li>
- <li>{@link
- android.support.v4.media.session.MediaSessionCompat.QueueItem#obtain
+ <li>{@code
MediaSessionCompat.QueueItem.obtain()} has been deprecated and replaced with
the more appropriately-named method
<a href="/reference/android/support/v4/media/session/MediaSessionCompat.QueueItem.html#fromQueueItem"><code>fromQueueItem()</code></a>.
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index b8d95e4..359cfac 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -4765,7 +4765,6 @@
&& (targetTypeLen = attrPrivate.size())
);
}
- break;
}
return 0;
}
diff --git a/libs/androidfw/tests/data/basic/R.h b/libs/androidfw/tests/data/basic/R.h
index 8e9741e..94a2a14 100644
--- a/libs/androidfw/tests/data/basic/R.h
+++ b/libs/androidfw/tests/data/basic/R.h
@@ -44,8 +44,8 @@
density = 0x7f030002,
// From feature
- test3 = 0x7f080000,
- test4 = 0x7f080001,
+ test3 = 0x80020000,
+ test4 = 0x80020001,
};
};
@@ -57,7 +57,7 @@
ref2 = 0x7f040003,
// From feature
- number3 = 0x7f090000,
+ number3 = 0x80030000,
};
};
diff --git a/libs/androidfw/tests/data/feature/AndroidManifest.xml b/libs/androidfw/tests/data/feature/AndroidManifest.xml
index c972372..12ca5b6 100644
--- a/libs/androidfw/tests/data/feature/AndroidManifest.xml
+++ b/libs/androidfw/tests/data/feature/AndroidManifest.xml
@@ -15,5 +15,6 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.basic">
+ package="com.android.basic"
+ featureName="feature">
</manifest>
diff --git a/libs/androidfw/tests/data/feature/build b/libs/androidfw/tests/data/feature/build
index 6ed3e41..aa2f716 100755
--- a/libs/androidfw/tests/data/feature/build
+++ b/libs/androidfw/tests/data/feature/build
@@ -19,4 +19,10 @@
PATH_TO_FRAMEWORK_RES=${ANDROID_BUILD_TOP}/prebuilts/sdk/current/android.jar
-aapt package -M AndroidManifest.xml -S res -I $PATH_TO_FRAMEWORK_RES --feature-of ../basic/basic.apk -F feature.apk -f
+aapt2 compile --dir res -o compiled.flata
+aapt2 link -o feature.apk \
+ --manifest AndroidManifest.xml \
+ -I $PATH_TO_FRAMEWORK_RES \
+ -I ../basic/basic.apk \
+ --package-id 0x80 \
+ compiled.flata
diff --git a/libs/androidfw/tests/data/feature/feature.apk b/libs/androidfw/tests/data/feature/feature.apk
index 767fed6..a0dae38 100644
--- a/libs/androidfw/tests/data/feature/feature.apk
+++ b/libs/androidfw/tests/data/feature/feature.apk
Binary files differ
diff --git a/libs/androidfw/tests/data/feature/res/values/values.xml b/libs/androidfw/tests/data/feature/res/values/values.xml
index 59f7d93..43e1517 100644
--- a/libs/androidfw/tests/data/feature/res/values/values.xml
+++ b/libs/androidfw/tests/data/feature/res/values/values.xml
@@ -15,13 +15,12 @@
-->
<resources>
- <!-- Features are offset, so 7f020000 will become 7f080000 at runtime. -->
- <public type="string" name="test3" id="0x7f020000" />
+ <public type="string" name="test3" id="0x80020000" />
<string name="test3">test3</string>
- <public type="string" name="test4" id="0x7f020001" />
+ <public type="string" name="test4" id="0x80020001" />
<string name="test4">test4</string>
- <public type="integer" name="number3" id="0x7f030000" />
+ <public type="integer" name="number3" id="0x80030000" />
<integer name="number3">200</integer>
</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index c64574f..1010a8a 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -775,7 +775,7 @@
<string name="daltonizer_type_overridden">Overridden by <xliff:g id="title" example="Simulate color space">%1$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for estimated remaining duration of battery charging/discharging -->
- <string name="power_remaining_duration_only">Approx. <xliff:g id="time">%1$s</xliff:g> left</string>
+ <string name="power_remaining_duration_only">about <xliff:g id="time">%1$s</xliff:g> left</string>
<!-- [CHAR_LIMIT=40] Short label for estimated remaining duration of battery charging/discharging -->
<string name="power_remaining_duration_only_short"><xliff:g id="time">%1$s</xliff:g> left</string>
@@ -793,25 +793,25 @@
<xliff:g id="state">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> -
- <xliff:g id="time">%2$s</xliff:g> until full</string>
+ <xliff:g id="time">%2$s</xliff:g> until fully charged</string>
<!-- [CHAR_LIMIT=40] Short label for battery level chart when charging with duration -->
<string name="power_charging_duration_short"><xliff:g id="level">%1$s</xliff:g> -
<xliff:g id="time">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration_ac"><xliff:g id="level">%1$s</xliff:g> -
- <xliff:g id="time">%2$s</xliff:g> until full on AC</string>
+ <xliff:g id="time">%2$s</xliff:g> until fully charged on AC</string>
<!-- [CHAR_LIMIT=40] Short label for battery level chart when charging with duration -->
<string name="power_charging_duration_ac_short"><xliff:g id="level">%1$s</xliff:g> -
<xliff:g id="time">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration_usb"><xliff:g id="level">%1$s</xliff:g> -
- <xliff:g id="time">%2$s</xliff:g> until full over USB</string>
+ <xliff:g id="time">%2$s</xliff:g> until fully charged over USB</string>
<!-- [CHAR_LIMIT=40] Short label for battery level chart when charging with duration -->
<string name="power_charging_duration_usb_short"><xliff:g id="level">%1$s</xliff:g> -
<xliff:g id="time">%2$s</xliff:g></string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration_wireless"><xliff:g id="level">%1$s</xliff:g> -
- <xliff:g id="time">%2$s</xliff:g> until full from wireless</string>
+ <xliff:g id="time">%2$s</xliff:g> until fully charged from wireless</string>
<!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
<string name="power_charging_duration_wireless_short"><xliff:g id="level">%1$s</xliff:g> -
<xliff:g id="time">%2$s</xliff:g></string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
index 3fc999f..f6f8168 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -48,7 +48,7 @@
/**
* List of the category's children
*/
- public List<Tile> tiles = new ArrayList<Tile>();
+ public List<Tile> tiles = new ArrayList<>();
public DashboardCategory() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 457ce76..af247bd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -30,7 +30,6 @@
import android.provider.Settings;
import android.util.ArraySet;
import android.util.Log;
-import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -40,10 +39,8 @@
import android.widget.Toolbar;
import com.android.settingslib.R;
-import com.android.settingslib.applications.InterestingConfigChanges;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
public class SettingsDrawerActivity extends Activity {
@@ -63,15 +60,6 @@
private FrameLayout mContentHeaderContainer;
- // Remove below after new IA
- @Deprecated
- private static List<DashboardCategory> sDashboardCategories;
- @Deprecated
- private static HashMap<Pair<String, String>, Tile> sTileCache;
- @Deprecated
- private static InterestingConfigChanges sConfigTracker;
- // Remove above after new IA
-
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -175,17 +163,6 @@
getActionBar().setDisplayHomeAsUpEnabled(true);
}
- public List<DashboardCategory> getDashboardCategories() {
- if (sDashboardCategories == null) {
- sTileCache = new HashMap<>();
- sConfigTracker = new InterestingConfigChanges();
- // Apply initial current config.
- sConfigTracker.applyNewConfig(getResources());
- sDashboardCategories = TileUtils.getCategories(this, sTileCache);
- }
- return sDashboardCategories;
- }
-
protected void onCategoriesChanged() {
final int N = mCategoryListeners.size();
for (int i = 0; i < N; i++) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 48f3e2a..fed48b4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -394,8 +394,9 @@
*
* <p>If the given connection is active, the existing value of {@link #mRssi} will be returned.
* If the given AccessPoint is not active, a value will be calculated from previous scan
- * results, returning the best RSSI for all matching AccessPoints. If the access point is not
- * connected and there are no scan results, the rssi will be set to {@link #UNREACHABLE_RSSI}.
+ * results, returning the best RSSI for all matching AccessPoints averaged with the previous
+ * value. If the access point is not connected and there are no scan results, the rssi will be
+ * set to {@link #UNREACHABLE_RSSI}.
*
* <p>Old scan results will be evicted from the cache when this method is invoked.
*/
@@ -413,7 +414,11 @@
}
}
- mRssi = rssi;
+ if (rssi != UNREACHABLE_RSSI && mRssi != UNREACHABLE_RSSI) {
+ mRssi = (mRssi + rssi) / 2; // half-life previous value
+ } else {
+ mRssi = rssi;
+ }
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index c9fa017..a9aaa05 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -43,10 +43,6 @@
private static final int[] STATE_SECURED = {
R.attr.state_encrypted
};
- private static final int[] STATE_SAVED = {
- R.attr.state_encrypted,
- R.attr.state_saved
- };
private static final int[] wifi_friction_attributes = { R.attr.wifi_friction };
@@ -152,10 +148,8 @@
}
view.itemView.setContentDescription(mContentDescription);
- if (!mForSavedNetworks) {
- ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon);
- bindFrictionImage(frictionImageView);
- }
+ ImageView frictionImageView = (ImageView) view.findViewById(R.id.friction_icon);
+ bindFrictionImage(frictionImageView);
}
protected void updateIcon(int level, Context context) {
@@ -184,11 +178,7 @@
return;
}
if (mAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
- if (mAccessPoint.isSaved()) {
- mFrictionSld.setState(STATE_SAVED);
- } else {
- mFrictionSld.setState(STATE_SECURED);
- }
+ mFrictionSld.setState(STATE_SECURED);
}
Drawable drawable = mFrictionSld.getCurrent();
frictionImageView.setImageDrawable(drawable);
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index e8a58c1..b9b4ef3 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -195,6 +195,26 @@
assertThat(ap.getRssi()).isEqualTo(newRssi);
}
+ @Test
+ public void testUpdateWithScanResultShouldAverageRssi() {
+ String ssid = "ssid";
+ int originalRssi = -65;
+ int newRssi = -80;
+ int expectedRssi = (originalRssi + newRssi) / 2;
+ AccessPoint ap =
+ new TestAccessPointBuilder(mContext).setSsid(ssid).setRssi(originalRssi).build();
+
+ ScanResult scanResult = new ScanResult();
+ scanResult.SSID = ssid;
+ scanResult.level = newRssi;
+ scanResult.BSSID = "bssid";
+ scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
+ scanResult.capabilities = "";
+ assertThat(ap.update(scanResult)).isTrue();
+
+ assertThat(ap.getRssi()).isEqualTo(expectedRssi);
+ }
+
private AccessPoint createAccessPointWithScanResultCache() {
Bundle bundle = new Bundle();
ArrayList<ScanResult> scanResults = new ArrayList<>();
@@ -203,6 +223,7 @@
scanResult.level = i;
scanResult.BSSID = "bssid-" + i;
scanResult.timestamp = SystemClock.elapsedRealtime() * 1000;
+ scanResult.capabilities = "";
scanResults.add(scanResult);
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
index 665c439..81bd723 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
@@ -73,25 +73,28 @@
return this;
}
+ public TestAccessPointBuilder setRssi(int rssi) {
+ mRssi = rssi;
+ return this;
+ }
+
/**
- * Set the signal level.
- * Side effect: if this AccessPoint was previously unreachable,
+ * Set the rssi based upon the desired signal level.
+ *
+ * <p>Side effect: if this AccessPoint was previously unreachable,
* setting the level will also make it reachable.
*/
public TestAccessPointBuilder setLevel(int level) {
- int outputRange = AccessPoint.SIGNAL_LEVELS - 1;
-
- if (level > outputRange) {
- level = outputRange;
- } else if (level < 0) {
- level = 0;
+ // Reversal of WifiManager.calculateSignalLevels
+ if (level == 0) {
+ mRssi = MIN_RSSI;
+ } else if (level >= AccessPoint.SIGNAL_LEVELS) {
+ mRssi = MAX_RSSI;
+ } else {
+ float inputRange = MAX_RSSI - MIN_RSSI;
+ float outputRange = AccessPoint.SIGNAL_LEVELS - 1;
+ mRssi = (int) (level * inputRange / outputRange + MIN_RSSI);
}
-
- int inputRange = MAX_RSSI - MIN_RSSI;
-
- // calculate the rssi required to get the level we want.
- // this is a rearrangement of the formula from WifiManager.calculateSignalLevel()
- mRssi = (int)((float)(level * inputRange) / (float)outputRange) + MIN_RSSI;
return this;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 8da17fa..b7b4a3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -23,7 +23,6 @@
import android.view.View;
import android.view.ViewGroup;
-import com.android.internal.widget.CachingIconView;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.ViewInvertHelper;
@@ -127,12 +126,7 @@
super.setDark(dark, fade, delay);
if (mDark == dark) return;
mDark = dark;
- if (fade) {
- mViewInvertHelper.fade(dark, delay);
- } else {
- mViewInvertHelper.update(dark);
- }
- mShelfIcons.setAmbient(dark);
+ mShelfIcons.setDark(dark, fade, delay);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 1101701..ffc4e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -27,6 +27,7 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -46,6 +47,7 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.statusbar.notification.NotificationIconDozeHelper;
import com.android.systemui.statusbar.notification.NotificationUtils;
import java.text.NumberFormat;
@@ -99,7 +101,6 @@
private int mDensity;
private float mIconScale = 1.0f;
private final Paint mDotPaint = new Paint();
- private boolean mDotVisible;
private float mDotRadius;
private int mStaticDotRadius;
private int mVisibleState = STATE_ICON;
@@ -110,6 +111,8 @@
private OnVisibilityChangedListener mOnVisibilityChangedListener;
private int mDrawableColor;
private int mIconColor;
+ private int mDecorColor;
+ private float mDarkAmount;
private ValueAnimator mColorAnimator;
private int mCurrentSetColor = NO_COLOR;
private int mAnimationStartColor = NO_COLOR;
@@ -119,6 +122,7 @@
animation.getAnimatedFraction());
setColorInternal(newColor);
};
+ private final NotificationIconDozeHelper mDozer;
public StatusBarIconView(Context context, String slot, Notification notification) {
this(context, slot, notification, false);
@@ -127,6 +131,7 @@
public StatusBarIconView(Context context, String slot, Notification notification,
boolean blocked) {
super(context);
+ mDozer = new NotificationIconDozeHelper(context);
mBlocked = blocked;
mSlot = slot;
mNumberPain = new Paint();
@@ -190,6 +195,7 @@
public StatusBarIconView(Context context, AttributeSet attrs) {
super(context, attrs);
+ mDozer = new NotificationIconDozeHelper(context);
mBlocked = false;
mAlwaysScaleIcon = true;
updateIconScale();
@@ -466,7 +472,19 @@
* to the drawable.
*/
public void setDecorColor(int iconTint) {
- mDotPaint.setColor(iconTint);
+ mDecorColor = iconTint;
+ updateDecorColor();
+ }
+
+ private void updateDecorColor() {
+ int color = NotificationUtils.interpolateColors(mDecorColor, Color.WHITE, mDarkAmount);
+ if (mDotPaint.getColor() != color) {
+ mDotPaint.setColor(color);
+
+ if (mDotAppearAmount != 0) {
+ invalidate();
+ }
+ }
}
/**
@@ -477,6 +495,7 @@
mDrawableColor = color;
setColorInternal(color);
mIconColor = color;
+ mDozer.setColor(color);
}
private void setColorInternal(int color) {
@@ -649,6 +668,14 @@
mOnVisibilityChangedListener = listener;
}
+ public void setDark(boolean dark, boolean fade, long delay) {
+ mDozer.setImageDark(this, dark, fade, delay, mIconColor != NO_COLOR);
+ mDozer.setIntensityDark(f -> {
+ mDarkAmount = f;
+ updateDecorColor();
+ }, dark, fade, delay);
+ }
+
public interface OnVisibilityChangedListener {
void onVisibilityChanged(int newVisibility);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
index 3efa29f..bca4b43 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationCustomViewWrapper.java
@@ -18,7 +18,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
+import android.content.Context;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.view.View;
@@ -38,8 +38,8 @@
private boolean mIsLegacy;
private int mLegacyColor;
- protected NotificationCustomViewWrapper(View view, ExpandableNotificationRow row) {
- super(view, row);
+ protected NotificationCustomViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
+ super(ctx, view, row);
mInvertHelper = new ViewInvertHelper(view, NotificationPanelView.DOZE_ANIMATION_DURATION);
mLegacyColor = row.getContext().getColor(R.color.notification_legacy_background_color);
}
@@ -67,13 +67,11 @@
}
protected void fadeGrayscale(final boolean dark, long delay) {
- startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- updateGrayscaleMatrix((float) animation.getAnimatedValue());
- mGreyPaint.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
- mView.setLayerPaint(mGreyPaint);
- }
+ getDozer().startIntensityAnimation(animation -> {
+ getDozer().updateGrayscaleMatrix((float) animation.getAnimatedValue());
+ mGreyPaint.setColorFilter(
+ new ColorMatrixColorFilter(getDozer().getGrayscaleColorMatrix()));
+ mView.setLayerPaint(mGreyPaint);
}, dark, delay, new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -86,9 +84,9 @@
protected void updateGrayscale(boolean dark) {
if (dark) {
- updateGrayscaleMatrix(1f);
+ getDozer().updateGrayscaleMatrix(1f);
mGreyPaint.setColorFilter(
- new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+ new ColorMatrixColorFilter(getDozer().getGrayscaleColorMatrix()));
mView.setLayerPaint(mGreyPaint);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
new file mode 100644
index 0000000..d592c5f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
+import android.widget.ImageView;
+
+import com.android.systemui.Interpolators;
+import com.android.systemui.statusbar.phone.NotificationPanelView;
+
+import java.util.function.Consumer;
+
+public class NotificationDozeHelper {
+ private final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
+
+ public void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
+ startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ updateGrayscaleMatrix((float) animation.getAnimatedValue());
+ target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+ }
+ }, dark, delay, new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (!dark) {
+ target.setColorFilter(null);
+ }
+ }
+ });
+ }
+
+ public void updateGrayscale(ImageView target, boolean dark) {
+ if (dark) {
+ updateGrayscaleMatrix(1f);
+ target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+ } else {
+ target.setColorFilter(null);
+ }
+ }
+
+ public void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
+ boolean dark, long delay, Animator.AnimatorListener listener) {
+ float startIntensity = dark ? 0f : 1f;
+ float endIntensity = dark ? 1f : 0f;
+ ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
+ animator.addUpdateListener(updateListener);
+ animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
+ animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
+ animator.setStartDelay(delay);
+ if (listener != null) {
+ animator.addListener(listener);
+ }
+ animator.start();
+ }
+
+ public void setIntensityDark(Consumer<Float> listener, boolean dark,
+ boolean animate, long delay) {
+ if (animate) {
+ startIntensityAnimation(a -> listener.accept((Float) a.getAnimatedValue()), dark, delay,
+ null /* listener */);
+ } else {
+ listener.accept(dark ? 1f : 0f);
+ }
+ }
+
+ public void updateGrayscaleMatrix(float intensity) {
+ mGrayscaleColorMatrix.setSaturation(1 - intensity);
+ }
+
+ public ColorMatrix getGrayscaleColorMatrix() {
+ return mGrayscaleColorMatrix;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
index 38e4ec1..1ffc944 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationHeaderViewWrapper.java
@@ -16,17 +16,10 @@
package com.android.systemui.statusbar.notification;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ValueAnimator;
import android.app.Notification;
import android.content.Context;
-import android.graphics.Color;
import android.graphics.ColorFilter;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
-import android.graphics.drawable.Drawable;
import android.util.ArraySet;
import android.view.NotificationHeaderView;
import android.view.View;
@@ -37,7 +30,6 @@
import android.widget.TextView;
import com.android.systemui.Interpolators;
-import com.android.systemui.R;
import com.android.systemui.ViewInvertHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.TransformableView;
@@ -55,10 +47,6 @@
private static final Interpolator LOW_PRIORITY_HEADER_CLOSE
= new PathInterpolator(0.4f, 0f, 0.7f, 1f);
- private final PorterDuffColorFilter mIconColorFilter = new PorterDuffColorFilter(
- 0, PorterDuff.Mode.SRC_ATOP);
- private final int mIconDarkAlpha;
- private final int mIconDarkColor = 0xffffffff;
protected final ViewInvertHelper mInvertHelper;
protected final ViewTransformationHelper mTransformationHelper;
@@ -74,8 +62,7 @@
private boolean mTransformLowPriorityTitle;
protected NotificationHeaderViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
- super(view, row);
- mIconDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
+ super(ctx, view, row);
mInvertHelper = new ViewInvertHelper(ctx, NotificationPanelView.DOZE_ANIMATION_DURATION);
mTransformationHelper = new ViewTransformationHelper();
@@ -108,6 +95,16 @@
updateInvertHelper();
}
+ @Override
+ protected NotificationDozeHelper createDozer(Context ctx) {
+ return new NotificationIconDozeHelper(ctx);
+ }
+
+ @Override
+ protected NotificationIconDozeHelper getDozer() {
+ return (NotificationIconDozeHelper) super.getDozer();
+ }
+
protected void resolveHeaderViews() {
mIcon = (ImageView) mView.findViewById(com.android.internal.R.id.icon);
mHeaderText = (TextView) mView.findViewById(com.android.internal.R.id.header_text);
@@ -116,6 +113,7 @@
mColor = resolveColor(mExpandButton);
mNotificationHeader = (NotificationHeaderView) mView.findViewById(
com.android.internal.R.id.notification_header);
+ getDozer().setColor(mColor);
}
private int resolveColor(ImageView icon) {
@@ -223,90 +221,8 @@
// It also may lead to bugs where the icon isn't correctly greyed out.
boolean hadColorFilter = mNotificationHeader.getOriginalIconColor()
!= NotificationHeaderView.NO_COLOR;
- if (fade) {
- if (hadColorFilter) {
- fadeIconColorFilter(mIcon, dark, delay);
- fadeIconAlpha(mIcon, dark, delay);
- } else {
- fadeGrayscale(mIcon, dark, delay);
- }
- } else {
- if (hadColorFilter) {
- updateIconColorFilter(mIcon, dark);
- updateIconAlpha(mIcon, dark);
- } else {
- updateGrayscale(mIcon, dark);
- }
- }
- }
- }
- private void fadeIconColorFilter(final ImageView target, boolean dark, long delay) {
- startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- updateIconColorFilter(target, (Float) animation.getAnimatedValue());
- }
- }, dark, delay, null /* listener */);
- }
-
- private void fadeIconAlpha(final ImageView target, boolean dark, long delay) {
- startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (float) animation.getAnimatedValue();
- target.setImageAlpha((int) (255 * (1f - t) + mIconDarkAlpha * t));
- }
- }, dark, delay, null /* listener */);
- }
-
- protected void fadeGrayscale(final ImageView target, final boolean dark, long delay) {
- startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- updateGrayscaleMatrix((float) animation.getAnimatedValue());
- target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
- }
- }, dark, delay, new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!dark) {
- target.setColorFilter(null);
- }
- }
- });
- }
-
- private void updateIconColorFilter(ImageView target, boolean dark) {
- updateIconColorFilter(target, dark ? 1f : 0f);
- }
-
- private void updateIconColorFilter(ImageView target, float intensity) {
- int color = interpolateColor(mColor, mIconDarkColor, intensity);
- mIconColorFilter.setColor(color);
- Drawable iconDrawable = target.getDrawable();
-
- // Also, the notification might have been modified during the animation, so background
- // might be null here.
- if (iconDrawable != null) {
- Drawable d = iconDrawable.mutate();
- // DrawableContainer ignores the color filter if it's already set, so clear it first to
- // get it set and invalidated properly.
- d.setColorFilter(null);
- d.setColorFilter(mIconColorFilter);
- }
- }
-
- private void updateIconAlpha(ImageView target, boolean dark) {
- target.setImageAlpha(dark ? mIconDarkAlpha : 255);
- }
-
- protected void updateGrayscale(ImageView target, boolean dark) {
- if (dark) {
- updateGrayscaleMatrix(1f);
- target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
- } else {
- target.setColorFilter(null);
+ getDozer().setImageDark(mIcon, dark, fade, delay, !hadColorFilter);
}
}
@@ -316,22 +232,6 @@
mNotificationHeader.setOnClickListener(expandable ? onClickListener : null);
}
- private static int interpolateColor(int source, int target, float t) {
- int aSource = Color.alpha(source);
- int rSource = Color.red(source);
- int gSource = Color.green(source);
- int bSource = Color.blue(source);
- int aTarget = Color.alpha(target);
- int rTarget = Color.red(target);
- int gTarget = Color.green(target);
- int bTarget = Color.blue(target);
- return Color.argb(
- (int) (aSource * (1f - t) + aTarget * t),
- (int) (rSource * (1f - t) + rTarget * t),
- (int) (gSource * (1f - t) + gTarget * t),
- (int) (bSource * (1f - t) + bTarget * t));
- }
-
@Override
public NotificationHeaderView getNotificationHeader() {
return mNotificationHeader;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java
new file mode 100644
index 0000000..9f79ef2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationIconDozeHelper.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.drawable.Drawable;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+public class NotificationIconDozeHelper extends NotificationDozeHelper {
+
+ private final int mImageDarkAlpha;
+ private final int mImageDarkColor = 0xffffffff;
+ private final PorterDuffColorFilter mImageColorFilter = new PorterDuffColorFilter(
+ 0, PorterDuff.Mode.SRC_ATOP);
+
+ private int mColor = Color.BLACK;
+
+ public NotificationIconDozeHelper(Context ctx) {
+ mImageDarkAlpha = ctx.getResources().getInteger(R.integer.doze_small_icon_alpha);
+ }
+
+ public void setColor(int color) {
+ mColor = color;
+ }
+
+ public void setImageDark(ImageView target, boolean dark, boolean fade, long delay,
+ boolean useGrayscale) {
+ if (fade) {
+ if (!useGrayscale) {
+ fadeImageColorFilter(target, dark, delay);
+ fadeImageAlpha(target, dark, delay);
+ } else {
+ fadeGrayscale(target, dark, delay);
+ }
+ } else {
+ if (!useGrayscale) {
+ updateImageColorFilter(target, dark);
+ updateImageAlpha(target, dark);
+ } else {
+ updateGrayscale(target, dark);
+ }
+ }
+ }
+
+ private void fadeImageColorFilter(final ImageView target, boolean dark, long delay) {
+ startIntensityAnimation(animation -> {
+ updateImageColorFilter(target, (Float) animation.getAnimatedValue());
+ }, dark, delay, null /* listener */);
+ }
+
+ private void fadeImageAlpha(final ImageView target, boolean dark, long delay) {
+ startIntensityAnimation(animation -> {
+ float t = (float) animation.getAnimatedValue();
+ target.setImageAlpha((int) (255 * (1f - t) + mImageDarkAlpha * t));
+ }, dark, delay, null /* listener */);
+ }
+
+ private void updateImageColorFilter(ImageView target, boolean dark) {
+ updateImageColorFilter(target, dark ? 1f : 0f);
+ }
+
+ private void updateImageColorFilter(ImageView target, float intensity) {
+ int color = NotificationUtils.interpolateColors(mColor, mImageDarkColor, intensity);
+ mImageColorFilter.setColor(color);
+ Drawable imageDrawable = target.getDrawable();
+
+ // Also, the notification might have been modified during the animation, so background
+ // might be null here.
+ if (imageDrawable != null) {
+ Drawable d = imageDrawable.mutate();
+ // DrawableContainer ignores the color filter if it's already set, so clear it first to
+ // get it set and invalidated properly.
+ d.setColorFilter(null);
+ d.setColorFilter(mImageColorFilter);
+ }
+ }
+
+ private void updateImageAlpha(ImageView target, boolean dark) {
+ target.setImageAlpha(dark ? mImageDarkAlpha : 255);
+ }
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
index 846d03a..f0b6b2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationTemplateViewWrapper.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
import android.service.notification.StatusBarNotification;
@@ -46,7 +45,8 @@
private int mContentHeight;
private int mMinHeightHint;
- protected NotificationTemplateViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
+ protected NotificationTemplateViewWrapper(Context ctx, View view,
+ ExpandableNotificationRow row) {
super(ctx, view, row);
mTransformationHelper.setCustomTransformation(
new ViewTransformationHelper.CustomTransformation() {
@@ -154,16 +154,20 @@
// This also clears the existing types
super.updateTransformedTypes();
if (mTitle != null) {
- mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE, mTitle);
+ mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TITLE,
+ mTitle);
}
if (mText != null) {
- mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT, mText);
+ mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_TEXT,
+ mText);
}
if (mPicture != null) {
- mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE, mPicture);
+ mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_IMAGE,
+ mPicture);
}
if (mProgressBar != null) {
- mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS, mProgressBar);
+ mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_PROGRESS,
+ mProgressBar);
}
}
@@ -173,7 +177,7 @@
return;
}
super.setDark(dark, fade, delay);
- setPictureGrayscale(dark, fade, delay);
+ setPictureDark(dark, fade, delay);
setProgressBarDark(dark, fade, delay);
}
@@ -188,12 +192,9 @@
}
private void fadeProgressDark(final ProgressBar target, final boolean dark, long delay) {
- startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = (float) animation.getAnimatedValue();
- updateProgressDark(target, t);
- }
+ getDozer().startIntensityAnimation(animation -> {
+ float t = (float) animation.getAnimatedValue();
+ updateProgressDark(target, t);
}, dark, delay, null /* listener */);
}
@@ -207,13 +208,9 @@
updateProgressDark(target, dark ? 1f : 0f);
}
- protected void setPictureGrayscale(boolean grayscale, boolean fade, long delay) {
+ private void setPictureDark(boolean dark, boolean fade, long delay) {
if (mPicture != null) {
- if (fade) {
- fadeGrayscale(mPicture, grayscale, delay);
- } else {
- updateGrayscale(mPicture, grayscale);
- }
+ getDozer().setImageDark(mPicture, dark, fade, delay, true /* useGrayscale */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
index c85e8d8..c86616b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationViewWrapper.java
@@ -16,24 +16,17 @@
package com.android.systemui.statusbar.notification;
-import android.animation.Animator;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
-import android.graphics.ColorMatrix;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
-import android.service.notification.StatusBarNotification;
import android.support.v4.graphics.ColorUtils;
import android.view.NotificationHeaderView;
import android.view.View;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.ExpandableNotificationRow;
import com.android.systemui.statusbar.TransformableView;
-import com.android.systemui.statusbar.phone.NotificationPanelView;
/**
* Wraps the actual notification content view; used to implement behaviors which are different for
@@ -41,14 +34,14 @@
*/
public abstract class NotificationViewWrapper implements TransformableView {
- protected final ColorMatrix mGrayscaleColorMatrix = new ColorMatrix();
protected final View mView;
protected final ExpandableNotificationRow mRow;
+ private final NotificationDozeHelper mDozer;
+
protected boolean mDark;
private int mBackgroundColor = 0;
protected boolean mShouldInvertDark;
protected boolean mDarkInitialized = false;
- private boolean mForcedInvisible;
public static NotificationViewWrapper wrap(Context ctx, View v, ExpandableNotificationRow row) {
if (v.getId() == com.android.internal.R.id.status_bar_latest_event_content) {
@@ -65,13 +58,22 @@
} else if (v instanceof NotificationHeaderView) {
return new NotificationHeaderViewWrapper(ctx, v, row);
} else {
- return new NotificationCustomViewWrapper(v, row);
+ return new NotificationCustomViewWrapper(ctx, v, row);
}
}
- protected NotificationViewWrapper(View view, ExpandableNotificationRow row) {
+ protected NotificationViewWrapper(Context ctx, View view, ExpandableNotificationRow row) {
mView = view;
mRow = row;
+ mDozer = createDozer(ctx);
+ }
+
+ protected NotificationDozeHelper createDozer(Context ctx) {
+ return new NotificationIconDozeHelper(mView.getContext());
+ }
+
+ protected NotificationDozeHelper getDozer() {
+ return mDozer;
}
/**
@@ -112,26 +114,6 @@
|| ColorUtils.calculateLuminance(backgroundColor) > 0.5;
}
-
- protected void startIntensityAnimation(ValueAnimator.AnimatorUpdateListener updateListener,
- boolean dark, long delay, Animator.AnimatorListener listener) {
- float startIntensity = dark ? 0f : 1f;
- float endIntensity = dark ? 1f : 0f;
- ValueAnimator animator = ValueAnimator.ofFloat(startIntensity, endIntensity);
- animator.addUpdateListener(updateListener);
- animator.setDuration(NotificationPanelView.DOZE_ANIMATION_DURATION);
- animator.setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN);
- animator.setStartDelay(delay);
- if (listener != null) {
- animator.addListener(listener);
- }
- animator.start();
- }
-
- protected void updateGrayscaleMatrix(float intensity) {
- mGrayscaleColorMatrix.setSaturation(1 - intensity);
- }
-
/**
* Update the appearance of the expand button.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
index 820638c..c30bb9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java
@@ -74,7 +74,7 @@
if (mUserListener == null) {
return false;
}
- return mUserListener.getUserCount() > 1;
+ return mUserListener.getUserCount() != 0;
}
public void setUserSwitcherController(UserSwitcherController userSwitcherController) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 3706dc8..dee15d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -95,7 +95,7 @@
private int mActualLayoutWidth = NO_VALUE;
private float mActualPaddingEnd = NO_VALUE;
private float mActualPaddingStart = NO_VALUE;
- private boolean mCentered;
+ private boolean mDark;
private boolean mChangingViewPositions;
private int mAddAnimationStartIndex = -1;
private int mCannedAnimationStartIndex = -1;
@@ -183,6 +183,9 @@
mAddAnimationStartIndex = Math.min(mAddAnimationStartIndex, childIndex);
}
}
+ if (mDark && child instanceof StatusBarIconView) {
+ ((StatusBarIconView) child).setDark(mDark, false, 0);
+ }
}
@Override
@@ -312,7 +315,8 @@
numDots++;
}
}
- if (mCentered && translationX < getLayoutEnd()) {
+ boolean center = mDark;
+ if (center && translationX < getLayoutEnd()) {
float delta = (getLayoutEnd() - translationX) / 2;
for (int i = 0; i < childCount; i++) {
View view = getChildAt(i);
@@ -390,9 +394,15 @@
mChangingViewPositions = changingViewPositions;
}
- public void setAmbient(boolean ambient) {
- mCentered = ambient;
+ public void setDark(boolean dark, boolean fade, long delay) {
+ mDark = dark;
mDisallowNextAnimation = true;
+ for (int i = 0; i < getChildCount(); i++) {
+ View view = getChildAt(i);
+ if (view instanceof StatusBarIconView) {
+ ((StatusBarIconView) view).setDark(dark, fade, delay);
+ }
+ }
}
public IconState getIconState(StatusBarIconView icon) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 55d661c..b40e709 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -5384,9 +5384,10 @@
ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids, String[] nativeProcs) {
// Use a FileObserver to detect when traces finish writing.
// The order of traces is considered important to maintain for legibility.
+ final boolean[] closed = new boolean[1];
FileObserver observer = new FileObserver(tracesPath, FileObserver.CLOSE_WRITE) {
@Override
- public synchronized void onEvent(int event, String path) { notify(); }
+ public synchronized void onEvent(int event, String path) { closed[0] = true; notify(); }
};
try {
@@ -5403,6 +5404,7 @@
final long sime = SystemClock.elapsedRealtime();
Process.sendSignal(firstPids.get(i), Process.SIGNAL_QUIT);
observer.wait(1000); // Wait for write-close, give up after 1 sec
+ if (!closed[0]) Slog.w(TAG, "Didn't see close of " + tracesPath);
if (DEBUG_ANR) Slog.d(TAG, "Done with pid " + firstPids.get(i)
+ " in " + (SystemClock.elapsedRealtime()-sime) + "ms");
}
@@ -21493,6 +21495,15 @@
return success;
}
+ private boolean isEphemeralLocked(int uid) {
+ String packages[] = mContext.getPackageManager().getPackagesForUid(uid);
+ if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid
+ return false;
+ }
+ return getPackageManagerInternalLocked().isPackageEphemeral(UserHandle.getUserId(uid),
+ packages[0]);
+ }
+
private final void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) {
final UidRecord.ChangeItem pendingChange;
if (uidRec == null || uidRec.pendingChange == null) {
@@ -21533,7 +21544,7 @@
pendingChange.change = change;
pendingChange.processState = uidRec != null
? uidRec.setProcState : ActivityManager.PROCESS_STATE_NONEXISTENT;
- pendingChange.ephemeral = uidRec.ephemeral;
+ pendingChange.ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid);
pendingChange.procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0;
// Directly update the power manager, since we sit on top of it and it is critical
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 36a913f..f927cce 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -857,17 +857,26 @@
ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
- String[] nativeProcs = NATIVE_STACKS_OF_INTEREST;
- // don't dump native PIDs for background ANRs
- File tracesFile = null;
+ // don't dump native PIDs for background ANRs unless it is the process of interest
+ String[] nativeProcs = null;
if (isSilentANR) {
- tracesFile = mService.dumpStackTraces(true, firstPids, null, lastPids,
- null);
+ for (int i = 0; i < NATIVE_STACKS_OF_INTEREST.length; i++) {
+ if (NATIVE_STACKS_OF_INTEREST[i].equals(app.processName)) {
+ nativeProcs = new String[] { app.processName };
+ break;
+ }
+ }
} else {
- tracesFile = mService.dumpStackTraces(true, firstPids, processCpuTracker, lastPids,
- nativeProcs);
+ nativeProcs = NATIVE_STACKS_OF_INTEREST;
}
+ // For background ANRs, don't pass the ProcessCpuTracker to
+ // avoid spending 1/2 second collecting stats to rank lastPids.
+ File tracesFile = mService.dumpStackTraces(true, firstPids,
+ (isSilentANR) ? null : processCpuTracker,
+ (isSilentANR) ? null : lastPids,
+ nativeProcs);
+
String cpuInfo = null;
if (ActivityManagerService.MONITOR_CPU_USAGE) {
mService.updateCpuStatsNow();
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index b316a3b..940f621 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -1106,8 +1106,7 @@
JobStatus runNow = (JobStatus) message.obj;
// runNow can be null, which is a controller's way of indicating that its
// state is such that all ready jobs should be run immediately.
- if (runNow != null && !mPendingJobs.contains(runNow)
- && mJobs.containsJob(runNow)) {
+ if (runNow != null && isReadyToBeExecutedLocked(runNow)) {
mJobPackageTracker.notePending(runNow);
mPendingJobs.add(runNow);
}
@@ -1310,12 +1309,27 @@
* - The component is enabled and runnable.
*/
private boolean isReadyToBeExecutedLocked(JobStatus job) {
+ final boolean jobExists = mJobs.containsJob(job);
final boolean jobReady = job.isReady();
final boolean jobPending = mPendingJobs.contains(job);
final boolean jobActive = isCurrentlyActiveLocked(job);
final int userId = job.getUserId();
final boolean userStarted = ArrayUtils.contains(mStartedUsers, userId);
+
+ if (DEBUG) {
+ Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
+ + " exists=" + jobExists
+ + " ready=" + jobReady + " pending=" + jobPending
+ + " active=" + jobActive + " userStarted=" + userStarted);
+ }
+
+ // Short circuit: don't do the expensive PM check unless we really think
+ // we might need to run this job now.
+ if (!jobExists || !userStarted || !jobReady || jobPending || jobActive) {
+ return false;
+ }
+
final boolean componentPresent;
try {
componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
@@ -1327,11 +1341,11 @@
if (DEBUG) {
Slog.v(TAG, "isReadyToBeExecutedLocked: " + job.toShortString()
- + " ready=" + jobReady + " pending=" + jobPending
- + " active=" + jobActive + " userStarted=" + userStarted
+ " componentPresent=" + componentPresent);
}
- return userStarted && componentPresent && jobReady && !jobPending && !jobActive;
+
+ // Everything else checked out so far, so this is the final yes/no check
+ return componentPresent;
}
/**
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 6af1c3b..db133f8 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -130,7 +130,7 @@
// installed and should be removed
final int storedOverlayInfosSize = storedOverlayInfos.size();
for (int i = 0; i < storedOverlayInfosSize; i++) {
- final OverlayInfo oi = storedOverlayInfos.get(i);
+ final OverlayInfo oi = storedOverlayInfos.valueAt(i);
mSettings.remove(oi.packageName, oi.userId);
removeIdmapIfPossible(oi);
packagesToUpdateAssets.add(oi.targetPackageName);
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index d8900c0..d364d17 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -290,8 +290,8 @@
PackageManagerService.REASON_BACKGROUND_DEXOPT,
/* force */ false)
: pm.performDexOptSecondary(pkg,
- PackageManagerServiceCompilerMapping.getFullCompilerFilter(),
- /* force */ true);
+ PackageManagerService.REASON_BACKGROUND_DEXOPT,
+ /* force */ false);
if (success) {
// Dexopt succeeded, remove package from the list of failing ones.
synchronized (failedPackageNames) {
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index aac04da..126f8c4 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -214,6 +214,7 @@
if (getAvailableSpace() > 0) {
dexoptCommandCountExecuted++;
+ Log.d(TAG, "Next command: " + next);
return next;
} else {
if (DEBUG_DEXOPT) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 0ec85aa..2e4a3a3 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -915,7 +915,7 @@
// This is kind of hacky; we're creating a half-parsed package that is
// straddled between the inherited and staged APKs.
- final PackageLite pkg = new PackageLite(null, baseApk, null, null,
+ final PackageLite pkg = new PackageLite(null, baseApk, null, null, null, null,
splitPaths.toArray(new String[splitPaths.size()]), null);
final boolean isForwardLocked =
(params.installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1d1bf0d..2882d20 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -8458,6 +8458,11 @@
return mDexManager.dexoptSecondaryDex(packageName, compilerFilter, force);
}
+ public boolean performDexOptSecondary(String packageName, int compileReason,
+ boolean force) {
+ return mDexManager.dexoptSecondaryDex(packageName, compileReason, force);
+ }
+
/**
* Reconcile the information we have about the secondary dex files belonging to
* {@code packagName} and the actual dex files. For all dex files that were
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 751d9af..25a596a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -171,7 +171,8 @@
if (file.isFile()) {
try {
ApkLite baseApk = PackageParser.parseApkLite(file, 0);
- PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null);
+ PackageLite pkgLite = new PackageLite(null, baseApk, null, null, null, null,
+ null, null);
params.sessionParams.setSize(PackageHelper.calculateInstalledSize(
pkgLite, false, params.sessionParams.abiOverride));
} catch (PackageParserException | IOException e) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 1f97d7d..a31258c3 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1493,6 +1493,10 @@
listeners[i].onUserRestrictionsChanged(userId,
newRestrictionsFinal, prevRestrictionsFinal);
}
+
+ final Intent broadcast = new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)
+ .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ mContext.sendBroadcastAsUser(broadcast, UserHandle.of(userId));
}
});
}
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 83dd392..a904d17 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -279,6 +279,17 @@
* @return true if all secondary dex files were processed successfully (compiled or skipped
* because they don't need to be compiled)..
*/
+ public boolean dexoptSecondaryDex(String packageName, int compilerReason, boolean force) {
+ return dexoptSecondaryDex(packageName,
+ PackageManagerServiceCompilerMapping.getCompilerFilterForReason(compilerReason),
+ force);
+ }
+
+ /**
+ * Perform dexopt on the package {@code packageName} secondary dex files.
+ * @return true if all secondary dex files were processed successfully (compiled or skipped
+ * because they don't need to be compiled)..
+ */
public boolean dexoptSecondaryDex(String packageName, String compilerFilter, boolean force) {
// Select the dex optimizer based on the force parameter.
// Forced compilation is done through ForcedUpdatePackageDexOptimizer which will adjust
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8987ac1..721d337 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -157,7 +157,6 @@
super.setUp();
mContext = getContext();
-
when(mContext.packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN)))
.thenReturn(true);
@@ -174,6 +173,12 @@
setUpUserManager();
}
+ @Override
+ protected void tearDown() throws Exception {
+ flushTasks();
+ super.tearDown();
+ }
+
private void initializeDpms() {
// Need clearCallingIdentity() to pass permission checks.
final long ident = mContext.binder.clearCallingIdentity();
@@ -1246,7 +1251,7 @@
mContext.applicationInfo = new ApplicationInfo();
mContext.callerPermissions.add(permission.MANAGE_USERS);
mContext.packageName = "com.android.frameworks.servicestests";
- mContext.userContexts.put(user, mContext);
+ mContext.addPackageContext(user, mContext);
when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
StringParceledListSlice oneCert = asSlice(new String[] {"1"});
@@ -3966,121 +3971,168 @@
}
public void testGetOwnerInstalledCaCertsForDeviceOwner() throws Exception {
+ mContext.packageName = mRealTestContext.getPackageName();
setDeviceOwner();
- mContext.packageName = admin1.getPackageName();
- mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
- verifyCanGetOwnerInstalledCaCerts(admin1);
+ final DpmMockContext caller = new DpmMockContext(mRealTestContext, "test-caller");
+ caller.packageName = admin1.getPackageName();
+ caller.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
+
+ verifyCanGetOwnerInstalledCaCerts(admin1, caller);
}
public void testGetOwnerInstalledCaCertsForProfileOwner() throws Exception {
+ mContext.packageName = mRealTestContext.getPackageName();
setAsProfileOwner(admin1);
- mContext.packageName = admin1.getPackageName();
- verifyCanGetOwnerInstalledCaCerts(admin1);
- verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(admin1);
+ final DpmMockContext caller = new DpmMockContext(mRealTestContext, "test-caller");
+ caller.packageName = admin1.getPackageName();
+ caller.binder.callingUid = DpmMockContext.CALLER_UID;
+
+ verifyCanGetOwnerInstalledCaCerts(admin1, caller);
+ verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(admin1, caller);
}
public void testGetOwnerInstalledCaCertsForDelegate() throws Exception {
+ mContext.packageName = mRealTestContext.getPackageName();
setAsProfileOwner(admin1);
final String delegate = "com.example.delegate";
final int delegateUid = setupPackageInPackageManager(delegate, 20988);
dpm.setCertInstallerPackage(admin1, delegate);
- mContext.packageName = delegate;
- mContext.binder.callingUid = delegateUid;
- verifyCanGetOwnerInstalledCaCerts(null);
- verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(null);
+ final DpmMockContext caller = new DpmMockContext(mRealTestContext, "test-caller");
+ caller.packageName = delegate;
+ caller.binder.callingUid = delegateUid;
+
+ verifyCanGetOwnerInstalledCaCerts(null, caller);
+ verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(null, caller);
}
- private void verifyCanGetOwnerInstalledCaCerts(ComponentName caller) throws Exception {
- final UserHandle user = UserHandle.getUserHandleForUid(mContext.binder.callingUid);
- final int ownerUid = user.equals(UserHandle.SYSTEM) ?
- DpmMockContext.CALLER_SYSTEM_USER_UID : DpmMockContext.CALLER_UID;
-
- mContext.applicationInfo = new ApplicationInfo();
- mContext.userContexts.put(user, mContext);
- when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
-
- // Install a CA cert.
+ private void verifyCanGetOwnerInstalledCaCerts(
+ final ComponentName caller, final DpmMockContext callerContext) throws Exception {
final String alias = "cert";
final byte[] caCert = TEST_CA.getBytes();
- when(mContext.keyChainConnection.getService().installCaCertificate(caCert))
- .thenReturn(alias);
- assertTrue(dpm.installCaCert(caller, caCert));
- when(mContext.keyChainConnection.getService().getUserCaAliases())
- .thenReturn(asSlice(new String[] {alias}));
- mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED));
+
+ // device admin (used for posting the tls notification)
+ final DpmMockContext admin1Context;
+ if (admin1.getPackageName().equals(callerContext.getPackageName())) {
+ admin1Context = callerContext;
+ } else {
+ admin1Context = new DpmMockContext(mRealTestContext, "test-admin");
+ admin1Context.packageName = admin1.getPackageName();
+ admin1Context.applicationInfo = new ApplicationInfo();
+ }
+ when(admin1Context.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
+
+ // caller: device admin or delegated certificate installer
+ callerContext.applicationInfo = new ApplicationInfo();
+ final UserHandle callerUser = callerContext.binder.getCallingUserHandle();
+
+ // system_server
+ final DpmMockContext serviceContext = mContext;
+ serviceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ serviceContext.addPackageContext(callerUser, admin1Context);
+ serviceContext.addPackageContext(callerUser, callerContext);
+
+ // Install a CA cert.
+ runAsCaller(callerContext, dpms, (dpm) -> {
+ when(mContext.keyChainConnection.getService().installCaCertificate(caCert))
+ .thenReturn(alias);
+ assertTrue(dpm.installCaCert(caller, caCert));
+ when(mContext.keyChainConnection.getService().getUserCaAliases())
+ .thenReturn(asSlice(new String[] {alias}));
+
+ });
+
+ serviceContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()));
flushTasks();
+ final List<String> ownerInstalledCaCerts = new ArrayList<>();
+
// Device Owner / Profile Owner can find out which CA certs were installed by itself.
- final String packageName = mContext.packageName;
- mContext.packageName = admin1.getPackageName();
- final long callerIdentity = mContext.binder.clearCallingIdentity();
- mContext.binder.callingUid = ownerUid;
- List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user);
- assertNotNull(ownerInstalledCaCerts);
- assertEquals(1, ownerInstalledCaCerts.size());
- assertTrue(ownerInstalledCaCerts.contains(alias));
+ runAsCaller(admin1Context, dpms, (dpm) -> {
+ final List<String> installedCaCerts = dpm.getOwnerInstalledCaCerts(callerUser);
+ assertEquals(Arrays.asList(alias), installedCaCerts);
+ ownerInstalledCaCerts.addAll(installedCaCerts);
+ });
// Restarting the DPMS should not lose information.
initializeDpms();
- assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(user));
+ runAsCaller(admin1Context, dpms, (dpm) -> {
+ assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(callerUser));
+ });
// System can find out which CA certs were installed by the Device Owner / Profile Owner.
- mContext.packageName = "com.android.frameworks.servicestests";
- mContext.binder.clearCallingIdentity();
- assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(user));
+ runAsCaller(serviceContext, dpms, (dpm) -> {
+ assertEquals(ownerInstalledCaCerts, dpm.getOwnerInstalledCaCerts(callerUser));
- // Remove the CA cert.
- mContext.packageName = packageName;
- mContext.binder.restoreCallingIdentity(callerIdentity);
- reset(mContext.keyChainConnection.getService());
- mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED));
+ // Remove the CA cert.
+ reset(mContext.keyChainConnection.getService());
+ });
+
+ serviceContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()));
flushTasks();
// Verify that the CA cert is no longer reported as installed by the Device Owner / Profile
// Owner.
- mContext.packageName = admin1.getPackageName();
- mContext.binder.callingUid = ownerUid;
- ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user);
- assertNotNull(ownerInstalledCaCerts);
- assertTrue(ownerInstalledCaCerts.isEmpty());
-
- mContext.packageName = packageName;
- mContext.binder.restoreCallingIdentity(callerIdentity);
+ runAsCaller(admin1Context, dpms, (dpm) -> {
+ MoreAsserts.assertEmpty(dpm.getOwnerInstalledCaCerts(callerUser));
+ });
}
- private void verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(ComponentName caller)
- throws Exception {
- final UserHandle user = UserHandle.of(DpmMockContext.CALLER_USER_HANDLE);
-
- mContext.applicationInfo = new ApplicationInfo();
- mContext.userContexts.put(user, mContext);
- when(mContext.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
-
- // Install a CA cert.
+ private void verifyCantGetOwnerInstalledCaCertsProfileOwnerRemoval(
+ final ComponentName callerName, final DpmMockContext callerContext) throws Exception {
final String alias = "cert";
final byte[] caCert = TEST_CA.getBytes();
- when(mContext.keyChainConnection.getService().installCaCertificate(caCert))
- .thenReturn(alias);
- assertTrue(dpm.installCaCert(caller, caCert));
- when(mContext.keyChainConnection.getService().getUserCaAliases())
+
+ // device admin (used for posting the tls notification)
+ final DpmMockContext admin1Context;
+ if (admin1.getPackageName().equals(callerContext.getPackageName())) {
+ admin1Context = callerContext;
+ } else {
+ admin1Context = new DpmMockContext(mRealTestContext, "test-admin");
+ admin1Context.packageName = admin1.getPackageName();
+ admin1Context.applicationInfo = new ApplicationInfo();
+ }
+ when(admin1Context.resources.getColor(anyInt(), anyObject())).thenReturn(Color.WHITE);
+
+ // caller: device admin or delegated certificate installer
+ callerContext.applicationInfo = new ApplicationInfo();
+ final UserHandle callerUser = callerContext.binder.getCallingUserHandle();
+
+ // system_server
+ final DpmMockContext serviceContext = mContext;
+ serviceContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
+ serviceContext.addPackageContext(callerUser, admin1Context);
+ serviceContext.addPackageContext(callerUser, callerContext);
+
+ // Install a CA cert as caller
+ runAsCaller(callerContext, dpms, (dpm) -> {
+ when(mContext.keyChainConnection.getService().installCaCertificate(caCert))
+ .thenReturn(alias);
+ assertTrue(dpm.installCaCert(callerName, caCert));
+ });
+
+ // Fake the CA cert as having been installed
+ when(serviceContext.keyChainConnection.getService().getUserCaAliases())
.thenReturn(asSlice(new String[] {alias}));
- mContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED));
+ serviceContext.injectBroadcast(new Intent(KeyChain.ACTION_TRUST_STORE_CHANGED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, callerUser.getIdentifier()));
flushTasks();
- // Removing the Profile Owner should clear the information which CA certs were installed
- // by it.
- mContext.packageName = admin1.getPackageName();
- mContext.binder.callingUid = DpmMockContext.CALLER_UID;
- dpm.clearProfileOwner(admin1);
- mContext.packageName = "com.android.frameworks.servicestests";
- mContext.binder.clearCallingIdentity();
- final List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(user);
- assertNotNull(ownerInstalledCaCerts);
- assertTrue(ownerInstalledCaCerts.isEmpty());
+ // Removing the Profile Owner should clear the information on which CA certs were installed
+ runAsCaller(admin1Context, dpms, (dpm) -> {
+ dpm.clearProfileOwner(admin1);
+ });
+
+ runAsCaller(serviceContext, dpms, (dpm) -> {
+ final List<String> ownerInstalledCaCerts = dpm.getOwnerInstalledCaCerts(callerUser);
+ assertNotNull(ownerInstalledCaCerts);
+ assertTrue(ownerInstalledCaCerts.isEmpty());
+ });
}
private void setUserSetupCompleteForUser(boolean isUserSetupComplete, int userhandle) {
@@ -4147,29 +4199,11 @@
}
private void flushTasks() throws Exception {
- Boolean tasksFlushed[] = new Boolean[] {false};
- final Runnable tasksFlushedNotifier = () -> {
- synchronized (tasksFlushed) {
- tasksFlushed[0] = true;
- tasksFlushed.notify();
- }
- };
+ dpms.mHandler.runWithScissors(() -> {}, 0 /*now*/);
+ dpms.mBackgroundHandler.runWithScissors(() -> {}, 0 /*now*/);
- // Flush main thread handler.
- dpms.mHandler.post(tasksFlushedNotifier);
- synchronized (tasksFlushed) {
- if (!tasksFlushed[0]) {
- tasksFlushed.wait();
- }
- }
-
- // Flush background thread handler.
- tasksFlushed[0] = false;
- dpms.mBackgroundHandler.post(tasksFlushedNotifier);
- synchronized (tasksFlushed) {
- if (!tasksFlushed[0]) {
- tasksFlushed.wait();
- }
- }
+ // We can't let exceptions happen on the background thread. Throw them here if they happen
+ // so they still cause the test to fail despite being suppressed.
+ mContext.rethrowBackgroundBroadcastExceptions();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
index 7d017c5..1ea12d8 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmMockContext.java
@@ -48,6 +48,7 @@
import android.test.mock.MockContentResolver;
import android.test.mock.MockContext;
import android.util.ArrayMap;
+import android.util.Pair;
import android.view.IWindowManager;
import com.android.internal.widget.LockPatternUtils;
@@ -61,6 +62,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
@@ -111,6 +113,7 @@
public static class MockBinder {
public int callingUid = CALLER_UID;
public int callingPid = CALLER_PID;
+ public final Map<Integer, List<String>> callingPermissions = new ArrayMap<>();
public long clearCallingIdentity() {
final long token = (((long) callingUid) << 32) | (callingPid);
@@ -303,14 +306,19 @@
/** Note this is a partial mock, not a real mock. */
public final PackageManager packageManager;
+ /** TODO: Migrate everything to use {@link #permissions} to avoid confusion. */
+ @Deprecated
public final List<String> callerPermissions = new ArrayList<>();
+ /** Less confusing alias for {@link #callerPermissions}. */
+ public final List<String> permissions = callerPermissions;
+
private final ArrayList<UserInfo> mUserInfos = new ArrayList<>();
public final BuildMock buildMock = new BuildMock();
/** Optional mapping of other user contexts for {@link #createPackageContextAsUser} to return */
- public final Map<UserHandle, Context> userContexts = new ArrayMap<>();
+ public final Map<Pair<UserHandle, String>, Context> userPackageContexts = new ArrayMap<>();
public String packageName = null;
@@ -324,6 +332,9 @@
public final IntentFilter filter;
public final Handler scheduler;
+ // Exceptions thrown in a background thread kill the whole test. Save them instead.
+ public final AtomicReference<Exception> backgroundException = new AtomicReference<>();
+
public BroadcastReceiverRegistration(BroadcastReceiver receiver, IntentFilter filter,
Handler scheduler) {
this.receiver = receiver;
@@ -337,20 +348,30 @@
0 /* type */, false /* ordered */, false /* sticky */, null /* token */, userId,
0 /* flags */);
if (filter.match(null, intent, false, "DpmMockContext") > 0) {
- if (scheduler != null) {
- scheduler.post(() -> {
- receiver.setPendingResult(result);
- receiver.onReceive(DpmMockContext.this, intent);
- });
- } else {
+ final Runnable send = () -> {
receiver.setPendingResult(result);
receiver.onReceive(DpmMockContext.this, intent);
+ };
+ if (scheduler != null) {
+ scheduler.post(() -> {
+ try {
+ send.run();
+ } catch (Exception e) {
+ backgroundException.compareAndSet(null, e);
+ }
+ });
+ } else {
+ send.run();
}
}
}
}
private List<BroadcastReceiverRegistration> mBroadcastReceivers = new ArrayList<>();
+ public DpmMockContext(Context realTestContext, String name) {
+ this(realTestContext, new File(realTestContext.getCacheDir(), name));
+ }
+
public DpmMockContext(Context context, File dataDir) {
realTestContext = context;
@@ -511,13 +532,29 @@
.thenReturn(isRunning);
}
- public void injectBroadcast(Intent intent) {
+ public void injectBroadcast(final Intent intent) {
final int userId = UserHandle.getUserId(binder.getCallingUid());
for (final BroadcastReceiverRegistration receiver : mBroadcastReceivers) {
receiver.sendBroadcastIfApplicable(userId, intent);
}
}
+ public void rethrowBackgroundBroadcastExceptions() throws Exception {
+ for (final BroadcastReceiverRegistration receiver : mBroadcastReceivers) {
+ final Exception e = receiver.backgroundException.getAndSet(null);
+ if (e != null) {
+ throw e;
+ }
+ }
+ }
+
+ public void addPackageContext(UserHandle user, Context context) {
+ if (context.getPackageName() == null) {
+ throw new NullPointerException("getPackageName() == null");
+ }
+ userPackageContexts.put(new Pair<>(user, context.getPackageName()), context);
+ }
+
@Override
public Resources getResources() {
return resources;
@@ -576,7 +613,16 @@
if (binder.getCallingUid() == SYSTEM_UID) {
return; // Assume system has all permissions.
}
- if (!callerPermissions.contains(permission)) {
+
+ List<String> permissions = binder.callingPermissions.get(binder.getCallingUid());
+ if (permissions == null) {
+ // TODO: delete the following line. to do this without breaking any tests, first it's
+ // necessary to remove all tests that set it directly.
+ permissions = callerPermissions;
+// throw new UnsupportedOperationException(
+// "Caller UID " + binder.getCallingUid() + " doesn't exist");
+ }
+ if (!permissions.contains(permission)) {
throw new SecurityException("Caller doesn't have " + permission + " : " + message);
}
}
@@ -751,14 +797,11 @@
@Override
public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
throws PackageManager.NameNotFoundException {
- if (!userContexts.containsKey(user)) {
- return super.createPackageContextAsUser(packageName, flags, user);
+ final Pair<UserHandle, String> key = new Pair<>(user, packageName);
+ if (userPackageContexts.containsKey(key)) {
+ return userPackageContexts.get(key);
}
- if (!getPackageName().equals(packageName)) {
- throw new UnsupportedOperationException(
- "Creating a context as another package is not implemented");
- }
- return userContexts.get(user);
+ throw new UnsupportedOperationException("No package " + packageName + " for user " + user);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
index 43e2610..5d68edd 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DpmTestBase.java
@@ -16,6 +16,7 @@
package com.android.server.devicepolicy;
+import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -70,6 +71,34 @@
return mMockContext;
}
+ protected interface DpmRunnable {
+ public void run(DevicePolicyManager dpm) throws Exception;
+ }
+
+ /**
+ * Simulate an RPC from {@param caller} to the service context ({@link #mContext}).
+ *
+ * The caller sees its own context. The server also sees its own separate context, with the
+ * appropriate calling UID and calling permissions fields already set up.
+ */
+ protected void runAsCaller(DpmMockContext caller, DevicePolicyManagerServiceTestable dpms,
+ DpmRunnable action) {
+ final DpmMockContext serviceContext = mMockContext;
+
+ final long origId = serviceContext.binder.clearCallingIdentity();
+ try {
+ serviceContext.binder.callingUid = caller.binder.callingUid;
+ serviceContext.binder.callingPid = caller.binder.callingPid;
+ serviceContext.binder.callingPermissions.put(caller.binder.callingUid,
+ caller.permissions);
+ action.run(new DevicePolicyManagerTestable(caller, dpms));
+ } catch (Exception e) {
+ throw new AssertionError(e);
+ } finally {
+ serviceContext.binder.restoreCallingIdentity(origId);
+ }
+ }
+
protected void markPackageAsInstalled(String packageName, ApplicationInfo ai, int userId)
throws Exception {
final PackageInfo pi = DpmTestUtils.cloneParcelable(
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 4178f4d..4da2853 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -169,6 +169,7 @@
public AdbSettingsObserver() {
super(null);
}
+
@Override
public void onChange(boolean selfChange) {
boolean enable = (Settings.Global.getInt(mContentResolver,
@@ -208,9 +209,9 @@
private final BroadcastReceiver mChargingReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
- boolean usbCharging = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
- mHandler.sendMessage(MSG_UPDATE_CHARGING_STATE, usbCharging);
+ int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+ boolean usbCharging = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
+ mHandler.sendMessage(MSG_UPDATE_CHARGING_STATE, usbCharging);
}
};
@@ -261,9 +262,10 @@
// TV-specific notification channel
mNotificationManager.createNotificationChannel(
new NotificationChannel(ADB_NOTIFICATION_CHANNEL_ID_TV,
- mContext.getString(
- com.android.internal.R.string.adb_debugging_notification_channel_tv),
- NotificationManager.IMPORTANCE_HIGH));
+ mContext.getString(
+ com.android.internal.R.string
+ .adb_debugging_notification_channel_tv),
+ NotificationManager.IMPORTANCE_HIGH));
}
// We do not show the USB notification if the primary volume supports mass storage.
@@ -309,8 +311,8 @@
boolean enableAudio = (nativeGetAudioMode() == AUDIO_MODE_SOURCE);
// don't start accessory mode if our mandatory strings have not been set
boolean enableAccessory = (mAccessoryStrings != null &&
- mAccessoryStrings[UsbAccessory.MANUFACTURER_STRING] != null &&
- mAccessoryStrings[UsbAccessory.MODEL_STRING] != null);
+ mAccessoryStrings[UsbAccessory.MANUFACTURER_STRING] != null &&
+ mAccessoryStrings[UsbAccessory.MODEL_STRING] != null);
String functions = null;
if (enableAccessory && enableAudio) {
@@ -341,14 +343,14 @@
int serialLength = serial.length();
// XOR the USB serial across the remaining 5 bytes
for (int i = 0; i < serialLength; i++) {
- address[i % (ETH_ALEN - 1) + 1] ^= (int)serial.charAt(i);
+ address[i % (ETH_ALEN - 1) + 1] ^= (int) serial.charAt(i);
}
String addrString = String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
- address[0], address[1], address[2], address[3], address[4], address[5]);
+ address[0], address[1], address[2], address[3], address[4], address[5]);
try {
FileUtils.stringToFile(RNDIS_ETH_ADDR_PATH, addrString);
} catch (IOException e) {
- Slog.e(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH);
+ Slog.e(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH);
}
}
@@ -400,7 +402,7 @@
// register observer to listen for settings changes
mContentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
- false, new AdbSettingsObserver());
+ false, new AdbSettingsObserver());
// Watch for USB configuration changes
mUEventObserver.startObserving(USB_STATE_MATCH);
@@ -466,9 +468,9 @@
}
SomeArgs args = SomeArgs.obtain();
- args.argi1 = hostConnected ? 1 :0;
- args.argi2 = sourcePower ? 1 :0;
- args.argi3 = sinkPower ? 1 :0;
+ args.argi1 = hostConnected ? 1 : 0;
+ args.argi2 = sourcePower ? 1 : 0;
+ args.argi3 = sinkPower ? 1 : 0;
removeMessages(MSG_UPDATE_HOST_STATE);
Message msg = obtainMessage(MSG_UPDATE_HOST_STATE, args);
@@ -506,7 +508,7 @@
// Persist the adb setting
String newFunction = applyAdbFunction(SystemProperties.get(
- USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE));
+ USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE));
SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, newFunction);
// Remove mtp from the config if file transfer is not enabled
@@ -529,8 +531,10 @@
*/
private void setEnabledFunctions(String functions, boolean forceRestart,
boolean usbDataUnlocked) {
- if (DEBUG) Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
- + "forceRestart=" + forceRestart);
+ if (DEBUG) {
+ Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
+ + "forceRestart=" + forceRestart);
+ }
if (usbDataUnlocked != mUsbDataUnlocked) {
mUsbDataUnlocked = usbDataUnlocked;
@@ -680,7 +684,7 @@
}
for (String key : keySet) {
if (intent.getBooleanExtra(key, false) !=
- mBroadcastedIntent.getBooleanExtra(key, false)) {
+ mBroadcastedIntent.getBooleanExtra(key, false)) {
return true;
}
}
@@ -697,7 +701,8 @@
intent.putExtra(UsbManager.USB_CONNECTED, mConnected);
intent.putExtra(UsbManager.USB_HOST_CONNECTED, mHostConnected);
intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured);
- intent.putExtra(UsbManager.USB_DATA_UNLOCKED, isUsbTransferAllowed() && mUsbDataUnlocked);
+ intent.putExtra(UsbManager.USB_DATA_UNLOCKED,
+ isUsbTransferAllowed() && mUsbDataUnlocked);
intent.putExtra(UsbManager.USB_CONFIG_CHANGED, configChanged);
if (mCurrentFunctions != null) {
@@ -825,7 +830,7 @@
setAdbEnabled(msg.arg1 == 1);
break;
case MSG_SET_CURRENT_FUNCTIONS:
- String functions = (String)msg.obj;
+ String functions = (String) msg.obj;
setEnabledFunctions(functions, false, msg.arg1 == 1);
break;
case MSG_UPDATE_USER_RESTRICTIONS:
@@ -854,9 +859,9 @@
if (mCurrentUser != msg.arg1) {
// Restart the USB stack and re-apply user restrictions for MTP or PTP.
final boolean active = UsbManager.containsFunction(mCurrentFunctions,
- UsbManager.USB_FUNCTION_MTP)
+ UsbManager.USB_FUNCTION_MTP)
|| UsbManager.containsFunction(mCurrentFunctions,
- UsbManager.USB_FUNCTION_PTP);
+ UsbManager.USB_FUNCTION_PTP);
if (mUsbDataUnlocked && active && mCurrentUser != UserHandle.USER_NULL) {
Slog.v(TAG, "Current user switched to " + mCurrentUser
+ "; resetting USB host stack for MTP or PTP");
@@ -885,7 +890,9 @@
private void updateUsbNotification() {
if (mNotificationManager == null || !mUseUsbNotification
- || ("0".equals(SystemProperties.get("persist.charging.notify")))) return;
+ || ("0".equals(SystemProperties.get("persist.charging.notify")))) {
+ return;
+ }
int id = 0;
Resources r = mContext.getResources();
if (mConnected) {
@@ -937,18 +944,19 @@
Notification notification =
new Notification.Builder(mContext, SystemNotificationChannels.USB)
- .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
- .setWhen(0)
- .setOngoing(true)
- .setTicker(title)
- .setDefaults(0) // please be quiet
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setContentTitle(title)
- .setContentText(message)
- .setContentIntent(pi)
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .build();
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setWhen(0)
+ .setOngoing(true)
+ .setTicker(title)
+ .setDefaults(0) // please be quiet
+ .setColor(mContext.getColor(
+ com.android.internal.R.color
+ .system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentText(message)
+ .setContentIntent(pi)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .build();
mNotificationManager.notifyAsUser(null, id, notification,
UserHandle.ALL);
mUsbNotificationId = id;
@@ -976,20 +984,21 @@
Notification notification =
new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER)
- .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
- .setWhen(0)
- .setOngoing(true)
- .setTicker(title)
- .setDefaults(0) // please be quiet
- .setColor(mContext.getColor(
- com.android.internal.R.color.system_notification_accent_color))
- .setContentTitle(title)
- .setContentText(message)
- .setContentIntent(pi)
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .extend(new Notification.TvExtender()
- .setChannel(ADB_NOTIFICATION_CHANNEL_ID_TV))
- .build();
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setWhen(0)
+ .setOngoing(true)
+ .setTicker(title)
+ .setDefaults(0) // please be quiet
+ .setColor(mContext.getColor(
+ com.android.internal.R.color
+ .system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentText(message)
+ .setContentIntent(pi)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .extend(new Notification.TvExtender()
+ .setChannel(ADB_NOTIFICATION_CHANNEL_ID_TV))
+ .build();
mAdbNotificationShown = true;
mNotificationManager.notifyAsUser(null, id, notification,
UserHandle.ALL);
@@ -1038,7 +1047,8 @@
}
/* opens the currently attached USB accessory */
- public ParcelFileDescriptor openAccessory(UsbAccessory accessory, UsbUserSettingsManager settings) {
+ public ParcelFileDescriptor openAccessory(UsbAccessory accessory,
+ UsbUserSettingsManager settings) {
UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
if (currentAccessory == null) {
throw new IllegalArgumentException("no accessory attached");
@@ -1058,17 +1068,19 @@
}
public void setCurrentFunctions(String functions, boolean usbDataUnlocked) {
- if (DEBUG) Slog.d(TAG, "setCurrentFunctions(" + functions + ", " +
- usbDataUnlocked + ")");
+ if (DEBUG) {
+ Slog.d(TAG, "setCurrentFunctions(" + functions + ", " +
+ usbDataUnlocked + ")");
+ }
mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, usbDataUnlocked);
}
private void readOemUsbOverrideConfig() {
String[] configList = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_oemUsbModeOverride);
+ com.android.internal.R.array.config_oemUsbModeOverride);
if (configList != null) {
- for (String config: configList) {
+ for (String config : configList) {
String[] items = config.split(":");
if (items.length == 3) {
if (mOemModeMap == null) {
@@ -1092,7 +1104,7 @@
List<Pair<String, String>> overrides = mOemModeMap.get(bootMode);
if (overrides != null) {
- for (Pair<String, String> pair: overrides) {
+ for (Pair<String, String> pair : overrides) {
if (pair.first.equals(usbFunctions)) {
Slog.d(TAG, "OEM USB override: " + pair.first + " ==> " + pair.second);
return pair.second;
@@ -1120,7 +1132,7 @@
mDebuggingManager.clearUsbDebuggingKeys();
} else {
throw new RuntimeException("Cannot clear Usb Debugging keys, "
- + "UsbDebuggingManager not enabled");
+ + "UsbDebuggingManager not enabled");
}
}
@@ -1134,7 +1146,10 @@
}
private native String[] nativeGetAccessoryStrings();
+
private native ParcelFileDescriptor nativeOpenAccessory();
+
private native boolean nativeIsStartRequested();
+
private native int nativeGetAudioMode();
}
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index 54ab19d..2516d51 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -25,75 +25,480 @@
public class PreciseDisconnectCause {
/** The disconnect cause is not valid (Not received a disconnect cause)*/
- public static final int NOT_VALID = -1;
+ public static final int NOT_VALID = -1;
/** No disconnect cause provided. Generally a local disconnect or an incoming missed call */
- public static final int NO_DISCONNECT_CAUSE_AVAILABLE = 0;
+ public static final int NO_DISCONNECT_CAUSE_AVAILABLE = 0;
/**
* The destination cannot be reached because the number, although valid,
* is not currently assigned
*/
- public static final int UNOBTAINABLE_NUMBER = 1;
+ public static final int UNOBTAINABLE_NUMBER = 1;
+ /** The user cannot be reached because the network through which the call has been
+ * routed does not serve the destination desired
+ */
+ public static final int NO_ROUTE_TO_DESTINATION = 3;
+ /** The channel most recently identified is not acceptable to the sending entity for
+ * use in this call
+ */
+ public static final int CHANNEL_UNACCEPTABLE = 6;
+ /** The MS has tried to access a service that the MS's network operator or service
+ * provider is not prepared to allow
+ */
+ public static final int OPERATOR_DETERMINED_BARRING = 8;
/** One of the users involved in the call has requested that the call is cleared */
- public static final int NORMAL = 16;
+ public static final int NORMAL = 16;
/** The called user is unable to accept another call */
- public static final int BUSY = 17;
+ public static final int BUSY = 17;
+ /** The user does not respond to a call establishment message with either an alerting
+ * or connect indication within the prescribed period of time allocated
+ */
+ public static final int NO_USER_RESPONDING = 18;
+ /** The user has provided an alerting indication but has not provided a connect
+ * indication within a prescribed period of time
+ */
+ public static final int NO_ANSWER_FROM_USER = 19;
+ /** The equipment sending this cause does not wish to accept this call */
+ public static final int CALL_REJECTED = 21;
/** The called number is no longer assigned */
- public static final int NUMBER_CHANGED = 22;
+ public static final int NUMBER_CHANGED = 22;
+ /** This cause is returned to the network when a mobile station clears an active
+ * call which is being pre-empted by another call with higher precedence
+ */
+ public static final int PREEMPTION = 25;
+ /** The destination indicated by the mobile station cannot be reached because
+ * the interface to the destination is not functioning correctly
+ */
+ public static final int DESTINATION_OUT_OF_ORDER = 27;
+ /** The called party number is not a valid format or is not complete */
+ public static final int INVALID_NUMBER_FORMAT = 28;
+ /** The facility requested by user can not be provided by the network */
+ public static final int FACILITY_REJECTED = 29;
/** Provided in response to a STATUS ENQUIRY message */
- public static final int STATUS_ENQUIRY = 30;
+ public static final int STATUS_ENQUIRY = 30;
/** Reports a normal disconnect only when no other normal cause applies */
- public static final int NORMAL_UNSPECIFIED = 31;
+ public static final int NORMAL_UNSPECIFIED = 31;
/** There is no channel presently available to handle the call */
- public static final int NO_CIRCUIT_AVAIL = 34;
+ public static final int NO_CIRCUIT_AVAIL = 34;
+ /** The network is not functioning correctly and that the condition is likely
+ * to last a relatively long period of time
+ */
+ public static final int NETWORK_OUT_OF_ORDER = 38;
/**
* The network is not functioning correctly and the condition is not likely to last
* a long period of time
*/
- public static final int TEMPORARY_FAILURE = 41;
+ public static final int TEMPORARY_FAILURE = 41;
/** The switching equipment is experiencing a period of high traffic */
- public static final int SWITCHING_CONGESTION = 42;
+ public static final int SWITCHING_CONGESTION = 42;
+ /** The network could not deliver access information to the remote user as requested */
+ public static final int ACCESS_INFORMATION_DISCARDED = 43;
/** The channel cannot be provided */
- public static final int CHANNEL_NOT_AVAIL = 44;
+ public static final int CHANNEL_NOT_AVAIL = 44;
+ /** This cause is used to report a resource unavailable event only when no other
+ * cause in the resource unavailable class applies
+ */
+ public static final int RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 44;
/** The requested quality of service (ITU-T X.213) cannot be provided */
- public static final int QOS_NOT_AVAIL = 49;
+ public static final int QOS_NOT_AVAIL = 49;
+ /** The facility could not be provided by the network because the user has no
+ * complete subscription
+ */
+ public static final int REQUESTED_FACILITY_NOT_SUBSCRIBED = 50;
+ /** Incoming calls are not allowed within this CUG */
+ public static final int INCOMING_CALLS_BARRED_WITHIN_CUG = 55;
+ /** The mobile station is not authorized to use bearer capability requested */
+ public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57;
/** The requested bearer capability is not available at this time */
- public static final int BEARER_NOT_AVAIL = 58;
+ public static final int BEARER_NOT_AVAIL = 58;
+ /** The service option is not availble at this time */
+ public static final int SERVICE_OPTION_NOT_AVAILABLE = 63;
+ /** The equipment sending this cause does not support the bearer capability requested */
+ public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65;
/** The call clearing is due to ACM being greater than or equal to ACMmax */
- public static final int ACM_LIMIT_EXCEEDED = 68;
+ public static final int ACM_LIMIT_EXCEEDED = 68;
+ /** The equipment sending this cause does not support the requested facility */
+ public static final int REQUESTED_FACILITY_NOT_IMPLEMENTED = 69;
+ /** The equipment sending this cause only supports the restricted version of
+ * the requested bearer capability
+ */
+ public static final int ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE = 70;
+ /** The service requested is not implemented at network */
+ public static final int SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79;
+ /** The equipment sending this cause has received a message with a transaction identifier
+ * which is not currently in use on the MS-network interface
+ */
+ public static final int INVALID_TRANSACTION_IDENTIFIER = 81;
+ /** The called user for the incoming CUG call is not a member of the specified CUG */
+ public static final int USER_NOT_MEMBER_OF_CUG = 87;
+ /** The equipment sending this cause has received a request which can't be accomodated */
+ public static final int INCOMPATIBLE_DESTINATION = 88;
+ /** This cause is used to report receipt of a message with semantically incorrect contents */
+ public static final int SEMANTICALLY_INCORRECT_MESSAGE = 95;
+ /** The equipment sending this cause has received a message with a non-semantical
+ * mandatory IE error
+ */
+ public static final int INVALID_MANDATORY_INFORMATION = 96;
+ /** This is sent in response to a message which is not defined, or defined but not
+ * implemented by the equipment sending this cause
+ */
+ public static final int MESSAGE_TYPE_NON_IMPLEMENTED = 97;
+ /** The equipment sending this cause has received a message not compatible with the
+ * protocol state
+ */
+ public static final int MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98;
+ /** The equipment sending this cause has received a message which includes information
+ * elements not recognized because its identifier is not defined or it is defined but not
+ * implemented by the equipment sending the cause
+ */
+ public static final int INFORMATION_ELEMENT_NON_EXISTENT = 99;
+ /** The equipment sending this cause has received a message with conditional IE errors */
+ public static final int CONDITIONAL_IE_ERROR = 100;
+ /** The message has been received which is incompatible with the protocol state */
+ public static final int MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101;
+ /** The procedure has been initiated by the expiry of a timer in association with
+ * 3GPP TS 24.008 error handling procedures
+ */
+ public static final int RECOVERY_ON_TIMER_EXPIRED = 102;
+ /** This protocol error event is reported only when no other cause in the protocol
+ * error class applies
+ */
+ public static final int PROTOCOL_ERROR_UNSPECIFIED = 111;
+ /** interworking with a network which does not provide causes for actions it takes
+ * thus, the precise cause for a message which is being sent cannot be ascertained
+ */
+ public static final int INTERWORKING_UNSPECIFIED = 127;
/** The call is restricted */
- public static final int CALL_BARRED = 240;
+ public static final int CALL_BARRED = 240;
/** The call is blocked by the Fixed Dialing Number list */
- public static final int FDN_BLOCKED = 241;
+ public static final int FDN_BLOCKED = 241;
/** The given IMSI is not known at the VLR */
/** TS 24.008 cause 4 */
- public static final int IMSI_UNKNOWN_IN_VLR = 242;
+ public static final int IMSI_UNKNOWN_IN_VLR = 242;
/**
* The network does not accept emergency call establishment using an IMEI or not accept attach
* procedure for emergency services using an IMEI
*/
- public static final int IMEI_NOT_ACCEPTED = 243;
+ public static final int IMEI_NOT_ACCEPTED = 243;
+ /** The call cannot be established because RADIO is OFF */
+ public static final int RADIO_OFF = 247;
+ /** The call cannot be established because of no cell coverage */
+ public static final int OUT_OF_SRV = 248;
+ /** The call cannot be established because of no valid SIM */
+ public static final int NO_VALID_SIM = 249;
+ /** The call is dropped or failed internally by modem */
+ public static final int RADIO_INTERNAL_ERROR = 250;
+ /** Call failed because of UE timer expired while waiting for a response from network */
+ public static final int NETWORK_RESP_TIMEOUT = 251;
+ /** Call failed because of a network reject */
+ public static final int NETWORK_REJECT = 252;
+ /** Call failed because of radio access failure. ex. RACH failure */
+ public static final int RADIO_ACCESS_FAILURE = 253;
+ /** Call failed/dropped because of a RLF */
+ public static final int RADIO_LINK_FAILURE = 254;
+ /** Call failed/dropped because of radio link lost */
+ public static final int RADIO_LINK_LOST = 255;
+ /** Call failed because of a radio uplink issue */
+ public static final int RADIO_UPLINK_FAILURE = 256;
+ /** Call failed because of a RRC connection setup failure */
+ public static final int RADIO_SETUP_FAILURE = 257;
+ /** Call failed/dropped because of RRC connection release from NW */
+ public static final int RADIO_RELEASE_NORMAL = 258;
+ /** Call failed/dropped because of RRC abnormally released by modem/network */
+ public static final int RADIO_RELEASE_ABNORMAL = 259;
+ /** Call setup failed because of access class barring */
+ public static final int ACCESS_CLASS_BLOCKED = 260;
+ /** Call failed/dropped because of a network detach */
+ public static final int NETWORK_DETACH = 261;
+
/** MS is locked until next power cycle */
- public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000;
+ public static final int CDMA_LOCKED_UNTIL_POWER_CYCLE = 1000;
/** Drop call*/
- public static final int CDMA_DROP = 1001;
+ public static final int CDMA_DROP = 1001;
/** INTERCEPT order received, MS state idle entered */
- public static final int CDMA_INTERCEPT = 1002;
+ public static final int CDMA_INTERCEPT = 1002;
/** MS has been redirected, call is cancelled */
- public static final int CDMA_REORDER = 1003;
+ public static final int CDMA_REORDER = 1003;
/** Service option rejection */
- public static final int CDMA_SO_REJECT = 1004;
+ public static final int CDMA_SO_REJECT = 1004;
/** Requested service is rejected, retry delay is set */
- public static final int CDMA_RETRY_ORDER = 1005;
+ public static final int CDMA_RETRY_ORDER = 1005;
/** Unable to obtain access to the CDMA system */
- public static final int CDMA_ACCESS_FAILURE = 1006;
+ public static final int CDMA_ACCESS_FAILURE = 1006;
/** Not a preempted call */
- public static final int CDMA_PREEMPTED = 1007;
+ public static final int CDMA_PREEMPTED = 1007;
/** Not an emergency call */
- public static final int CDMA_NOT_EMERGENCY = 1008;
+ public static final int CDMA_NOT_EMERGENCY = 1008;
/** Access Blocked by CDMA network */
- public static final int CDMA_ACCESS_BLOCKED = 1009;
+ public static final int CDMA_ACCESS_BLOCKED = 1009;
+
+ /** Mapped from ImsReasonInfo */
+ /* The passed argument is an invalid */
+ public static final int LOCAL_ILLEGAL_ARGUMENT = 1200;
+ // The operation is invoked in invalid call state
+ public static final int LOCAL_ILLEGAL_STATE = 1201;
+ // IMS service internal error
+ public static final int LOCAL_INTERNAL_ERROR = 1202;
+ // IMS service goes down (service connection is lost)
+ public static final int LOCAL_IMS_SERVICE_DOWN = 1203;
+ // No pending incoming call exists
+ public static final int LOCAL_NO_PENDING_CALL = 1204;
+ // Service unavailable; by power off
+ public static final int LOCAL_POWER_OFF = 1205;
+ // Service unavailable; by low battery
+ public static final int LOCAL_LOW_BATTERY = 1206;
+ // Service unavailable; by out of service (data service state)
+ public static final int LOCAL_NETWORK_NO_SERVICE = 1207;
+ /* Service unavailable; by no LTE coverage
+ * (VoLTE is not supported even though IMS is registered)
+ */
+ public static final int LOCAL_NETWORK_NO_LTE_COVERAGE = 1208;
+ /** Service unavailable; by located in roaming area */
+ public static final int LOCAL_NETWORK_ROAMING = 1209;
+ /** Service unavailable; by IP changed */
+ public static final int LOCAL_NETWORK_IP_CHANGED = 1210;
+ /** Service unavailable; other */
+ public static final int LOCAL_SERVICE_UNAVAILABLE = 1211;
+ /* Service unavailable; IMS connection is lost (IMS is not registered) */
+ public static final int LOCAL_NOT_REGISTERED = 1212;
+ /** Max call exceeded */
+ public static final int LOCAL_MAX_CALL_EXCEEDED = 1213;
+ /** Call decline */
+ public static final int LOCAL_CALL_DECLINE = 1214;
+ /** SRVCC is in progress */
+ public static final int LOCAL_CALL_VCC_ON_PROGRESSING = 1215;
+ /** Resource reservation is failed (QoS precondition) */
+ public static final int LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 1216;
+ /** Retry CS call; VoLTE service can't be provided by the network or remote end
+ * Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
+ */
+ public static final int LOCAL_CALL_CS_RETRY_REQUIRED = 1217;
+ /** Retry VoLTE call; VoLTE service can't be provided by the network temporarily */
+ public static final int LOCAL_CALL_VOLTE_RETRY_REQUIRED = 1218;
+ /** IMS call is already terminated (in TERMINATED state) */
+ public static final int LOCAL_CALL_TERMINATED = 1219;
+ /** Handover not feasible */
+ public static final int LOCAL_HO_NOT_FEASIBLE = 1220;
+
+ /** 1xx waiting timer is expired after sending INVITE request (MO only) */
+ public static final int TIMEOUT_1XX_WAITING = 1221;
+ /** User no answer during call setup operation (MO/MT)
+ * MO : 200 OK to INVITE request is not received,
+ * MT : No action from user after alerting the call
+ */
+ public static final int TIMEOUT_NO_ANSWER = 1222;
+ /** User no answer during call update operation (MO/MT)
+ * MO : 200 OK to re-INVITE request is not received,
+ * MT : No action from user after alerting the call
+ */
+ public static final int TIMEOUT_NO_ANSWER_CALL_UPDATE = 1223;
+
+ /**
+ * STATUSCODE (SIP response code) (IMS -> Telephony)
+ */
+ /** SIP request is redirected */
+ public static final int SIP_REDIRECTED = 1300;
+ /** 4xx responses */
+ /** 400 : Bad Request */
+ public static final int SIP_BAD_REQUEST = 1310;
+ /** 403 : Forbidden */
+ public static final int SIP_FORBIDDEN = 1311;
+ /** 404 : Not Found */
+ public static final int SIP_NOT_FOUND = 1312;
+ /** 415 : Unsupported Media Type
+ * 416 : Unsupported URI Scheme
+ * 420 : Bad Extension
+ */
+ public static final int SIP_NOT_SUPPORTED = 1313;
+ /** 408 : Request Timeout */
+ public static final int SIP_REQUEST_TIMEOUT = 1314;
+ /** 480 : Temporarily Unavailable */
+ public static final int SIP_TEMPRARILY_UNAVAILABLE = 1315;
+ /** 484 : Address Incomplete */
+ public static final int SIP_BAD_ADDRESS = 1316;
+ /** 486 : Busy Here
+ * 600 : Busy Everywhere
+ */
+ public static final int SIP_BUSY = 1317;
+ /** 487 : Request Terminated */
+ public static final int SIP_REQUEST_CANCELLED = 1318;
+ /** 406 : Not Acceptable
+ * 488 : Not Acceptable Here
+ * 606 : Not Acceptable
+ */
+ public static final int SIP_NOT_ACCEPTABLE = 1319;
+ /** 410 : Gone
+ * 604 : Does Not Exist Anywhere
+ */
+ public static final int SIP_NOT_REACHABLE = 1320;
+ /** Others */
+ public static final int SIP_CLIENT_ERROR = 1321;
+ /** 5xx responses
+ * 501 : Server Internal Error
+ */
+ public static final int SIP_SERVER_INTERNAL_ERROR = 1330;
+ /** 503 : Service Unavailable */
+ public static final int SIP_SERVICE_UNAVAILABLE = 1331;
+ /** 504 : Server Time-out */
+ public static final int SIP_SERVER_TIMEOUT = 1332;
+ /** Others */
+ public static final int SIP_SERVER_ERROR = 1333;
+ /** 6xx responses
+ * 603 : Decline
+ */
+ public static final int SIP_USER_REJECTED = 1340;
+ /** Others */
+ public static final int SIP_GLOBAL_ERROR = 1341;
+ /** Emergency failure */
+ public static final int EMERGENCY_TEMP_FAILURE = 1342;
+ public static final int EMERGENCY_PERM_FAILURE = 1343;
+ /** Media resource initialization failed */
+ public static final int MEDIA_INIT_FAILED = 1400;
+ /** RTP timeout (no audio / video traffic in the session) */
+ public static final int MEDIA_NO_DATA = 1401;
+ /** Media is not supported; so dropped the call */
+ public static final int MEDIA_NOT_ACCEPTABLE = 1402;
+ /** Unknown media related errors */
+ public static final int MEDIA_UNSPECIFIED = 1403;
+ /** User triggers the call end */
+ public static final int USER_TERMINATED = 1500;
+ /** No action while an incoming call is ringing */
+ public static final int USER_NOANSWER = 1501;
+ /** User ignores an incoming call */
+ public static final int USER_IGNORE = 1502;
+ /** User declines an incoming call */
+ public static final int USER_DECLINE = 1503;
+ /** Device declines/ends a call due to low battery */
+ public static final int LOW_BATTERY = 1504;
+ /** Device declines call due to blacklisted call ID */
+ public static final int BLACKLISTED_CALL_ID = 1505;
+ /** The call is terminated by the network or remote user */
+ public static final int USER_TERMINATED_BY_REMOTE = 1510;
+
+ /**
+ * UT
+ */
+ public static final int UT_NOT_SUPPORTED = 1800;
+ public static final int UT_SERVICE_UNAVAILABLE = 1801;
+ public static final int UT_OPERATION_NOT_ALLOWED = 1802;
+ public static final int UT_NETWORK_ERROR = 1803;
+ public static final int UT_CB_PASSWORD_MISMATCH = 1804;
+
+ /**
+ * ECBM
+ */
+ public static final int ECBM_NOT_SUPPORTED = 1900;
+
+ /**
+ * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework.
+ */
+ public static final int MULTIENDPOINT_NOT_SUPPORTED = 1901;
+
+ /**
+ * CALL DROP error codes (Call could drop because of many reasons like Network not available,
+ * handover, failed, etc)
+ */
+
+ /**
+ * CALL DROP error code for the case when a device is ePDG capable and when the user is on an
+ * active wifi call and at the edge of coverage and there is no qualified LTE network available
+ * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
+ * code is received as part of the handover message.
+ */
+ public static final int CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 2000;
+
+ /**
+ * MT call has ended due to a release from the network
+ * because the call was answered elsewhere
+ */
+ public static final int ANSWERED_ELSEWHERE = 2100;
+
+ /**
+ * For MultiEndpoint - Call Pull request has failed
+ */
+ public static final int CALL_PULL_OUT_OF_SYNC = 2101;
+
+ /**
+ * For MultiEndpoint - Call has been pulled from primary to secondary
+ */
+ public static final int CALL_PULLED = 2102;
+
+ /**
+ * Supplementary services (HOLD/RESUME) failure error codes.
+ * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+ */
+ public static final int SUPP_SVC_FAILED = 2300;
+ public static final int SUPP_SVC_CANCELLED = 2301;
+ public static final int SUPP_SVC_REINVITE_COLLISION = 2302;
+
+ /**
+ * DPD Procedure received no response or send failed
+ */
+ public static final int IWLAN_DPD_FAILURE = 2400;
+
+ /**
+ * Establishment of the ePDG Tunnel Failed
+ */
+ public static final int EPDG_TUNNEL_ESTABLISH_FAILURE = 2500;
+
+ /**
+ * Re-keying of the ePDG Tunnel Failed; may not always result in teardown
+ */
+ public static final int EPDG_TUNNEL_REKEY_FAILURE = 2501;
+
+ /**
+ * Connection to the packet gateway is lost
+ */
+ public static final int EPDG_TUNNEL_LOST_CONNECTION = 2502;
+
+ /**
+ * The maximum number of calls allowed has been reached. Used in a multi-endpoint scenario
+ * where the number of calls across all connected devices has reached the maximum.
+ */
+ public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 2503;
+
+ /**
+ * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has
+ * declined the call. Used in a multi-endpoint scenario where a remote device declined an
+ * incoming call.
+ */
+ public static final int REMOTE_CALL_DECLINE = 2504;
+
+ /**
+ * Indicates the call was disconnected due to the user reaching their data limit.
+ */
+ public static final int DATA_LIMIT_REACHED = 2505;
+
+ /**
+ * Indicates the call was disconnected due to the user disabling cellular data.
+ */
+ public static final int DATA_DISABLED = 2506;
+
+ /**
+ * Indicates a call was disconnected due to loss of wifi signal.
+ */
+ public static final int WIFI_LOST = 2507;
+
+
+ /* OEM specific error codes. To be used by OEMs when they don't want to
+ reveal error code which would be replaced by ERROR_UNSPECIFIED */
+ public static final int OEM_CAUSE_1 = 0xf001;
+ public static final int OEM_CAUSE_2 = 0xf002;
+ public static final int OEM_CAUSE_3 = 0xf003;
+ public static final int OEM_CAUSE_4 = 0xf004;
+ public static final int OEM_CAUSE_5 = 0xf005;
+ public static final int OEM_CAUSE_6 = 0xf006;
+ public static final int OEM_CAUSE_7 = 0xf007;
+ public static final int OEM_CAUSE_8 = 0xf008;
+ public static final int OEM_CAUSE_9 = 0xf009;
+ public static final int OEM_CAUSE_10 = 0xf00a;
+ public static final int OEM_CAUSE_11 = 0xf00b;
+ public static final int OEM_CAUSE_12 = 0xf00c;
+ public static final int OEM_CAUSE_13 = 0xf00d;
+ public static final int OEM_CAUSE_14 = 0xf00e;
+ public static final int OEM_CAUSE_15 = 0xf00f;
+
/** Disconnected due to unspecified reasons */
- public static final int ERROR_UNSPECIFIED = 0xffff;
+ public static final int ERROR_UNSPECIFIED = 0xffff;
/** Private constructor to avoid class instantiation. */
private PreciseDisconnectCause() {
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index e4f380f..bd8492b3 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -318,6 +318,66 @@
*/
public static final int CODE_IKEV2_AUTH_FAILURE = 1408;
+ /** The call cannot be established because RADIO is OFF */
+ public static final int CODE_RADIO_OFF = 1500;
+
+ /** The call cannot be established because of no valid SIM */
+ public static final int CODE_NO_VALID_SIM = 1501;
+
+ /** The failure is due internal error at modem */
+ public static final int CODE_RADIO_INTERNAL_ERROR = 1502;
+
+ /** The failure is due to UE timer expired while waiting for a response from network */
+ public static final int CODE_NETWORK_RESP_TIMEOUT = 1503;
+
+ /** The failure is due to explicit reject from network */
+ public static final int CODE_NETWORK_REJECT = 1504;
+
+ /** The failure is due to radio access failure. ex. RACH failure */
+ public static final int CODE_RADIO_ACCESS_FAILURE = 1505;
+
+ /** Call/IMS registration failed/dropped because of a RLF */
+ public static final int CODE_RADIO_LINK_FAILURE = 1506;
+
+ /** Call/IMS registration failed/dropped because of radio link lost */
+ public static final int CODE_RADIO_LINK_LOST = 1507;
+
+ /** The call Call/IMS registration failed because of a radio uplink issue */
+ public static final int CODE_RADIO_UPLINK_FAILURE = 1508;
+
+ /** Call failed because of a RRC connection setup failure */
+ public static final int CODE_RADIO_SETUP_FAILURE = 1509;
+
+ /** Call failed/dropped because of RRC connection release from NW */
+ public static final int CODE_RADIO_RELEASE_NORMAL = 1510;
+
+ /** Call failed/dropped because of RRC abnormally released by modem/network */
+ public static final int CODE_RADIO_RELEASE_ABNORMAL = 1511;
+
+ /** Call failed because of access class barring */
+ public static final int CODE_ACCESS_CLASS_BLOCKED = 1512;
+
+ /** Call/IMS registration is failed/dropped because of a network detach */
+ public static final int CODE_NETWORK_DETACH = 1513;
+
+ /* OEM specific error codes. To be used by OEMs when they don't want to
+ reveal error code which would be replaced by ERROR_UNSPECIFIED */
+ public static final int CODE_OEM_CAUSE_1 = 0xf001;
+ public static final int CODE_OEM_CAUSE_2 = 0xf002;
+ public static final int CODE_OEM_CAUSE_3 = 0xf003;
+ public static final int CODE_OEM_CAUSE_4 = 0xf004;
+ public static final int CODE_OEM_CAUSE_5 = 0xf005;
+ public static final int CODE_OEM_CAUSE_6 = 0xf006;
+ public static final int CODE_OEM_CAUSE_7 = 0xf007;
+ public static final int CODE_OEM_CAUSE_8 = 0xf008;
+ public static final int CODE_OEM_CAUSE_9 = 0xf009;
+ public static final int CODE_OEM_CAUSE_10 = 0xf00a;
+ public static final int CODE_OEM_CAUSE_11 = 0xf00b;
+ public static final int CODE_OEM_CAUSE_12 = 0xf00c;
+ public static final int CODE_OEM_CAUSE_13 = 0xf00d;
+ public static final int CODE_OEM_CAUSE_14 = 0xf00e;
+ public static final int CODE_OEM_CAUSE_15 = 0xf00f;
+
/**
* Network string error messages.
* mExtraMessage may have these values.
diff --git a/tools/aapt2/AppInfo.h b/tools/aapt2/AppInfo.h
index 1e488f7..9db21aa 100644
--- a/tools/aapt2/AppInfo.h
+++ b/tools/aapt2/AppInfo.h
@@ -23,30 +23,22 @@
namespace aapt {
-/**
- * Holds basic information about the app being built. Most of this information
- * will come from the app's AndroidManifest.
- */
+// Information relevant to building an app, parsed from the app's AndroidManifest.xml.
struct AppInfo {
- /**
- * App's package name.
- */
+ // The app's package name.
std::string package;
- /**
- * The App's minimum SDK version.
- */
+ // The app's minimum SDK version, if it is defined.
Maybe<std::string> min_sdk_version;
- /**
- * The Version code of the app.
- */
+ // The app's version code, if it is defined.
Maybe<uint32_t> version_code;
- /**
- * The revision code of the app.
- */
+ // The app's revision code, if it is defined.
Maybe<uint32_t> revision_code;
+
+ // The app's split name, if it is a split.
+ Maybe<std::string> split_name;
};
} // namespace aapt
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index 1042111..1b4d5bb 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -751,70 +751,67 @@
return true;
}
- Maybe<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res,
- IDiagnostics* diag) {
+ Maybe<AppInfo> ExtractAppInfoFromManifest(xml::XmlResource* xml_res, IDiagnostics* diag) {
// Make sure the first element is <manifest> with package attribute.
- if (xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get())) {
- AppInfo app_info;
-
- if (!manifest_el->namespace_uri.empty() ||
- manifest_el->name != "manifest") {
- diag->Error(DiagMessage(xml_res->file.source)
- << "root tag must be <manifest>");
- return {};
- }
-
- xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package");
- if (!package_attr) {
- diag->Error(DiagMessage(xml_res->file.source)
- << "<manifest> must have a 'package' attribute");
- return {};
- }
-
- app_info.package = package_attr->value;
-
- if (xml::Attribute* version_code_attr =
- manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
- Maybe<uint32_t> maybe_code =
- ResourceUtils::ParseInt(version_code_attr->value);
- if (!maybe_code) {
- diag->Error(DiagMessage(xml_res->file.source.WithLine(
- manifest_el->line_number))
- << "invalid android:versionCode '"
- << version_code_attr->value << "'");
- return {};
- }
- app_info.version_code = maybe_code.value();
- }
-
- if (xml::Attribute* revision_code_attr =
- manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
- Maybe<uint32_t> maybe_code =
- ResourceUtils::ParseInt(revision_code_attr->value);
- if (!maybe_code) {
- diag->Error(DiagMessage(xml_res->file.source.WithLine(
- manifest_el->line_number))
- << "invalid android:revisionCode '"
- << revision_code_attr->value << "'");
- return {};
- }
- app_info.revision_code = maybe_code.value();
- }
-
- if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
- if (xml::Attribute* min_sdk = uses_sdk_el->FindAttribute(
- xml::kSchemaAndroid, "minSdkVersion")) {
- app_info.min_sdk_version = min_sdk->value;
- }
- }
- return app_info;
+ xml::Element* manifest_el = xml::FindRootElement(xml_res->root.get());
+ if (manifest_el == nullptr) {
+ return {};
}
- return {};
+
+ AppInfo app_info;
+
+ if (!manifest_el->namespace_uri.empty() || manifest_el->name != "manifest") {
+ diag->Error(DiagMessage(xml_res->file.source) << "root tag must be <manifest>");
+ return {};
+ }
+
+ xml::Attribute* package_attr = manifest_el->FindAttribute({}, "package");
+ if (!package_attr) {
+ diag->Error(DiagMessage(xml_res->file.source)
+ << "<manifest> must have a 'package' attribute");
+ return {};
+ }
+ app_info.package = package_attr->value;
+
+ if (xml::Attribute* version_code_attr =
+ manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode")) {
+ Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(version_code_attr->value);
+ if (!maybe_code) {
+ diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
+ << "invalid android:versionCode '" << version_code_attr->value << "'");
+ return {};
+ }
+ app_info.version_code = maybe_code.value();
+ }
+
+ if (xml::Attribute* revision_code_attr =
+ manifest_el->FindAttribute(xml::kSchemaAndroid, "revisionCode")) {
+ Maybe<uint32_t> maybe_code = ResourceUtils::ParseInt(revision_code_attr->value);
+ if (!maybe_code) {
+ diag->Error(DiagMessage(xml_res->file.source.WithLine(manifest_el->line_number))
+ << "invalid android:revisionCode '" << revision_code_attr->value << "'");
+ return {};
+ }
+ app_info.revision_code = maybe_code.value();
+ }
+
+ if (xml::Attribute* split_name_attr = manifest_el->FindAttribute({}, "split")) {
+ if (!split_name_attr->value.empty()) {
+ app_info.split_name = split_name_attr->value;
+ }
+ }
+
+ if (xml::Element* uses_sdk_el = manifest_el->FindChild({}, "uses-sdk")) {
+ if (xml::Attribute* min_sdk =
+ uses_sdk_el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion")) {
+ app_info.min_sdk_version = min_sdk->value;
+ }
+ }
+ return app_info;
}
/**
- * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it
- * linked.
+ * Precondition: ResourceTable doesn't have any IDs assigned yet, nor is it linked.
* Postcondition: ResourceTable has only one package left. All others are
* stripped, or there is an error and false is returned.
*/
@@ -1367,45 +1364,44 @@
return true;
}
- std::unique_ptr<xml::XmlResource> GenerateSplitManifest(
- const AppInfo& app_info, const SplitConstraints& constraints) {
- std::unique_ptr<xml::XmlResource> doc =
- util::make_unique<xml::XmlResource>();
+ std::unique_ptr<xml::XmlResource> GenerateSplitManifest(const AppInfo& app_info,
+ const SplitConstraints& constraints) {
+ std::unique_ptr<xml::XmlResource> doc = util::make_unique<xml::XmlResource>();
- std::unique_ptr<xml::Namespace> namespace_android =
- util::make_unique<xml::Namespace>();
+ std::unique_ptr<xml::Namespace> namespace_android = util::make_unique<xml::Namespace>();
namespace_android->namespace_uri = xml::kSchemaAndroid;
namespace_android->namespace_prefix = "android";
- std::unique_ptr<xml::Element> manifest_el =
- util::make_unique<xml::Element>();
+ std::unique_ptr<xml::Element> manifest_el = util::make_unique<xml::Element>();
manifest_el->name = "manifest";
- manifest_el->attributes.push_back(
- xml::Attribute{"", "package", app_info.package});
+ manifest_el->attributes.push_back(xml::Attribute{"", "package", app_info.package});
if (app_info.version_code) {
- manifest_el->attributes.push_back(
- xml::Attribute{xml::kSchemaAndroid, "versionCode",
- std::to_string(app_info.version_code.value())});
+ manifest_el->attributes.push_back(xml::Attribute{
+ xml::kSchemaAndroid, "versionCode", std::to_string(app_info.version_code.value())});
}
if (app_info.revision_code) {
- manifest_el->attributes.push_back(
- xml::Attribute{xml::kSchemaAndroid, "revisionCode",
- std::to_string(app_info.revision_code.value())});
+ manifest_el->attributes.push_back(xml::Attribute{
+ xml::kSchemaAndroid, "revisionCode", std::to_string(app_info.revision_code.value())});
}
std::stringstream split_name;
+ if (app_info.split_name) {
+ split_name << app_info.split_name.value() << ".";
+ }
split_name << "config." << util::Joiner(constraints.configs, "_");
- manifest_el->attributes.push_back(
- xml::Attribute{"", "split", split_name.str()});
+ manifest_el->attributes.push_back(xml::Attribute{"", "split", split_name.str()});
- std::unique_ptr<xml::Element> application_el =
- util::make_unique<xml::Element>();
+ if (app_info.split_name) {
+ manifest_el->attributes.push_back(
+ xml::Attribute{"", "configForSplit", app_info.split_name.value()});
+ }
+
+ std::unique_ptr<xml::Element> application_el = util::make_unique<xml::Element>();
application_el->name = "application";
- application_el->attributes.push_back(
- xml::Attribute{xml::kSchemaAndroid, "hasCode", "false"});
+ application_el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, "hasCode", "false"});
manifest_el->AppendChild(std::move(application_el));
namespace_android->AppendChild(std::move(manifest_el));
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 313fe45..0c19c7a 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -29,10 +29,7 @@
namespace aapt {
-/**
- * This is how PackageManager builds class names from AndroidManifest.xml
- * entries.
- */
+// This is how PackageManager builds class names from AndroidManifest.xml entries.
static bool NameIsJavaClassName(xml::Element* el, xml::Attribute* attr,
SourcePathDiagnostics* diag) {
// We allow unqualified class names (ie: .HelloActivity)
@@ -90,6 +87,36 @@
};
}
+static bool AutoGenerateIsFeatureSplit(xml::Element* el, SourcePathDiagnostics* diag) {
+ constexpr const char* kFeatureSplit = "featureSplit";
+ constexpr const char* kIsFeatureSplit = "isFeatureSplit";
+
+ xml::Attribute* attr = el->FindAttribute({}, kFeatureSplit);
+ if (attr != nullptr) {
+ // Rewrite the featureSplit attribute to be "split". This is what the
+ // platform recognizes.
+ attr->name = "split";
+
+ // Now inject the android:isFeatureSplit="true" attribute.
+ xml::Attribute* attr = el->FindAttribute(xml::kSchemaAndroid, kIsFeatureSplit);
+ if (attr != nullptr) {
+ if (!ResourceUtils::ParseBool(attr->value).value_or_default(false)) {
+ // The isFeatureSplit attribute is false, which conflicts with the use
+ // of "featureSplit".
+ diag->Error(DiagMessage(el->line_number)
+ << "attribute 'featureSplit' used in <manifest> but 'android:isFeatureSplit' "
+ "is not 'true'");
+ return false;
+ }
+
+ // The attribute is already there and set to true, nothing to do.
+ } else {
+ el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, kIsFeatureSplit, "true"});
+ }
+ }
+ return true;
+}
+
static bool VerifyManifest(xml::Element* el, SourcePathDiagnostics* diag) {
xml::Attribute* attr = el->FindAttribute({}, "package");
if (!attr) {
@@ -97,31 +124,34 @@
<< "<manifest> tag is missing 'package' attribute");
return false;
} else if (ResourceUtils::IsReference(attr->value)) {
- diag->Error(
- DiagMessage(el->line_number)
- << "attribute 'package' in <manifest> tag must not be a reference");
+ diag->Error(DiagMessage(el->line_number)
+ << "attribute 'package' in <manifest> tag must not be a reference");
return false;
} else if (!util::IsJavaPackageName(attr->value)) {
diag->Error(DiagMessage(el->line_number)
- << "attribute 'package' in <manifest> tag is not a valid Java "
- "package name: '"
+ << "attribute 'package' in <manifest> tag is not a valid Java package name: '"
<< attr->value << "'");
return false;
}
+
+ attr = el->FindAttribute({}, "split");
+ if (attr) {
+ if (!util::IsJavaPackageName(attr->value)) {
+ diag->Error(DiagMessage(el->line_number) << "attribute 'split' in <manifest> tag is not a "
+ "valid split name");
+ return false;
+ }
+ }
return true;
}
-/**
- * The coreApp attribute in <manifest> is not a regular AAPT attribute, so type
- * checking on it is manual.
- */
+// The coreApp attribute in <manifest> is not a regular AAPT attribute, so type
+// checking on it is manual.
static bool FixCoreAppAttribute(xml::Element* el, SourcePathDiagnostics* diag) {
if (xml::Attribute* attr = el->FindAttribute("", "coreApp")) {
- std::unique_ptr<BinaryPrimitive> result =
- ResourceUtils::TryParseBool(attr->value);
+ std::unique_ptr<BinaryPrimitive> result = ResourceUtils::TryParseBool(attr->value);
if (!result) {
- diag->Error(DiagMessage(el->line_number)
- << "attribute coreApp must be a boolean");
+ diag->Error(DiagMessage(el->line_number) << "attribute coreApp must be a boolean");
return false;
}
attr->compiled_value = std::move(result);
@@ -172,8 +202,7 @@
}
if (options_.rename_instrumentation_target_package) {
- if (!util::IsJavaPackageName(
- options_.rename_instrumentation_target_package.value())) {
+ if (!util::IsJavaPackageName(options_.rename_instrumentation_target_package.value())) {
diag->Error(DiagMessage()
<< "invalid instrumentation target package override '"
<< options_.rename_instrumentation_target_package.value()
@@ -203,6 +232,7 @@
// Manifest actions.
xml::XmlNodeAction& manifest_action = (*executor)["manifest"];
+ manifest_action.Action(AutoGenerateIsFeatureSplit);
manifest_action.Action(VerifyManifest);
manifest_action.Action(FixCoreAppAttribute);
manifest_action.Action([&](xml::Element* el) -> bool {
@@ -276,6 +306,7 @@
manifest_action["compatible-screens"]["screen"];
manifest_action["supports-gl-texture"];
manifest_action["meta-data"] = meta_data_action;
+ manifest_action["uses-split"].Action(RequiredNameIsJavaPackage);
// Application actions.
xml::XmlNodeAction& application_action = manifest_action["application"];
@@ -311,15 +342,13 @@
public:
using xml::Visitor::Visit;
- explicit FullyQualifiedClassNameVisitor(const StringPiece& package)
- : package_(package) {}
+ explicit FullyQualifiedClassNameVisitor(const StringPiece& package) : package_(package) {}
void Visit(xml::Element* el) override {
for (xml::Attribute& attr : el->attributes) {
if (attr.namespace_uri == xml::kSchemaAndroid &&
class_attributes_.find(attr.name) != class_attributes_.end()) {
- if (Maybe<std::string> new_value =
- util::GetFullyQualifiedClassName(package_, attr.value)) {
+ if (Maybe<std::string> new_value = util::GetFullyQualifiedClassName(package_, attr.value)) {
attr.value = std::move(new_value.value());
}
}
@@ -334,8 +363,7 @@
std::unordered_set<StringPiece> class_attributes_ = {"name"};
};
-static bool RenameManifestPackage(const StringPiece& package_override,
- xml::Element* manifest_el) {
+static bool RenameManifestPackage(const StringPiece& package_override, xml::Element* manifest_el) {
xml::Attribute* attr = manifest_el->FindAttribute({}, "package");
// We've already verified that the manifest element is present, with a package
@@ -358,8 +386,7 @@
return false;
}
- if ((options_.min_sdk_version_default ||
- options_.target_sdk_version_default) &&
+ if ((options_.min_sdk_version_default || options_.target_sdk_version_default) &&
root->FindChild({}, "uses-sdk") == nullptr) {
// Auto insert a <uses-sdk> element. This must be inserted before the
// <application> tag. The device runtime PackageParser will make SDK version
@@ -374,8 +401,7 @@
return false;
}
- if (!executor.Execute(xml::XmlActionExecutorPolicy::kWhitelist,
- context->GetDiagnostics(), doc)) {
+ if (!executor.Execute(xml::XmlActionExecutorPolicy::kWhitelist, context->GetDiagnostics(), doc)) {
return false;
}
@@ -383,8 +409,7 @@
// Rename manifest package outside of the XmlActionExecutor.
// We need to extract the old package name and FullyQualify all class
// names.
- if (!RenameManifestPackage(options_.rename_manifest_package.value(),
- root)) {
+ if (!RenameManifestPackage(options_.rename_manifest_package.value(), root)) {
return false;
}
}
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index eb8a1cc..008344c 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -521,36 +521,21 @@
# add zwj sequences not in the current emoji-zwj-sequences.txt
adjusted_emoji_zwj_sequences = dict(_emoji_zwj_sequences)
adjusted_emoji_zwj_sequences.update(_emoji_zwj_sequences)
- # single parent families
+ # Wrestlers with modifiers
additional_emoji_zwj = (
- (0x1F468, 0x200D, 0x1F466),
- (0x1F468, 0x200D, 0x1F467),
- (0x1F468, 0x200D, 0x1F466, 0x200D, 0x1F466),
- (0x1F468, 0x200D, 0x1F467, 0x200D, 0x1F466),
- (0x1F468, 0x200D, 0x1F467, 0x200D, 0x1F467),
- (0x1F469, 0x200D, 0x1F466),
- (0x1F469, 0x200D, 0x1F467),
- (0x1F469, 0x200D, 0x1F466, 0x200D, 0x1F466),
- (0x1F469, 0x200D, 0x1F467, 0x200D, 0x1F466),
- (0x1F469, 0x200D, 0x1F467, 0x200D, 0x1F467),
- )
- # sequences formed from man and woman and optional fitzpatrick modifier
- modified_extensions = (
- 0x2696,
- 0x2708,
- 0x1F3A8,
- 0x1F680,
- 0x1F692,
+ (0x1F93C, 0x1F3FB, 0x200D, 0x2640),
+ (0x1F93C, 0x1F3FB, 0x200D, 0x2642),
+ (0x1F93C, 0x1F3FC, 0x200D, 0x2640),
+ (0x1F93C, 0x1F3FC, 0x200D, 0x2642),
+ (0x1F93C, 0x1F3FD, 0x200D, 0x2640),
+ (0x1F93C, 0x1F3FD, 0x200D, 0x2642),
+ (0x1F93C, 0x1F3FE, 0x200D, 0x2640),
+ (0x1F93C, 0x1F3FE, 0x200D, 0x2642),
+ (0x1F93C, 0x1F3FF, 0x200D, 0x2640),
+ (0x1F93C, 0x1F3FF, 0x200D, 0x2642),
)
for seq in additional_emoji_zwj:
adjusted_emoji_zwj_sequences[seq] = 'Emoji_ZWJ_Sequence'
- for ext in modified_extensions:
- for base in (0x1F468, 0x1F469):
- seq = (base, 0x200D, ext)
- adjusted_emoji_zwj_sequences[seq] = 'Emoji_ZWJ_Sequence'
- for modifier in range(0x1F3FB, 0x1F400):
- seq = (base, modifier, 0x200D, ext)
- adjusted_emoji_zwj_sequences[seq] = 'Emoji_ZWJ_Sequence'
for sequence in _emoji_sequences.keys():
sequence = tuple(ch for ch in sequence if ch != EMOJI_VS)
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 7de55aa..333a4f7d 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -28,6 +28,7 @@
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@@ -329,6 +330,50 @@
mUsageLimitStartTimeInMs, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes);
}
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("UpdateIdentifier: ").append(mUpdateIdentifier).append("\n");
+ builder.append("CredentialPriority: ").append(mCredentialPriority).append("\n");
+ builder.append("SubscriptionCreationTime: ").append(
+ mSubscriptionCreationTimeInMs != Long.MIN_VALUE
+ ? new Date(mSubscriptionCreationTimeInMs) : "Not specified").append("\n");
+ builder.append("SubscriptionExpirationTime: ").append(
+ mSubscriptionExpirationTimeInMs != Long.MIN_VALUE
+ ? new Date(mSubscriptionExpirationTimeInMs) : "Not specified").append("\n");
+ builder.append("UsageLimitStartTime: ").append(mUsageLimitStartTimeInMs != Long.MIN_VALUE
+ ? new Date(mUsageLimitStartTimeInMs) : "Not specified").append("\n");
+ builder.append("UsageTimePeriod: ").append(mUsageLimitUsageTimePeriodInMinutes)
+ .append("\n");
+ builder.append("UsageLimitDataLimit: ").append(mUsageLimitDataLimit).append("\n");
+ builder.append("UsageLimitTimeLimit: ").append(mUsageLimitTimeLimitInMinutes).append("\n");
+ if (mHomeSp != null) {
+ builder.append("HomeSP Begin ---\n");
+ builder.append(mHomeSp);
+ builder.append("HomeSP End ---\n");
+ }
+ if (mCredential != null) {
+ builder.append("Credential Begin ---\n");
+ builder.append(mCredential);
+ builder.append("Credential End ---\n");
+ }
+ if (mPolicy != null) {
+ builder.append("Policy Begin ---\n");
+ builder.append(mPolicy);
+ builder.append("Policy End ---\n");
+ }
+ if (mSubscriptionUpdate != null) {
+ builder.append("SubscriptionUpdate Begin ---\n");
+ builder.append(mSubscriptionUpdate);
+ builder.append("SubscriptionUpdate End ---\n");
+ }
+ if (mTrustRootCertList != null) {
+ builder.append("TrustRootCertServers: ").append(mTrustRootCertList.keySet())
+ .append("\n");
+ }
+ return builder.toString();
+ }
+
/**
* Validate the configuration data.
*
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index d8da84f..67fa1bb 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -30,6 +30,7 @@
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
+import java.util.Date;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@@ -282,6 +283,18 @@
mAbleToShare, mEapType, mNonEapInnerMethod);
}
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Username: ").append(mUsername).append("\n");
+ builder.append("MachineManaged: ").append(mMachineManaged).append("\n");
+ builder.append("SoftTokenApp: ").append(mSoftTokenApp).append("\n");
+ builder.append("AbleToShare: ").append(mAbleToShare).append("\n");
+ builder.append("EAPType: ").append(mEapType).append("\n");
+ builder.append("AuthMethod: ").append(mNonEapInnerMethod).append("\n");
+ return builder.toString();
+ }
+
/**
* Validate the configuration data.
*
@@ -440,6 +453,11 @@
return Objects.hash(mCertType, mCertSha256Fingerprint);
}
+ @Override
+ public String toString() {
+ return "CertificateType: " + mCertType + "\n";
+ }
+
/**
* Validate the configuration data.
*
@@ -562,6 +580,14 @@
}
@Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("IMSI: ").append(mImsi).append("\n");
+ builder.append("EAPType: ").append(mEapType).append("\n");
+ return builder.toString();
+ }
+
+ @Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mImsi);
dest.writeInt(mEapType);
@@ -767,6 +793,33 @@
mCaCertificate, mClientCertificateChain, mClientPrivateKey);
}
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Realm: ").append(mRealm).append("\n");
+ builder.append("CreationTime: ").append(mCreationTimeInMs != Long.MIN_VALUE
+ ? new Date(mCreationTimeInMs) : "Not specified").append("\n");
+ builder.append("ExpirationTime: ").append(mExpirationTimeInMs != Long.MIN_VALUE
+ ? new Date(mExpirationTimeInMs) : "Not specified").append("\n");
+ builder.append("CheckAAAServerStatus: ").append(mCheckAaaServerCertStatus).append("\n");
+ if (mUserCredential != null) {
+ builder.append("UserCredential Begin ---\n");
+ builder.append(mUserCredential);
+ builder.append("UserCredential End ---\n");
+ }
+ if (mCertCredential != null) {
+ builder.append("CertificateCredential Begin ---\n");
+ builder.append(mCertCredential);
+ builder.append("CertificateCredential End ---\n");
+ }
+ if (mSimCredential != null) {
+ builder.append("SIMCredential Begin ---\n");
+ builder.append(mSimCredential);
+ builder.append("SIMCredential End ---\n");
+ }
+ return builder.toString();
+ }
+
/**
* Validate the configuration data.
*
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
index 68bdf37..9192ab0 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java
@@ -241,6 +241,20 @@
mMatchAnyOis, mOtherHomePartners, mRoamingConsortiumOis);
}
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("FQDN: ").append(mFqdn).append("\n");
+ builder.append("FriendlyName: ").append(mFriendlyName).append("\n");
+ builder.append("IconURL: ").append(mIconUrl).append("\n");
+ builder.append("HomeNetworkIDs: ").append(mHomeNetworkIds).append("\n");
+ builder.append("MatchAllOIs: ").append(mMatchAllOis).append("\n");
+ builder.append("MatchAnyOIs: ").append(mMatchAnyOis).append("\n");
+ builder.append("OtherHomePartners: ").append(mOtherHomePartners).append("\n");
+ builder.append("RoamingConsortiumOIs: ").append(mRoamingConsortiumOis).append("\n");
+ return builder.toString();
+ }
+
/**
* Validate HomeSp data.
*
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Policy.java b/wifi/java/android/net/wifi/hotspot2/pps/Policy.java
index da36a11..1df70f8 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Policy.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Policy.java
@@ -249,6 +249,16 @@
return Objects.hash(mFqdn, mFqdnExactMatch, mPriority, mCountries);
}
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("FQDN: ").append(mFqdn).append("\n");
+ builder.append("ExactMatch: ").append("mFqdnExactMatch").append("\n");
+ builder.append("Priority: ").append(mPriority).append("\n");
+ builder.append("Countries: ").append(mCountries).append("\n");
+ return builder.toString();
+ }
+
/**
* Validate RoamingParnter data.
*
@@ -390,6 +400,29 @@
mPolicyUpdate);
}
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("MinHomeDownlinkBandwidth: ").append(mMinHomeDownlinkBandwidth)
+ .append("\n");
+ builder.append("MinHomeUplinkBandwidth: ").append(mMinHomeUplinkBandwidth).append("\n");
+ builder.append("MinRoamingDownlinkBandwidth: ").append(mMinRoamingDownlinkBandwidth)
+ .append("\n");
+ builder.append("MinRoamingUplinkBandwidth: ").append(mMinRoamingUplinkBandwidth)
+ .append("\n");
+ builder.append("ExcludedSSIDList: ").append(mExcludedSsidList).append("\n");
+ builder.append("RequiredProtoPortMap: ").append(mRequiredProtoPortMap).append("\n");
+ builder.append("MaximumBSSLoadValue: ").append(mMaximumBssLoadValue).append("\n");
+ builder.append("PreferredRoamingPartnerList: ").append(mPreferredRoamingPartnerList)
+ .append("\n");
+ if (mPolicyUpdate != null) {
+ builder.append("PolicyUpdate Begin ---\n");
+ builder.append(mPolicyUpdate);
+ builder.append("PolicyUpdate End ---\n");
+ }
+ return builder.toString();
+ }
+
/**
* Validate Policy data.
*
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java
index ae051b0..a7adfeb 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java
@@ -247,6 +247,18 @@
mTrustRootCertSha256Fingerprint);
}
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("UpdateInterval: ").append(mUpdateIntervalInMinutes).append("\n");
+ builder.append("UpdateMethod: ").append(mUpdateMethod).append("\n");
+ builder.append("Restriction: ").append(mRestriction).append("\n");
+ builder.append("ServerURI: ").append(mServerUri).append("\n");
+ builder.append("Username: ").append(mUsername).append("\n");
+ builder.append("TrustRootCertURL: ").append(mTrustRootCertUrl).append("\n");
+ return builder.toString();
+ }
+
/**
* Validate UpdateParameter data.
*