Merge "Allow GSM RSSI levels to be customized by CarrierConfig" into qt-dev
diff --git a/api/current.txt b/api/current.txt
index 5997043..bc634c6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10307,6 +10307,7 @@
field public static final String CATEGORY_APP_CALENDAR = "android.intent.category.APP_CALENDAR";
field public static final String CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS";
field public static final String CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL";
+ field public static final String CATEGORY_APP_FILES = "android.intent.category.APP_FILES";
field public static final String CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY";
field public static final String CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS";
field public static final String CATEGORY_APP_MARKET = "android.intent.category.APP_MARKET";
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index e3c2f42..f7608f5 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -4919,6 +4919,8 @@
UNKNOWN = 0;
SOME = 1;
FULL = 2;
+ PERSISTENT = 3;
+ BFGS = 4;
}
optional Action action = 3;
diff --git a/config/preloaded-classes b/config/preloaded-classes
index abdbab2..5910c28 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -756,7 +756,6 @@
android.content.AsyncQueryHandler$WorkerArgs
android.content.AsyncQueryHandler$WorkerHandler
android.content.AsyncQueryHandler
-android.content.AsyncTaskLoader$LoadTask
android.content.AsyncTaskLoader
android.content.BroadcastReceiver$PendingResult$1
android.content.BroadcastReceiver$PendingResult
@@ -3186,7 +3185,6 @@
android.speech.tts.ITextToSpeechService$Stub$Proxy
android.speech.tts.ITextToSpeechService
android.speech.tts.TextToSpeech$Action
-android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
android.speech.tts.TextToSpeech$Connection
android.speech.tts.TextToSpeech$OnInitListener
android.speech.tts.TextToSpeech
diff --git a/config/preloaded-classes-blacklist b/config/preloaded-classes-blacklist
index 59f605d..cd5a120 100644
--- a/config/preloaded-classes-blacklist
+++ b/config/preloaded-classes-blacklist
@@ -1,6 +1,8 @@
+android.content.AsyncTaskLoader$LoadTask
android.net.ConnectivityThread$Singleton
android.os.AsyncTask
android.os.FileObserver
+android.speech.tts.TextToSpeech$Connection$SetupConnectionAsyncTask
android.widget.Magnifier
sun.nio.fs.UnixChannelFactory
com.android.server.SystemConfig$PermissionEntry
diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java
index 2637764..c822d20 100644
--- a/core/java/android/accounts/Account.java
+++ b/core/java/android/accounts/Account.java
@@ -93,6 +93,12 @@
public Account(Parcel in) {
this.name = in.readString();
this.type = in.readString();
+ if (TextUtils.isEmpty(name)) {
+ throw new android.os.BadParcelableException("the name must not be empty: " + name);
+ }
+ if (TextUtils.isEmpty(type)) {
+ throw new android.os.BadParcelableException("the type must not be empty: " + type);
+ }
this.accessId = in.readString();
if (accessId != null) {
synchronized (sAccessedAccounts) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d87171e..8628d32 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4731,6 +4731,18 @@
@SdkConstant(SdkConstantType.INTENT_CATEGORY)
public static final String CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC";
+ /**
+ * Used with {@link #ACTION_MAIN} to launch the files application.
+ * The activity should be able to browse and manage files stored on the device.
+ * <p>NOTE: This should not be used as the primary key of an Intent,
+ * since it will not result in the app launching with the correct
+ * action and category. Instead, use this with
+ * {@link #makeMainSelectorActivity(String, String)} to generate a main
+ * Intent with this category in the selector.</p>
+ */
+ @SdkConstant(SdkConstantType.INTENT_CATEGORY)
+ public static final String CATEGORY_APP_FILES = "android.intent.category.APP_FILES";
+
// ---------------------------------------------------------------------
// ---------------------------------------------------------------------
// Standard extra data keys.
diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java
index fc79a42..639335e 100644
--- a/core/java/android/content/om/OverlayInfo.java
+++ b/core/java/android/content/om/OverlayInfo.java
@@ -451,7 +451,7 @@
public String toString() {
return "OverlayInfo { overlay=" + packageName + ", targetPackage=" + targetPackageName
+ ((targetOverlayableName == null) ? ""
- : ", targetOverlyabale=" + targetOverlayableName)
+ : ", targetOverlayable=" + targetOverlayableName)
+ ", state=" + state + " (" + stateToString(state) + "), userId=" + userId + " }";
}
}
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 9e9e68d..ed5c1b1 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -1148,9 +1148,9 @@
final Context context = AppGlobals.getInitialApplication();
final AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
- final boolean hasLegacy = appOps.noteOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE,
+ final boolean hasLegacy = appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE,
context.getApplicationInfo().uid,
- context.getPackageName()) == AppOpsManager.MODE_ALLOWED;
+ context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED;
// STOPSHIP: only use app-op once permission model has fully landed
final boolean requestedLegacy = !AppGlobals.getInitialApplication().getApplicationInfo()
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index c57bf91..075b650 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1652,6 +1652,26 @@
*/
public static boolean checkPermissionAndAppOp(Context context, boolean enforce,
int pid, int uid, String packageName, String permission, int op) {
+ return checkPermissionAndAppOp(context, enforce, pid, uid, packageName, permission, op,
+ true);
+ }
+
+ /**
+ * Check that given app holds both permission and appop but do not noteOp.
+ * @hide
+ */
+ public static boolean checkPermissionAndCheckOp(Context context, boolean enforce,
+ int pid, int uid, String packageName, String permission, int op) {
+ return checkPermissionAndAppOp(context, enforce, pid, uid, packageName, permission, op,
+ false);
+ }
+
+ /**
+ * Check that given app holds both permission and appop.
+ * @hide
+ */
+ private static boolean checkPermissionAndAppOp(Context context, boolean enforce,
+ int pid, int uid, String packageName, String permission, int op, boolean note) {
if (context.checkPermission(permission, pid, uid) != PERMISSION_GRANTED) {
if (enforce) {
throw new SecurityException(
@@ -1662,7 +1682,21 @@
}
AppOpsManager appOps = context.getSystemService(AppOpsManager.class);
- final int mode = appOps.noteOpNoThrow(op, uid, packageName);
+ final int mode;
+ if (note) {
+ mode = appOps.noteOpNoThrow(op, uid, packageName);
+ } else {
+ try {
+ appOps.checkPackage(uid, packageName);
+ } catch (SecurityException e) {
+ if (enforce) {
+ throw e;
+ } else {
+ return false;
+ }
+ }
+ mode = appOps.checkOpNoThrow(op, uid, packageName);
+ }
switch (mode) {
case AppOpsManager.MODE_ALLOWED:
return true;
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 2b7e8b8..5a91802 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -674,6 +674,8 @@
* Called when the Android system disconnects from the service.
*
* <p> At this point this service may no longer be an active {@link AutofillService}.
+ * It should not make calls on {@link AutofillManager} that requires the caller to be
+ * the current service.
*/
public void onDisconnected() {
}
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 96b861b..b00eb8a 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -41,6 +41,7 @@
import android.util.SparseArray;
import android.util.TimeUtils;
import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAugmentedAutofillManagerClient;
import android.view.autofill.IAutofillWindowPresenter;
@@ -183,6 +184,8 @@
* Called when the Android system disconnects from the service.
*
* <p> At this point this service may no longer be an active {@link AugmentedAutofillService}.
+ * It should not make calls on {@link AutofillManager} that requires the caller to be
+ * the current service.
*/
public void onDisconnected() {
}
diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java
index dc57a15..5be73b9 100644
--- a/core/java/android/service/contentcapture/ContentCaptureService.java
+++ b/core/java/android/service/contentcapture/ContentCaptureService.java
@@ -37,7 +37,6 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
-import android.service.autofill.AutofillService;
import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -350,7 +349,9 @@
/**
* Called when the Android system disconnects from the service.
*
- * <p> At this point this service may no longer be an active {@link AutofillService}.
+ * <p> At this point this service may no longer be an active {@link ContentCaptureService}.
+ * It should not make calls on {@link ContentCaptureManager} that requires the caller to be
+ * the current service.
*/
public void onDisconnected() {
Slog.i(TAG, "unbinding from " + getClass().getName());
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 597b34bf..956161a 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -41,9 +41,11 @@
* with remote animation targets should be relinquished. If {@param moveHomeToTop} is true, then
* the home activity should be moved to the top. Otherwise, the home activity is hidden and the
* user is returned to the app.
+ * @param sendUserLeaveHint If set to true, {@link Activity#onUserLeaving} will be sent to the
+ * top resumed app, false otherwise.
*/
@UnsupportedAppUsage
- void finish(boolean moveHomeToTop);
+ void finish(boolean moveHomeToTop, boolean sendUserLeaveHint);
/**
* Called by the handler to indicate that the recents animation input consumer should be
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index b26efc0..2f9136a 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -78,8 +78,8 @@
STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI
STATE_TOP, // ActivityManager.PROCESS_STATE_TOP
STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE_LOCATION
- STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_BOUND_TOP
+ STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 13bcbf6..3135c62 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -317,7 +317,8 @@
buffer->getPixelFormat(),
(jint)buffer->getUsage(),
(jlong)buffer.get(),
- namedColorSpace);
+ namedColorSpace,
+ false /* capturedSecureLayers */);
}
static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) {
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 0996352..77ebd02 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -462,7 +462,6 @@
}
if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
- LOG(INFO) << "Ignoring open file descriptor " << fd;
continue;
}
@@ -496,7 +495,6 @@
}
if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
- LOG(INFO) << "Ignoring open file descriptor " << fd;
continue;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index d4d0519..e02709e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -83,17 +83,18 @@
* as needed.
*/
public class ApplicationsState {
- static final String TAG = "ApplicationsState";
- static final boolean DEBUG = false;
- static final boolean DEBUG_LOCKING = false;
+ private static final String TAG = "ApplicationsState";
public static final int SIZE_UNKNOWN = -1;
public static final int SIZE_INVALID = -2;
- static final Pattern REMOVE_DIACRITICALS_PATTERN
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_LOCKING = false;
+ private static final Object sLock = new Object();
+ private static final Pattern REMOVE_DIACRITICALS_PATTERN
= Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
- static final Object sLock = new Object();
+ @VisibleForTesting
static ApplicationsState sInstance;
public static ApplicationsState getInstance(Application app) {
@@ -126,13 +127,12 @@
// Information about all applications. Synchronize on mEntriesMap
// to protect access to these.
- final ArrayList<Session> mSessions = new ArrayList<Session>();
- final ArrayList<Session> mRebuildingSessions = new ArrayList<Session>();
+ final ArrayList<Session> mSessions = new ArrayList<>();
+ final ArrayList<Session> mRebuildingSessions = new ArrayList<>();
private InterestingConfigChanges mInterestingConfigChanges = new InterestingConfigChanges();
// Map: userid => (Map: package name => AppEntry)
- final SparseArray<HashMap<String, AppEntry>> mEntriesMap =
- new SparseArray<HashMap<String, AppEntry>>();
- final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
+ final SparseArray<HashMap<String, AppEntry>> mEntriesMap = new SparseArray<>();
+ final ArrayList<AppEntry> mAppEntries = new ArrayList<>();
List<ApplicationInfo> mApplications = new ArrayList<>();
long mCurId = 1;
UUID mCurComputingSizeUuid;
@@ -182,9 +182,10 @@
mInterestingConfigChanges = interestingConfigChanges;
}
- public static final @SessionFlags int DEFAULT_SESSION_FLAGS =
+ @SessionFlags
+ public static final int DEFAULT_SESSION_FLAGS =
FLAG_SESSION_REQUEST_HOME_APP | FLAG_SESSION_REQUEST_ICONS |
- FLAG_SESSION_REQUEST_SIZES | FLAG_SESSION_REQUEST_LAUNCHER;
+ FLAG_SESSION_REQUEST_SIZES | FLAG_SESSION_REQUEST_LAUNCHER;
private ApplicationsState(Application app, IPackageManager iPackageManager) {
mContext = app;
@@ -194,7 +195,7 @@
mUm = mContext.getSystemService(UserManager.class);
mStats = mContext.getSystemService(StorageStatsManager.class);
for (int userId : mUm.getProfileIdsWithDisabled(UserHandle.myUserId())) {
- mEntriesMap.put(userId, new HashMap<String, AppEntry>());
+ mEntriesMap.put(userId, new HashMap<>());
}
mThread = new HandlerThread("ApplicationsState.Loader",
@@ -683,9 +684,16 @@
private AppEntry getEntryLocked(ApplicationInfo info) {
int userId = UserHandle.getUserId(info.uid);
AppEntry entry = mEntriesMap.get(userId).get(info.packageName);
- if (DEBUG) Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
+ if (DEBUG) {
+ Log.i(TAG, "Looking up entry of pkg " + info.packageName + ": " + entry);
+ }
if (entry == null) {
- if (DEBUG) Log.i(TAG, "Creating AppEntry for " + info.packageName);
+ if (mHiddenModules.contains(info.packageName)) {
+ return null;
+ }
+ if (DEBUG) {
+ Log.i(TAG, "Creating AppEntry for " + info.packageName);
+ }
entry = new AppEntry(mContext, info, mCurId++);
mEntriesMap.get(userId).put(info.packageName, entry);
mAppEntries.add(entry);
@@ -759,7 +767,8 @@
boolean mRebuildForeground;
private final boolean mHasLifecycle;
- @SessionFlags private int mFlags = DEFAULT_SESSION_FLAGS;
+ @SessionFlags
+ private int mFlags = DEFAULT_SESSION_FLAGS;
Session(Callbacks callbacks, Lifecycle lifecycle) {
mCallbacks = callbacks;
@@ -771,7 +780,8 @@
}
}
- public @SessionFlags int getSessionFlags() {
+ @SessionFlags
+ public int getSessionFlags() {
return mFlags;
}
@@ -863,25 +873,32 @@
filter.init(mContext);
}
- List<AppEntry> apps;
+ final List<AppEntry> apps;
synchronized (mEntriesMap) {
apps = new ArrayList<>(mAppEntries);
}
- ArrayList<AppEntry> filteredApps = new ArrayList<AppEntry>();
- if (DEBUG) Log.i(TAG, "Rebuilding...");
- for (int i = 0; i < apps.size(); i++) {
- AppEntry entry = apps.get(i);
+ ArrayList<AppEntry> filteredApps = new ArrayList<>();
+ if (DEBUG) {
+ Log.i(TAG, "Rebuilding...");
+ }
+ for (AppEntry entry : apps) {
if (entry != null && (filter == null || filter.filterApp(entry))) {
synchronized (mEntriesMap) {
- if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
+ if (DEBUG_LOCKING) {
+ Log.v(TAG, "rebuild acquired lock");
+ }
if (comparator != null) {
// Only need the label if we are going to be sorting.
entry.ensureLabel(mContext);
}
- if (DEBUG) Log.i(TAG, "Using " + entry.info.packageName + ": " + entry);
+ if (DEBUG) {
+ Log.i(TAG, "Using " + entry.info.packageName + ": " + entry);
+ }
filteredApps.add(entry);
- if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");
+ if (DEBUG_LOCKING) {
+ Log.v(TAG, "rebuild releasing lock");
+ }
}
}
}
@@ -1290,7 +1307,8 @@
}
}
- private @SessionFlags int getCombinedSessionFlags(List<Session> sessions) {
+ @SessionFlags
+ private int getCombinedSessionFlags(List<Session> sessions) {
synchronized (mEntriesMap) {
int flags = 0;
for (Session session : sessions) {
@@ -1601,7 +1619,7 @@
}
if (object1.info != null && object2.info != null) {
compareResult =
- sCollator.compare(object1.info.packageName, object2.info.packageName);
+ sCollator.compare(object1.info.packageName, object2.info.packageName);
if (compareResult != 0) {
return compareResult;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 2711e31..3a53d29 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -22,6 +22,7 @@
import android.util.Log;
import android.util.Pair;
+import com.android.settingslib.R;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -47,7 +48,9 @@
@Override
public String getSummary() {
- return mCachedDevice.getConnectionSummary();
+ return isConnected() || mCachedDevice.isBusy()
+ ? mCachedDevice.getConnectionSummary()
+ : mContext.getString(R.string.bluetooth_disconnected);
}
@Override
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 1d9105c..d2fe5cd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -77,9 +77,15 @@
}
}
- public void finish(boolean toHome) {
+ /**
+ * Finish the current recents animation.
+ * @param toHome Going to home or back to the previous app.
+ * @param sendUserLeaveHint determines whether userLeaveHint will be set true to the previous
+ * app.
+ */
+ public void finish(boolean toHome, boolean sendUserLeaveHint) {
try {
- mAnimationController.finish(toHome);
+ mAnimationController.finish(toHome, sendUserLeaveHint);
} catch (RemoteException e) {
Log.e(TAG, "Failed to finish recents animation", e);
}
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 0786b18..da9cffa 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3836,9 +3836,9 @@
}
// Determine if caller is holding runtime permission
- final boolean hasRead = StorageManager.checkPermissionAndAppOp(mContext, false, 0,
+ final boolean hasRead = StorageManager.checkPermissionAndCheckOp(mContext, false, 0,
uid, packageName, READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE);
- final boolean hasWrite = StorageManager.checkPermissionAndAppOp(mContext, false, 0,
+ final boolean hasWrite = StorageManager.checkPermissionAndCheckOp(mContext, false, 0,
uid, packageName, WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE);
// STOPSHIP: remove this temporary hack once we have dynamic runtime
// permissions fully enabled again
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 5cc9bfd..6bb3200 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -22,9 +22,10 @@
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.Preconditions;
+import com.android.server.am.ActivityManagerService;
+import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@@ -47,6 +48,8 @@
private ExecutorService mService = ConcurrentUtils.newFixedThreadPool(4,
"system-server-init-thread", Process.THREAD_PRIORITY_FOREGROUND);
+ private List<String> mPendingTasks = new ArrayList<>();
+
public static synchronized SystemServerInitThreadPool get() {
if (sInstance == null) {
sInstance = new SystemServerInitThreadPool();
@@ -57,19 +60,26 @@
}
public Future<?> submit(Runnable runnable, String description) {
- if (IS_DEBUGGABLE) {
- return mService.submit(() -> {
- Slog.d(TAG, "Started executing " + description);
- try {
- runnable.run();
- } catch (RuntimeException e) {
- Slog.e(TAG, "Failure in " + description + ": " + e, e);
- throw e;
- }
- Slog.d(TAG, "Finished executing " + description);
- });
+ synchronized (mPendingTasks) {
+ mPendingTasks.add(description);
}
- return mService.submit(runnable);
+ return mService.submit(() -> {
+ if (IS_DEBUGGABLE) {
+ Slog.d(TAG, "Started executing " + description);
+ }
+ try {
+ runnable.run();
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "Failure in " + description + ": " + e, e);
+ throw e;
+ }
+ synchronized (mPendingTasks) {
+ mPendingTasks.remove(description);
+ }
+ if (IS_DEBUGGABLE) {
+ Slog.d(TAG, "Finished executing " + description);
+ }
+ });
}
static synchronized void shutdown() {
@@ -81,16 +91,36 @@
TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
+ dumpStackTraces();
throw new IllegalStateException(TAG + " init interrupted");
}
+ if (!terminated) {
+ // dump stack must be called before shutdownNow() to collect stacktrace of threads
+ // in the thread pool.
+ dumpStackTraces();
+ }
List<Runnable> unstartedRunnables = sInstance.mService.shutdownNow();
if (!terminated) {
+ final List<String> copy = new ArrayList<>();
+ synchronized (sInstance.mPendingTasks) {
+ copy.addAll(sInstance.mPendingTasks);
+ }
throw new IllegalStateException("Cannot shutdown. Unstarted tasks "
- + unstartedRunnables);
+ + unstartedRunnables + " Unfinished tasks " + copy);
}
sInstance.mService = null; // Make mService eligible for GC
+ sInstance.mPendingTasks = null;
Slog.d(TAG, "Shutdown successful");
}
}
+ /**
+ * A helper function to call ActivityManagerService.dumpStackTraces().
+ */
+ private static void dumpStackTraces() {
+ final ArrayList<Integer> pids = new ArrayList<>();
+ pids.add(Process.myPid());
+ ActivityManagerService.dumpStackTraces(
+ pids, null, null, null);
+ }
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index c647e2e..ac584e9 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1304,12 +1304,12 @@
return;
}
if (VDBG) {
- log("notifyUserMobileDataStateChangedForSubscriberPhoneID: subId=" + phoneId
- + " state=" + state);
+ log("notifyUserMobileDataStateChangedForSubscriberPhoneID: PhoneId=" + phoneId
+ + " subId=" + subId + " state=" + state);
}
synchronized (mRecords) {
if (validatePhoneId(phoneId)) {
- mMessageWaiting[phoneId] = state;
+ mUserMobileDataState[phoneId] = state;
for (Record r : mRecords) {
if (r.matchPhoneStateListenerEvent(
PhoneStateListener.LISTEN_USER_MOBILE_DATA_STATE) &&
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 5d47c9d..44d435f 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1262,15 +1262,16 @@
// processes). These should not bring the current process
// into the top state, since they are not on top. Instead
// give them the best bound state after that.
+ final int bestState = cr.hasFlag(Context.BIND_INCLUDE_CAPABILITIES)
+ ? PROCESS_STATE_FOREGROUND_SERVICE_LOCATION
+ : PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
if ((cr.flags & Context.BIND_FOREGROUND_SERVICE) != 0) {
- clientProcState =
- PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ clientProcState = bestState;
} else if (mService.mWakefulness
== PowerManagerInternal.WAKEFULNESS_AWAKE
&& (cr.flags & Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)
!= 0) {
- clientProcState =
- PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+ clientProcState = bestState;
} else {
clientProcState =
PROCESS_STATE_IMPORTANT_FOREGROUND;
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index bc78d1a..3dbea0d 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -22,7 +22,6 @@
import static android.service.attention.AttentionService.ATTENTION_FAILURE_UNKNOWN;
import android.Manifest;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
@@ -51,6 +50,7 @@
import android.provider.Settings;
import android.service.attention.AttentionService;
import android.service.attention.AttentionService.AttentionFailureCodes;
+import android.service.attention.AttentionService.AttentionSuccessCodes;
import android.service.attention.IAttentionCallback;
import android.service.attention.IAttentionService;
import android.text.TextUtils;
@@ -75,6 +75,7 @@
*/
public class AttentionManagerService extends SystemService {
private static final String LOG_TAG = "AttentionManagerService";
+ private static final boolean DEBUG = false;
/**
* DeviceConfig flag name, allows a CTS to inject a fake implementation.
@@ -156,7 +157,11 @@
/**
* Checks whether user attention is at the screen and calls in the provided callback.
*
- * @return {@code true} if the framework was able to send the provided callback to the service
+ * Calling this multiple times quickly in a row will result in either a) returning a cached
+ * value, if present, or b) returning {@code false} because only one active request at a time is
+ * allowed.
+ *
+ * @return {@code true} if the framework was able to dispatch the request
*/
private boolean checkAttention(long timeout, AttentionCallbackInternal callbackInternal) {
Preconditions.checkNotNull(callbackInternal);
@@ -182,54 +187,30 @@
return false;
}
- if (userState.mService == null) {
- // make sure every callback is called back
- if (userState.mPendingAttentionCheck != null) {
- userState.mPendingAttentionCheck.cancel(
- ATTENTION_FAILURE_CANCELLED);
- }
- // fire the check when the service is started
- userState.mPendingAttentionCheck = new PendingAttentionCheck(
- callbackInternal, () -> checkAttention(timeout, callbackInternal));
- } else {
- try {
- // throttle frequent requests
- final AttentionCheckCache cache = userState.mAttentionCheckCache;
- if (cache != null && now < cache.mLastComputed + STALE_AFTER_MILLIS) {
- callbackInternal.onSuccess(cache.mResult, cache.mTimestamp);
- return true;
- }
+ // throttle frequent requests
+ final AttentionCheckCache cache = userState.mAttentionCheckCache;
+ if (cache != null && now < cache.mLastComputed + STALE_AFTER_MILLIS) {
+ callbackInternal.onSuccess(cache.mResult, cache.mTimestamp);
+ return true;
+ }
+ // prevent spamming with multiple requests, only one at a time is allowed
+ if (userState.mCurrentAttentionCheck != null) {
+ if (!userState.mCurrentAttentionCheck.mIsDispatched
+ || !userState.mCurrentAttentionCheck.mIsFulfilled) {
+ return false;
+ }
+ }
+
+ userState.mCurrentAttentionCheck = createAttentionCheck(callbackInternal, userState);
+
+ if (userState.mService != null) {
+ try {
// schedule request cancellation if not returned by that point yet
cancelAfterTimeoutLocked(timeout);
-
- userState.mCurrentAttentionCheck = new AttentionCheck(callbackInternal,
- new IAttentionCallback.Stub() {
- @Override
- public void onSuccess(int result, long timestamp) {
- callbackInternal.onSuccess(result, timestamp);
- synchronized (mLock) {
- userState.mAttentionCheckCache = new AttentionCheckCache(
- SystemClock.uptimeMillis(), result,
- timestamp);
- userState.mCurrentAttentionCheckIsFulfilled = true;
- }
- StatsLog.write(
- StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
- result);
- }
-
- @Override
- public void onFailure(int error) {
- callbackInternal.onFailure(error);
- userState.mCurrentAttentionCheckIsFulfilled = true;
- StatsLog.write(
- StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
- error);
- }
- });
userState.mService.checkAttention(
userState.mCurrentAttentionCheck.mIAttentionCallback);
+ userState.mCurrentAttentionCheck.mIsDispatched = true;
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Cannot call into the AttentionService");
return false;
@@ -239,6 +220,44 @@
}
}
+ private AttentionCheck createAttentionCheck(AttentionCallbackInternal callbackInternal,
+ UserState userState) {
+ final IAttentionCallback iAttentionCallback = new IAttentionCallback.Stub() {
+ @Override
+ public void onSuccess(@AttentionSuccessCodes int result, long timestamp) {
+ // the callback might have been cancelled already
+ if (!userState.mCurrentAttentionCheck.mIsFulfilled) {
+ callbackInternal.onSuccess(result, timestamp);
+ userState.mCurrentAttentionCheck.mIsFulfilled = true;
+ }
+
+ synchronized (mLock) {
+ userState.mAttentionCheckCache = new AttentionCheckCache(
+ SystemClock.uptimeMillis(), result,
+ timestamp);
+ }
+ StatsLog.write(
+ StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
+ result);
+ }
+
+ @Override
+ public void onFailure(@AttentionFailureCodes int error) {
+ // the callback might have been cancelled already
+ if (!userState.mCurrentAttentionCheck.mIsFulfilled) {
+ callbackInternal.onFailure(error);
+ userState.mCurrentAttentionCheck.mIsFulfilled = true;
+ }
+
+ StatsLog.write(
+ StatsLog.ATTENTION_MANAGER_SERVICE_RESULT_REPORTED,
+ error);
+ }
+ };
+
+ return new AttentionCheck(callbackInternal, iAttentionCallback);
+ }
+
/** Cancels the specified attention check. */
private void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) {
synchronized (mLock) {
@@ -246,25 +265,13 @@
if (userState == null) {
return;
}
- if (userState.mService == null) {
- if (userState.mPendingAttentionCheck != null
- && userState.mPendingAttentionCheck.mCallbackInternal.equals(
- callbackInternal)) {
- userState.mPendingAttentionCheck.cancel(ATTENTION_FAILURE_UNKNOWN);
- userState.mPendingAttentionCheck = null;
- }
+
+ if (!userState.mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) {
+ Slog.e(LOG_TAG, "Cannot cancel a non-current request");
return;
}
- if (userState.mCurrentAttentionCheck.mCallbackInternal.equals(callbackInternal)) {
- try {
- userState.mService.cancelAttentionCheck(
- userState.mCurrentAttentionCheck.mIAttentionCallback);
- } catch (RemoteException e) {
- Slog.e(LOG_TAG, "Cannot call into the AttentionService");
- }
- } else {
- Slog.e(LOG_TAG, "Cannot cancel a non-current request");
- }
+
+ cancel(userState);
}
}
@@ -272,6 +279,9 @@
private void disableSelf() {
final long identity = Binder.clearCallingIdentity();
try {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Disabling self.");
+ }
Settings.System.putInt(mContext.getContentResolver(), ADAPTIVE_SLEEP, 0);
} finally {
Binder.restoreCallingIdentity(identity);
@@ -428,34 +438,21 @@
}
}
- private static final class PendingAttentionCheck {
- private final AttentionCallbackInternal mCallbackInternal;
- private final Runnable mRunnable;
-
- PendingAttentionCheck(AttentionCallbackInternal callbackInternal,
- Runnable runnable) {
- mCallbackInternal = callbackInternal;
- mRunnable = runnable;
- }
-
- void cancel(@AttentionFailureCodes int failureCode) {
- mCallbackInternal.onFailure(failureCode);
- }
-
- void run() {
- mRunnable.run();
- }
- }
-
private static final class AttentionCheck {
private final AttentionCallbackInternal mCallbackInternal;
private final IAttentionCallback mIAttentionCallback;
+ private boolean mIsDispatched;
+ private boolean mIsFulfilled;
AttentionCheck(AttentionCallbackInternal callbackInternal,
IAttentionCallback iAttentionCallback) {
mCallbackInternal = callbackInternal;
mIAttentionCallback = iAttentionCallback;
}
+
+ void cancelInternal() {
+ mCallbackInternal.onFailure(ATTENTION_FAILURE_CANCELLED);
+ }
}
private static final class UserState {
@@ -469,11 +466,6 @@
@GuardedBy("mLock")
AttentionCheck mCurrentAttentionCheck;
@GuardedBy("mLock")
- boolean mCurrentAttentionCheckIsFulfilled;
-
- @GuardedBy("mLock")
- PendingAttentionCheck mPendingAttentionCheck;
- @GuardedBy("mLock")
AttentionCheckCache mAttentionCheckCache;
@UserIdInt
@@ -491,9 +483,17 @@
@GuardedBy("mLock")
private void handlePendingCallbackLocked() {
- if (mService != null && mPendingAttentionCheck != null) {
- mPendingAttentionCheck.run();
- mPendingAttentionCheck = null;
+ if (!mCurrentAttentionCheck.mIsDispatched) {
+ if (mService != null) {
+ try {
+ mService.checkAttention(mCurrentAttentionCheck.mIAttentionCallback);
+ mCurrentAttentionCheck.mIsDispatched = true;
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Cannot call into the AttentionService");
+ }
+ } else {
+ mCurrentAttentionCheck.mCallbackInternal.onFailure(ATTENTION_FAILURE_UNKNOWN);
+ }
}
}
@@ -526,7 +526,6 @@
pw.printPair("userId", mUserId);
synchronized (mLock) {
pw.printPair("binding", mBinding);
- pw.printPair("isAttentionCheckPending", mPendingAttentionCheck != null);
}
}
@@ -586,14 +585,7 @@
// Callee is no longer interested in the attention check result - cancel.
case ATTENTION_CHECK_TIMEOUT: {
synchronized (mLock) {
- final UserState userState = peekCurrentUserStateLocked();
- if (userState != null) {
- // If not called back already.
- if (!userState.mCurrentAttentionCheckIsFulfilled) {
- cancel(userState, AttentionService.ATTENTION_FAILURE_TIMED_OUT);
- }
-
- }
+ cancel(peekCurrentUserStateLocked());
}
}
break;
@@ -604,19 +596,30 @@
}
}
- private void cancel(@NonNull UserState userState, @AttentionFailureCodes int failureCode) {
- if (userState.mService != null) {
- try {
- userState.mService.cancelAttentionCheck(
- userState.mCurrentAttentionCheck.mIAttentionCallback);
- } catch (RemoteException e) {
- Slog.e(LOG_TAG, "Unable to cancel attention check");
- }
+ private void cancel(UserState userState) {
+ if (userState == null || userState.mCurrentAttentionCheck == null) {
+ return;
+ }
- if (userState.mPendingAttentionCheck != null) {
- userState.mPendingAttentionCheck.cancel(failureCode);
- userState.mPendingAttentionCheck = null;
+ if (userState.mCurrentAttentionCheck.mIsFulfilled) {
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "Trying to cancel the check that has been already fulfilled.");
}
+ return;
+ }
+ userState.mCurrentAttentionCheck.mIsFulfilled = true;
+
+ if (userState.mService == null) {
+ userState.mCurrentAttentionCheck.cancelInternal();
+ return;
+ }
+
+ try {
+ userState.mService.cancelAttentionCheck(
+ userState.mCurrentAttentionCheck.mIAttentionCallback);
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Unable to cancel attention check");
+ userState.mCurrentAttentionCheck.cancelInternal();
}
}
@@ -626,7 +629,12 @@
if (userState == null) {
return;
}
- cancel(userState, ATTENTION_FAILURE_UNKNOWN);
+
+ cancel(userState);
+
+ if (userState.mService == null) {
+ return;
+ }
mContext.unbindService(userState.mConnection);
userState.mConnection.cleanupService();
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 32781a9..3fc2d37 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -855,8 +855,7 @@
public void onSystemReady() {
mSystemReady = true;
- sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE,
- 0, 0, null, 0);
+ scheduleLoadSoundEffects();
mDeviceBroker.onSystemReady();
@@ -3225,6 +3224,14 @@
}
/**
+ * Schedule loading samples into the soundpool.
+ * This method can be overridden to schedule loading at a later time.
+ */
+ protected void scheduleLoadSoundEffects() {
+ sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, null, 0);
+ }
+
+ /**
* Unloads samples from the sound pool.
* This method can be called to free some memory when
* sound effects are disabled.
diff --git a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
index e7181e2..c32ae97 100644
--- a/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
+++ b/services/core/java/com/android/server/display/whitebalance/DisplayWhiteBalanceController.java
@@ -271,7 +271,7 @@
final long time = System.currentTimeMillis();
float ambientColorTemperature = mColorTemperatureFilter.getEstimate(time);
- if (mAmbientToDisplayColorTemperatureSpline != null) {
+ if (mAmbientToDisplayColorTemperatureSpline != null && ambientColorTemperature != -1.0f) {
ambientColorTemperature =
mAmbientToDisplayColorTemperatureSpline.interpolate(ambientColorTemperature);
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 647e952..6f1929f 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -231,6 +231,7 @@
Context.BIND_AUTO_CREATE
| Context.BIND_TREAT_LIKE_ACTIVITY
| Context.BIND_FOREGROUND_SERVICE
+ | Context.BIND_INCLUDE_CAPABILITIES
| Context.BIND_SHOWING_UI
| Context.BIND_SCHEDULE_LIKE_TOP_APP;
diff --git a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
index ab75b21..2948aaf 100644
--- a/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/GnssNetworkConnectivityHandler.java
@@ -199,29 +199,27 @@
}
/**
- * called from native code to update AGPS status
+ * Called from native code to update AGPS connection status, or to request or release a SUPL
+ * connection.
+ *
+ * <p>Note: {@code suplIpAddr} parameter is not present from IAGnssCallback.hal@2.0 onwards
+ * and is set to {@code null}.
*/
void onReportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
+ if (DEBUG) Log.d(TAG, "AGPS_DATA_CONNECTION: " + agpsDataConnStatusAsString(agpsStatus));
switch (agpsStatus) {
case GPS_REQUEST_AGPS_DATA_CONN:
- if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN");
runOnHandler(() -> handleRequestSuplConnection(agpsType, suplIpAddr));
break;
case GPS_RELEASE_AGPS_DATA_CONN:
- if (DEBUG) Log.d(TAG, "GPS_RELEASE_AGPS_DATA_CONN");
runOnHandler(() -> handleReleaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN));
break;
case GPS_AGPS_DATA_CONNECTED:
- if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONNECTED");
- break;
case GPS_AGPS_DATA_CONN_DONE:
- if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_DONE");
- break;
case GPS_AGPS_DATA_CONN_FAILED:
- if (DEBUG) Log.d(TAG, "GPS_AGPS_DATA_CONN_FAILED");
break;
default:
- if (DEBUG) Log.d(TAG, "Received Unknown AGPS status: " + agpsStatus);
+ Log.w(TAG, "Received unknown AGPS status: " + agpsStatus);
}
}
@@ -459,11 +457,17 @@
}
mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING;
- NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
- requestBuilder.addCapability(getNetworkCapability(mAGpsType));
- NetworkRequest request = requestBuilder.build();
+ // The NetworkRequest.Builder class is not used to construct the network request because
+ // the ConnectivityService requires the network request to be constructed in this way
+ // to extend support for requestRouteToHostAddress() method for pre-gnss@2.0 devices.
+ NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ networkCapabilities.addCapability(getNetworkCapability(mAGpsType));
+ NetworkRequest networkRequest = new NetworkRequest(networkCapabilities,
+ getLegacyDataConnectionType(agpsType), ConnectivityManager.REQUEST_ID_UNSET,
+ NetworkRequest.Type.REQUEST);
mConnMgr.requestNetwork(
- request,
+ networkRequest,
mSuplConnectivityCallback,
mHandler,
SUPL_NETWORK_REQUEST_TIMEOUT_MILLIS);
@@ -483,6 +487,19 @@
}
}
+ private int getLegacyDataConnectionType(int agpsType) {
+ switch (agpsType) {
+ case AGPS_TYPE_C2K:
+ case AGPS_TYPE_SUPL:
+ return ConnectivityManager.TYPE_MOBILE_SUPL;
+ case AGPS_TYPE_EIMS:
+ return ConnectivityManager.TYPE_MOBILE_EMERGENCY;
+ case AGPS_TYPE_IMS:
+ return ConnectivityManager.TYPE_MOBILE_IMS;
+ default:
+ throw new IllegalArgumentException("agpsType: " + agpsType);
+ }
+ }
private void handleReleaseSuplConnection(int agpsDataConnStatus) {
if (DEBUG) {
String message = String.format(
@@ -546,7 +563,7 @@
case AGPS_DATA_CONNECTION_OPENING:
return "OPENING";
default:
- return "<Unknown>";
+ return "<Unknown>(" + mAGpsDataConnectionState + ")";
}
}
@@ -566,7 +583,7 @@
case GPS_REQUEST_AGPS_DATA_CONN:
return "REQUEST";
default:
- return "<Unknown>";
+ return "<Unknown>(" + agpsDataConnStatus + ")";
}
}
@@ -581,7 +598,7 @@
case AGPS_TYPE_IMS:
return "IMS";
default:
- return "<Unknown>";
+ return "<Unknown>(" + agpsType + ")";
}
}
@@ -658,4 +675,4 @@
private native void native_update_network_state(boolean connected, int type, boolean roaming,
boolean available, String apn, long networkHandle, short capabilities);
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index f34b2cb..642fa7f 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -38,7 +38,6 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Pair;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.proto.ProtoOutputStream;
@@ -180,7 +179,7 @@
}
}
- PackagePreferences r = getOrCreatePackagePreferences(name, uid,
+ PackagePreferences r = getOrCreatePackagePreferencesLocked(name, uid,
XmlUtils.readIntAttribute(
parser, ATT_IMPORTANCE, DEFAULT_IMPORTANCE),
XmlUtils.readIntAttribute(parser, ATT_PRIORITY,
@@ -264,9 +263,9 @@
}
try {
- deleteDefaultChannelIfNeeded(r);
+ deleteDefaultChannelIfNeededLocked(r);
} catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "deleteDefaultChannelIfNeeded - Exception: " + e);
+ Slog.e(TAG, "deleteDefaultChannelIfNeededLocked - Exception: " + e);
}
}
}
@@ -276,50 +275,46 @@
throw new IllegalStateException("Failed to reach END_DOCUMENT");
}
- private PackagePreferences getPackagePreferences(String pkg, int uid) {
+ private PackagePreferences getPackagePreferencesLocked(String pkg, int uid) {
final String key = packagePreferencesKey(pkg, uid);
- synchronized (mPackagePreferences) {
- return mPackagePreferences.get(key);
- }
+ return mPackagePreferences.get(key);
}
- private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid) {
- return getOrCreatePackagePreferences(pkg, uid,
+ private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg, int uid) {
+ return getOrCreatePackagePreferencesLocked(pkg, uid,
DEFAULT_IMPORTANCE, DEFAULT_PRIORITY, DEFAULT_VISIBILITY, DEFAULT_SHOW_BADGE,
DEFAULT_ALLOW_BUBBLE);
}
- private PackagePreferences getOrCreatePackagePreferences(String pkg, int uid, int importance,
- int priority, int visibility, boolean showBadge, boolean allowBubble) {
+ private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg, int uid,
+ int importance, int priority, int visibility, boolean showBadge, boolean allowBubble) {
final String key = packagePreferencesKey(pkg, uid);
- synchronized (mPackagePreferences) {
- PackagePreferences
- r = (uid == UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg)
- : mPackagePreferences.get(key);
- if (r == null) {
- r = new PackagePreferences();
- r.pkg = pkg;
- r.uid = uid;
- r.importance = importance;
- r.priority = priority;
- r.visibility = visibility;
- r.showBadge = showBadge;
- r.allowBubble = allowBubble;
+ PackagePreferences
+ r = (uid == UNKNOWN_UID) ? mRestoredWithoutUids.get(pkg)
+ : mPackagePreferences.get(key);
+ if (r == null) {
+ r = new PackagePreferences();
+ r.pkg = pkg;
+ r.uid = uid;
+ r.importance = importance;
+ r.priority = priority;
+ r.visibility = visibility;
+ r.showBadge = showBadge;
+ r.allowBubble = allowBubble;
- try {
- createDefaultChannelIfNeeded(r);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "createDefaultChannelIfNeeded - Exception: " + e);
- }
-
- if (r.uid == UNKNOWN_UID) {
- mRestoredWithoutUids.put(pkg, r);
- } else {
- mPackagePreferences.put(key, r);
- }
+ try {
+ createDefaultChannelIfNeededLocked(r);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "createDefaultChannelIfNeededLocked - Exception: " + e);
}
- return r;
+
+ if (r.uid == UNKNOWN_UID) {
+ mRestoredWithoutUids.put(pkg, r);
+ } else {
+ mPackagePreferences.put(key, r);
+ }
}
+ return r;
}
private boolean shouldHaveDefaultChannel(PackagePreferences r) throws
@@ -336,7 +331,7 @@
return true;
}
- private void deleteDefaultChannelIfNeeded(PackagePreferences r) throws
+ private void deleteDefaultChannelIfNeededLocked(PackagePreferences r) throws
PackageManager.NameNotFoundException {
if (!r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
// Not present
@@ -352,7 +347,7 @@
r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
}
- private void createDefaultChannelIfNeeded(PackagePreferences r) throws
+ private void createDefaultChannelIfNeededLocked(PackagePreferences r) throws
PackageManager.NameNotFoundException {
if (r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
r.channels.get(NotificationChannel.DEFAULT_CHANNEL_ID).setName(mContext.getString(
@@ -479,9 +474,11 @@
* @param allowed whether bubbles are allowed.
*/
public void setBubblesAllowed(String pkg, int uid, boolean allowed) {
- PackagePreferences p = getOrCreatePackagePreferences(pkg, uid);
- p.allowBubble = allowed;
- p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE;
+ synchronized (mPackagePreferences) {
+ PackagePreferences p = getOrCreatePackagePreferencesLocked(pkg, uid);
+ p.allowBubble = allowed;
+ p.lockedAppFields = p.lockedAppFields | LockableAppFields.USER_LOCKED_BUBBLE;
+ }
}
/**
@@ -493,11 +490,15 @@
*/
@Override
public boolean areBubblesAllowed(String pkg, int uid) {
- return getOrCreatePackagePreferences(pkg, uid).allowBubble;
+ synchronized (mPackagePreferences) {
+ return getOrCreatePackagePreferencesLocked(pkg, uid).allowBubble;
+ }
}
public int getAppLockedFields(String pkg, int uid) {
- return getOrCreatePackagePreferences(pkg, uid).lockedAppFields;
+ synchronized (mPackagePreferences) {
+ return getOrCreatePackagePreferencesLocked(pkg, uid).lockedAppFields;
+ }
}
/**
@@ -505,7 +506,9 @@
*/
@Override
public int getImportance(String packageName, int uid) {
- return getOrCreatePackagePreferences(packageName, uid).importance;
+ synchronized (mPackagePreferences) {
+ return getOrCreatePackagePreferencesLocked(packageName, uid).importance;
+ }
}
/**
@@ -514,18 +517,24 @@
* locking field, see {@link NotificationChannel#USER_LOCKED_IMPORTANCE}.
*/
public boolean getIsAppImportanceLocked(String packageName, int uid) {
- int userLockedFields = getOrCreatePackagePreferences(packageName, uid).lockedAppFields;
- return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
+ synchronized (mPackagePreferences) {
+ int userLockedFields = getOrCreatePackagePreferencesLocked(packageName, uid).lockedAppFields;
+ return (userLockedFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0;
+ }
}
@Override
public boolean canShowBadge(String packageName, int uid) {
- return getOrCreatePackagePreferences(packageName, uid).showBadge;
+ synchronized (mPackagePreferences) {
+ return getOrCreatePackagePreferencesLocked(packageName, uid).showBadge;
+ }
}
@Override
public void setShowBadge(String packageName, int uid, boolean showBadge) {
- getOrCreatePackagePreferences(packageName, uid).showBadge = showBadge;
+ synchronized (mPackagePreferences) {
+ getOrCreatePackagePreferencesLocked(packageName, uid).showBadge = showBadge;
+ }
updateConfig();
}
@@ -534,20 +543,26 @@
if (groupId == null) {
return false;
}
- PackagePreferences r = getOrCreatePackagePreferences(packageName, uid);
- NotificationChannelGroup group = r.groups.get(groupId);
- if (group == null) {
- return false;
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getOrCreatePackagePreferencesLocked(packageName, uid);
+ NotificationChannelGroup group = r.groups.get(groupId);
+ if (group == null) {
+ return false;
+ }
+ return group.isBlocked();
}
- return group.isBlocked();
}
int getPackagePriority(String pkg, int uid) {
- return getOrCreatePackagePreferences(pkg, uid).priority;
+ synchronized (mPackagePreferences) {
+ return getOrCreatePackagePreferencesLocked(pkg, uid).priority;
+ }
}
int getPackageVisibility(String pkg, int uid) {
- return getOrCreatePackagePreferences(pkg, uid).visibility;
+ synchronized (mPackagePreferences) {
+ return getOrCreatePackagePreferencesLocked(pkg, uid).visibility;
+ }
}
@Override
@@ -557,32 +572,34 @@
Preconditions.checkNotNull(group);
Preconditions.checkNotNull(group.getId());
Preconditions.checkNotNull(!TextUtils.isEmpty(group.getName()));
- PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
- if (r == null) {
- throw new IllegalArgumentException("Invalid package");
- }
- final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
- if (!group.equals(oldGroup)) {
- // will log for new entries as well as name/description changes
- MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
- }
- if (oldGroup != null) {
- group.setChannels(oldGroup.getChannels());
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ throw new IllegalArgumentException("Invalid package");
+ }
+ final NotificationChannelGroup oldGroup = r.groups.get(group.getId());
+ if (!group.equals(oldGroup)) {
+ // will log for new entries as well as name/description changes
+ MetricsLogger.action(getChannelGroupLog(group.getId(), pkg));
+ }
+ if (oldGroup != null) {
+ group.setChannels(oldGroup.getChannels());
- // apps can't update the blocked status or app overlay permission
- if (fromTargetApp) {
- group.setBlocked(oldGroup.isBlocked());
- group.unlockFields(group.getUserLockedFields());
- group.lockFields(oldGroup.getUserLockedFields());
- } else {
- // but the system can
- if (group.isBlocked() != oldGroup.isBlocked()) {
- group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
- updateChannelsBypassingDnd(mContext.getUserId());
+ // apps can't update the blocked status or app overlay permission
+ if (fromTargetApp) {
+ group.setBlocked(oldGroup.isBlocked());
+ group.unlockFields(group.getUserLockedFields());
+ group.lockFields(oldGroup.getUserLockedFields());
+ } else {
+ // but the system can
+ if (group.isBlocked() != oldGroup.isBlocked()) {
+ group.lockFields(NotificationChannelGroup.USER_LOCKED_BLOCKED_STATE);
+ updateChannelsBypassingDnd(mContext.getUserId());
+ }
}
}
+ r.groups.put(group.getId(), group);
}
- r.groups.put(group.getId(), group);
}
@Override
@@ -592,94 +609,96 @@
Preconditions.checkNotNull(channel);
Preconditions.checkNotNull(channel.getId());
Preconditions.checkArgument(!TextUtils.isEmpty(channel.getName()));
- PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
- if (r == null) {
- throw new IllegalArgumentException("Invalid package");
- }
- if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
- throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
- }
- if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
- throw new IllegalArgumentException("Reserved id");
- }
- NotificationChannel existing = r.channels.get(channel.getId());
- // Keep most of the existing settings
- if (existing != null && fromTargetApp) {
- if (existing.isDeleted()) {
- existing.setDeleted(false);
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ throw new IllegalArgumentException("Invalid package");
+ }
+ if (channel.getGroup() != null && !r.groups.containsKey(channel.getGroup())) {
+ throw new IllegalArgumentException("NotificationChannelGroup doesn't exist");
+ }
+ if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(channel.getId())) {
+ throw new IllegalArgumentException("Reserved id");
+ }
+ NotificationChannel existing = r.channels.get(channel.getId());
+ // Keep most of the existing settings
+ if (existing != null && fromTargetApp) {
+ if (existing.isDeleted()) {
+ existing.setDeleted(false);
- // log a resurrected channel as if it's new again
- MetricsLogger.action(getChannelLog(channel, pkg).setType(
- com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
+ // log a resurrected channel as if it's new again
+ MetricsLogger.action(getChannelLog(channel, pkg).setType(
+ com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
+ }
+
+ existing.setName(channel.getName().toString());
+ existing.setDescription(channel.getDescription());
+ existing.setBlockableSystem(channel.isBlockableSystem());
+ if (existing.getGroup() == null) {
+ existing.setGroup(channel.getGroup());
+ }
+
+ // Apps are allowed to downgrade channel importance if the user has not changed any
+ // fields on this channel yet.
+ final int previousExistingImportance = existing.getImportance();
+ if (existing.getUserLockedFields() == 0 &&
+ channel.getImportance() < existing.getImportance()) {
+ existing.setImportance(channel.getImportance());
+ }
+
+ // system apps and dnd access apps can bypass dnd if the user hasn't changed any
+ // fields on the channel yet
+ if (existing.getUserLockedFields() == 0 && hasDndAccess) {
+ boolean bypassDnd = channel.canBypassDnd();
+ existing.setBypassDnd(bypassDnd);
+
+ if (bypassDnd != mAreChannelsBypassingDnd
+ || previousExistingImportance != existing.getImportance()) {
+ updateChannelsBypassingDnd(mContext.getUserId());
+ }
+ }
+
+ updateConfig();
+ return;
+ }
+ if (channel.getImportance() < IMPORTANCE_NONE
+ || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
+ throw new IllegalArgumentException("Invalid importance level");
}
- existing.setName(channel.getName().toString());
- existing.setDescription(channel.getDescription());
- existing.setBlockableSystem(channel.isBlockableSystem());
- if (existing.getGroup() == null) {
- existing.setGroup(channel.getGroup());
+ // Reset fields that apps aren't allowed to set.
+ if (fromTargetApp && !hasDndAccess) {
+ channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
}
-
- // Apps are allowed to downgrade channel importance if the user has not changed any
- // fields on this channel yet.
- final int previousExistingImportance = existing.getImportance();
- if (existing.getUserLockedFields() == 0 &&
- channel.getImportance() < existing.getImportance()) {
- existing.setImportance(channel.getImportance());
+ if (fromTargetApp) {
+ channel.setLockscreenVisibility(r.visibility);
}
-
- // system apps and dnd access apps can bypass dnd if the user hasn't changed any
- // fields on the channel yet
- if (existing.getUserLockedFields() == 0 && hasDndAccess) {
- boolean bypassDnd = channel.canBypassDnd();
- existing.setBypassDnd(bypassDnd);
-
- if (bypassDnd != mAreChannelsBypassingDnd
- || previousExistingImportance != existing.getImportance()) {
- updateChannelsBypassingDnd(mContext.getUserId());
+ clearLockedFieldsLocked(channel);
+ channel.setImportanceLockedByOEM(r.oemLockedImportance);
+ if (!channel.isImportanceLockedByOEM()) {
+ if (r.futureOemLockedChannels.remove(channel.getId())) {
+ channel.setImportanceLockedByOEM(true);
}
}
-
- updateConfig();
- return;
- }
- if (channel.getImportance() < IMPORTANCE_NONE
- || channel.getImportance() > NotificationManager.IMPORTANCE_MAX) {
- throw new IllegalArgumentException("Invalid importance level");
- }
-
- // Reset fields that apps aren't allowed to set.
- if (fromTargetApp && !hasDndAccess) {
- channel.setBypassDnd(r.priority == Notification.PRIORITY_MAX);
- }
- if (fromTargetApp) {
- channel.setLockscreenVisibility(r.visibility);
- }
- clearLockedFields(channel);
- channel.setImportanceLockedByOEM(r.oemLockedImportance);
- if (!channel.isImportanceLockedByOEM()) {
- if (r.futureOemLockedChannels.remove(channel.getId())) {
- channel.setImportanceLockedByOEM(true);
+ channel.setImportanceLockedByCriticalDeviceFunction(r.defaultAppLockedImportance);
+ if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
+ channel.setLockscreenVisibility(
+ NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
}
- }
- channel.setImportanceLockedByCriticalDeviceFunction(r.defaultAppLockedImportance);
- if (channel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
- channel.setLockscreenVisibility(
- NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
- }
- if (!r.showBadge) {
- channel.setShowBadge(false);
- }
+ if (!r.showBadge) {
+ channel.setShowBadge(false);
+ }
- r.channels.put(channel.getId(), channel);
- if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
- updateChannelsBypassingDnd(mContext.getUserId());
+ r.channels.put(channel.getId(), channel);
+ if (channel.canBypassDnd() != mAreChannelsBypassingDnd) {
+ updateChannelsBypassingDnd(mContext.getUserId());
+ }
+ MetricsLogger.action(getChannelLog(channel, pkg).setType(
+ com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
}
- MetricsLogger.action(getChannelLog(channel, pkg).setType(
- com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_OPEN));
}
- void clearLockedFields(NotificationChannel channel) {
+ void clearLockedFieldsLocked(NotificationChannel channel) {
channel.unlockFields(channel.getUserLockedFields());
}
@@ -688,55 +707,58 @@
boolean fromUser) {
Preconditions.checkNotNull(updatedChannel);
Preconditions.checkNotNull(updatedChannel.getId());
- PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
- if (r == null) {
- throw new IllegalArgumentException("Invalid package");
- }
- NotificationChannel channel = r.channels.get(updatedChannel.getId());
- if (channel == null || channel.isDeleted()) {
- throw new IllegalArgumentException("Channel does not exist");
- }
- if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
- updatedChannel.setLockscreenVisibility(
- NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
- }
- if (fromUser) {
- updatedChannel.lockFields(channel.getUserLockedFields());
- lockFieldsForUpdate(channel, updatedChannel);
- } else {
- updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
- }
- // no importance updates are allowed if OEM blocked it
- updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
- if (updatedChannel.isImportanceLockedByOEM()) {
- updatedChannel.setImportance(channel.getImportance());
- }
- updatedChannel.setImportanceLockedByCriticalDeviceFunction(r.defaultAppLockedImportance);
- if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()) {
- updatedChannel.setImportance(channel.getImportance());
- }
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ throw new IllegalArgumentException("Invalid package");
+ }
+ NotificationChannel channel = r.channels.get(updatedChannel.getId());
+ if (channel == null || channel.isDeleted()) {
+ throw new IllegalArgumentException("Channel does not exist");
+ }
+ if (updatedChannel.getLockscreenVisibility() == Notification.VISIBILITY_PUBLIC) {
+ updatedChannel.setLockscreenVisibility(
+ NotificationListenerService.Ranking.VISIBILITY_NO_OVERRIDE);
+ }
+ if (fromUser) {
+ updatedChannel.lockFields(channel.getUserLockedFields());
+ lockFieldsForUpdateLocked(channel, updatedChannel);
+ } else {
+ updatedChannel.unlockFields(updatedChannel.getUserLockedFields());
+ }
+ // no importance updates are allowed if OEM blocked it
+ updatedChannel.setImportanceLockedByOEM(channel.isImportanceLockedByOEM());
+ if (updatedChannel.isImportanceLockedByOEM()) {
+ updatedChannel.setImportance(channel.getImportance());
+ }
+ updatedChannel.setImportanceLockedByCriticalDeviceFunction(
+ r.defaultAppLockedImportance);
+ if (updatedChannel.isImportanceLockedByCriticalDeviceFunction()) {
+ updatedChannel.setImportance(channel.getImportance());
+ }
- r.channels.put(updatedChannel.getId(), updatedChannel);
+ r.channels.put(updatedChannel.getId(), updatedChannel);
- if (onlyHasDefaultChannel(pkg, uid)) {
- // copy settings to app level so they are inherited by new channels
- // when the app migrates
- r.importance = updatedChannel.getImportance();
- r.priority = updatedChannel.canBypassDnd()
- ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
- r.visibility = updatedChannel.getLockscreenVisibility();
- r.showBadge = updatedChannel.canShowBadge();
- }
+ if (onlyHasDefaultChannel(pkg, uid)) {
+ // copy settings to app level so they are inherited by new channels
+ // when the app migrates
+ r.importance = updatedChannel.getImportance();
+ r.priority = updatedChannel.canBypassDnd()
+ ? Notification.PRIORITY_MAX : Notification.PRIORITY_DEFAULT;
+ r.visibility = updatedChannel.getLockscreenVisibility();
+ r.showBadge = updatedChannel.canShowBadge();
+ }
- if (!channel.equals(updatedChannel)) {
- // only log if there are real changes
- MetricsLogger.action(getChannelLog(updatedChannel, pkg)
- .setSubtype(fromUser ? 1 : 0));
- }
+ if (!channel.equals(updatedChannel)) {
+ // only log if there are real changes
+ MetricsLogger.action(getChannelLog(updatedChannel, pkg)
+ .setSubtype(fromUser ? 1 : 0));
+ }
- if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
- || channel.getImportance() != updatedChannel.getImportance()) {
- updateChannelsBypassingDnd(mContext.getUserId());
+ if (updatedChannel.canBypassDnd() != mAreChannelsBypassingDnd
+ || channel.getImportance() != updatedChannel.getImportance()) {
+ updateChannelsBypassingDnd(mContext.getUserId());
+ }
}
updateConfig();
}
@@ -745,35 +767,39 @@
public NotificationChannel getNotificationChannel(String pkg, int uid, String channelId,
boolean includeDeleted) {
Preconditions.checkNotNull(pkg);
- PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
- if (r == null) {
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ return null;
+ }
+ if (channelId == null) {
+ channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
+ }
+ final NotificationChannel nc = r.channels.get(channelId);
+ if (nc != null && (includeDeleted || !nc.isDeleted())) {
+ return nc;
+ }
return null;
}
- if (channelId == null) {
- channelId = NotificationChannel.DEFAULT_CHANNEL_ID;
- }
- final NotificationChannel nc = r.channels.get(channelId);
- if (nc != null && (includeDeleted || !nc.isDeleted())) {
- return nc;
- }
- return null;
}
@Override
public void deleteNotificationChannel(String pkg, int uid, String channelId) {
- PackagePreferences r = getPackagePreferences(pkg, uid);
- if (r == null) {
- return;
- }
- NotificationChannel channel = r.channels.get(channelId);
- if (channel != null) {
- channel.setDeleted(true);
- LogMaker lm = getChannelLog(channel, pkg);
- lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
- MetricsLogger.action(lm);
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ return;
+ }
+ NotificationChannel channel = r.channels.get(channelId);
+ if (channel != null) {
+ channel.setDeleted(true);
+ LogMaker lm = getChannelLog(channel, pkg);
+ lm.setType(com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_CLOSE);
+ MetricsLogger.action(lm);
- if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
- updateChannelsBypassingDnd(mContext.getUserId());
+ if (mAreChannelsBypassingDnd && channel.canBypassDnd()) {
+ updateChannelsBypassingDnd(mContext.getUserId());
+ }
}
}
}
@@ -783,25 +809,29 @@
public void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId) {
Preconditions.checkNotNull(pkg);
Preconditions.checkNotNull(channelId);
- PackagePreferences r = getPackagePreferences(pkg, uid);
- if (r == null) {
- return;
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ return;
+ }
+ r.channels.remove(channelId);
}
- r.channels.remove(channelId);
}
@Override
public void permanentlyDeleteNotificationChannels(String pkg, int uid) {
Preconditions.checkNotNull(pkg);
- PackagePreferences r = getPackagePreferences(pkg, uid);
- if (r == null) {
- return;
- }
- int N = r.channels.size() - 1;
- for (int i = N; i >= 0; i--) {
- String key = r.channels.keyAt(i);
- if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
- r.channels.remove(key);
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ return;
+ }
+ int N = r.channels.size() - 1;
+ for (int i = N; i >= 0; i--) {
+ String key = r.channels.keyAt(i);
+ if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
+ r.channels.remove(key);
+ }
}
}
}
@@ -875,32 +905,36 @@
public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
int uid, String groupId, boolean includeDeleted) {
Preconditions.checkNotNull(pkg);
- PackagePreferences r = getPackagePreferences(pkg, uid);
- if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
- return null;
- }
- NotificationChannelGroup group = r.groups.get(groupId).clone();
- group.setChannels(new ArrayList<>());
- int N = r.channels.size();
- for (int i = 0; i < N; i++) {
- final NotificationChannel nc = r.channels.valueAt(i);
- if (includeDeleted || !nc.isDeleted()) {
- if (groupId.equals(nc.getGroup())) {
- group.addChannel(nc);
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+ if (r == null || groupId == null || !r.groups.containsKey(groupId)) {
+ return null;
+ }
+ NotificationChannelGroup group = r.groups.get(groupId).clone();
+ group.setChannels(new ArrayList<>());
+ int N = r.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = r.channels.valueAt(i);
+ if (includeDeleted || !nc.isDeleted()) {
+ if (groupId.equals(nc.getGroup())) {
+ group.addChannel(nc);
+ }
}
}
+ return group;
}
- return group;
}
public NotificationChannelGroup getNotificationChannelGroup(String groupId, String pkg,
int uid) {
Preconditions.checkNotNull(pkg);
- PackagePreferences r = getPackagePreferences(pkg, uid);
- if (r == null) {
- return null;
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ return null;
+ }
+ return r.groups.get(groupId);
}
- return r.groups.get(groupId);
}
@Override
@@ -908,60 +942,64 @@
int uid, boolean includeDeleted, boolean includeNonGrouped, boolean includeEmpty) {
Preconditions.checkNotNull(pkg);
Map<String, NotificationChannelGroup> groups = new ArrayMap<>();
- PackagePreferences r = getPackagePreferences(pkg, uid);
- if (r == null) {
- return ParceledListSlice.emptyList();
- }
- NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
- int N = r.channels.size();
- for (int i = 0; i < N; i++) {
- final NotificationChannel nc = r.channels.valueAt(i);
- if (includeDeleted || !nc.isDeleted()) {
- if (nc.getGroup() != null) {
- if (r.groups.get(nc.getGroup()) != null) {
- NotificationChannelGroup ncg = groups.get(nc.getGroup());
- if (ncg == null) {
- ncg = r.groups.get(nc.getGroup()).clone();
- ncg.setChannels(new ArrayList<>());
- groups.put(nc.getGroup(), ncg);
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ return ParceledListSlice.emptyList();
+ }
+ NotificationChannelGroup nonGrouped = new NotificationChannelGroup(null, null);
+ int N = r.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = r.channels.valueAt(i);
+ if (includeDeleted || !nc.isDeleted()) {
+ if (nc.getGroup() != null) {
+ if (r.groups.get(nc.getGroup()) != null) {
+ NotificationChannelGroup ncg = groups.get(nc.getGroup());
+ if (ncg == null) {
+ ncg = r.groups.get(nc.getGroup()).clone();
+ ncg.setChannels(new ArrayList<>());
+ groups.put(nc.getGroup(), ncg);
+ }
+ ncg.addChannel(nc);
}
- ncg.addChannel(nc);
+ } else {
+ nonGrouped.addChannel(nc);
}
- } else {
- nonGrouped.addChannel(nc);
}
}
- }
- if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
- groups.put(null, nonGrouped);
- }
- if (includeEmpty) {
- for (NotificationChannelGroup group : r.groups.values()) {
- if (!groups.containsKey(group.getId())) {
- groups.put(group.getId(), group);
+ if (includeNonGrouped && nonGrouped.getChannels().size() > 0) {
+ groups.put(null, nonGrouped);
+ }
+ if (includeEmpty) {
+ for (NotificationChannelGroup group : r.groups.values()) {
+ if (!groups.containsKey(group.getId())) {
+ groups.put(group.getId(), group);
+ }
}
}
+ return new ParceledListSlice<>(new ArrayList<>(groups.values()));
}
- return new ParceledListSlice<>(new ArrayList<>(groups.values()));
}
public List<NotificationChannel> deleteNotificationChannelGroup(String pkg, int uid,
String groupId) {
List<NotificationChannel> deletedChannels = new ArrayList<>();
- PackagePreferences r = getPackagePreferences(pkg, uid);
- if (r == null || TextUtils.isEmpty(groupId)) {
- return deletedChannels;
- }
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+ if (r == null || TextUtils.isEmpty(groupId)) {
+ return deletedChannels;
+ }
- r.groups.remove(groupId);
+ r.groups.remove(groupId);
- int N = r.channels.size();
- for (int i = 0; i < N; i++) {
- final NotificationChannel nc = r.channels.valueAt(i);
- if (groupId.equals(nc.getGroup())) {
- nc.setDeleted(true);
- deletedChannels.add(nc);
+ int N = r.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = r.channels.valueAt(i);
+ if (groupId.equals(nc.getGroup())) {
+ nc.setDeleted(true);
+ deletedChannels.add(nc);
+ }
}
}
return deletedChannels;
@@ -970,11 +1008,15 @@
@Override
public Collection<NotificationChannelGroup> getNotificationChannelGroups(String pkg,
int uid) {
- PackagePreferences r = getPackagePreferences(pkg, uid);
- if (r == null) {
- return new ArrayList<>();
+ List<NotificationChannelGroup> groups = new ArrayList<>();
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ return groups;
+ }
+ groups.addAll(r.groups.values());
}
- return r.groups.values();
+ return groups;
}
@Override
@@ -982,18 +1024,20 @@
boolean includeDeleted) {
Preconditions.checkNotNull(pkg);
List<NotificationChannel> channels = new ArrayList<>();
- PackagePreferences r = getPackagePreferences(pkg, uid);
- if (r == null) {
- return ParceledListSlice.emptyList();
- }
- int N = r.channels.size();
- for (int i = 0; i < N; i++) {
- final NotificationChannel nc = r.channels.valueAt(i);
- if (includeDeleted || !nc.isDeleted()) {
- channels.add(nc);
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ return ParceledListSlice.emptyList();
}
+ int N = r.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = r.channels.valueAt(i);
+ if (includeDeleted || !nc.isDeleted()) {
+ channels.add(nc);
+ }
+ }
+ return new ParceledListSlice<>(channels);
}
- return new ParceledListSlice<>(channels);
}
/**
@@ -1008,7 +1052,7 @@
// notifications from this package aren't blocked
if (r != null && r.importance != IMPORTANCE_NONE) {
for (NotificationChannel channel : r.channels.values()) {
- if (channelIsLive(r, channel) && channel.canBypassDnd()) {
+ if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
channels.add(channel);
}
}
@@ -1024,46 +1068,52 @@
* upgrades.
*/
public boolean onlyHasDefaultChannel(String pkg, int uid) {
- PackagePreferences r = getOrCreatePackagePreferences(pkg, uid);
- if (r.channels.size() == 1
- && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- return true;
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getOrCreatePackagePreferencesLocked(pkg, uid);
+ if (r.channels.size() == 1
+ && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
+ return true;
+ }
+ return false;
}
- return false;
}
public int getDeletedChannelCount(String pkg, int uid) {
Preconditions.checkNotNull(pkg);
int deletedCount = 0;
- PackagePreferences r = getPackagePreferences(pkg, uid);
- if (r == null) {
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ return deletedCount;
+ }
+ int N = r.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = r.channels.valueAt(i);
+ if (nc.isDeleted()) {
+ deletedCount++;
+ }
+ }
return deletedCount;
}
- int N = r.channels.size();
- for (int i = 0; i < N; i++) {
- final NotificationChannel nc = r.channels.valueAt(i);
- if (nc.isDeleted()) {
- deletedCount++;
- }
- }
- return deletedCount;
}
public int getBlockedChannelCount(String pkg, int uid) {
Preconditions.checkNotNull(pkg);
int blockedCount = 0;
- PackagePreferences r = getPackagePreferences(pkg, uid);
- if (r == null) {
+ synchronized (mPackagePreferences) {
+ PackagePreferences r = getPackagePreferencesLocked(pkg, uid);
+ if (r == null) {
+ return blockedCount;
+ }
+ int N = r.channels.size();
+ for (int i = 0; i < N; i++) {
+ final NotificationChannel nc = r.channels.valueAt(i);
+ if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
+ blockedCount++;
+ }
+ }
return blockedCount;
}
- int N = r.channels.size();
- for (int i = 0; i < N; i++) {
- final NotificationChannel nc = r.channels.valueAt(i);
- if (!nc.isDeleted() && IMPORTANCE_NONE == nc.getImportance()) {
- blockedCount++;
- }
- }
- return blockedCount;
}
public int getBlockedAppCount(int userId) {
@@ -1098,7 +1148,7 @@
}
for (NotificationChannel channel : r.channels.values()) {
- if (channelIsLive(r, channel) && channel.canBypassDnd()) {
+ if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
count++;
break;
}
@@ -1136,7 +1186,7 @@
}
for (NotificationChannel channel : r.channels.values()) {
- if (channelIsLive(r, channel) && channel.canBypassDnd()) {
+ if (channelIsLiveLocked(r, channel) && channel.canBypassDnd()) {
if (!mAreChannelsBypassingDnd) {
mAreChannelsBypassingDnd = true;
updateZenPolicy(true);
@@ -1153,7 +1203,7 @@
}
}
- private boolean channelIsLive(PackagePreferences pkgPref, NotificationChannel channel) {
+ private boolean channelIsLiveLocked(PackagePreferences pkgPref, NotificationChannel channel) {
// Channel is in a group that's blocked
if (isGroupBlocked(pkgPref.pkg, pkgPref.uid, channel.getGroup())) {
return false;
@@ -1185,7 +1235,9 @@
*/
@Override
public void setImportance(String pkgName, int uid, int importance) {
- getOrCreatePackagePreferences(pkgName, uid).importance = importance;
+ synchronized (mPackagePreferences) {
+ getOrCreatePackagePreferencesLocked(pkgName, uid).importance = importance;
+ }
updateConfig();
}
@@ -1204,12 +1256,15 @@
* considered for sentiment adjustments (and thus never show a blocking helper).
*/
public void setAppImportanceLocked(String packageName, int uid) {
- PackagePreferences prefs = getOrCreatePackagePreferences(packageName, uid);
- if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
- return;
- }
+ synchronized (mPackagePreferences) {
+ PackagePreferences prefs = getOrCreatePackagePreferencesLocked(packageName, uid);
+ if ((prefs.lockedAppFields & LockableAppFields.USER_LOCKED_IMPORTANCE) != 0) {
+ return;
+ }
- prefs.lockedAppFields = prefs.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
+ prefs.lockedAppFields =
+ prefs.lockedAppFields | LockableAppFields.USER_LOCKED_IMPORTANCE;
+ }
updateConfig();
}
@@ -1217,15 +1272,17 @@
* Returns the delegate for a given package, if it's allowed by the package and the user.
*/
public @Nullable String getNotificationDelegate(String sourcePkg, int sourceUid) {
- PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
+ synchronized (mPackagePreferences) {
+ PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
- if (prefs == null || prefs.delegate == null) {
- return null;
+ if (prefs == null || prefs.delegate == null) {
+ return null;
+ }
+ if (!prefs.delegate.mUserAllowed || !prefs.delegate.mEnabled) {
+ return null;
+ }
+ return prefs.delegate.mPkg;
}
- if (!prefs.delegate.mUserAllowed || !prefs.delegate.mEnabled) {
- return null;
- }
- return prefs.delegate.mPkg;
}
/**
@@ -1233,11 +1290,13 @@
*/
public void setNotificationDelegate(String sourcePkg, int sourceUid,
String delegatePkg, int delegateUid) {
- PackagePreferences prefs = getOrCreatePackagePreferences(sourcePkg, sourceUid);
+ synchronized (mPackagePreferences) {
+ PackagePreferences prefs = getOrCreatePackagePreferencesLocked(sourcePkg, sourceUid);
- boolean userAllowed = prefs.delegate == null || prefs.delegate.mUserAllowed;
- Delegate delegate = new Delegate(delegatePkg, delegateUid, true, userAllowed);
- prefs.delegate = delegate;
+ boolean userAllowed = prefs.delegate == null || prefs.delegate.mUserAllowed;
+ Delegate delegate = new Delegate(delegatePkg, delegateUid, true, userAllowed);
+ prefs.delegate = delegate;
+ }
updateConfig();
}
@@ -1245,9 +1304,15 @@
* Used by an app to turn off its notification delegate.
*/
public void revokeNotificationDelegate(String sourcePkg, int sourceUid) {
- PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
- if (prefs != null && prefs.delegate != null) {
- prefs.delegate.mEnabled = false;
+ boolean changed = false;
+ synchronized (mPackagePreferences) {
+ PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
+ if (prefs != null && prefs.delegate != null) {
+ prefs.delegate.mEnabled = false;
+ changed = true;
+ }
+ }
+ if (changed) {
updateConfig();
}
}
@@ -1256,9 +1321,15 @@
* Toggles whether an app can have a notification delegate on behalf of a user.
*/
public void toggleNotificationDelegate(String sourcePkg, int sourceUid, boolean userAllowed) {
- PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
- if (prefs != null && prefs.delegate != null) {
- prefs.delegate.mUserAllowed = userAllowed;
+ boolean changed = false;
+ synchronized (mPackagePreferences) {
+ PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
+ if (prefs != null && prefs.delegate != null) {
+ prefs.delegate.mUserAllowed = userAllowed;
+ changed = true;
+ }
+ }
+ if (changed) {
updateConfig();
}
}
@@ -1269,13 +1340,16 @@
*/
public boolean isDelegateAllowed(String sourcePkg, int sourceUid,
String potentialDelegatePkg, int potentialDelegateUid) {
- PackagePreferences prefs = getPackagePreferences(sourcePkg, sourceUid);
+ synchronized (mPackagePreferences) {
+ PackagePreferences prefs = getPackagePreferencesLocked(sourcePkg, sourceUid);
- return prefs != null && prefs.isValidDelegate(potentialDelegatePkg, potentialDelegateUid);
+ return prefs != null && prefs.isValidDelegate(potentialDelegatePkg,
+ potentialDelegateUid);
+ }
}
@VisibleForTesting
- void lockFieldsForUpdate(NotificationChannel original, NotificationChannel update) {
+ void lockFieldsForUpdateLocked(NotificationChannel original, NotificationChannel update) {
if (original.canBypassDnd() != update.canBypassDnd()) {
update.lockFields(NotificationChannel.USER_LOCKED_PRIORITY);
}
@@ -1309,30 +1383,30 @@
pw.print(prefix);
pw.println("per-package config:");
- pw.println("PackagePreferencess:");
+ pw.println("PackagePreferences:");
synchronized (mPackagePreferences) {
- dumpPackagePreferencess(pw, prefix, filter, mPackagePreferences);
+ dumpPackagePreferencesLocked(pw, prefix, filter, mPackagePreferences);
}
pw.println("Restored without uid:");
- dumpPackagePreferencess(pw, prefix, filter, mRestoredWithoutUids);
+ dumpPackagePreferencesLocked(pw, prefix, filter, mRestoredWithoutUids);
}
public void dump(ProtoOutputStream proto,
@NonNull NotificationManagerService.DumpFilter filter) {
synchronized (mPackagePreferences) {
- dumpPackagePreferencess(proto, RankingHelperProto.RECORDS, filter,
+ dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS, filter,
mPackagePreferences);
}
- dumpPackagePreferencess(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
+ dumpPackagePreferencesLocked(proto, RankingHelperProto.RECORDS_RESTORED_WITHOUT_UID, filter,
mRestoredWithoutUids);
}
- private static void dumpPackagePreferencess(PrintWriter pw, String prefix,
+ private static void dumpPackagePreferencesLocked(PrintWriter pw, String prefix,
@NonNull NotificationManagerService.DumpFilter filter,
- ArrayMap<String, PackagePreferences> PackagePreferencess) {
- final int N = PackagePreferencess.size();
+ ArrayMap<String, PackagePreferences> packagePreferences) {
+ final int N = packagePreferences.size();
for (int i = 0; i < N; i++) {
- final PackagePreferences r = PackagePreferencess.valueAt(i);
+ final PackagePreferences r = packagePreferences.valueAt(i);
if (filter.matches(r.pkg)) {
pw.print(prefix);
pw.print(" AppSettings: ");
@@ -1369,13 +1443,13 @@
}
}
- private static void dumpPackagePreferencess(ProtoOutputStream proto, long fieldId,
+ private static void dumpPackagePreferencesLocked(ProtoOutputStream proto, long fieldId,
@NonNull NotificationManagerService.DumpFilter filter,
- ArrayMap<String, PackagePreferences> PackagePreferencess) {
- final int N = PackagePreferencess.size();
+ ArrayMap<String, PackagePreferences> packagePreferences) {
+ final int N = packagePreferences.size();
long fToken;
for (int i = 0; i < N; i++) {
- final PackagePreferences r = PackagePreferencess.valueAt(i);
+ final PackagePreferences r = packagePreferences.valueAt(i);
if (filter.matches(r.pkg)) {
fToken = proto.start(fieldId);
@@ -1626,11 +1700,11 @@
// Package upgrade
try {
synchronized (mPackagePreferences) {
- PackagePreferences fullPackagePreferences = getPackagePreferences(pkg,
+ PackagePreferences fullPackagePreferences = getPackagePreferencesLocked(pkg,
mPm.getPackageUidAsUser(pkg, changeUserId));
if (fullPackagePreferences != null) {
- createDefaultChannelIfNeeded(fullPackagePreferences);
- deleteDefaultChannelIfNeeded(fullPackagePreferences);
+ createDefaultChannelIfNeededLocked(fullPackagePreferences);
+ deleteDefaultChannelIfNeededLocked(fullPackagePreferences);
}
}
} catch (PackageManager.NameNotFoundException e) {
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 51d5acc..ee07c7d 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -166,6 +166,13 @@
* . . . . . . . . . . . . . . . . . . . . . .
* </pre>
*
+ * <p>To test the OMS, execute:
+ * <code>
+ * atest FrameworksServicesTests:com.android.server.om # internal tests
+ * atest OverlayDeviceTests OverlayHostTests # public API tests
+ * </code>
+ * </p>
+ *
* <p>Finally, here is a list of keywords used in the OMS context.</p>
*
* <ul>
diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java
index 36b5beb..f35c707 100644
--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java
+++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java
@@ -26,6 +26,7 @@
import android.util.Slog;
import android.util.Xml;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
@@ -327,7 +328,8 @@
Serializer.persist(mItems, os);
}
- private static final class Serializer {
+ @VisibleForTesting
+ static final class Serializer {
private static final String TAG_OVERLAYS = "overlays";
private static final String TAG_ITEM = "item";
@@ -343,7 +345,8 @@
private static final String ATTR_USER_ID = "userId";
private static final String ATTR_VERSION = "version";
- private static final int CURRENT_VERSION = 3;
+ @VisibleForTesting
+ static final int CURRENT_VERSION = 3;
public static void restore(@NonNull final ArrayList<SettingsItem> table,
@NonNull final InputStream is) throws IOException, XmlPullParserException {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index d52ba16..9908b36 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2629,7 +2629,8 @@
0, null, new UserHandle(serviceUserId)));
if (!mContext.bindServiceAsUser(intent, newConn,
Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI
- | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+ | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
+ | Context.BIND_INCLUDE_CAPABILITIES,
new UserHandle(serviceUserId))) {
String msg = "Unable to bind service: "
+ componentName;
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index f9980be..c1d872f 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -73,6 +73,13 @@
/** Schedule a PiP mode changed callback when this animation ends. */
public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2;
+ public static final int BOUNDS = 0;
+ public static final int FADE_IN = 1;
+
+ @IntDef({BOUNDS, FADE_IN}) public @interface AnimationType {}
+
+ private static final int FADE_IN_DURATION = 500;
+
// Only accessed on UI thread.
private ArrayMap<BoundsAnimationTarget, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
@@ -115,6 +122,7 @@
private boolean mFinishAnimationAfterTransition = false;
private final AnimationHandler mAnimationHandler;
private Choreographer mChoreographer;
+ private @AnimationType int mAnimationType;
private static final int WAIT_FOR_DRAW_TIMEOUT_MS = 3000;
@@ -140,6 +148,7 @@
implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
private final BoundsAnimationTarget mTarget;
+ private final @AnimationType int mAnimationType;
private final Rect mFrom = new Rect();
private final Rect mTo = new Rect();
private final Rect mTmpRect = new Rect();
@@ -166,8 +175,8 @@
// Depending on whether we are animating from
// a smaller to a larger size
- private final int mFrozenTaskWidth;
- private final int mFrozenTaskHeight;
+ private int mFrozenTaskWidth;
+ private int mFrozenTaskHeight;
// Timeout callback to ensure we continue the animation if waiting for resuming or app
// windows drawn fails
@@ -176,12 +185,13 @@
resume();
};
- BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to,
- @SchedulePipModeChangedState int schedulePipModeChangedState,
+ BoundsAnimator(BoundsAnimationTarget target, @AnimationType int animationType, Rect from,
+ Rect to, @SchedulePipModeChangedState int schedulePipModeChangedState,
@SchedulePipModeChangedState int prevShedulePipModeChangedState,
boolean moveFromFullscreen, boolean moveToFullscreen, Rect frozenTask) {
super();
mTarget = target;
+ mAnimationType = animationType;
mFrom.set(from);
mTo.set(to);
mSchedulePipModeChangedState = schedulePipModeChangedState;
@@ -195,12 +205,14 @@
// to their final size immediately so we can use scaling to make the window
// larger. Likewise if we are going from bigger to smaller, we want to wait until
// the end so we don't have to upscale from the smaller finished size.
- if (animatingToLargerSize()) {
- mFrozenTaskWidth = mTo.width();
- mFrozenTaskHeight = mTo.height();
- } else {
- mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width();
- mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height();
+ if (mAnimationType == BOUNDS) {
+ if (animatingToLargerSize()) {
+ mFrozenTaskWidth = mTo.width();
+ mFrozenTaskHeight = mTo.height();
+ } else {
+ mFrozenTaskWidth = frozenTask.isEmpty() ? mFrom.width() : frozenTask.width();
+ mFrozenTaskHeight = frozenTask.isEmpty() ? mFrom.height() : frozenTask.height();
+ }
}
}
@@ -222,8 +234,9 @@
// otherwise.
boolean continueAnimation;
if (mPrevSchedulePipModeChangedState == NO_PIP_MODE_CHANGED_CALLBACKS) {
- continueAnimation = mTarget.onAnimationStart(mSchedulePipModeChangedState ==
- SCHEDULE_PIP_MODE_CHANGED_ON_START, false /* forceUpdate */);
+ continueAnimation = mTarget.onAnimationStart(
+ mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START,
+ false /* forceUpdate */, mAnimationType);
// When starting an animation from fullscreen, pause here and wait for the
// windows-drawn signal before we start the rest of the transition down into PiP.
@@ -238,7 +251,8 @@
// However, we still need to report to them that they are leaving PiP, so this will
// force an update via a mode changed callback.
continueAnimation = mTarget.onAnimationStart(
- true /* schedulePipModeChangedCallback */, true /* forceUpdate */);
+ true /* schedulePipModeChangedCallback */, true /* forceUpdate */,
+ mAnimationType);
} else {
// The animation is already running, but we should check that the TaskStack is still
// valid before continuing with the animation
@@ -285,6 +299,13 @@
@Override
public void onAnimationUpdate(ValueAnimator animation) {
final float value = (Float) animation.getAnimatedValue();
+ if (mAnimationType == FADE_IN) {
+ if (!mTarget.setPinnedStackAlpha(value)) {
+ cancelAndCallAnimationEnd();
+ }
+ return;
+ }
+
final float remains = 1 - value;
mTmpRect.left = (int) (mFrom.left * remains + mTo.left * value + 0.5f);
mTmpRect.top = (int) (mFrom.top * remains + mTo.top * value + 0.5f);
@@ -408,16 +429,29 @@
public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to,
int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
- boolean moveFromFullscreen, boolean moveToFullscreen) {
+ boolean moveFromFullscreen, boolean moveToFullscreen,
+ @AnimationType int animationType) {
animateBoundsImpl(target, from, to, animationDuration, schedulePipModeChangedState,
- moveFromFullscreen, moveToFullscreen);
+ moveFromFullscreen, moveToFullscreen, animationType);
}
@VisibleForTesting
BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to,
int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
- boolean moveFromFullscreen, boolean moveToFullscreen) {
+ boolean moveFromFullscreen, boolean moveToFullscreen,
+ @AnimationType int animationType) {
final BoundsAnimator existing = mRunningAnimations.get(target);
+ // animateBoundsImpl gets called twice for each animation. The second time we get the final
+ // to rect that respects the shelf, which is when we want to resize. Our signal for fade in
+ // comes in from how to enter into pip, but we also need to use the to and from rect to
+ // decide which animation we want to run finally.
+ boolean shouldResize = false;
+ if (isRunningFadeInAnimation(target)) {
+ shouldResize = true;
+ if (from.contains(to)) {
+ animationType = FADE_IN;
+ }
+ }
final boolean replacing = existing != null;
@SchedulePipModeChangedState int prevSchedulePipModeChangedState =
NO_PIP_MODE_CHANGED_CALLBACKS;
@@ -477,18 +511,34 @@
// Since we are replacing, we skip both animation start and end callbacks
existing.cancel();
}
- final BoundsAnimator animator = new BoundsAnimator(target, from, to,
+ if (shouldResize) {
+ target.setPinnedStackSize(to, null);
+ }
+ final BoundsAnimator animator = new BoundsAnimator(target, animationType, from, to,
schedulePipModeChangedState, prevSchedulePipModeChangedState,
moveFromFullscreen, moveToFullscreen, frozenTask);
mRunningAnimations.put(target, animator);
animator.setFloatValues(0f, 1f);
- animator.setDuration((animationDuration != -1 ? animationDuration
- : DEFAULT_TRANSITION_DURATION) * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
+ animator.setDuration(animationType == FADE_IN ? FADE_IN_DURATION
+ : (animationDuration != -1 ? animationDuration : DEFAULT_TRANSITION_DURATION)
+ * DEBUG_ANIMATION_SLOW_DOWN_FACTOR);
animator.setInterpolator(mFastOutSlowInInterpolator);
animator.start();
return animator;
}
+ public void setAnimationType(@AnimationType int animationType) {
+ mAnimationType = animationType;
+ }
+
+ /** return the current animation type. */
+ public @AnimationType int getAnimationType() {
+ @AnimationType int animationType = mAnimationType;
+ // Default to BOUNDS.
+ mAnimationType = BOUNDS;
+ return animationType;
+ }
+
public Handler getHandler() {
return mHandler;
}
@@ -498,6 +548,11 @@
mHandler.post(this::resume);
}
+ private boolean isRunningFadeInAnimation(final BoundsAnimationTarget target) {
+ final BoundsAnimator existing = mRunningAnimations.get(target);
+ return existing != null && existing.mAnimationType == FADE_IN && existing.isStarted();
+ }
+
private void resume() {
for (int i = 0; i < mRunningAnimations.size(); i++) {
final BoundsAnimator b = mRunningAnimations.valueAt(i);
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
index 5cb80de..9f54e49e0 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
@@ -32,7 +32,8 @@
* callbacks
* @return whether to continue the animation
*/
- boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate);
+ boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate,
+ @BoundsAnimationController.AnimationType int animationType);
/**
* @return Whether the animation should be paused waiting for the windows to draw before
@@ -53,6 +54,9 @@
*/
boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds);
+ /** Sets the alpha of the animation target */
+ boolean setPinnedStackAlpha(float alpha);
+
/**
* Callback for the target to inform it that the animation has ended, so it can do some
* necessary cleanup.
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 144efb4..07d3fb9 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -26,6 +26,8 @@
import static android.view.WindowManager.TRANSIT_NONE;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.BoundsAnimationController.BOUNDS;
+import static com.android.server.wm.BoundsAnimationController.FADE_IN;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_TOP;
@@ -201,7 +203,8 @@
}
}
- private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
+ private void finishAnimation(@RecentsAnimationController.ReorderMode int reorderMode,
+ boolean sendUserLeaveHint) {
synchronized (mService.mGlobalLock) {
if (DEBUG) Slog.d(TAG, "onAnimationFinished(): controller="
+ mWindowManager.getRecentsAnimationController()
@@ -246,7 +249,18 @@
if (reorderMode == REORDER_MOVE_TO_TOP) {
// Bring the target stack to the front
mStackSupervisor.mNoAnimActivities.add(targetActivity);
- targetStack.moveToFront("RecentsAnimation.onAnimationFinished()");
+
+ if (sendUserLeaveHint) {
+ // Setting this allows the previous app to PiP.
+ mStackSupervisor.mUserLeaving = true;
+ targetStack.moveTaskToFrontLocked(targetActivity.getTaskRecord(),
+ true /* noAnimation */, null /* activityOptions */,
+ targetActivity.appTimeTracker,
+ "RecentsAnimation.onAnimationFinished()");
+ } else {
+ targetStack.moveToFront("RecentsAnimation.onAnimationFinished()");
+ }
+
if (DEBUG) {
final ActivityStack topStack = getTopNonAlwaysOnTopStack();
if (topStack != targetStack) {
@@ -300,11 +314,11 @@
@Override
public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode,
- boolean runSychronously) {
+ boolean runSychronously, boolean sendUserLeaveHint) {
if (runSychronously) {
- finishAnimation(reorderMode);
+ finishAnimation(reorderMode, sendUserLeaveHint);
} else {
- mService.mH.post(() -> finishAnimation(reorderMode));
+ mService.mH.post(() -> finishAnimation(reorderMode, sendUserLeaveHint));
}
}
@@ -317,6 +331,10 @@
}
final RecentsAnimationController controller =
mWindowManager.getRecentsAnimationController();
+ final DisplayContent dc =
+ mService.mRootActivityContainer.getDefaultDisplay().mDisplayContent;
+ dc.mBoundsAnimationController.setAnimationType(
+ controller.shouldCancelWithDeferredScreenshot() ? FADE_IN : BOUNDS);
// Cancel running recents animation and screenshot previous task when the next
// transition starts in below cases:
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 3813669..d2c510f 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -27,6 +27,7 @@
import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
import static com.android.server.wm.AnimationAdapterProto.REMOTE;
+import static com.android.server.wm.BoundsAnimationController.FADE_IN;
import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
@@ -142,7 +143,9 @@
};
public interface RecentsAnimationCallbacks {
- void onAnimationFinished(@ReorderMode int reorderMode, boolean runSychronously);
+ /** Callback when recents animation is finished. */
+ void onAnimationFinished(@ReorderMode int reorderMode, boolean runSychronously,
+ boolean sendUserLeaveHint);
}
private final IRecentsAnimationController mController =
@@ -179,7 +182,7 @@
}
@Override
- public void finish(boolean moveHomeToTop) {
+ public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint) {
if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "finish(" + moveHomeToTop + "):"
+ " mCanceled=" + mCanceled);
final long token = Binder.clearCallingIdentity();
@@ -195,7 +198,9 @@
mCallbacks.onAnimationFinished(moveHomeToTop
? REORDER_MOVE_TO_TOP
: REORDER_MOVE_TO_ORIGINAL_POSITION,
- true /* runSynchronously */);
+ true /* runSynchronously */, sendUserLeaveHint);
+ final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
+ dc.mBoundsAnimationController.setAnimationType(FADE_IN);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -497,7 +502,8 @@
Slog.e(TAG, "Failed to cancel recents animation", e);
}
// Clean up and return to the previous app
- mCallbacks.onAnimationFinished(reorderMode, runSynchronously);
+ mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
+ false /* sendUserLeaveHint */);
}
}
@@ -542,7 +548,8 @@
if (DEBUG_RECENTS_ANIMATIONS) {
Slog.d(TAG, "mRecentScreenshotAnimator finish");
}
- mCallbacks.onAnimationFinished(reorderMode, runSynchronously);
+ mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
+ false /* sendUserLeaveHint */);
}, mService);
mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator);
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 09baf8c..bdb4d04 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -34,6 +34,7 @@
import static android.view.WindowManager.DOCKED_RIGHT;
import static android.view.WindowManager.DOCKED_TOP;
+import static com.android.server.wm.BoundsAnimationController.FADE_IN;
import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
@@ -148,6 +149,7 @@
private boolean mCancelCurrentBoundsAnimation = false;
private Rect mBoundsAnimationTarget = new Rect();
private Rect mBoundsAnimationSourceHintBounds = new Rect();
+ private @BoundsAnimationController.AnimationType int mAnimationType;
Rect mPreAnimationBounds = new Rect();
@@ -1572,7 +1574,8 @@
}
@Override // AnimatesBounds
- public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate) {
+ public boolean onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate,
+ @BoundsAnimationController.AnimationType int animationType) {
// Hold the lock since this is called from the BoundsAnimator running on the UiThread
synchronized (mWmService.mGlobalLock) {
if (!isAttached()) {
@@ -1583,6 +1586,7 @@
mBoundsAnimatingRequested = false;
mBoundsAnimating = true;
mCancelCurrentBoundsAnimation = false;
+ mAnimationType = animationType;
// If we are changing UI mode, as in the PiP to fullscreen
// transition, then we need to wait for the window to draw.
@@ -1599,7 +1603,8 @@
// I don't believe you...
}
- if (schedulePipModeChangedCallback && mActivityStack != null) {
+ if ((schedulePipModeChangedCallback || animationType == FADE_IN)
+ && mActivityStack != null) {
// We need to schedule the PiP mode change before the animation up. It is possible
// in this case for the animation down to not have been completed, so always
// force-schedule and update to the client to ensure that it is notified that it
@@ -1614,6 +1619,21 @@
@Override // AnimatesBounds
public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
boolean moveToFullscreen) {
+ if (mAnimationType == BoundsAnimationController.FADE_IN) {
+ setPinnedStackAlpha(1f);
+ try {
+ mWmService.mActivityTaskManager.notifyPinnedStackAnimationEnded();
+ } catch (RemoteException e) {
+ // I don't believe you...
+ }
+ return;
+ }
+
+ onBoundAnimationEnd(schedulePipModeChangedCallback, finalStackSize, moveToFullscreen);
+ }
+
+ private void onBoundAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
+ boolean moveToFullscreen) {
if (inPinnedWindowingMode()) {
// Update to the final bounds if requested. This is done here instead of in the bounds
// animator to allow us to coordinate this after we notify the PiP mode changed
@@ -1725,10 +1745,23 @@
final @SchedulePipModeChangedState int finalSchedulePipModeChangedState =
schedulePipModeChangedState;
final DisplayContent displayContent = getDisplayContent();
+ @BoundsAnimationController.AnimationType int intendedAnimationType =
+ displayContent.mBoundsAnimationController.getAnimationType();
+ if (intendedAnimationType == FADE_IN) {
+ if (fromFullscreen) {
+ setPinnedStackAlpha(0f);
+ }
+ if (toBounds.width() == fromBounds.width()
+ && toBounds.height() == fromBounds.height()) {
+ intendedAnimationType = BoundsAnimationController.BOUNDS;
+ }
+ }
+
+ final @BoundsAnimationController.AnimationType int animationType = intendedAnimationType;
displayContent.mBoundsAnimationController.getHandler().post(() -> {
displayContent.mBoundsAnimationController.animateBounds(this, fromBounds,
finalToBounds, animationDuration, finalSchedulePipModeChangedState,
- fromFullscreen, toFullscreen);
+ fromFullscreen, toFullscreen, animationType);
});
}
@@ -1905,6 +1938,20 @@
}
}
+ @Override
+ public boolean setPinnedStackAlpha(float alpha) {
+ // Hold the lock since this is called from the BoundsAnimator running on the UiThread
+ synchronized (mWmService.mGlobalLock) {
+ if (mCancelCurrentBoundsAnimation) {
+ return false;
+ }
+ getPendingTransaction().setAlpha(getSurfaceControl(), alpha);
+ scheduleAnimation();
+ }
+
+ return true;
+ }
+
public DisplayInfo getDisplayInfo() {
return mDisplayContent.getDisplayInfo();
}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
new file mode 100644
index 0000000..3f9a57e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -0,0 +1,504 @@
+/*
+ * Copyright (C) 2019 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.server.om;
+
+import static android.content.om.OverlayInfo.STATE_DISABLED;
+import static android.content.om.OverlayInfo.STATE_ENABLED;
+import static android.content.om.OverlayInfo.STATE_MISSING_TARGET;
+import static android.content.om.OverlayInfo.STATE_TARGET_IS_BEING_REPLACED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.NonNull;
+import android.content.om.OverlayInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.util.ArraySet;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class OverlayManagerServiceImplTests {
+ private OverlayManagerServiceImpl mImpl;
+ private DummyDeviceState mState;
+ private DummyListener mListener;
+
+ private static final String OVERLAY = "com.dummy.overlay";
+ private static final String TARGET = "com.dummy.target";
+ private static final int USER = 0;
+
+ private static final String OVERLAY2 = OVERLAY + "2";
+ private static final String TARGET2 = TARGET + "2";
+ private static final int USER2 = USER + 1;
+
+ private static final String OVERLAY3 = OVERLAY + "3";
+ private static final int USER3 = USER2 + 1;
+
+
+ @Before
+ public void setUp() throws Exception {
+ mState = new DummyDeviceState();
+ mListener = new DummyListener();
+ DummyPackageManagerHelper pmh = new DummyPackageManagerHelper(mState);
+ mImpl = new OverlayManagerServiceImpl(pmh,
+ new DummyIdmapManager(mState, pmh),
+ new OverlayManagerSettings(),
+ new String[0],
+ mListener);
+ }
+
+ // tests: basics
+
+ @Test
+ public void testGetOverlayInfo() throws Exception {
+ installOverlayPackage(OVERLAY, TARGET, USER, false);
+ final OverlayInfo oi = mImpl.getOverlayInfo(OVERLAY, USER);
+ assertNotNull(oi);
+ assertEquals(oi.packageName, OVERLAY);
+ assertEquals(oi.targetPackageName, TARGET);
+ assertEquals(oi.userId, USER);
+ }
+
+ @Test
+ public void testGetOverlayInfosForTarget() throws Exception {
+ installOverlayPackage(OVERLAY, TARGET, USER, false);
+ installOverlayPackage(OVERLAY2, TARGET, USER, false);
+
+ installOverlayPackage(OVERLAY3, TARGET, USER2, false);
+
+ final List<OverlayInfo> ois = mImpl.getOverlayInfosForTarget(TARGET, USER);
+ assertEquals(ois.size(), 2);
+ assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY, USER)));
+ assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY2, USER)));
+
+ final List<OverlayInfo> ois2 = mImpl.getOverlayInfosForTarget(TARGET, USER2);
+ assertEquals(ois2.size(), 1);
+ assertTrue(ois2.contains(mImpl.getOverlayInfo(OVERLAY3, USER2)));
+
+ final List<OverlayInfo> ois3 = mImpl.getOverlayInfosForTarget(TARGET, USER3);
+ assertNotNull(ois3);
+ assertEquals(ois3.size(), 0);
+
+ final List<OverlayInfo> ois4 = mImpl.getOverlayInfosForTarget("no.such.overlay", USER);
+ assertNotNull(ois4);
+ assertEquals(ois4.size(), 0);
+ }
+
+ @Test
+ public void testGetOverlayInfosForUser() throws Exception {
+ installOverlayPackage(OVERLAY, TARGET, USER, false);
+ installOverlayPackage(OVERLAY2, TARGET, USER, false);
+ installOverlayPackage(OVERLAY3, TARGET2, USER, false);
+
+ final Map<String, List<OverlayInfo>> everything = mImpl.getOverlaysForUser(USER);
+ assertEquals(everything.size(), 2);
+
+ final List<OverlayInfo> ois = everything.get(TARGET);
+ assertNotNull(ois);
+ assertEquals(ois.size(), 2);
+ assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY, USER)));
+ assertTrue(ois.contains(mImpl.getOverlayInfo(OVERLAY2, USER)));
+
+ final List<OverlayInfo> ois2 = everything.get(TARGET2);
+ assertNotNull(ois2);
+ assertEquals(ois2.size(), 1);
+ assertTrue(ois2.contains(mImpl.getOverlayInfo(OVERLAY3, USER)));
+
+ final Map<String, List<OverlayInfo>> everything2 = mImpl.getOverlaysForUser(USER2);
+ assertNotNull(everything2);
+ assertEquals(everything2.size(), 0);
+ }
+
+ @Test
+ public void testPriority() throws Exception {
+ installOverlayPackage(OVERLAY, TARGET, USER, false);
+ installOverlayPackage(OVERLAY2, TARGET, USER, false);
+ installOverlayPackage(OVERLAY3, TARGET, USER, false);
+
+ final OverlayInfo o1 = mImpl.getOverlayInfo(OVERLAY, USER);
+ final OverlayInfo o2 = mImpl.getOverlayInfo(OVERLAY2, USER);
+ final OverlayInfo o3 = mImpl.getOverlayInfo(OVERLAY3, USER);
+
+ assertOverlayInfoList(TARGET, USER, o1, o2, o3);
+
+ assertTrue(mImpl.setLowestPriority(OVERLAY3, USER));
+ assertOverlayInfoList(TARGET, USER, o3, o1, o2);
+
+ assertTrue(mImpl.setHighestPriority(OVERLAY3, USER));
+ assertOverlayInfoList(TARGET, USER, o1, o2, o3);
+
+ assertTrue(mImpl.setPriority(OVERLAY, OVERLAY2, USER));
+ assertOverlayInfoList(TARGET, USER, o2, o1, o3);
+ }
+
+ @Test
+ public void testOverlayInfoStateTransitions() throws Exception {
+ assertNull(mImpl.getOverlayInfo(OVERLAY, USER));
+
+ installOverlayPackage(OVERLAY, TARGET, USER, true);
+ assertState(STATE_MISSING_TARGET, OVERLAY, USER);
+
+ installTargetPackage(TARGET, USER);
+ assertState(STATE_DISABLED, OVERLAY, USER);
+
+ mImpl.setEnabled(OVERLAY, true, USER);
+ assertState(STATE_ENABLED, OVERLAY, USER);
+
+ beginUpgradeTargetPackage(TARGET, USER);
+ assertState(STATE_TARGET_IS_BEING_REPLACED, OVERLAY, USER);
+
+ endUpgradeTargetPackage(TARGET, USER);
+ assertState(STATE_ENABLED, OVERLAY, USER);
+
+ uninstallTargetPackage(TARGET, USER);
+ assertState(STATE_MISSING_TARGET, OVERLAY, USER);
+
+ installTargetPackage(TARGET, USER);
+ assertState(STATE_ENABLED, OVERLAY, USER);
+ }
+
+ @Test
+ public void testUpdateOverlaysForUser() throws Exception {
+ installTargetPackage(TARGET, USER);
+ installTargetPackage("some.other.target", USER);
+ installOverlayPackage(OVERLAY, TARGET, USER, true);
+
+ // do nothing, expect no change
+ List<String> a = mImpl.updateOverlaysForUser(USER);
+ assertEquals(1, a.size());
+ assertTrue(a.contains(TARGET));
+
+ // upgrade overlay, keep target
+ upgradeOverlayPackage(OVERLAY, TARGET, USER, true);
+ List<String> b = mImpl.updateOverlaysForUser(USER);
+ assertEquals(1, b.size());
+ assertTrue(b.contains(TARGET));
+
+ // do nothing, expect no change
+ List<String> c = mImpl.updateOverlaysForUser(USER);
+ assertEquals(1, c.size());
+ assertTrue(c.contains(TARGET));
+
+ // upgrade overlay, switch to new target
+ upgradeOverlayPackage(OVERLAY, "some.other.target", USER, true);
+ List<String> d = mImpl.updateOverlaysForUser(USER);
+ assertEquals(2, d.size());
+ assertTrue(d.containsAll(Arrays.asList(TARGET, "some.other.target")));
+
+ // do nothing, expect no change
+ List<String> e = mImpl.updateOverlaysForUser(USER);
+ assertEquals(1, e.size());
+ assertTrue(e.contains("some.other.target"));
+ }
+
+ @Test
+ public void testOnOverlayPackageUpgraded() throws Exception {
+ installTargetPackage(TARGET, USER);
+ installOverlayPackage(OVERLAY, TARGET, USER, true);
+ mImpl.onOverlayPackageReplacing(OVERLAY, USER);
+ mListener.count = 0;
+ mImpl.onOverlayPackageReplaced(OVERLAY, USER);
+ assertEquals(1, mListener.count);
+
+ // upgrade to a version where the overlay has changed its target
+ upgradeOverlayPackage(OVERLAY, "some.other.target", USER, true);
+ mImpl.onOverlayPackageReplacing(OVERLAY, USER);
+ mListener.count = 0;
+ mImpl.onOverlayPackageReplaced(OVERLAY, USER);
+ // expect once for the old target package, once for the new target package
+ assertEquals(2, mListener.count);
+
+ upgradeOverlayPackage(OVERLAY, "some.other.target", USER, true);
+ mImpl.onOverlayPackageReplacing(OVERLAY, USER);
+ mListener.count = 0;
+ mImpl.onOverlayPackageReplaced(OVERLAY, USER);
+ assertEquals(1, mListener.count);
+ }
+
+ // tests: listener interface
+
+ @Test
+ public void testListener() throws Exception {
+ installOverlayPackage(OVERLAY, TARGET, USER, true);
+ assertEquals(1, mListener.count);
+ mListener.count = 0;
+
+ installTargetPackage(TARGET, USER);
+ assertEquals(1, mListener.count);
+ mListener.count = 0;
+
+ mImpl.setEnabled(OVERLAY, true, USER);
+ assertEquals(1, mListener.count);
+ mListener.count = 0;
+
+ mImpl.setEnabled(OVERLAY, true, USER);
+ assertEquals(0, mListener.count);
+ }
+
+ // helper methods
+
+ private void assertState(int expected, final String overlayPackageName, int userId) {
+ int actual = mImpl.getOverlayInfo(OVERLAY, USER).state;
+ String msg = String.format("expected %s but was %s:",
+ OverlayInfo.stateToString(expected), OverlayInfo.stateToString(actual));
+ assertEquals(msg, expected, actual);
+ }
+
+ private void assertOverlayInfoList(final String targetPackageName, int userId,
+ OverlayInfo... overlayInfos) {
+ final List<OverlayInfo> expected =
+ mImpl.getOverlayInfosForTarget(targetPackageName, userId);
+ final List<OverlayInfo> actual = Arrays.asList(overlayInfos);
+ assertEquals(expected, actual);
+ }
+
+ private void installTargetPackage(String packageName, int userId) {
+ if (mState.select(packageName, userId) != null) {
+ throw new IllegalStateException("package already installed");
+ }
+ mState.add(packageName, null, userId, false);
+ mImpl.onTargetPackageAdded(packageName, userId);
+ }
+
+ private void beginUpgradeTargetPackage(String packageName, int userId) {
+ if (mState.select(packageName, userId) == null) {
+ throw new IllegalStateException("package not installed");
+ }
+ mState.add(packageName, null, userId, false);
+ mImpl.onTargetPackageReplacing(packageName, userId);
+ }
+
+ private void endUpgradeTargetPackage(String packageName, int userId) {
+ if (mState.select(packageName, userId) == null) {
+ throw new IllegalStateException("package not installed");
+ }
+ mState.add(packageName, null, userId, false);
+ mImpl.onTargetPackageReplaced(packageName, userId);
+ }
+
+ private void uninstallTargetPackage(String packageName, int userId) {
+ if (mState.select(packageName, userId) == null) {
+ throw new IllegalStateException("package not installed");
+ }
+ mState.remove(packageName, userId);
+ mImpl.onTargetPackageRemoved(packageName, userId);
+ }
+
+ private void installOverlayPackage(String packageName, String targetPackageName, int userId,
+ boolean canCreateIdmap) {
+ if (mState.select(packageName, userId) != null) {
+ throw new IllegalStateException("package already installed");
+ }
+ mState.add(packageName, targetPackageName, userId, canCreateIdmap);
+ mImpl.onOverlayPackageAdded(packageName, userId);
+ }
+
+ private void upgradeOverlayPackage(String packageName, String targetPackageName, int userId,
+ boolean canCreateIdmap) {
+ DummyDeviceState.Package pkg = mState.select(packageName, userId);
+ if (pkg == null) {
+ throw new IllegalStateException("package not installed, cannot upgrade");
+ }
+ pkg.targetPackageName = targetPackageName;
+ pkg.canCreateIdmap = canCreateIdmap;
+ }
+
+ private void uninstallOverlayPackage(String packageName, int userId) {
+ // implement this when adding support for downloadable overlays
+ throw new IllegalArgumentException("not implemented");
+ }
+
+ private static final class DummyDeviceState {
+ private List<Package> mPackages = new ArrayList<>();
+
+ public void add(String packageName, String targetPackageName, int userId,
+ boolean canCreateIdmap) {
+ remove(packageName, userId);
+ Package pkg = new Package();
+ pkg.packageName = packageName;
+ pkg.targetPackageName = targetPackageName;
+ pkg.userId = userId;
+ pkg.canCreateIdmap = canCreateIdmap;
+ mPackages.add(pkg);
+ }
+
+ public void remove(String packageName, int userId) {
+ final Iterator<Package> iter = mPackages.iterator();
+ while (iter.hasNext()) {
+ final Package pkg = iter.next();
+ if (pkg.packageName.equals(packageName) && pkg.userId == userId) {
+ iter.remove();
+ return;
+ }
+ }
+ }
+
+ public List<Package> select(int userId) {
+ List<Package> out = new ArrayList<>();
+ final int packageCount = mPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ final Package pkg = mPackages.get(i);
+ if (pkg.userId == userId) {
+ out.add(pkg);
+ }
+ }
+ return out;
+ }
+
+ public Package select(String packageName, int userId) {
+ final int packageCount = mPackages.size();
+ for (int i = 0; i < packageCount; i++) {
+ final Package pkg = mPackages.get(i);
+ if (pkg.packageName.equals(packageName) && pkg.userId == userId) {
+ return pkg;
+ }
+ }
+ return null;
+ }
+
+ private static final class Package {
+ public String packageName;
+ public int userId;
+ public String targetPackageName;
+ public boolean canCreateIdmap;
+ }
+ }
+
+ private static final class DummyPackageManagerHelper implements
+ OverlayManagerServiceImpl.PackageManagerHelper {
+ private final DummyDeviceState mState;
+
+ DummyPackageManagerHelper(DummyDeviceState state) {
+ mState = state;
+ }
+
+ @Override
+ public PackageInfo getPackageInfo(@NonNull String packageName, int userId) {
+ final DummyDeviceState.Package pkg = mState.select(packageName, userId);
+ if (pkg == null) {
+ return null;
+ }
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.sourceDir = String.format("%s/%s/base.apk",
+ pkg.targetPackageName == null ? "/system/app/" : "/vendor/overlay/",
+ pkg.packageName);
+ PackageInfo pi = new PackageInfo();
+ pi.applicationInfo = ai;
+ pi.packageName = pkg.packageName;
+ pi.overlayTarget = pkg.targetPackageName;
+ pi.overlayCategory = "dummy-category-" + pkg.targetPackageName;
+ return pi;
+ }
+
+ @Override
+ public boolean signaturesMatching(@NonNull String packageName1,
+ @NonNull String packageName2, int userId) {
+ return false;
+ }
+
+ @Override
+ public List<PackageInfo> getOverlayPackages(int userId) {
+ List<PackageInfo> out = new ArrayList<>();
+ final List<DummyDeviceState.Package> packages = mState.select(userId);
+ final int packageCount = packages.size();
+ for (int i = 0; i < packageCount; i++) {
+ final DummyDeviceState.Package pkg = packages.get(i);
+ if (pkg.targetPackageName != null) {
+ out.add(getPackageInfo(pkg.packageName, pkg.userId));
+ }
+ }
+ return out;
+ }
+ }
+
+ private static class DummyIdmapManager extends IdmapManager {
+ private final DummyDeviceState mState;
+ private Set<String> mIdmapFiles = new ArraySet<>();
+
+ DummyIdmapManager(DummyDeviceState state, DummyPackageManagerHelper packageManagerHelper) {
+ super(null, packageManagerHelper);
+ mState = state;
+ }
+
+ @Override
+ boolean createIdmap(@NonNull final PackageInfo targetPackage,
+ @NonNull final PackageInfo overlayPackage, int userId) {
+ final DummyDeviceState.Package t = mState.select(targetPackage.packageName, userId);
+ if (t == null) {
+ return false;
+ }
+ final DummyDeviceState.Package o = mState.select(overlayPackage.packageName, userId);
+ if (o == null) {
+ return false;
+ }
+ if (!o.canCreateIdmap) {
+ return false;
+ }
+ final String key = createKey(overlayPackage.packageName, userId);
+ mIdmapFiles.add(key);
+ return true;
+ }
+
+ @Override
+ boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) {
+ final String key = createKey(oi.packageName, oi.userId);
+ if (!mIdmapFiles.contains(key)) {
+ return false;
+ }
+ mIdmapFiles.remove(key);
+ return true;
+ }
+
+ @Override
+ boolean idmapExists(@NonNull final OverlayInfo oi) {
+ final String key = createKey(oi.packageName, oi.userId);
+ return mIdmapFiles.contains(key);
+ }
+
+ @Override
+ boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) {
+ final String key = createKey(overlayPackage.packageName, userId);
+ return mIdmapFiles.contains(key);
+ }
+
+ private String createKey(@NonNull final String packageName, final int userId) {
+ return String.format("%s:%d", packageName, userId);
+ }
+ }
+
+ private static class DummyListener implements OverlayManagerServiceImpl.OverlayChangeListener {
+ public int count;
+
+ public void onOverlaysChanged(@NonNull String targetPackage, int userId) {
+ count++;
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
new file mode 100644
index 0000000..8ff8b6e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2019 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.server.om;
+
+import static android.content.om.OverlayInfo.STATE_DISABLED;
+import static android.content.om.OverlayInfo.STATE_ENABLED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.om.OverlayInfo;
+import android.text.TextUtils;
+import android.util.Xml;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.IntStream;
+import java.util.stream.Stream;
+
+@RunWith(AndroidJUnit4.class)
+public class OverlayManagerSettingsTests {
+ private OverlayManagerSettings mSettings;
+
+ private static final OverlayInfo OVERLAY_A0 = new OverlayInfo(
+ "com.dummy.overlay_a",
+ "com.dummy.target",
+ null,
+ "some-category",
+ "/data/app/com.dummy.overlay_a-1/base.apk",
+ STATE_DISABLED,
+ 0,
+ 0,
+ false);
+
+ private static final OverlayInfo OVERLAY_B0 = new OverlayInfo(
+ "com.dummy.overlay_b",
+ "com.dummy.target",
+ null,
+ "some-category",
+ "/data/app/com.dummy.overlay_b-1/base.apk",
+ STATE_DISABLED,
+ 0,
+ 0,
+ false);
+
+ private static final OverlayInfo OVERLAY_C0 = new OverlayInfo(
+ "com.dummy.overlay_c",
+ "com.dummy.target",
+ null,
+ "some-category",
+ "/data/app/com.dummy.overlay_c-1/base.apk",
+ STATE_DISABLED,
+ 0,
+ 0,
+ false);
+
+ private static final OverlayInfo OVERLAY_A1 = new OverlayInfo(
+ "com.dummy.overlay_a",
+ "com.dummy.target",
+ null,
+ "some-category",
+ "/data/app/com.dummy.overlay_a-1/base.apk",
+ STATE_DISABLED,
+ 1,
+ 0,
+ false);
+
+ private static final OverlayInfo OVERLAY_B1 = new OverlayInfo(
+ "com.dummy.overlay_b",
+ "com.dummy.target",
+ null,
+ "some-category",
+ "/data/app/com.dummy.overlay_b-1/base.apk",
+ STATE_DISABLED,
+ 1,
+ 0,
+ false);
+
+ @Before
+ public void setUp() throws Exception {
+ mSettings = new OverlayManagerSettings();
+ }
+
+ // tests: generic functionality
+
+ @Test
+ public void testSettingsInitiallyEmpty() throws Exception {
+ final int userId = 0;
+ Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(userId);
+ assertEquals(0, map.size());
+ }
+
+ @Test
+ public void testBasicSetAndGet() throws Exception {
+ assertDoesNotContain(mSettings, OVERLAY_A0.packageName, OVERLAY_A0.userId);
+
+ insert(OVERLAY_A0);
+ assertContains(mSettings, OVERLAY_A0);
+ OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_A0.packageName, OVERLAY_A0.userId);
+ assertEquals(OVERLAY_A0, oi);
+
+ assertTrue(mSettings.remove(OVERLAY_A0.packageName, OVERLAY_A0.userId));
+ assertDoesNotContain(mSettings, OVERLAY_A0.packageName, OVERLAY_A0.userId);
+ }
+
+ @Test
+ public void testGetUsers() throws Exception {
+ int[] users = mSettings.getUsers();
+ assertEquals(0, users.length);
+
+ insert(OVERLAY_A0);
+ users = mSettings.getUsers();
+ assertEquals(1, users.length);
+ assertContains(users, OVERLAY_A0.userId);
+
+ insert(OVERLAY_A1);
+ insert(OVERLAY_B1);
+ users = mSettings.getUsers();
+ assertEquals(2, users.length);
+ assertContains(users, OVERLAY_A0.userId);
+ assertContains(users, OVERLAY_A1.userId);
+ }
+
+ @Test
+ public void testGetOverlaysForUser() throws Exception {
+ insert(OVERLAY_A0);
+ insert(OVERLAY_B0);
+ insert(OVERLAY_A1);
+ insert(OVERLAY_B1);
+
+ Map<String, List<OverlayInfo>> map = mSettings.getOverlaysForUser(OVERLAY_A0.userId);
+ assertEquals(1, map.keySet().size());
+ assertTrue(map.keySet().contains(OVERLAY_A0.targetPackageName));
+
+ List<OverlayInfo> list = map.get(OVERLAY_A0.targetPackageName);
+ assertEquals(2, list.size());
+ assertTrue(list.contains(OVERLAY_A0));
+ assertTrue(list.contains(OVERLAY_B0));
+
+ // getOverlaysForUser should never return null
+ map = mSettings.getOverlaysForUser(-1);
+ assertNotNull(map);
+ assertEquals(0, map.size());
+ }
+
+ @Test
+ public void testRemoveUser() throws Exception {
+ insert(OVERLAY_A0);
+ insert(OVERLAY_B0);
+ insert(OVERLAY_A1);
+
+ assertContains(mSettings, OVERLAY_A0);
+ assertContains(mSettings, OVERLAY_B0);
+ assertContains(mSettings, OVERLAY_A1);
+
+ mSettings.removeUser(OVERLAY_A0.userId);
+
+ assertDoesNotContain(mSettings, OVERLAY_A0);
+ assertDoesNotContain(mSettings, OVERLAY_B0);
+ assertContains(mSettings, OVERLAY_A1);
+ }
+
+ @Test
+ public void testOrderOfNewlyAddedItems() throws Exception {
+ // new items are appended to the list
+ insert(OVERLAY_A0);
+ insert(OVERLAY_B0);
+ insert(OVERLAY_C0);
+
+ List<OverlayInfo> list =
+ mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
+ assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
+
+ // overlays keep their positions when updated
+ mSettings.setState(OVERLAY_B0.packageName, OVERLAY_B0.userId, STATE_ENABLED);
+ OverlayInfo oi = mSettings.getOverlayInfo(OVERLAY_B0.packageName, OVERLAY_B0.userId);
+
+ list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
+ assertListsAreEqual(list, OVERLAY_A0, oi, OVERLAY_C0);
+ }
+
+ @Test
+ public void testSetPriority() throws Exception {
+ insert(OVERLAY_A0);
+ insert(OVERLAY_B0);
+ insert(OVERLAY_C0);
+
+ List<OverlayInfo> list =
+ mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
+ assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
+
+ boolean changed = mSettings.setPriority(OVERLAY_B0.packageName, OVERLAY_C0.packageName,
+ OVERLAY_B0.userId);
+ assertTrue(changed);
+ list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
+ assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
+
+ changed =
+ mSettings.setPriority(OVERLAY_B0.packageName, "does.not.exist", OVERLAY_B0.userId);
+ assertFalse(changed);
+ list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
+ assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
+
+ OverlayInfo otherTarget = new OverlayInfo(
+ "com.dummy.overlay_other",
+ "com.dummy.some.other.target",
+ null,
+ "some-category",
+ "/data/app/com.dummy.overlay_other-1/base.apk",
+ STATE_DISABLED,
+ 0,
+ 0,
+ false);
+ insert(otherTarget);
+ changed = mSettings.setPriority(OVERLAY_A0.packageName, otherTarget.packageName,
+ OVERLAY_A0.userId);
+ assertFalse(changed);
+ }
+
+ @Test
+ public void testSetLowestPriority() throws Exception {
+ insert(OVERLAY_A0);
+ insert(OVERLAY_B0);
+ insert(OVERLAY_C0);
+
+ List<OverlayInfo> list =
+ mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
+ assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
+
+ boolean changed = mSettings.setLowestPriority(OVERLAY_B0.packageName, OVERLAY_B0.userId);
+ assertTrue(changed);
+
+ list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
+ assertListsAreEqual(list, OVERLAY_B0, OVERLAY_A0, OVERLAY_C0);
+ }
+
+ @Test
+ public void testSetHighestPriority() throws Exception {
+ insert(OVERLAY_A0);
+ insert(OVERLAY_B0);
+ insert(OVERLAY_C0);
+
+ List<OverlayInfo> list =
+ mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
+ assertListsAreEqual(list, OVERLAY_A0, OVERLAY_B0, OVERLAY_C0);
+
+ boolean changed = mSettings.setHighestPriority(OVERLAY_B0.packageName, OVERLAY_B0.userId);
+ assertTrue(changed);
+
+ list = mSettings.getOverlaysForTarget(OVERLAY_A0.targetPackageName, OVERLAY_A0.userId);
+ assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0);
+ }
+
+ // tests: persist and restore
+
+ @Test
+ public void testPersistEmpty() throws Exception {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ mSettings.persist(os);
+ String xml = new String(os.toByteArray(), "utf-8");
+
+ assertEquals(1, countXmlTags(xml, "overlays"));
+ assertEquals(0, countXmlTags(xml, "item"));
+ }
+
+ @Test
+ public void testPersistDifferentOverlaysSameUser() throws Exception {
+ insert(OVERLAY_A0);
+ insert(OVERLAY_B0);
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ mSettings.persist(os);
+ final String xml = new String(os.toByteArray(), "utf-8");
+
+ assertEquals(1, countXmlTags(xml, "overlays"));
+ assertEquals(2, countXmlTags(xml, "item"));
+ assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName",
+ OVERLAY_A0.packageName));
+ assertEquals(1, countXmlAttributesWhere(xml, "item", "packageName",
+ OVERLAY_B0.packageName));
+ assertEquals(2, countXmlAttributesWhere(xml, "item", "userId",
+ Integer.toString(OVERLAY_A0.userId)));
+ }
+
+ @Test
+ public void testPersistSameOverlayDifferentUsers() throws Exception {
+ insert(OVERLAY_A0);
+ insert(OVERLAY_A1);
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ mSettings.persist(os);
+ String xml = new String(os.toByteArray(), "utf-8");
+
+ assertEquals(1, countXmlTags(xml, "overlays"));
+ assertEquals(2, countXmlTags(xml, "item"));
+ assertEquals(2, countXmlAttributesWhere(xml, "item", "packageName",
+ OVERLAY_A0.packageName));
+ assertEquals(1, countXmlAttributesWhere(xml, "item", "userId",
+ Integer.toString(OVERLAY_A0.userId)));
+ assertEquals(1, countXmlAttributesWhere(xml, "item", "userId",
+ Integer.toString(OVERLAY_A1.userId)));
+ }
+
+ @Test
+ public void testPersistEnabled() throws Exception {
+ insert(OVERLAY_A0);
+ mSettings.setEnabled(OVERLAY_A0.packageName, OVERLAY_A0.userId, true);
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ mSettings.persist(os);
+ String xml = new String(os.toByteArray(), "utf-8");
+
+ assertEquals(1, countXmlAttributesWhere(xml, "item", "isEnabled", "true"));
+ }
+
+ @Test
+ public void testRestoreEmpty() throws Exception {
+ final int version = OverlayManagerSettings.Serializer.CURRENT_VERSION;
+ final String xml =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>\n"
+ + "<overlays version=\"" + version + "\" />\n";
+ ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
+
+ mSettings.restore(is);
+ assertDoesNotContain(mSettings, "com.dummy.overlay", 0);
+ }
+
+ @Test
+ public void testRestoreSingleUserSingleOverlay() throws Exception {
+ final int version = OverlayManagerSettings.Serializer.CURRENT_VERSION;
+ final String xml =
+ "<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n"
+ + "<overlays version='" + version + "'>\n"
+ + "<item packageName='com.dummy.overlay'\n"
+ + " userId='1234'\n"
+ + " targetPackageName='com.dummy.target'\n"
+ + " baseCodePath='/data/app/com.dummy.overlay-1/base.apk'\n"
+ + " state='" + STATE_DISABLED + "'\n"
+ + " isEnabled='false'\n"
+ + " category='dummy-category'\n"
+ + " isStatic='false'\n"
+ + " priority='0' />\n"
+ + "</overlays>\n";
+ ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
+
+ mSettings.restore(is);
+ OverlayInfo oi = mSettings.getOverlayInfo("com.dummy.overlay", 1234);
+ assertNotNull(oi);
+ assertEquals("com.dummy.overlay", oi.packageName);
+ assertEquals("com.dummy.target", oi.targetPackageName);
+ assertEquals("/data/app/com.dummy.overlay-1/base.apk", oi.baseCodePath);
+ assertEquals(1234, oi.userId);
+ assertEquals(STATE_DISABLED, oi.state);
+ assertFalse(mSettings.getEnabled("com.dummy.overlay", 1234));
+ }
+
+ @Test
+ public void testPersistAndRestore() throws Exception {
+ insert(OVERLAY_A0);
+ insert(OVERLAY_B1);
+
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ mSettings.persist(os);
+ String xml = new String(os.toByteArray(), "utf-8");
+ ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8"));
+ OverlayManagerSettings newSettings = new OverlayManagerSettings();
+ newSettings.restore(is);
+
+ OverlayInfo a = newSettings.getOverlayInfo(OVERLAY_A0.packageName, OVERLAY_A0.userId);
+ assertEquals(OVERLAY_A0, a);
+
+ OverlayInfo b = newSettings.getOverlayInfo(OVERLAY_B1.packageName, OVERLAY_B1.userId);
+ assertEquals(OVERLAY_B1, b);
+ }
+
+ private int countXmlTags(String xml, String tagToLookFor) throws Exception {
+ int count = 0;
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new StringReader(xml));
+ int event = parser.getEventType();
+ while (event != XmlPullParser.END_DOCUMENT) {
+ if (event == XmlPullParser.START_TAG && tagToLookFor.equals(parser.getName())) {
+ count++;
+ }
+ event = parser.next();
+ }
+ return count;
+ }
+
+ private int countXmlAttributesWhere(String xml, String tag, String attr, String value)
+ throws Exception {
+ int count = 0;
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new StringReader(xml));
+ int event = parser.getEventType();
+ while (event != XmlPullParser.END_DOCUMENT) {
+ if (event == XmlPullParser.START_TAG && tag.equals(parser.getName())) {
+ String v = parser.getAttributeValue(null, attr);
+ if (value.equals(v)) {
+ count++;
+ }
+ }
+ event = parser.next();
+ }
+ return count;
+ }
+
+ private void insert(OverlayInfo oi) throws Exception {
+ mSettings.init(oi.packageName, oi.userId, oi.targetPackageName, null, oi.baseCodePath,
+ false, 0, oi.category);
+ mSettings.setState(oi.packageName, oi.userId, oi.state);
+ mSettings.setEnabled(oi.packageName, oi.userId, false);
+ }
+
+ private static void assertContains(final OverlayManagerSettings settings,
+ final OverlayInfo oi) {
+ assertContains(settings, oi.packageName, oi.userId);
+ }
+
+ private static void assertContains(final OverlayManagerSettings settings,
+ final String packageName, int userId) {
+ try {
+ settings.getOverlayInfo(packageName, userId);
+ } catch (OverlayManagerSettings.BadKeyException e) {
+ fail(String.format("settings does not contain packageName=%s userId=%d",
+ packageName, userId));
+ }
+ }
+
+ private static void assertDoesNotContain(final OverlayManagerSettings settings,
+ final OverlayInfo oi) {
+ assertDoesNotContain(settings, oi.packageName, oi.userId);
+ }
+
+ private static void assertDoesNotContain(final OverlayManagerSettings settings,
+ final String packageName, int userId) {
+ try {
+ settings.getOverlayInfo(packageName, userId);
+ fail(String.format("settings contains packageName=%s userId=%d", packageName, userId));
+ } catch (OverlayManagerSettings.BadKeyException e) {
+ // do nothing: we expect to end up here
+ }
+ }
+
+ private static void assertContains(int[] haystack, int needle) {
+ List<Integer> list = IntStream.of(haystack)
+ .boxed()
+ .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
+ if (!list.contains(needle)) {
+ fail(String.format("integer array [%s] does not contain value %s",
+ TextUtils.join(",", list), needle));
+ }
+ }
+
+ private static void assertDoesNotContain(int[] haystack, int needle) {
+ List<Integer> list = IntStream.of(haystack)
+ .boxed()
+ .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
+ if (list.contains(needle)) {
+ fail(String.format("integer array [%s] contains value %s",
+ TextUtils.join(",", list), needle));
+ }
+ }
+
+ private static void assertListsAreEqual(List<OverlayInfo> list, OverlayInfo... array) {
+ List<OverlayInfo> other = Stream.of(array)
+ .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
+ assertListsAreEqual(list, other);
+ }
+
+ private static void assertListsAreEqual(List<OverlayInfo> list, List<OverlayInfo> other) {
+ if (!list.equals(other)) {
+ fail(String.format("lists [%s] and [%s] differ",
+ TextUtils.join(",", list), TextUtils.join(",", other)));
+ }
+ }
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 6ed78b3..b34bd25 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -940,12 +940,12 @@
@Test
public void testClearLockedFields() {
final NotificationChannel channel = getChannel();
- mHelper.clearLockedFields(channel);
+ mHelper.clearLockedFieldsLocked(channel);
assertEquals(0, channel.getUserLockedFields());
channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY
| NotificationChannel.USER_LOCKED_IMPORTANCE);
- mHelper.clearLockedFields(channel);
+ mHelper.clearLockedFieldsLocked(channel);
assertEquals(0, channel.getUserLockedFields());
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index 9ce5795..beec1a8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -18,6 +18,8 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.server.wm.BoundsAnimationController.BOUNDS;
+import static com.android.server.wm.BoundsAnimationController.FADE_IN;
import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
@@ -131,6 +133,8 @@
boolean mCancelRequested;
Rect mStackBounds;
Rect mTaskBounds;
+ float mAlpha;
+ @BoundsAnimationController.AnimationType int mAnimationType;
void initialize(Rect from) {
mAwaitingAnimationStart = true;
@@ -148,11 +152,12 @@
@Override
public boolean onAnimationStart(boolean schedulePipModeChangedCallback,
- boolean forceUpdate) {
+ boolean forceUpdate, @BoundsAnimationController.AnimationType int animationType) {
mAwaitingAnimationStart = false;
mAnimationStarted = true;
mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback;
mForcePipModeChangedCallback = forceUpdate;
+ mAnimationType = animationType;
return true;
}
@@ -185,6 +190,12 @@
mMovedToFullscreen = moveToFullscreen;
mTaskBounds = null;
}
+
+ @Override
+ public boolean setPinnedStackAlpha(float alpha) {
+ mAlpha = alpha;
+ return true;
+ }
}
/**
@@ -201,6 +212,7 @@
private Rect mTo;
private Rect mLargerBounds;
private Rect mExpectedFinalBounds;
+ private @BoundsAnimationController.AnimationType int mAnimationType;
BoundsAnimationDriver(BoundsAnimationController controller,
TestBoundsAnimationTarget target, MockValueAnimator mockValueAnimator) {
@@ -209,7 +221,8 @@
mMockAnimator = mockValueAnimator;
}
- BoundsAnimationDriver start(Rect from, Rect to) {
+ BoundsAnimationDriver start(Rect from, Rect to,
+ @BoundsAnimationController.AnimationType int animationType) {
if (mAnimator != null) {
throw new IllegalArgumentException("Call restart() to restart an animation");
}
@@ -223,7 +236,7 @@
assertTrue(mTarget.mAwaitingAnimationStart);
assertFalse(mTarget.mAnimationStarted);
- startImpl(from, to);
+ startImpl(from, to, animationType);
// Ensure that the animator is paused for the all windows drawn signal when animating
// to/from fullscreen
@@ -253,7 +266,7 @@
mTarget.mAnimationStarted = false;
// Start animation
- startImpl(mTarget.mStackBounds, to);
+ startImpl(mTarget.mStackBounds, to, BOUNDS);
if (toSameBounds) {
// Same animator if same final bounds
@@ -273,13 +286,15 @@
return this;
}
- private BoundsAnimationDriver startImpl(Rect from, Rect to) {
+ private BoundsAnimationDriver startImpl(Rect from, Rect to,
+ @BoundsAnimationController.AnimationType int animationType) {
boolean fromFullscreen = from.equals(BOUNDS_FULL);
boolean toFullscreen = to.equals(BOUNDS_FULL);
mFrom = new Rect(from);
mTo = new Rect(to);
mExpectedFinalBounds = new Rect(to);
mLargerBounds = getLargerBounds(mFrom, mTo);
+ mAnimationType = animationType;
// Start animation
final @SchedulePipModeChangedState int schedulePipModeChangedState = toFullscreen
@@ -288,17 +303,19 @@
? SCHEDULE_PIP_MODE_CHANGED_ON_END
: NO_PIP_MODE_CHANGED_CALLBACKS;
mAnimator = mController.animateBoundsImpl(mTarget, from, to, DURATION,
- schedulePipModeChangedState, fromFullscreen, toFullscreen);
+ schedulePipModeChangedState, fromFullscreen, toFullscreen, animationType);
- // Original stack bounds, frozen task bounds
- assertEquals(mFrom, mTarget.mStackBounds);
- assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds);
+ if (animationType == BOUNDS) {
+ // Original stack bounds, frozen task bounds
+ assertEquals(mFrom, mTarget.mStackBounds);
+ assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds);
- // Animating to larger size
- if (mFrom.equals(mLargerBounds)) {
- assertFalse(mAnimator.animatingToLargerSize());
- } else if (mTo.equals(mLargerBounds)) {
- assertTrue(mAnimator.animatingToLargerSize());
+ // Animating to larger size
+ if (mFrom.equals(mLargerBounds)) {
+ assertFalse(mAnimator.animatingToLargerSize());
+ } else if (mTo.equals(mLargerBounds)) {
+ assertTrue(mAnimator.animatingToLargerSize());
+ }
}
return this;
@@ -315,16 +332,20 @@
BoundsAnimationDriver update(float t) {
mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(t));
- // Temporary stack bounds, frozen task bounds
- if (t == 0f) {
- assertEquals(mFrom, mTarget.mStackBounds);
- } else if (t == 1f) {
- assertEquals(mTo, mTarget.mStackBounds);
+ if (mAnimationType == BOUNDS) {
+ // Temporary stack bounds, frozen task bounds
+ if (t == 0f) {
+ assertEquals(mFrom, mTarget.mStackBounds);
+ } else if (t == 1f) {
+ assertEquals(mTo, mTarget.mStackBounds);
+ } else {
+ assertNotEquals(mFrom, mTarget.mStackBounds);
+ assertNotEquals(mTo, mTarget.mStackBounds);
+ }
+ assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds);
} else {
- assertNotEquals(mFrom, mTarget.mStackBounds);
- assertNotEquals(mTo, mTarget.mStackBounds);
+ assertEquals((float) mMockAnimator.getAnimatedValue(), mTarget.mAlpha, 0.01f);
}
- assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds);
return this;
}
@@ -353,10 +374,14 @@
BoundsAnimationDriver end() {
mAnimator.end();
- // Final stack bounds
- assertEquals(mTo, mTarget.mStackBounds);
- assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds);
- assertNull(mTarget.mTaskBounds);
+ if (mAnimationType == BOUNDS) {
+ // Final stack bounds
+ assertEquals(mTo, mTarget.mStackBounds);
+ assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds);
+ assertNull(mTarget.mTaskBounds);
+ } else {
+ assertEquals(mTarget.mAlpha, 1f, 0.01f);
+ }
return this;
}
@@ -413,7 +438,7 @@
@UiThreadTest
@Test
public void testFullscreenToFloatingTransition() {
- mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
+ mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0f)
.update(0.5f)
@@ -425,7 +450,7 @@
@UiThreadTest
@Test
public void testFloatingToFullscreenTransition() {
- mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
+ mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
.expectStarted(SCHEDULE_PIP_MODE_CHANGED)
.update(0f)
.update(0.5f)
@@ -437,7 +462,7 @@
@UiThreadTest
@Test
public void testFloatingToSmallerFloatingTransition() {
- mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING)
+ mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0f)
.update(0.5f)
@@ -449,7 +474,7 @@
@UiThreadTest
@Test
public void testFloatingToLargerFloatingTransition() {
- mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING)
+ mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0f)
.update(0.5f)
@@ -463,7 +488,7 @@
@UiThreadTest
@Test
public void testFullscreenToFloatingCancelFromTarget() {
- mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
+ mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
.cancel()
@@ -473,7 +498,7 @@
@UiThreadTest
@Test
public void testFullscreenToFloatingCancelFromAnimationToSameBounds() {
- mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
+ mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
.restart(BOUNDS_FLOATING, false /* expectStartedAndPipModeChangedCallback */)
@@ -484,7 +509,7 @@
@UiThreadTest
@Test
public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() {
- mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
+ mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
.restart(BOUNDS_SMALLER_FLOATING,
@@ -498,7 +523,7 @@
public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() {
// When animating from fullscreen and the animation is interruped, we expect the animation
// start callback to be made, with a forced pip mode change callback
- mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
+ mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, BOUNDS)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
.restart(BOUNDS_FULL, true /* expectStartedAndPipModeChangedCallback */)
@@ -511,7 +536,7 @@
@UiThreadTest
@Test
public void testFloatingToFullscreenCancelFromTarget() {
- mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
+ mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
.expectStarted(SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
.cancel()
@@ -521,7 +546,7 @@
@UiThreadTest
@Test
public void testFloatingToFullscreenCancelFromAnimationToSameBounds() {
- mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
+ mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
.expectStarted(SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
.restart(BOUNDS_FULL, false /* expectStartedAndPipModeChangedCallback */)
@@ -532,7 +557,7 @@
@UiThreadTest
@Test
public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() {
- mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
+ mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL, BOUNDS)
.expectStarted(SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
.restart(BOUNDS_SMALLER_FLOATING,
@@ -546,7 +571,7 @@
@UiThreadTest
@Test
public void testFloatingToSmallerFloatingCancelFromTarget() {
- mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING)
+ mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING, BOUNDS)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
.cancel()
@@ -556,13 +581,25 @@
@UiThreadTest
@Test
public void testFloatingToLargerFloatingCancelFromTarget() {
- mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING)
+ mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING, BOUNDS)
.expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
.update(0.25f)
.cancel()
.expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
}
+ @UiThreadTest
+ @Test
+ public void testFadeIn() {
+ mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING, FADE_IN)
+ .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
+ .update(0f)
+ .update(0.5f)
+ .update(1f)
+ .end()
+ .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
+ }
+
/** MISC **/
@UiThreadTest
@@ -570,7 +607,7 @@
public void testBoundsAreCopied() {
Rect from = new Rect(0, 0, 100, 100);
Rect to = new Rect(25, 25, 75, 75);
- mDriver.start(from, to)
+ mDriver.start(from, to, BOUNDS)
.update(0.25f)
.end();
assertEquals(new Rect(0, 0, 100, 100), from);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index e392353..0c2ce61 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -163,7 +163,7 @@
// Assume IRecentsAnimationController#cleanupScreenshot called to finish screenshot
// animation.
mController.mRecentScreenshotAnimator.cancelAnimation();
- verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true);
+ verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false);
}
private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index 5625ea4..f615823 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -71,6 +71,7 @@
@Test
public void testCancelAnimationOnVisibleStackOrderChange() {
ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
+ display.mDisplayContent.mBoundsAnimationController = mock(BoundsAnimationController.class);
ActivityStack fullscreenStack = display.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, true /* onTop */);
new ActivityBuilder(mService)
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 8d2cbca..ea52377 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -25,9 +25,6 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
-
-import com.android.internal.app.IVoiceActionCheckCallback;
-import com.android.server.wm.ActivityTaskManagerInternal;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.content.BroadcastReceiver;
@@ -51,15 +48,16 @@
import android.util.Slog;
import android.view.IWindowManager;
+import com.android.internal.app.IVoiceActionCheckCallback;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.server.LocalServices;
+import com.android.server.wm.ActivityTaskManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConnection.Callback {
final static String TAG = "VoiceInteractionServiceManager";
@@ -358,6 +356,7 @@
intent.setComponent(mComponent);
mBound = mContext.bindServiceAsUser(intent, mConnection,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
+ | Context.BIND_INCLUDE_CAPABILITIES
| Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, new UserHandle(mUser));
if (!mBound) {
Slog.w(TAG, "Failed binding to voice interaction service " + mComponent);
diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
index ddbd851..b562f32 100644
--- a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
@@ -34,14 +34,14 @@
private static final String LOG_TAG = "CellSignalStrengthTdscdma";
private static final boolean DBG = false;
- private static final int TDSCDMA_RSSI_MAX = -51;
- private static final int TDSCDMA_RSSI_GREAT = -77;
- private static final int TDSCDMA_RSSI_GOOD = -87;
- private static final int TDSCDMA_RSSI_MODERATE = -97;
- private static final int TDSCDMA_RSSI_POOR = -107;
-
- private static final int TDSCDMA_RSCP_MIN = -120;
+ // These levels are arbitrary but carried over from SignalStrength.java for consistency.
private static final int TDSCDMA_RSCP_MAX = -24;
+ private static final int TDSCDMA_RSCP_GREAT = -49;
+ private static final int TDSCDMA_RSCP_GOOD = -73;
+ private static final int TDSCDMA_RSCP_MODERATE = -97;
+ private static final int TDSCDMA_RSCP_POOR = -110;
+ private static final int TDSCDMA_RSCP_MIN = -120;
+
private int mRssi; // in dBm [-113, -51], CellInfo.UNAVAILABLE
@@ -135,11 +135,11 @@
/** @hide */
@Override
public void updateLevel(PersistableBundle cc, ServiceState ss) {
- if (mRssi > TDSCDMA_RSSI_MAX) mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
- else if (mRssi >= TDSCDMA_RSSI_GREAT) mLevel = SIGNAL_STRENGTH_GREAT;
- else if (mRssi >= TDSCDMA_RSSI_GOOD) mLevel = SIGNAL_STRENGTH_GOOD;
- else if (mRssi >= TDSCDMA_RSSI_MODERATE) mLevel = SIGNAL_STRENGTH_MODERATE;
- else if (mRssi >= TDSCDMA_RSSI_POOR) mLevel = SIGNAL_STRENGTH_POOR;
+ if (mRscp > TDSCDMA_RSCP_MAX) mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
+ else if (mRscp >= TDSCDMA_RSCP_GREAT) mLevel = SIGNAL_STRENGTH_GREAT;
+ else if (mRscp >= TDSCDMA_RSCP_GOOD) mLevel = SIGNAL_STRENGTH_GOOD;
+ else if (mRscp >= TDSCDMA_RSCP_MODERATE) mLevel = SIGNAL_STRENGTH_MODERATE;
+ else if (mRscp >= TDSCDMA_RSCP_POOR) mLevel = SIGNAL_STRENGTH_POOR;
else mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
@@ -159,6 +159,23 @@
}
/**
+ * Get the RSSI as dBm value -113..-51dBm or {@link CellInfo#UNAVAILABLE UNAVAILABLE}.
+ *
+ * @hide
+ */
+ public int getRssi() {
+ return mRssi;
+ }
+
+ /**
+ * Get the BER as an ASU value 0..7, 99, or {@link CellInfo#UNAVAILABLE UNAVAILABLE}.
+ * @hide
+ */
+ public int getBitErrorRate() {
+ return mBitErrorRate;
+ }
+
+ /**
* Get the RSCP in ASU.
*
* Asu is calculated based on 3GPP RSRP. Refer to 3GPP 27.007 (Ver 10.3.0) Sec 8.69
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index d9fd7f3..8efc0f2 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -66,7 +66,7 @@
public static final String LEVEL_CALCULATION_METHOD_RSCP = "rscp";
// Default to RSSI for backwards compatibility with older devices
- private static final String sLevelCalculationMethod = LEVEL_CALCULATION_METHOD_RSSI;
+ private static final String DEFAULT_LEVEL_CALCULATION_METHOD = LEVEL_CALCULATION_METHOD_RSSI;
private int mRssi; // in dBm [-113, 51] or CellInfo.UNAVAILABLE if unknown
private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5 or
@@ -161,14 +161,14 @@
int[] rscpThresholds;
if (cc == null) {
- calcMethod = sLevelCalculationMethod;
+ calcMethod = DEFAULT_LEVEL_CALCULATION_METHOD;
rscpThresholds = sRscpThresholds;
} else {
// TODO: abstract this entire thing into a series of functions
calcMethod = cc.getString(
CarrierConfigManager.KEY_WCDMA_DEFAULT_SIGNAL_STRENGTH_MEASUREMENT_STRING,
- sLevelCalculationMethod);
- if (TextUtils.isEmpty(calcMethod)) calcMethod = sLevelCalculationMethod;
+ DEFAULT_LEVEL_CALCULATION_METHOD);
+ if (TextUtils.isEmpty(calcMethod)) calcMethod = DEFAULT_LEVEL_CALCULATION_METHOD;
rscpThresholds = cc.getIntArray(
CarrierConfigManager.KEY_WCDMA_RSCP_THRESHOLDS_INT_ARRAY);
if (rscpThresholds == null || rscpThresholds.length != NUM_SIGNAL_STRENGTH_THRESHOLDS) {
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index a933da7..57c84a6 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2749,6 +2749,8 @@
*
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
+ * @throws IllegalArgumentException if any of the subscriptions in the list doesn't exist.
+ * @throws IllegalStateException if Telephony service is in bad state.
*
* @param subIdList list of subId that will be in the same group
* @return groupUUID a UUID assigned to the subscription group.
@@ -2797,6 +2799,7 @@
* outlined above.
* @throws IllegalArgumentException if the some subscriptions in the list doesn't exist,
* or the groupUuid doesn't exist.
+ * @throws IllegalStateException if Telephony service is in bad state.
*
* @param subIdList list of subId that need adding into the group
* @param groupUuid the groupUuid the subscriptions are being added to.
@@ -2849,6 +2852,7 @@
* outlined above.
* @throws IllegalArgumentException if the some subscriptions in the list doesn't belong
* the specified group.
+ * @throws IllegalStateException if Telephony service is in bad state.
*
* @param subIdList list of subId that need removing from their groups.
*
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 7eea218..a86fda4 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -78,10 +78,11 @@
*/
public static final int TYPE_NONE = ApnTypes.NONE;
/**
- * APN type for all APNs.
+ * APN type for all APNs (except wild-cardable types).
* @hide
*/
- public static final int TYPE_ALL = ApnTypes.ALL | ApnTypes.MCX;
+ public static final int TYPE_ALL = ApnTypes.DEFAULT | ApnTypes.HIPRI | ApnTypes.MMS
+ | ApnTypes.SUPL | ApnTypes.DUN | ApnTypes.FOTA | ApnTypes.IMS | ApnTypes.CBS;
/** APN type for default data traffic. */
public static final int TYPE_DEFAULT = ApnTypes.DEFAULT | ApnTypes.HIPRI;
/** APN type for MMS traffic. */