Merge "Add ExternalStorageService API"
diff --git a/Android.bp b/Android.bp
index b80ef88..9d05ffd 100644
--- a/Android.bp
+++ b/Android.bp
@@ -25,27 +25,32 @@
//
// READ ME: ########################################################
+filegroup {
+ name: "framework-defaults-java-srcs",
+ srcs: [
+ // java sources under this directory
+ "core/java/**/*.java",
+ "drm/java/**/*.java",
+ "graphics/java/**/*.java",
+ "keystore/java/**/*.java",
+ "location/java/**/*.java",
+ "lowpan/java/**/*.java",
+ "media/java/**/*.java",
+ "media/mca/effect/java/**/*.java",
+ "media/mca/filterfw/java/**/*.java",
+ "media/mca/filterpacks/java/**/*.java",
+ "opengl/java/**/*.java",
+ "rs/java/**/*.java",
+ "sax/java/**/*.java",
+ "telecomm/java/**/*.java",
+ "telephony/java/**/*.java",
+ "wifi/java/**/*.java",
+ ],
+}
+
// TODO(b/70046217): make these as filegroups where the base directory for aidl files
// is given as 'path'. Eliminate the need for aidl_local_include_dirs.
framework_srcs = [
- // java sources under this directory
- "core/java/**/*.java",
- "drm/java/**/*.java",
- "graphics/java/**/*.java",
- "keystore/java/**/*.java",
- "location/java/**/*.java",
- "lowpan/java/**/*.java",
- "media/java/**/*.java",
- "media/mca/effect/java/**/*.java",
- "media/mca/filterfw/java/**/*.java",
- "media/mca/filterpacks/java/**/*.java",
- "opengl/java/**/*.java",
- "rs/java/**/*.java",
- "sax/java/**/*.java",
- "telecomm/java/**/*.java",
- "telephony/java/**/*.java",
- "wifi/java/**/*.java",
-
// aidl under this directory
// b/70046217#comment15 These MUST come after all java srcs.
// TODO(b/70046217) remove the above requirement
@@ -132,7 +137,9 @@
defaults: ["framework-aidl-export-defaults"],
installable: true,
- srcs: framework_srcs,
+ srcs: [
+ ":framework-defaults-java-srcs",
+ ] + framework_srcs,
aidl: {
local_include_dirs: framework_aidl_local_include_dirs,
@@ -241,10 +248,6 @@
name: "framework-minus-apex",
defaults: ["framework-defaults"],
javac_shard_size: 150,
- required: [
- "framework-platform-compat-config",
- "libcore-platform-compat-config",
- ],
}
java_library {
@@ -255,6 +258,10 @@
"framework-minus-apex",
"jobscheduler-framework",
],
+ required: [
+ "framework-platform-compat-config",
+ "libcore-platform-compat-config",
+ ],
sdk_version: "core_platform",
}
@@ -1165,10 +1172,10 @@
name: "hiddenapi-mappings",
defaults: ["metalava-api-stubs-default"],
srcs: [
- ":openjdk_java_files",
+ ":framework-defaults-java-srcs",
":non_openjdk_java_files",
+ ":openjdk_java_files",
":opt-telephony-common-srcs",
- "core/java/**/*.java",
],
arg_files: [
"core/res/AndroidManifest.xml",
diff --git a/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java b/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java
index 005b189..b7e8cf6 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/GrantedUriPermissions.java
@@ -16,7 +16,6 @@
package com.android.server.job;
-import android.app.IActivityManager;
import android.app.UriGrantsManager;
import android.content.ClipData;
import android.content.ContentProvider;
@@ -40,7 +39,7 @@
private final IBinder mPermissionOwner;
private final ArrayList<Uri> mUris = new ArrayList<>();
- private GrantedUriPermissions(IActivityManager am, int grantFlags, int uid, String tag)
+ private GrantedUriPermissions(int grantFlags, int uid, String tag)
throws RemoteException {
mGrantFlags = grantFlags;
mSourceUserId = UserHandle.getUserId(uid);
@@ -49,7 +48,7 @@
.getService(UriGrantsManagerInternal.class).newUriPermissionOwner("job: " + tag);
}
- public void revoke(IActivityManager am) {
+ public void revoke() {
for (int i = mUris.size()-1; i >= 0; i--) {
LocalServices.getService(UriGrantsManagerInternal.class).revokeUriPermissionFromOwner(
mPermissionOwner, mUris.get(i), mGrantFlags, mSourceUserId);
@@ -62,7 +61,7 @@
|Intent.FLAG_GRANT_READ_URI_PERMISSION)) != 0;
}
- public static GrantedUriPermissions createFromIntent(IActivityManager am, Intent intent,
+ public static GrantedUriPermissions createFromIntent(Intent intent,
int sourceUid, String targetPackage, int targetUserId, String tag) {
int grantFlags = intent.getFlags();
if (!checkGrantFlags(grantFlags)) {
@@ -73,44 +72,44 @@
Uri data = intent.getData();
if (data != null) {
- perms = grantUri(am, data, sourceUid, targetPackage, targetUserId, grantFlags, tag,
+ perms = grantUri(data, sourceUid, targetPackage, targetUserId, grantFlags, tag,
perms);
}
ClipData clip = intent.getClipData();
if (clip != null) {
- perms = grantClip(am, clip, sourceUid, targetPackage, targetUserId, grantFlags, tag,
+ perms = grantClip(clip, sourceUid, targetPackage, targetUserId, grantFlags, tag,
perms);
}
return perms;
}
- public static GrantedUriPermissions createFromClip(IActivityManager am, ClipData clip,
+ public static GrantedUriPermissions createFromClip(ClipData clip,
int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag) {
if (!checkGrantFlags(grantFlags)) {
return null;
}
GrantedUriPermissions perms = null;
if (clip != null) {
- perms = grantClip(am, clip, sourceUid, targetPackage, targetUserId, grantFlags,
+ perms = grantClip(clip, sourceUid, targetPackage, targetUserId, grantFlags,
tag, perms);
}
return perms;
}
- private static GrantedUriPermissions grantClip(IActivityManager am, ClipData clip,
+ private static GrantedUriPermissions grantClip(ClipData clip,
int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag,
GrantedUriPermissions curPerms) {
final int N = clip.getItemCount();
for (int i = 0; i < N; i++) {
- curPerms = grantItem(am, clip.getItemAt(i), sourceUid, targetPackage, targetUserId,
+ curPerms = grantItem(clip.getItemAt(i), sourceUid, targetPackage, targetUserId,
grantFlags, tag, curPerms);
}
return curPerms;
}
- private static GrantedUriPermissions grantUri(IActivityManager am, Uri uri,
+ private static GrantedUriPermissions grantUri(Uri uri,
int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag,
GrantedUriPermissions curPerms) {
try {
@@ -118,7 +117,7 @@
UserHandle.getUserId(sourceUid));
uri = ContentProvider.getUriWithoutUserId(uri);
if (curPerms == null) {
- curPerms = new GrantedUriPermissions(am, grantFlags, sourceUid, tag);
+ curPerms = new GrantedUriPermissions(grantFlags, sourceUid, tag);
}
UriGrantsManager.getService().grantUriPermissionFromOwner(curPerms.mPermissionOwner,
sourceUid, targetPackage, uri, grantFlags, sourceUserId, targetUserId);
@@ -129,16 +128,16 @@
return curPerms;
}
- private static GrantedUriPermissions grantItem(IActivityManager am, ClipData.Item item,
+ private static GrantedUriPermissions grantItem(ClipData.Item item,
int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag,
GrantedUriPermissions curPerms) {
if (item.getUri() != null) {
- curPerms = grantUri(am, item.getUri(), sourceUid, targetPackage, targetUserId,
+ curPerms = grantUri(item.getUri(), sourceUid, targetPackage, targetUserId,
grantFlags, tag, curPerms);
}
Intent intent = item.getIntent();
if (intent != null && intent.getData() != null) {
- curPerms = grantUri(am, intent.getData(), sourceUid, targetPackage, targetUserId,
+ curPerms = grantUri(intent.getData(), sourceUid, targetPackage, targetUserId,
grantFlags, tag, curPerms);
}
return curPerms;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 4e424c3..a633350 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -963,7 +963,7 @@
// changing. We can just directly enqueue this work in to the job.
if (toCancel.getJob().equals(job)) {
- toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
+ toCancel.enqueueWorkLocked(work);
// If any of work item is enqueued when the source is in the foreground,
// exempt the entire job.
@@ -992,11 +992,11 @@
}
// This may throw a SecurityException.
- jobStatus.prepareLocked(ActivityManager.getService());
+ jobStatus.prepareLocked();
if (work != null) {
// If work has been supplied, enqueue it into the new job.
- jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
+ jobStatus.enqueueWorkLocked(work);
}
if (toCancel != null) {
@@ -1144,7 +1144,7 @@
*/
private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob, String reason) {
if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
- cancelled.unprepareLocked(ActivityManager.getService());
+ cancelled.unprepareLocked();
stopTrackingJobLocked(cancelled, incomingJob, true /* writeBack */);
// Remove from pending queue.
if (mPendingJobs.remove(cancelled)) {
@@ -1449,7 +1449,7 @@
private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
boolean removeFromPersisted) {
// Deal with any remaining work items in the old job.
- jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
+ jobStatus.stopTrackingJobLocked(incomingJob);
// Remove from store as well as controllers.
final boolean removed = mJobs.remove(jobStatus, removeFromPersisted);
@@ -1705,7 +1705,7 @@
if (rescheduledJob != null) {
try {
- rescheduledJob.prepareLocked(ActivityManager.getService());
+ rescheduledJob.prepareLocked();
} catch (SecurityException e) {
Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
}
@@ -1713,13 +1713,13 @@
} else if (jobStatus.getJob().isPeriodic()) {
JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
try {
- rescheduledPeriodic.prepareLocked(ActivityManager.getService());
+ rescheduledPeriodic.prepareLocked();
} catch (SecurityException e) {
Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledPeriodic);
}
startTrackingJobLocked(rescheduledPeriodic, jobStatus);
}
- jobStatus.unprepareLocked(ActivityManager.getService());
+ jobStatus.unprepareLocked();
reportActiveLocked();
mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 4d9f133..782e646 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -18,7 +18,6 @@
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
-import android.app.ActivityManager;
import android.app.job.IJobCallback;
import android.app.job.IJobService;
import android.app.job.JobInfo;
@@ -389,7 +388,7 @@
try {
synchronized (mLock) {
assertCallerLocked(cb);
- return mRunningJob.completeWorkLocked(ActivityManager.getService(), workId);
+ return mRunningJob.completeWorkLocked(workId);
}
} finally {
Binder.restoreCallingIdentity(ident);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
index 4321fc7..c2bdb6c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -20,8 +20,6 @@
import static com.android.server.job.JobSchedulerService.sSystemClock;
import android.annotation.Nullable;
-import android.app.ActivityManager;
-import android.app.IActivityManager;
import android.app.job.JobInfo;
import android.content.ComponentName;
import android.content.Context;
@@ -180,7 +178,6 @@
public void getRtcCorrectedJobsLocked(final ArrayList<JobStatus> toAdd,
final ArrayList<JobStatus> toRemove) {
final long elapsedNow = sElapsedRealtimeClock.millis();
- final IActivityManager am = ActivityManager.getService();
// Find the jobs that need to be fixed up, collecting them for post-iteration
// replacement with their new versions
@@ -192,7 +189,7 @@
JobStatus newJob = new JobStatus(job,
elapsedRuntimes.first, elapsedRuntimes.second,
0, job.getLastSuccessfulRunTime(), job.getLastFailedRunTime());
- newJob.prepareLocked(am);
+ newJob.prepareLocked();
toAdd.add(newJob);
toRemove.add(job);
}
@@ -667,10 +664,9 @@
jobs = readJobMapImpl(fis, rtcGood);
if (jobs != null) {
long now = sElapsedRealtimeClock.millis();
- IActivityManager am = ActivityManager.getService();
for (int i=0; i<jobs.size(); i++) {
JobStatus js = jobs.get(i);
- js.prepareLocked(am);
+ js.prepareLocked();
js.enqueueTime = now;
this.jobSet.add(js);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 1133f7b..adb4314 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -20,7 +20,6 @@
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import android.app.AppGlobals;
-import android.app.IActivityManager;
import android.app.job.JobInfo;
import android.app.job.JobWorkItem;
import android.content.ClipData;
@@ -528,7 +527,7 @@
/*innerFlags=*/ 0);
}
- public void enqueueWorkLocked(IActivityManager am, JobWorkItem work) {
+ public void enqueueWorkLocked(JobWorkItem work) {
if (pendingWork == null) {
pendingWork = new ArrayList<>();
}
@@ -536,7 +535,7 @@
nextPendingWorkId++;
if (work.getIntent() != null
&& GrantedUriPermissions.checkGrantFlags(work.getIntent().getFlags())) {
- work.setGrants(GrantedUriPermissions.createFromIntent(am, work.getIntent(), sourceUid,
+ work.setGrants(GrantedUriPermissions.createFromIntent(work.getIntent(), sourceUid,
sourcePackageName, sourceUserId, toShortString()));
}
pendingWork.add(work);
@@ -567,20 +566,20 @@
return executingWork != null && executingWork.size() > 0;
}
- private static void ungrantWorkItem(IActivityManager am, JobWorkItem work) {
+ private static void ungrantWorkItem(JobWorkItem work) {
if (work.getGrants() != null) {
- ((GrantedUriPermissions)work.getGrants()).revoke(am);
+ ((GrantedUriPermissions)work.getGrants()).revoke();
}
}
- public boolean completeWorkLocked(IActivityManager am, int workId) {
+ public boolean completeWorkLocked(int workId) {
if (executingWork != null) {
final int N = executingWork.size();
for (int i = 0; i < N; i++) {
JobWorkItem work = executingWork.get(i);
if (work.getWorkId() == workId) {
executingWork.remove(i);
- ungrantWorkItem(am, work);
+ ungrantWorkItem(work);
return true;
}
}
@@ -588,16 +587,16 @@
return false;
}
- private static void ungrantWorkList(IActivityManager am, ArrayList<JobWorkItem> list) {
+ private static void ungrantWorkList(ArrayList<JobWorkItem> list) {
if (list != null) {
final int N = list.size();
for (int i = 0; i < N; i++) {
- ungrantWorkItem(am, list.get(i));
+ ungrantWorkItem(list.get(i));
}
}
}
- public void stopTrackingJobLocked(IActivityManager am, JobStatus incomingJob) {
+ public void stopTrackingJobLocked(JobStatus incomingJob) {
if (incomingJob != null) {
// We are replacing with a new job -- transfer the work! We do any executing
// work first, since that was originally at the front of the pending work.
@@ -615,15 +614,15 @@
incomingJob.updateEstimatedNetworkBytesLocked();
} else {
// We are completely stopping the job... need to clean up work.
- ungrantWorkList(am, pendingWork);
+ ungrantWorkList(pendingWork);
pendingWork = null;
- ungrantWorkList(am, executingWork);
+ ungrantWorkList(executingWork);
executingWork = null;
}
updateEstimatedNetworkBytesLocked();
}
- public void prepareLocked(IActivityManager am) {
+ public void prepareLocked() {
if (prepared) {
Slog.wtf(TAG, "Already prepared: " + this);
return;
@@ -634,12 +633,12 @@
}
final ClipData clip = job.getClipData();
if (clip != null) {
- uriPerms = GrantedUriPermissions.createFromClip(am, clip, sourceUid, sourcePackageName,
+ uriPerms = GrantedUriPermissions.createFromClip(clip, sourceUid, sourcePackageName,
sourceUserId, job.getClipGrantFlags(), toShortString());
}
}
- public void unprepareLocked(IActivityManager am) {
+ public void unprepareLocked() {
if (!prepared) {
Slog.wtf(TAG, "Hasn't been prepared: " + this);
if (DEBUG_PREPARE && unpreparedPoint != null) {
@@ -652,7 +651,7 @@
unpreparedPoint = new Throwable().fillInStackTrace();
}
if (uriPerms != null) {
- uriPerms.revoke(am);
+ uriPerms.revoke();
uriPerms = null;
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
index 4c11947..1bb9e96 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/TimeController.java
@@ -18,13 +18,20 @@
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
+import android.content.ContentResolver;
import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
import android.os.Process;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.provider.Settings;
+import android.util.KeyValueListParser;
import android.util.Log;
import android.util.Slog;
import android.util.TimeUtils;
@@ -32,6 +39,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.job.ConstantsProto;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.StateControllerProto;
@@ -55,6 +63,9 @@
/** Delay alarm tag for logging purposes */
private final String DELAY_TAG = "*job.delay*";
+ private final Handler mHandler;
+ private final TcConstants mTcConstants;
+
private long mNextJobExpiredElapsedMillis;
private long mNextDelayExpiredElapsedMillis;
@@ -70,6 +81,14 @@
mNextJobExpiredElapsedMillis = Long.MAX_VALUE;
mNextDelayExpiredElapsedMillis = Long.MAX_VALUE;
mChainedAttributionEnabled = mService.isChainedAttributionEnabled();
+
+ mHandler = new Handler(mContext.getMainLooper());
+ mTcConstants = new TcConstants(mHandler);
+ }
+
+ @Override
+ public void onSystemServicesReady() {
+ mTcConstants.start(mContext.getContentResolver());
}
/**
@@ -294,8 +313,7 @@
} else {
if (!wouldBeReadyWithConstraintLocked(job, JobStatus.CONSTRAINT_TIMING_DELAY)) {
if (DEBUG) {
- Slog.i(TAG,
- "Skipping " + job + " because delay won't make it ready.");
+ Slog.i(TAG, "Skipping " + job + " because delay won't make it ready.");
}
continue;
}
@@ -354,7 +372,8 @@
/**
* Set an alarm with the {@link android.app.AlarmManager} for the next time at which a job's
* delay will expire.
- * This alarm <b>will</b> wake up the phone.
+ * This alarm <b>will not</b> wake up the phone if
+ * {@link TcConstants#USE_NON_WAKEUP_ALARM_FOR_DELAY} is true.
*/
private void setDelayExpiredAlarmLocked(long alarmTimeElapsedMillis, WorkSource ws) {
alarmTimeElapsedMillis = maybeAdjustAlarmTime(alarmTimeElapsedMillis);
@@ -362,8 +381,11 @@
return;
}
mNextDelayExpiredElapsedMillis = alarmTimeElapsedMillis;
- updateAlarmWithListenerLocked(DELAY_TAG, mNextDelayExpiredListener,
- mNextDelayExpiredElapsedMillis, ws);
+ final int alarmType =
+ mTcConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY
+ ? AlarmManager.ELAPSED_REALTIME : AlarmManager.ELAPSED_REALTIME_WAKEUP;
+ updateAlarmWithListenerLocked(DELAY_TAG, alarmType,
+ mNextDelayExpiredListener, mNextDelayExpiredElapsedMillis, ws);
}
/**
@@ -377,16 +399,16 @@
return;
}
mNextJobExpiredElapsedMillis = alarmTimeElapsedMillis;
- updateAlarmWithListenerLocked(DEADLINE_TAG, mDeadlineExpiredListener,
- mNextJobExpiredElapsedMillis, ws);
+ updateAlarmWithListenerLocked(DEADLINE_TAG, AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ mDeadlineExpiredListener, mNextJobExpiredElapsedMillis, ws);
}
private long maybeAdjustAlarmTime(long proposedAlarmTimeElapsedMillis) {
return Math.max(proposedAlarmTimeElapsedMillis, sElapsedRealtimeClock.millis());
}
- private void updateAlarmWithListenerLocked(String tag, OnAlarmListener listener,
- long alarmTimeElapsed, WorkSource ws) {
+ private void updateAlarmWithListenerLocked(String tag, @AlarmManager.AlarmType int alarmType,
+ OnAlarmListener listener, long alarmTimeElapsed, WorkSource ws) {
ensureAlarmServiceLocked();
if (alarmTimeElapsed == Long.MAX_VALUE) {
mAlarmService.cancel(listener);
@@ -394,7 +416,7 @@
if (DEBUG) {
Slog.d(TAG, "Setting " + tag + " for: " + alarmTimeElapsed);
}
- mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTimeElapsed,
+ mAlarmService.set(alarmType, alarmTimeElapsed,
AlarmManager.WINDOW_HEURISTIC, 0, tag, listener, null, ws);
}
}
@@ -422,9 +444,77 @@
};
@VisibleForTesting
- void recheckAlarmsLocked() {
- checkExpiredDeadlinesAndResetAlarm();
- checkExpiredDelaysAndResetAlarm();
+ class TcConstants extends ContentObserver {
+ private ContentResolver mResolver;
+ private final KeyValueListParser mParser = new KeyValueListParser(',');
+
+ private static final String KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY =
+ "use_non_wakeup_delay_alarm";
+
+ private static final boolean DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY = true;
+
+ /**
+ * Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
+ * ready now.
+ */
+ public boolean USE_NON_WAKEUP_ALARM_FOR_DELAY = DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY;
+
+ /**
+ * Creates a content observer.
+ *
+ * @param handler The handler to run {@link #onChange} on, or null if none.
+ */
+ TcConstants(Handler handler) {
+ super(handler);
+ }
+
+ private void start(ContentResolver resolver) {
+ mResolver = resolver;
+ mResolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS), false, this);
+ onChange(true, null);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ final String constants = Settings.Global.getString(
+ mResolver, Settings.Global.JOB_SCHEDULER_TIME_CONTROLLER_CONSTANTS);
+
+ try {
+ mParser.setString(constants);
+ } catch (Exception e) {
+ // Failed to parse the settings string, log this and move on with defaults.
+ Slog.e(TAG, "Bad jobscheduler time controller settings", e);
+ }
+
+ USE_NON_WAKEUP_ALARM_FOR_DELAY = mParser.getBoolean(
+ KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY, DEFAULT_USE_NON_WAKEUP_ALARM_FOR_DELAY);
+ // Intentionally not calling checkExpiredDelaysAndResetAlarm() here. There's no need to
+ // iterate through the entire list again for this constant change. The next delay alarm
+ // that is set will make use of the new constant value.
+ }
+
+ private void dump(IndentingPrintWriter pw) {
+ pw.println();
+ pw.println("TimeController:");
+ pw.increaseIndent();
+ pw.printPair(KEY_USE_NON_WAKEUP_ALARM_FOR_DELAY,
+ USE_NON_WAKEUP_ALARM_FOR_DELAY).println();
+ pw.decreaseIndent();
+ }
+
+ private void dump(ProtoOutputStream proto) {
+ final long tcToken = proto.start(ConstantsProto.TIME_CONTROLLER);
+ proto.write(ConstantsProto.TimeController.USE_NON_WAKEUP_ALARM_FOR_DELAY,
+ USE_NON_WAKEUP_ALARM_FOR_DELAY);
+ proto.end(tcToken);
+ }
+ }
+
+ @VisibleForTesting
+ @NonNull
+ TcConstants getTcConstants() {
+ return mTcConstants;
}
@Override
@@ -501,4 +591,14 @@
proto.end(mToken);
proto.end(token);
}
+
+ @Override
+ public void dumpConstants(IndentingPrintWriter pw) {
+ mTcConstants.dump(pw);
+ }
+
+ @Override
+ public void dumpConstants(ProtoOutputStream proto) {
+ mTcConstants.dump(proto);
+ }
}
diff --git a/api/current.txt b/api/current.txt
index 9473a84..a16b24e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6727,6 +6727,7 @@
method @Nullable public java.util.List<android.os.PersistableBundle> getTrustAgentConfiguration(@Nullable android.content.ComponentName, @NonNull android.content.ComponentName);
method @NonNull public android.os.Bundle getUserRestrictions(@NonNull android.content.ComponentName);
method @Nullable public String getWifiMacAddress(@NonNull android.content.ComponentName);
+ method public boolean grantKeyPairToApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
method public boolean hasCaCertInstalled(@Nullable android.content.ComponentName, byte[]);
method public boolean hasGrantedPolicy(@NonNull android.content.ComponentName, int);
method public boolean installCaCert(@Nullable android.content.ComponentName, byte[]);
@@ -6773,6 +6774,7 @@
method @Nullable public java.util.List<android.app.admin.NetworkEvent> retrieveNetworkLogs(@Nullable android.content.ComponentName, long);
method @Nullable public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrievePreRebootSecurityLogs(@NonNull android.content.ComponentName);
method @Nullable public java.util.List<android.app.admin.SecurityLog.SecurityEvent> retrieveSecurityLogs(@NonNull android.content.ComponentName);
+ method public boolean revokeKeyPairFromApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String);
method public void setAccountManagementDisabled(@NonNull android.content.ComponentName, String, boolean);
method public void setAffiliationIds(@NonNull android.content.ComponentName, @NonNull java.util.Set<java.lang.String>);
method public void setAlwaysOnVpnPackage(@NonNull android.content.ComponentName, @Nullable String, boolean) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -6796,7 +6798,6 @@
method @WorkerThread public int setGlobalPrivateDnsModeSpecifiedHost(@NonNull android.content.ComponentName, @NonNull String);
method public void setGlobalSetting(@NonNull android.content.ComponentName, String, String);
method public void setKeepUninstalledPackages(@Nullable android.content.ComponentName, @NonNull java.util.List<java.lang.String>);
- method public boolean setKeyGrantForApp(@Nullable android.content.ComponentName, @NonNull String, @NonNull String, boolean);
method public boolean setKeyPairCertificate(@Nullable android.content.ComponentName, @NonNull String, @NonNull java.util.List<java.security.cert.Certificate>, boolean);
method public boolean setKeyguardDisabled(@NonNull android.content.ComponentName, boolean);
method public void setKeyguardDisabledFeatures(@NonNull android.content.ComponentName, int);
@@ -13459,6 +13460,7 @@
public final class GestureLibraries {
method public static android.gesture.GestureLibrary fromFile(String);
method public static android.gesture.GestureLibrary fromFile(java.io.File);
+ method @NonNull public static android.gesture.GestureLibrary fromFileDescriptor(@NonNull android.os.ParcelFileDescriptor);
method public static android.gesture.GestureLibrary fromPrivateFile(android.content.Context, String);
method public static android.gesture.GestureLibrary fromRawResource(android.content.Context, @RawRes int);
}
@@ -18417,8 +18419,12 @@
field public static final int EARLY_DYNASTIC_CUNEIFORM_ID = 257; // 0x101
field public static final android.icu.lang.UCharacter.UnicodeBlock EGYPTIAN_HIEROGLYPHS;
field public static final int EGYPTIAN_HIEROGLYPHS_ID = 194; // 0xc2
+ field public static final android.icu.lang.UCharacter.UnicodeBlock EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS;
+ field public static final int EGYPTIAN_HIEROGLYPH_FORMAT_CONTROLS_ID = 292; // 0x124
field public static final android.icu.lang.UCharacter.UnicodeBlock ELBASAN;
field public static final int ELBASAN_ID = 226; // 0xe2
+ field public static final android.icu.lang.UCharacter.UnicodeBlock ELYMAIC;
+ field public static final int ELYMAIC_ID = 293; // 0x125
field public static final android.icu.lang.UCharacter.UnicodeBlock EMOTICONS;
field public static final int EMOTICONS_ID = 206; // 0xce
field public static final android.icu.lang.UCharacter.UnicodeBlock ENCLOSED_ALPHANUMERICS;
@@ -18645,6 +18651,8 @@
field public static final int MYANMAR_ID = 28; // 0x1c
field public static final android.icu.lang.UCharacter.UnicodeBlock NABATAEAN;
field public static final int NABATAEAN_ID = 239; // 0xef
+ field public static final android.icu.lang.UCharacter.UnicodeBlock NANDINAGARI;
+ field public static final int NANDINAGARI_ID = 294; // 0x126
field public static final android.icu.lang.UCharacter.UnicodeBlock NEWA;
field public static final int NEWA_ID = 270; // 0x10e
field public static final android.icu.lang.UCharacter.UnicodeBlock NEW_TAI_LUE;
@@ -18656,6 +18664,8 @@
field public static final int NUMBER_FORMS_ID = 45; // 0x2d
field public static final android.icu.lang.UCharacter.UnicodeBlock NUSHU;
field public static final int NUSHU_ID = 277; // 0x115
+ field public static final android.icu.lang.UCharacter.UnicodeBlock NYIAKENG_PUACHUE_HMONG;
+ field public static final int NYIAKENG_PUACHUE_HMONG_ID = 295; // 0x127
field public static final android.icu.lang.UCharacter.UnicodeBlock OGHAM;
field public static final int OGHAM_ID = 34; // 0x22
field public static final android.icu.lang.UCharacter.UnicodeBlock OLD_HUNGARIAN;
@@ -18686,6 +18696,8 @@
field public static final int OSAGE_ID = 271; // 0x10f
field public static final android.icu.lang.UCharacter.UnicodeBlock OSMANYA;
field public static final int OSMANYA_ID = 122; // 0x7a
+ field public static final android.icu.lang.UCharacter.UnicodeBlock OTTOMAN_SIYAQ_NUMBERS;
+ field public static final int OTTOMAN_SIYAQ_NUMBERS_ID = 296; // 0x128
field public static final android.icu.lang.UCharacter.UnicodeBlock PAHAWH_HMONG;
field public static final int PAHAWH_HMONG_ID = 243; // 0xf3
field public static final android.icu.lang.UCharacter.UnicodeBlock PALMYRENE;
@@ -18734,6 +18746,8 @@
field public static final int SINHALA_ID = 24; // 0x18
field public static final android.icu.lang.UCharacter.UnicodeBlock SMALL_FORM_VARIANTS;
field public static final int SMALL_FORM_VARIANTS_ID = 84; // 0x54
+ field public static final android.icu.lang.UCharacter.UnicodeBlock SMALL_KANA_EXTENSION;
+ field public static final int SMALL_KANA_EXTENSION_ID = 297; // 0x129
field public static final android.icu.lang.UCharacter.UnicodeBlock SOGDIAN;
field public static final int SOGDIAN_ID = 291; // 0x123
field public static final android.icu.lang.UCharacter.UnicodeBlock SORA_SOMPENG;
@@ -18770,6 +18784,8 @@
field public static final int SUTTON_SIGNWRITING_ID = 262; // 0x106
field public static final android.icu.lang.UCharacter.UnicodeBlock SYLOTI_NAGRI;
field public static final int SYLOTI_NAGRI_ID = 143; // 0x8f
+ field public static final android.icu.lang.UCharacter.UnicodeBlock SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A;
+ field public static final int SYMBOLS_AND_PICTOGRAPHS_EXTENDED_A_ID = 298; // 0x12a
field public static final android.icu.lang.UCharacter.UnicodeBlock SYRIAC;
field public static final int SYRIAC_ID = 13; // 0xd
field public static final android.icu.lang.UCharacter.UnicodeBlock SYRIAC_SUPPLEMENT;
@@ -18792,6 +18808,8 @@
field public static final int TAKRI_ID = 220; // 0xdc
field public static final android.icu.lang.UCharacter.UnicodeBlock TAMIL;
field public static final int TAMIL_ID = 20; // 0x14
+ field public static final android.icu.lang.UCharacter.UnicodeBlock TAMIL_SUPPLEMENT;
+ field public static final int TAMIL_SUPPLEMENT_ID = 299; // 0x12b
field public static final android.icu.lang.UCharacter.UnicodeBlock TANGUT;
field public static final android.icu.lang.UCharacter.UnicodeBlock TANGUT_COMPONENTS;
field public static final int TANGUT_COMPONENTS_ID = 273; // 0x111
@@ -18826,6 +18844,8 @@
field public static final int VEDIC_EXTENSIONS_ID = 175; // 0xaf
field public static final android.icu.lang.UCharacter.UnicodeBlock VERTICAL_FORMS;
field public static final int VERTICAL_FORMS_ID = 145; // 0x91
+ field public static final android.icu.lang.UCharacter.UnicodeBlock WANCHO;
+ field public static final int WANCHO_ID = 300; // 0x12c
field public static final android.icu.lang.UCharacter.UnicodeBlock WARANG_CITI;
field public static final int WARANG_CITI_ID = 252; // 0xfc
field public static final android.icu.lang.UCharacter.UnicodeBlock YIJING_HEXAGRAM_SYMBOLS;
@@ -19138,6 +19158,7 @@
field public static final int EASTERN_SYRIAC = 97; // 0x61
field public static final int EGYPTIAN_HIEROGLYPHS = 71; // 0x47
field public static final int ELBASAN = 136; // 0x88
+ field public static final int ELYMAIC = 185; // 0xb9
field public static final int ESTRANGELO_SYRIAC = 95; // 0x5f
field public static final int ETHIOPIC = 11; // 0xb
field public static final int GEORGIAN = 12; // 0xc
@@ -19217,10 +19238,12 @@
field public static final int MYANMAR = 28; // 0x1c
field public static final int NABATAEAN = 143; // 0x8f
field public static final int NAKHI_GEBA = 132; // 0x84
+ field public static final int NANDINAGARI = 187; // 0xbb
field public static final int NEWA = 170; // 0xaa
field public static final int NEW_TAI_LUE = 59; // 0x3b
field public static final int NKO = 87; // 0x57
field public static final int NUSHU = 150; // 0x96
+ field public static final int NYIAKENG_PUACHUE_HMONG = 186; // 0xba
field public static final int OGHAM = 29; // 0x1d
field public static final int OLD_CHURCH_SLAVONIC_CYRILLIC = 68; // 0x44
field public static final int OLD_HUNGARIAN = 76; // 0x4c
@@ -19284,6 +19307,7 @@
field public static final int UNWRITTEN_LANGUAGES = 102; // 0x66
field public static final int VAI = 99; // 0x63
field public static final int VISIBLE_SPEECH = 100; // 0x64
+ field public static final int WANCHO = 188; // 0xbc
field public static final int WARANG_CITI = 146; // 0x92
field public static final int WESTERN_SYRIAC = 96; // 0x60
field public static final int WOLEAI = 155; // 0x9b
@@ -20072,6 +20096,7 @@
method public String getDateTimeFormat();
method public String getDecimal();
method public static android.icu.text.DateTimePatternGenerator getEmptyInstance();
+ method public String getFieldDisplayName(int, android.icu.text.DateTimePatternGenerator.DisplayWidth);
method public static android.icu.text.DateTimePatternGenerator getInstance();
method public static android.icu.text.DateTimePatternGenerator getInstance(android.icu.util.ULocale);
method public static android.icu.text.DateTimePatternGenerator getInstance(java.util.Locale);
@@ -20105,6 +20130,12 @@
field public static final int ZONE = 15; // 0xf
}
+ public enum DateTimePatternGenerator.DisplayWidth {
+ enum_constant public static final android.icu.text.DateTimePatternGenerator.DisplayWidth ABBREVIATED;
+ enum_constant public static final android.icu.text.DateTimePatternGenerator.DisplayWidth NARROW;
+ enum_constant public static final android.icu.text.DateTimePatternGenerator.DisplayWidth WIDE;
+ }
+
public static final class DateTimePatternGenerator.PatternInfo {
ctor public DateTimePatternGenerator.PatternInfo();
field public static final int BASE_CONFLICT = 1; // 0x1
@@ -21659,6 +21690,7 @@
method public static boolean isAvailable(String, java.util.Date, java.util.Date);
method public java.util.Currency toJavaCurrency();
field public static final int LONG_NAME = 1; // 0x1
+ field public static final int NARROW_SYMBOL_NAME = 3; // 0x3
field public static final int PLURAL_LONG_NAME = 2; // 0x2
field public static final int SYMBOL_NAME = 0; // 0x0
}
@@ -21861,6 +21893,7 @@
ctor public JapaneseCalendar(int, int, int, int, int, int);
field public static final int HEISEI;
field public static final int MEIJI;
+ field public static final int REIWA;
field public static final int SHOWA;
field public static final int TAISHO;
}
@@ -22336,6 +22369,8 @@
field public static final android.icu.util.VersionInfo UCOL_RUNTIME_VERSION;
field public static final android.icu.util.VersionInfo UNICODE_10_0;
field public static final android.icu.util.VersionInfo UNICODE_11_0;
+ field public static final android.icu.util.VersionInfo UNICODE_12_0;
+ field public static final android.icu.util.VersionInfo UNICODE_12_1;
field public static final android.icu.util.VersionInfo UNICODE_1_0;
field public static final android.icu.util.VersionInfo UNICODE_1_0_1;
field public static final android.icu.util.VersionInfo UNICODE_1_1_0;
@@ -42206,6 +42241,7 @@
method public int speak(CharSequence, int, android.os.Bundle, String);
method @Deprecated public int speak(String, int, java.util.HashMap<java.lang.String,java.lang.String>);
method public int stop();
+ method public int synthesizeToFile(@NonNull CharSequence, @NonNull android.os.Bundle, @NonNull android.os.ParcelFileDescriptor, @NonNull String);
method public int synthesizeToFile(CharSequence, android.os.Bundle, java.io.File, String);
method @Deprecated public int synthesizeToFile(String, java.util.HashMap<java.lang.String,java.lang.String>, String);
field public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED = "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
diff --git a/api/test-current.txt b/api/test-current.txt
index e9615111..1b2c59d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -619,10 +619,13 @@
method public int describeContents();
method public static android.content.AutofillOptions forWhitelistingItself();
method public boolean isAugmentedAutofillEnabled(@NonNull android.content.Context);
+ method public boolean isAutofillDisabledLocked(@NonNull android.content.ComponentName);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.AutofillOptions> CREATOR;
+ field public long appDisabledExpiration;
field public boolean augmentedAutofillEnabled;
field public final boolean compatModeEnabled;
+ field @Nullable public android.util.ArrayMap<java.lang.String,java.lang.Long> disabledActivities;
field public final int loggingLevel;
field @Nullable public android.util.ArraySet<android.content.ComponentName> whitelistedActivitiesForAugmentedAutofill;
}
diff --git a/cmds/idmap2/tests/FileUtilsTests.cpp b/cmds/idmap2/tests/FileUtilsTests.cpp
index f4a306e..f55acee 100644
--- a/cmds/idmap2/tests/FileUtilsTests.cpp
+++ b/cmds/idmap2/tests/FileUtilsTests.cpp
@@ -15,6 +15,7 @@
*/
#include <dirent.h>
+#include <fcntl.h>
#include <set>
#include <string>
@@ -69,7 +70,7 @@
TEST(FileUtilsTests, ReadFile) {
int pipefd[2];
- ASSERT_EQ(pipe(pipefd), 0);
+ ASSERT_EQ(pipe2(pipefd, O_CLOEXEC), 0);
ASSERT_EQ(write(pipefd[1], "foobar", 6), 6);
close(pipefd[1]);
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index eb53b7c..9f48f8a 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1174,214 +1174,11 @@
Lcom/android/internal/statusbar/IStatusBarService$Stub;-><init>()V
Lcom/android/internal/statusbar/IStatusBarService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/statusbar/IStatusBarService;
Lcom/android/internal/telecom/ITelecomService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telecom/ITelecomService;
-Lcom/android/internal/telephony/Call$State;->ALERTING:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->DIALING:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->DISCONNECTED:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->DISCONNECTING:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->HOLDING:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->IDLE:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->INCOMING:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->values()[Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call$State;->WAITING:Lcom/android/internal/telephony/Call$State;
-Lcom/android/internal/telephony/Call;-><init>()V
-Lcom/android/internal/telephony/CallerInfoAsyncQuery$CallerInfoAsyncQueryHandler;-><init>(Lcom/android/internal/telephony/CallerInfoAsyncQuery;Landroid/content/Context;)V
-Lcom/android/internal/telephony/CallerInfoAsyncQuery$CookieWrapper;-><init>()V
-Lcom/android/internal/telephony/CallerInfoAsyncQuery;->release()V
-Lcom/android/internal/telephony/CallForwardInfo;-><init>()V
-Lcom/android/internal/telephony/CallTracker;-><init>()V
-Lcom/android/internal/telephony/cat/AppInterface$CommandType;->values()[Lcom/android/internal/telephony/cat/AppInterface$CommandType;
-Lcom/android/internal/telephony/cat/ResponseData;-><init>()V
-Lcom/android/internal/telephony/cat/ResultCode;->values()[Lcom/android/internal/telephony/cat/ResultCode;
-Lcom/android/internal/telephony/cat/RilMessageDecoder;->mCurrentRilMessage:Lcom/android/internal/telephony/cat/RilMessage;
-Lcom/android/internal/telephony/cat/RilMessageDecoder;->sendCmdForExecution(Lcom/android/internal/telephony/cat/RilMessage;)V
-Lcom/android/internal/telephony/cat/RilMessageDecoder;->sendStartDecodingMessageParams(Lcom/android/internal/telephony/cat/RilMessage;)V
-Lcom/android/internal/telephony/cat/ValueObject;-><init>()V
-Lcom/android/internal/telephony/cat/ValueParser;->retrieveDeviceIdentities(Lcom/android/internal/telephony/cat/ComprehensionTlv;)Lcom/android/internal/telephony/cat/DeviceIdentities;
-Lcom/android/internal/telephony/cdma/sms/BearerData$CodingException;-><init>(Ljava/lang/String;)V
-Lcom/android/internal/telephony/cdma/sms/BearerData$TimeStamp;-><init>()V
-Lcom/android/internal/telephony/cdma/sms/BearerData;-><init>()V
-Lcom/android/internal/telephony/cdma/sms/BearerData;->countAsciiSeptets(Ljava/lang/CharSequence;Z)I
-Lcom/android/internal/telephony/cdma/sms/BearerData;->decodeUserDataPayload(Lcom/android/internal/telephony/cdma/sms/UserData;Z)V
-Lcom/android/internal/telephony/cdma/sms/BearerData;->displayMode:I
-Lcom/android/internal/telephony/cdma/sms/BearerData;->encode(Lcom/android/internal/telephony/cdma/sms/BearerData;)[B
-Lcom/android/internal/telephony/cdma/sms/BearerData;->encode7bitAscii(Ljava/lang/String;Z)[B
-Lcom/android/internal/telephony/cdma/sms/BearerData;->getBitsForNumFields(II)I
-Lcom/android/internal/telephony/cdma/sms/BearerData;->hasUserDataHeader:Z
-Lcom/android/internal/telephony/cdma/sms/BearerData;->messageId:I
-Lcom/android/internal/telephony/cdma/sms/BearerData;->msgCenterTimeStamp:Lcom/android/internal/telephony/cdma/sms/BearerData$TimeStamp;
-Lcom/android/internal/telephony/cdma/sms/BearerData;->priority:I
-Lcom/android/internal/telephony/cdma/sms/BearerData;->priorityIndicatorSet:Z
-Lcom/android/internal/telephony/cdma/sms/BearerData;->userData:Lcom/android/internal/telephony/cdma/sms/UserData;
-Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;-><init>()V
-Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->digitMode:I
-Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->numberMode:I
-Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->numberOfDigits:I
-Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->numberPlan:I
-Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;->parse(Ljava/lang/String;)Lcom/android/internal/telephony/cdma/sms/CdmaSmsAddress;
-Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;-><init>()V
-Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;->bearerData:[B
-Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;->serviceCategory:I
-Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;->teleService:I
-Lcom/android/internal/telephony/cdma/sms/UserData;-><init>()V
-Lcom/android/internal/telephony/cdma/sms/UserData;->charToAscii:Landroid/util/SparseIntArray;
-Lcom/android/internal/telephony/cdma/sms/UserData;->msgEncoding:I
-Lcom/android/internal/telephony/cdma/sms/UserData;->msgEncodingSet:Z
-Lcom/android/internal/telephony/cdma/sms/UserData;->numFields:I
-Lcom/android/internal/telephony/cdma/sms/UserData;->payload:[B
-Lcom/android/internal/telephony/cdma/sms/UserData;->payloadStr:Ljava/lang/String;
-Lcom/android/internal/telephony/cdma/sms/UserData;->userDataHeader:Lcom/android/internal/telephony/SmsHeader;
-Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;-><init>()V
-Lcom/android/internal/telephony/cdma/SmsMessage;-><init>()V
-Lcom/android/internal/telephony/cdma/SmsMessage;->calculateLength(Ljava/lang/CharSequence;ZZ)Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;
-Lcom/android/internal/telephony/cdma/SmsMessage;->createFromEfRecord(I[B)Lcom/android/internal/telephony/cdma/SmsMessage;
-Lcom/android/internal/telephony/cdma/SmsMessage;->createFromPdu([B)Lcom/android/internal/telephony/cdma/SmsMessage;
-Lcom/android/internal/telephony/cdma/SmsMessage;->getIncomingSmsFingerprint()[B
-Lcom/android/internal/telephony/cdma/SmsMessage;->getMessageType()I
-Lcom/android/internal/telephony/cdma/SmsMessage;->getNextMessageId()I
-Lcom/android/internal/telephony/cdma/SmsMessage;->getNumOfVoicemails()I
-Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Lcom/android/internal/telephony/cdma/sms/UserData;Z)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Lcom/android/internal/telephony/cdma/sms/UserData;ZI)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;I[BZ)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLcom/android/internal/telephony/SmsHeader;)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/cdma/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLcom/android/internal/telephony/SmsHeader;I)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/cdma/SmsMessage;->getTeleService()I
-Lcom/android/internal/telephony/cdma/SmsMessage;->isStatusReportMessage()Z
-Lcom/android/internal/telephony/cdma/SmsMessage;->mBearerData:Lcom/android/internal/telephony/cdma/sms/BearerData;
-Lcom/android/internal/telephony/cdma/SmsMessage;->mEnvelope:Lcom/android/internal/telephony/cdma/sms/SmsEnvelope;
-Lcom/android/internal/telephony/cdma/SmsMessage;->parseSms()V
-Lcom/android/internal/telephony/cdma/SmsMessage;->privateGetSubmitPdu(Ljava/lang/String;ZLcom/android/internal/telephony/cdma/sms/UserData;)Lcom/android/internal/telephony/cdma/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/DctConstants$Activity;->DATAIN:Lcom/android/internal/telephony/DctConstants$Activity;
-Lcom/android/internal/telephony/DctConstants$Activity;->DATAINANDOUT:Lcom/android/internal/telephony/DctConstants$Activity;
-Lcom/android/internal/telephony/DctConstants$Activity;->DATAOUT:Lcom/android/internal/telephony/DctConstants$Activity;
-Lcom/android/internal/telephony/DctConstants$Activity;->DORMANT:Lcom/android/internal/telephony/DctConstants$Activity;
-Lcom/android/internal/telephony/DctConstants$Activity;->values()[Lcom/android/internal/telephony/DctConstants$Activity;
-Lcom/android/internal/telephony/DctConstants$State;->CONNECTED:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DctConstants$State;->CONNECTING:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DctConstants$State;->DISCONNECTING:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DctConstants$State;->FAILED:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DctConstants$State;->IDLE:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DctConstants$State;->RETRYING:Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DctConstants$State;->values()[Lcom/android/internal/telephony/DctConstants$State;
-Lcom/android/internal/telephony/DriverCall$State;->values()[Lcom/android/internal/telephony/DriverCall$State;
-Lcom/android/internal/telephony/gsm/GsmCellBroadcastHandler$SmsCbConcatInfo;-><init>(Lcom/android/internal/telephony/gsm/SmsCbHeader;Landroid/telephony/SmsCbLocation;)V
-Lcom/android/internal/telephony/gsm/GsmCellBroadcastHandler$SmsCbConcatInfo;->matchesLocation(Ljava/lang/String;II)Z
-Lcom/android/internal/telephony/gsm/GsmCellBroadcastHandler;->mSmsCbPageMap:Ljava/util/HashMap;
-Lcom/android/internal/telephony/gsm/GsmInboundSmsHandler;->acknowledgeLastIncomingSms(ZILandroid/os/Message;)V
-Lcom/android/internal/telephony/gsm/GsmMmiCode;-><init>(Lcom/android/internal/telephony/GsmCdmaPhone;Lcom/android/internal/telephony/uicc/UiccCardApplication;)V
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->getCLIRMode()I
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->getScString()Ljava/lang/CharSequence;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isActivate()Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isDeactivate()Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isErasure()Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isInterrogate()Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isRegister()Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isServiceCodeCallBarring(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isServiceCodeCallForwarding(Ljava/lang/String;)Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->isTemporaryModeCLIR()Z
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->makeEmptyNull(Ljava/lang/String;)Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mContext:Landroid/content/Context;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mDialingNumber:Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mIccRecords:Lcom/android/internal/telephony/uicc/IccRecords;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mPhone:Lcom/android/internal/telephony/GsmCdmaPhone;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mSc:Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mSia:Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mSib:Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->mSic:Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->newFromDialString(Ljava/lang/String;Lcom/android/internal/telephony/GsmCdmaPhone;Lcom/android/internal/telephony/uicc/UiccCardApplication;)Lcom/android/internal/telephony/gsm/GsmMmiCode;
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->processCode()V
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->siToServiceClass(Ljava/lang/String;)I
-Lcom/android/internal/telephony/gsm/GsmMmiCode;->sPatternSuppService:Ljava/util/regex/Pattern;
-Lcom/android/internal/telephony/gsm/GsmSmsAddress;-><init>([BII)V
-Lcom/android/internal/telephony/gsm/GsmSmsAddress;->isCphsVoiceMessageClear()Z
-Lcom/android/internal/telephony/gsm/GsmSmsAddress;->isCphsVoiceMessageSet()Z
-Lcom/android/internal/telephony/gsm/GsmSMSDispatcher;->getFormat()Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/GsmSMSDispatcher;->mGsmInboundSmsHandler:Lcom/android/internal/telephony/gsm/GsmInboundSmsHandler;
-Lcom/android/internal/telephony/gsm/GsmSMSDispatcher;->sendSms(Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
-Lcom/android/internal/telephony/gsm/SimTlv;-><init>([BII)V
-Lcom/android/internal/telephony/gsm/SimTlv;->getData()[B
-Lcom/android/internal/telephony/gsm/SimTlv;->getTag()I
-Lcom/android/internal/telephony/gsm/SimTlv;->isValidObject()Z
-Lcom/android/internal/telephony/gsm/SimTlv;->mHasValidTlvObject:Z
-Lcom/android/internal/telephony/gsm/SimTlv;->nextObject()Z
-Lcom/android/internal/telephony/gsm/SmsCbHeader;-><init>([B)V
-Lcom/android/internal/telephony/gsm/SmsCbHeader;->getGeographicalScope()I
-Lcom/android/internal/telephony/gsm/SmsCbHeader;->getNumberOfPages()I
-Lcom/android/internal/telephony/gsm/SmsCbHeader;->getPageIndex()I
-Lcom/android/internal/telephony/gsm/SmsCbHeader;->getSerialNumber()I
-Lcom/android/internal/telephony/gsm/SmsCbHeader;->getServiceCategory()I
-Lcom/android/internal/telephony/gsm/SmsCbHeader;->mMessageIdentifier:I
-Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;-><init>([B)V
-Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->getByte()I
-Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->getUserData()[B
-Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->getUserDataUCS2(I)Ljava/lang/String;
-Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->mCur:I
-Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->mPdu:[B
-Lcom/android/internal/telephony/gsm/SmsMessage$PduParser;->mUserDataSeptetPadding:I
-Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;-><init>()V
-Lcom/android/internal/telephony/gsm/SmsMessage;-><init>()V
-Lcom/android/internal/telephony/gsm/SmsMessage;->calculateLength(Ljava/lang/CharSequence;Z)Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;
-Lcom/android/internal/telephony/gsm/SmsMessage;->createFromEfRecord(I[B)Lcom/android/internal/telephony/gsm/SmsMessage;
-Lcom/android/internal/telephony/gsm/SmsMessage;->createFromPdu([B)Lcom/android/internal/telephony/gsm/SmsMessage;
-Lcom/android/internal/telephony/gsm/SmsMessage;->encodeUCS2(Ljava/lang/String;[B)[B
-Lcom/android/internal/telephony/gsm/SmsMessage;->getStatus()I
-Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZI)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z[B)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z[BIII)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPdu(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z[BIIII)Lcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;
-Lcom/android/internal/telephony/gsm/SmsMessage;->getSubmitPduHead(Ljava/lang/String;Ljava/lang/String;BZLcom/android/internal/telephony/gsm/SmsMessage$SubmitPdu;)Ljava/io/ByteArrayOutputStream;
-Lcom/android/internal/telephony/gsm/SmsMessage;->isMWIClearMessage()Z
-Lcom/android/internal/telephony/gsm/SmsMessage;->isMwiDontStore()Z
-Lcom/android/internal/telephony/gsm/SmsMessage;->isMWISetMessage()Z
-Lcom/android/internal/telephony/gsm/SmsMessage;->isStatusReportMessage()Z
-Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->loadEfFilesFromUsim()Ljava/util/ArrayList;
-Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->mFh:Lcom/android/internal/telephony/uicc/IccFileHandler;
-Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->mLock:Ljava/lang/Object;
-Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->mPhoneBookRecords:Ljava/util/ArrayList;
-Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->reset()V
-Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;-><init>()V
-Lcom/android/internal/telephony/GsmCdmaConnection$MyHandler;-><init>(Lcom/android/internal/telephony/GsmCdmaConnection;Landroid/os/Looper;)V
-Lcom/android/internal/telephony/IccCardConstants$State;->ABSENT:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->CARD_IO_ERROR:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->NETWORK_LOCKED:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->NOT_READY:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->PERM_DISABLED:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->PIN_REQUIRED:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->PUK_REQUIRED:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->READY:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->UNKNOWN:Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccCardConstants$State;->values()[Lcom/android/internal/telephony/IccCardConstants$State;
-Lcom/android/internal/telephony/IccProvider;-><init>()V
Lcom/android/internal/telephony/IIccPhoneBook$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Lcom/android/internal/telephony/IIccPhoneBook$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IIccPhoneBook;
-Lcom/android/internal/telephony/IIccPhoneBook;->getAdnRecordsInEf(I)Ljava/util/List;
-Lcom/android/internal/telephony/IIccPhoneBook;->getAdnRecordsInEfForSubscriber(II)Ljava/util/List;
-Lcom/android/internal/telephony/IIccPhoneBook;->getAdnRecordsSize(I)[I
-Lcom/android/internal/telephony/IIccPhoneBook;->getAdnRecordsSizeForSubscriber(II)[I
-Lcom/android/internal/telephony/IIccPhoneBook;->updateAdnRecordsInEfBySearch(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
Lcom/android/internal/telephony/IMms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IMms;
-Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker$ExternalCallStateListener;-><init>(Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker;)V
-Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker$ExternalConnectionListener;-><init>(Lcom/android/internal/telephony/imsphone/ImsExternalCallTracker;)V
-Lcom/android/internal/telephony/imsphone/ImsPhone;->notifyCallForwardingIndicator()V
-Lcom/android/internal/telephony/imsphone/ImsPhone;->notifyPreciseCallStateChanged()V
-Lcom/android/internal/telephony/imsphone/ImsPhoneCall;->getImsCall()Lcom/android/ims/ImsCall;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->findConnection(Lcom/android/ims/ImsCall;)Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->getEcbmInterface()Lcom/android/ims/ImsEcbm;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mCallExpectedToResume:Lcom/android/ims/ImsCall;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mImsCallListener:Lcom/android/ims/ImsCall$Listener;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mImsManager:Lcom/android/ims/ImsManager;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->mUssdSession:Lcom/android/ims/ImsCall;
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->processCallStateChange(Lcom/android/ims/ImsCall;Lcom/android/internal/telephony/Call$State;I)V
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->processCallStateChange(Lcom/android/ims/ImsCall;Lcom/android/internal/telephony/Call$State;IZ)V
-Lcom/android/internal/telephony/imsphone/ImsPhoneCallTracker;->setVideoCallProvider(Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;Lcom/android/ims/ImsCall;)V
-Lcom/android/internal/telephony/imsphone/ImsPhoneConnection$MyHandler;-><init>(Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;Landroid/os/Looper;)V
-Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->mImsCall:Lcom/android/ims/ImsCall;
-Lcom/android/internal/telephony/imsphone/ImsPhoneConnection;->update(Lcom/android/ims/ImsCall;Lcom/android/internal/telephony/Call$State;)Z
-Lcom/android/internal/telephony/InboundSmsHandler$SmsBroadcastReceiver;-><init>(Lcom/android/internal/telephony/InboundSmsHandler;Lcom/android/internal/telephony/InboundSmsTracker;)V
Lcom/android/internal/telephony/IPhoneStateListener$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneStateListener;
Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String;
Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneSubInfo;
Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->TRANSACTION_getDeviceId:I
Lcom/android/internal/telephony/ISms$Stub;-><init>()V
@@ -1389,8 +1186,6 @@
Lcom/android/internal/telephony/ISub$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Lcom/android/internal/telephony/ISub$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISub;
Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->getDeviceId(Ljava/lang/String;)Ljava/lang/String;
-Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->isRadioOn(Ljava/lang/String;)Z
Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony;
Lcom/android/internal/telephony/ITelephony$Stub;->DESCRIPTOR:Ljava/lang/String;
@@ -1400,82 +1195,6 @@
Lcom/android/internal/telephony/ITelephonyRegistry$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Lcom/android/internal/telephony/ITelephonyRegistry$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephonyRegistry;
Lcom/android/internal/telephony/IWapPushManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IWapPushManager;
-Lcom/android/internal/telephony/PhoneConstants$DataState;->CONNECTED:Lcom/android/internal/telephony/PhoneConstants$DataState;
-Lcom/android/internal/telephony/PhoneConstants$DataState;->CONNECTING:Lcom/android/internal/telephony/PhoneConstants$DataState;
-Lcom/android/internal/telephony/PhoneConstants$DataState;->DISCONNECTED:Lcom/android/internal/telephony/PhoneConstants$DataState;
-Lcom/android/internal/telephony/PhoneConstants$DataState;->SUSPENDED:Lcom/android/internal/telephony/PhoneConstants$DataState;
-Lcom/android/internal/telephony/PhoneConstants$DataState;->values()[Lcom/android/internal/telephony/PhoneConstants$DataState;
-Lcom/android/internal/telephony/PhoneConstants$State;->IDLE:Lcom/android/internal/telephony/PhoneConstants$State;
-Lcom/android/internal/telephony/PhoneConstants$State;->OFFHOOK:Lcom/android/internal/telephony/PhoneConstants$State;
-Lcom/android/internal/telephony/PhoneConstants$State;->RINGING:Lcom/android/internal/telephony/PhoneConstants$State;
-Lcom/android/internal/telephony/PhoneConstants$State;->values()[Lcom/android/internal/telephony/PhoneConstants$State;
-Lcom/android/internal/telephony/PhoneConstants;->PRESENTATION_ALLOWED:I
-Lcom/android/internal/telephony/PhoneConstants;->PRESENTATION_PAYPHONE:I
-Lcom/android/internal/telephony/PhoneConstants;->PRESENTATION_RESTRICTED:I
-Lcom/android/internal/telephony/PhoneConstants;->PRESENTATION_UNKNOWN:I
-Lcom/android/internal/telephony/RILConstants;->PREFERRED_NETWORK_MODE:I
-Lcom/android/internal/telephony/sip/SipPhone$SipCall;->hold()V
-Lcom/android/internal/telephony/sip/SipPhone$SipCall;->switchWith(Lcom/android/internal/telephony/sip/SipPhone$SipCall;)V
-Lcom/android/internal/telephony/sip/SipPhone$SipCall;->unhold()V
-Lcom/android/internal/telephony/sip/SipPhone;->log(Ljava/lang/String;)V
-Lcom/android/internal/telephony/sip/SipPhone;->loge(Ljava/lang/String;)V
-Lcom/android/internal/telephony/sip/SipPhone;->mBackgroundCall:Lcom/android/internal/telephony/sip/SipPhone$SipCall;
-Lcom/android/internal/telephony/sip/SipPhone;->mForegroundCall:Lcom/android/internal/telephony/sip/SipPhone$SipCall;
-Lcom/android/internal/telephony/Sms7BitEncodingTranslator;->DBG:Z
-Lcom/android/internal/telephony/Sms7BitEncodingTranslator;->mTranslationTableCDMA:Landroid/util/SparseIntArray;
-Lcom/android/internal/telephony/Sms7BitEncodingTranslator;->mTranslationTableCommon:Landroid/util/SparseIntArray;
-Lcom/android/internal/telephony/Sms7BitEncodingTranslator;->mTranslationTableGSM:Landroid/util/SparseIntArray;
-Lcom/android/internal/telephony/SmsApplication$SmsApplicationData;->mApplicationName:Ljava/lang/String;
-Lcom/android/internal/telephony/SmsApplication;->configurePreferredActivity(Landroid/content/pm/PackageManager;Landroid/content/ComponentName;I)V
-Lcom/android/internal/telephony/SmsApplication;->getApplicationCollection(Landroid/content/Context;)Ljava/util/Collection;
-Lcom/android/internal/telephony/SmsApplication;->getDefaultMmsApplication(Landroid/content/Context;Z)Landroid/content/ComponentName;
-Lcom/android/internal/telephony/SmsApplication;->getDefaultRespondViaMessageApplication(Landroid/content/Context;Z)Landroid/content/ComponentName;
-Lcom/android/internal/telephony/SmsApplication;->getDefaultSmsApplication(Landroid/content/Context;Z)Landroid/content/ComponentName;
-Lcom/android/internal/telephony/SmsApplication;->getSmsApplicationData(Ljava/lang/String;Landroid/content/Context;)Lcom/android/internal/telephony/SmsApplication$SmsApplicationData;
-Lcom/android/internal/telephony/SmsApplication;->isDefaultSmsApplication(Landroid/content/Context;Ljava/lang/String;)Z
-Lcom/android/internal/telephony/SmsApplication;->setDefaultApplication(Ljava/lang/String;Landroid/content/Context;)V
-Lcom/android/internal/telephony/SmsApplication;->shouldWriteMessageForPackage(Ljava/lang/String;Landroid/content/Context;)Z
-Lcom/android/internal/telephony/SMSDispatcher$DataSmsSender;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
-Lcom/android/internal/telephony/SMSDispatcher$MultipartSmsSender;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Ljava/util/ArrayList;[Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
-Lcom/android/internal/telephony/SMSDispatcher$MultipartSmsSenderCallback;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Lcom/android/internal/telephony/SMSDispatcher$MultipartSmsSender;)V
-Lcom/android/internal/telephony/SMSDispatcher$SmsSenderCallback;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Lcom/android/internal/telephony/SMSDispatcher$SmsSender;)V
-Lcom/android/internal/telephony/SMSDispatcher$TextSmsSender;-><init>(Lcom/android/internal/telephony/SMSDispatcher;Lcom/android/internal/telephony/SMSDispatcher$SmsTracker;)V
-Lcom/android/internal/telephony/SmsHeader$ConcatRef;-><init>()V
-Lcom/android/internal/telephony/SmsHeader$PortAddrs;-><init>()V
-Lcom/android/internal/telephony/SmsMessageBase;-><init>()V
-Lcom/android/internal/telephony/TelephonyProperties;->PROPERTY_ICC_OPERATOR_NUMERIC:Ljava/lang/String;
-Lcom/android/internal/telephony/test/InterpreterEx;-><init>(Ljava/lang/String;)V
-Lcom/android/internal/telephony/test/SimulatedCommands;->acceptCall(Landroid/os/Message;)V
-Lcom/android/internal/telephony/test/SimulatedCommands;->mDcSuccess:Z
-Lcom/android/internal/telephony/test/SimulatedCommands;->resultFail(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)V
-Lcom/android/internal/telephony/test/SimulatedCommands;->resultSuccess(Landroid/os/Message;Ljava/lang/Object;)V
-Lcom/android/internal/telephony/test/SimulatedCommands;->simulatedCallState:Lcom/android/internal/telephony/test/SimulatedGsmCallState;
-Lcom/android/internal/telephony/test/SimulatedCommands;->unimplemented(Landroid/os/Message;)V
-Lcom/android/internal/telephony/test/SimulatedCommandsVerifier;->getInstance()Lcom/android/internal/telephony/test/SimulatedCommandsVerifier;
-Lcom/android/internal/telephony/test/SimulatedCommandsVerifier;->setCallForward(IIILjava/lang/String;ILandroid/os/Message;)V
-Lcom/android/internal/telephony/test/SimulatedGsmCallState;->conference()Z
-Lcom/android/internal/telephony/test/SimulatedGsmCallState;->onChld(CC)Z
-Lcom/android/internal/telephony/test/SimulatedGsmCallState;->releaseActiveAcceptHeldOrWaiting()Z
-Lcom/android/internal/telephony/test/SimulatedGsmCallState;->releaseHeldOrUDUB()Z
-Lcom/android/internal/telephony/test/SimulatedGsmCallState;->separateCall(I)Z
-Lcom/android/internal/telephony/test/SimulatedGsmCallState;->switchActiveAndHeldOrWaiting()Z
-Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;->values()[Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppState;
-Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;->values()[Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$AppType;
-Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;->values()[Lcom/android/internal/telephony/uicc/IccCardApplicationStatus$PersoSubState;
-Lcom/android/internal/telephony/uicc/IccCardApplicationStatus;-><init>()V
-Lcom/android/internal/telephony/uicc/IccRefreshResponse;-><init>()V
-Lcom/android/internal/telephony/uicc/IccUtils;->adnStringFieldToString([BII)Ljava/lang/String;
-Lcom/android/internal/telephony/uicc/IccUtils;->bcdToString([BII)Ljava/lang/String;
-Lcom/android/internal/telephony/uicc/IccUtils;->bytesToHexString([B)Ljava/lang/String;
-Lcom/android/internal/telephony/uicc/IccUtils;->cdmaBcdByteToInt(B)I
-Lcom/android/internal/telephony/uicc/IccUtils;->cdmaBcdToString([BII)Ljava/lang/String;
-Lcom/android/internal/telephony/uicc/IccUtils;->gsmBcdByteToInt(B)I
-Lcom/android/internal/telephony/uicc/IccUtils;->hexCharToInt(C)I
-Lcom/android/internal/telephony/uicc/IccUtils;->hexStringToBytes(Ljava/lang/String;)[B
-Lcom/android/internal/telephony/uicc/IccUtils;->networkNameToString([BII)Ljava/lang/String;
-Lcom/android/internal/telephony/uicc/IccUtils;->parseToBnW([BI)Landroid/graphics/Bitmap;
-Lcom/android/internal/telephony/uicc/IccUtils;->parseToRGB([BIZ)Landroid/graphics/Bitmap;
-Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;->values()[Lcom/android/internal/telephony/uicc/SIMRecords$GetSpnFsmState;
Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Lcom/android/internal/util/MemInfoReader;-><init>()V
Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -1488,283 +1207,5 @@
Lcom/android/server/ResettableTimeout$T;-><init>(Lcom/android/server/ResettableTimeout;)V
Lcom/google/android/gles_jni/EGLImpl;-><init>()V
Lcom/google/android/gles_jni/GLImpl;-><init>()V
-Lcom/google/android/mms/ContentType;->getAudioTypes()Ljava/util/ArrayList;
-Lcom/google/android/mms/ContentType;->getImageTypes()Ljava/util/ArrayList;
-Lcom/google/android/mms/ContentType;->getVideoTypes()Ljava/util/ArrayList;
-Lcom/google/android/mms/ContentType;->isAudioType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isDrmType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isImageType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedAudioType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedImageType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isSupportedVideoType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isTextType(Ljava/lang/String;)Z
-Lcom/google/android/mms/ContentType;->isVideoType(Ljava/lang/String;)Z
-Lcom/google/android/mms/InvalidHeaderValueException;-><init>(Ljava/lang/String;)V
-Lcom/google/android/mms/MmsException;-><init>()V
-Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;)V
-Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/String;Ljava/lang/Throwable;)V
-Lcom/google/android/mms/MmsException;-><init>(Ljava/lang/Throwable;)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(I[B)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;->setReportAllowed(I)V
-Lcom/google/android/mms/pdu/AcknowledgeInd;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/Base64;->decodeBase64([B)[B
-Lcom/google/android/mms/pdu/CharacterSets;->getMibEnumValue(Ljava/lang/String;)I
-Lcom/google/android/mms/pdu/CharacterSets;->getMimeName(I)Ljava/lang/String;
-Lcom/google/android/mms/pdu/DeliveryInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/DeliveryInd;->getDate()J
-Lcom/google/android/mms/pdu/DeliveryInd;->getMessageId()[B
-Lcom/google/android/mms/pdu/DeliveryInd;->getStatus()I
-Lcom/google/android/mms/pdu/DeliveryInd;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(I[B)V
-Lcom/google/android/mms/pdu/EncodedStringValue;-><init>(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/EncodedStringValue;-><init>([B)V
-Lcom/google/android/mms/pdu/EncodedStringValue;->appendTextString([B)V
-Lcom/google/android/mms/pdu/EncodedStringValue;->concat([Lcom/google/android/mms/pdu/EncodedStringValue;)Ljava/lang/String;
-Lcom/google/android/mms/pdu/EncodedStringValue;->copy(Lcom/google/android/mms/pdu/EncodedStringValue;)Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;->encodeStrings([Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;->extract(Ljava/lang/String;)[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/EncodedStringValue;->getCharacterSet()I
-Lcom/google/android/mms/pdu/EncodedStringValue;->getString()Ljava/lang/String;
-Lcom/google/android/mms/pdu/EncodedStringValue;->getTextString()[B
-Lcom/google/android/mms/pdu/EncodedStringValue;->setCharacterSet(I)V
-Lcom/google/android/mms/pdu/EncodedStringValue;->setTextString([B)V
-Lcom/google/android/mms/pdu/GenericPdu;-><init>()V
-Lcom/google/android/mms/pdu/GenericPdu;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/GenericPdu;->getMessageType()I
-Lcom/google/android/mms/pdu/GenericPdu;->getPduHeaders()Lcom/google/android/mms/pdu/PduHeaders;
-Lcom/google/android/mms/pdu/GenericPdu;->mPduHeaders:Lcom/google/android/mms/pdu/PduHeaders;
-Lcom/google/android/mms/pdu/GenericPdu;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/GenericPdu;->setMessageType(I)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;-><init>()V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->addTo(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getBody()Lcom/google/android/mms/pdu/PduBody;
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getDate()J
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getPriority()I
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->getTo()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setBody(Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setDate(J)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setPriority(I)V
-Lcom/google/android/mms/pdu/MultimediaMessagePdu;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/NotificationInd;-><init>()V
-Lcom/google/android/mms/pdu/NotificationInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/NotificationInd;->getContentClass()I
-Lcom/google/android/mms/pdu/NotificationInd;->getContentLocation()[B
-Lcom/google/android/mms/pdu/NotificationInd;->getDeliveryReport()I
-Lcom/google/android/mms/pdu/NotificationInd;->getExpiry()J
-Lcom/google/android/mms/pdu/NotificationInd;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/NotificationInd;->getMessageClass()[B
-Lcom/google/android/mms/pdu/NotificationInd;->getMessageSize()J
-Lcom/google/android/mms/pdu/NotificationInd;->getSubject()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/NotificationInd;->getTransactionId()[B
-Lcom/google/android/mms/pdu/NotificationInd;->setContentClass(I)V
-Lcom/google/android/mms/pdu/NotificationInd;->setContentLocation([B)V
-Lcom/google/android/mms/pdu/NotificationInd;->setDeliveryReport(I)V
-Lcom/google/android/mms/pdu/NotificationInd;->setExpiry(J)V
-Lcom/google/android/mms/pdu/NotificationInd;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/NotificationInd;->setMessageClass([B)V
-Lcom/google/android/mms/pdu/NotificationInd;->setMessageSize(J)V
-Lcom/google/android/mms/pdu/NotificationInd;->setSubject(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/NotificationInd;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(I[BI)V
-Lcom/google/android/mms/pdu/NotifyRespInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/NotifyRespInd;->setReportAllowed(I)V
-Lcom/google/android/mms/pdu/NotifyRespInd;->setStatus(I)V
-Lcom/google/android/mms/pdu/NotifyRespInd;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/PduBody;-><init>()V
-Lcom/google/android/mms/pdu/PduBody;->addPart(ILcom/google/android/mms/pdu/PduPart;)V
-Lcom/google/android/mms/pdu/PduBody;->addPart(Lcom/google/android/mms/pdu/PduPart;)Z
-Lcom/google/android/mms/pdu/PduBody;->getPart(I)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByContentId(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByContentLocation(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByFileName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartByName(Ljava/lang/String;)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduBody;->getPartIndex(Lcom/google/android/mms/pdu/PduPart;)I
-Lcom/google/android/mms/pdu/PduBody;->getPartsNum()I
-Lcom/google/android/mms/pdu/PduBody;->removePart(I)Lcom/google/android/mms/pdu/PduPart;
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->copy()V
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->mark()Lcom/google/android/mms/pdu/PduComposer$PositionMarker;
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->newbuf()V
-Lcom/google/android/mms/pdu/PduComposer$BufferStack;->pop()V
-Lcom/google/android/mms/pdu/PduComposer$PositionMarker;->getLength()I
-Lcom/google/android/mms/pdu/PduComposer;-><init>(Landroid/content/Context;Lcom/google/android/mms/pdu/GenericPdu;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendEncodedString(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendHeader(I)I
-Lcom/google/android/mms/pdu/PduComposer;->appendLongInteger(J)V
-Lcom/google/android/mms/pdu/PduComposer;->appendOctet(I)V
-Lcom/google/android/mms/pdu/PduComposer;->appendQuotedString(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendQuotedString([B)V
-Lcom/google/android/mms/pdu/PduComposer;->appendShortInteger(I)V
-Lcom/google/android/mms/pdu/PduComposer;->appendTextString(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/PduComposer;->appendTextString([B)V
-Lcom/google/android/mms/pdu/PduComposer;->appendUintvarInteger(J)V
-Lcom/google/android/mms/pdu/PduComposer;->appendValueLength(J)V
-Lcom/google/android/mms/pdu/PduComposer;->arraycopy([BII)V
-Lcom/google/android/mms/pdu/PduComposer;->make()[B
-Lcom/google/android/mms/pdu/PduComposer;->mContentTypeMap:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduComposer;->mMessage:Ljava/io/ByteArrayOutputStream;
-Lcom/google/android/mms/pdu/PduComposer;->mPdu:Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/pdu/PduComposer;->mPduHeader:Lcom/google/android/mms/pdu/PduHeaders;
-Lcom/google/android/mms/pdu/PduComposer;->mPosition:I
-Lcom/google/android/mms/pdu/PduComposer;->mResolver:Landroid/content/ContentResolver;
-Lcom/google/android/mms/pdu/PduComposer;->mStack:Lcom/google/android/mms/pdu/PduComposer$BufferStack;
-Lcom/google/android/mms/pdu/PduContentTypes;->contentTypes:[Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduHeaders;-><init>()V
-Lcom/google/android/mms/pdu/PduHeaders;->appendEncodedStringValue(Lcom/google/android/mms/pdu/EncodedStringValue;I)V
-Lcom/google/android/mms/pdu/PduHeaders;->getEncodedStringValue(I)Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/PduHeaders;->getEncodedStringValues(I)[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/PduHeaders;->getLongInteger(I)J
-Lcom/google/android/mms/pdu/PduHeaders;->getOctet(I)I
-Lcom/google/android/mms/pdu/PduHeaders;->getTextString(I)[B
-Lcom/google/android/mms/pdu/PduHeaders;->setEncodedStringValue(Lcom/google/android/mms/pdu/EncodedStringValue;I)V
-Lcom/google/android/mms/pdu/PduHeaders;->setLongInteger(JI)V
-Lcom/google/android/mms/pdu/PduHeaders;->setOctet(II)V
Lcom/google/android/mms/pdu/PduParser;->$assertionsDisabled:Z
-Lcom/google/android/mms/pdu/PduParser;-><init>([BZ)V
-Lcom/google/android/mms/pdu/PduParser;->checkPartPosition(Lcom/google/android/mms/pdu/PduPart;)I
-Lcom/google/android/mms/pdu/PduParser;->log(Ljava/lang/String;)V
-Lcom/google/android/mms/pdu/PduParser;->parse()Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/pdu/PduParser;->parseContentType(Ljava/io/ByteArrayInputStream;Ljava/util/HashMap;)[B
-Lcom/google/android/mms/pdu/PduParser;->parsePartHeaders(Ljava/io/ByteArrayInputStream;Lcom/google/android/mms/pdu/PduPart;I)Z
-Lcom/google/android/mms/pdu/PduParser;->parseShortInteger(Ljava/io/ByteArrayInputStream;)I
-Lcom/google/android/mms/pdu/PduParser;->parseUnsignedInt(Ljava/io/ByteArrayInputStream;)I
-Lcom/google/android/mms/pdu/PduParser;->parseValueLength(Ljava/io/ByteArrayInputStream;)I
-Lcom/google/android/mms/pdu/PduParser;->parseWapString(Ljava/io/ByteArrayInputStream;I)[B
-Lcom/google/android/mms/pdu/PduPart;-><init>()V
-Lcom/google/android/mms/pdu/PduPart;->generateLocation()Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPart;->getCharset()I
-Lcom/google/android/mms/pdu/PduPart;->getContentDisposition()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentId()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentLocation()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentTransferEncoding()[B
-Lcom/google/android/mms/pdu/PduPart;->getContentType()[B
-Lcom/google/android/mms/pdu/PduPart;->getData()[B
-Lcom/google/android/mms/pdu/PduPart;->getDataLength()I
-Lcom/google/android/mms/pdu/PduPart;->getDataUri()Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPart;->getFilename()[B
-Lcom/google/android/mms/pdu/PduPart;->getName()[B
-Lcom/google/android/mms/pdu/PduPart;->setCharset(I)V
-Lcom/google/android/mms/pdu/PduPart;->setContentDisposition([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentId([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentLocation([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentTransferEncoding([B)V
-Lcom/google/android/mms/pdu/PduPart;->setContentType([B)V
-Lcom/google/android/mms/pdu/PduPart;->setData([B)V
-Lcom/google/android/mms/pdu/PduPart;->setDataUri(Landroid/net/Uri;)V
-Lcom/google/android/mms/pdu/PduPart;->setFilename([B)V
-Lcom/google/android/mms/pdu/PduPart;->setName([B)V
-Lcom/google/android/mms/pdu/PduPersister;->ADDRESS_FIELDS:[I
-Lcom/google/android/mms/pdu/PduPersister;->CHARSET_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->ENCODED_STRING_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->getByteArrayFromPartColumn(Landroid/database/Cursor;I)[B
-Lcom/google/android/mms/pdu/PduPersister;->getBytes(Ljava/lang/String;)[B
-Lcom/google/android/mms/pdu/PduPersister;->getIntegerFromPartColumn(Landroid/database/Cursor;I)Ljava/lang/Integer;
-Lcom/google/android/mms/pdu/PduPersister;->getPartContentType(Lcom/google/android/mms/pdu/PduPart;)Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPersister;->getPduPersister(Landroid/content/Context;)Lcom/google/android/mms/pdu/PduPersister;
-Lcom/google/android/mms/pdu/PduPersister;->getPendingMessages(J)Landroid/database/Cursor;
-Lcom/google/android/mms/pdu/PduPersister;->load(Landroid/net/Uri;)Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/pdu/PduPersister;->loadRecipients(ILjava/util/HashSet;Ljava/util/HashMap;Z)V
-Lcom/google/android/mms/pdu/PduPersister;->LONG_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->mContentResolver:Landroid/content/ContentResolver;
-Lcom/google/android/mms/pdu/PduPersister;->mContext:Landroid/content/Context;
-Lcom/google/android/mms/pdu/PduPersister;->MESSAGE_BOX_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->move(Landroid/net/Uri;Landroid/net/Uri;)Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPersister;->mTelephonyManager:Landroid/telephony/TelephonyManager;
-Lcom/google/android/mms/pdu/PduPersister;->OCTET_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->PART_PROJECTION:[Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPersister;->PDU_CACHE_INSTANCE:Lcom/google/android/mms/util/PduCache;
-Lcom/google/android/mms/pdu/PduPersister;->persist(Lcom/google/android/mms/pdu/GenericPdu;Landroid/net/Uri;ZZLjava/util/HashMap;)Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPersister;->persistAddress(JI[Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/PduPersister;->persistPart(Lcom/google/android/mms/pdu/PduPart;JLjava/util/HashMap;)Landroid/net/Uri;
-Lcom/google/android/mms/pdu/PduPersister;->TEXT_STRING_COLUMN_NAME_MAP:Ljava/util/HashMap;
-Lcom/google/android/mms/pdu/PduPersister;->toIsoString([B)Ljava/lang/String;
-Lcom/google/android/mms/pdu/PduPersister;->updateAddress(JI[Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/PduPersister;->updateHeaders(Landroid/net/Uri;Lcom/google/android/mms/pdu/SendReq;)V
-Lcom/google/android/mms/pdu/PduPersister;->updateParts(Landroid/net/Uri;Lcom/google/android/mms/pdu/PduBody;Ljava/util/HashMap;)V
-Lcom/google/android/mms/pdu/QuotedPrintable;->decodeQuotedPrintable([B)[B
-Lcom/google/android/mms/pdu/ReadOrigInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/ReadOrigInd;->getMessageId()[B
-Lcom/google/android/mms/pdu/ReadOrigInd;->getReadStatus()I
-Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/EncodedStringValue;[BII[Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/ReadRecInd;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/ReadRecInd;->getMessageId()[B
-Lcom/google/android/mms/pdu/ReadRecInd;->setDate(J)V
-Lcom/google/android/mms/pdu/RetrieveConf;-><init>()V
-Lcom/google/android/mms/pdu/RetrieveConf;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->addCc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->getCc()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/RetrieveConf;->getContentType()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->getDeliveryReport()I
-Lcom/google/android/mms/pdu/RetrieveConf;->getFrom()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/RetrieveConf;->getMessageClass()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->getMessageId()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->getReadReport()I
-Lcom/google/android/mms/pdu/RetrieveConf;->getRetrieveStatus()I
-Lcom/google/android/mms/pdu/RetrieveConf;->getRetrieveText()Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/RetrieveConf;->getTransactionId()[B
-Lcom/google/android/mms/pdu/RetrieveConf;->setContentType([B)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setDeliveryReport(I)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setFrom(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setMessageClass([B)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setMessageId([B)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setReadReport(I)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setRetrieveStatus(I)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setRetrieveText(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/RetrieveConf;->setTransactionId([B)V
-Lcom/google/android/mms/pdu/SendConf;-><init>()V
-Lcom/google/android/mms/pdu/SendConf;-><init>(Lcom/google/android/mms/pdu/PduHeaders;)V
-Lcom/google/android/mms/pdu/SendConf;->getMessageId()[B
-Lcom/google/android/mms/pdu/SendConf;->getResponseStatus()I
-Lcom/google/android/mms/pdu/SendConf;->getTransactionId()[B
-Lcom/google/android/mms/pdu/SendReq;-><init>()V
-Lcom/google/android/mms/pdu/SendReq;-><init>(Lcom/google/android/mms/pdu/PduHeaders;Lcom/google/android/mms/pdu/PduBody;)V
-Lcom/google/android/mms/pdu/SendReq;->addBcc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->addCc(Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->getBcc()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/SendReq;->getCc()[Lcom/google/android/mms/pdu/EncodedStringValue;
-Lcom/google/android/mms/pdu/SendReq;->getContentType()[B
-Lcom/google/android/mms/pdu/SendReq;->getDeliveryReport()I
-Lcom/google/android/mms/pdu/SendReq;->getExpiry()J
-Lcom/google/android/mms/pdu/SendReq;->getMessageClass()[B
-Lcom/google/android/mms/pdu/SendReq;->getMessageSize()J
-Lcom/google/android/mms/pdu/SendReq;->getReadReport()I
-Lcom/google/android/mms/pdu/SendReq;->getTransactionId()[B
-Lcom/google/android/mms/pdu/SendReq;->setBcc([Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->setCc([Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->setContentType([B)V
-Lcom/google/android/mms/pdu/SendReq;->setDeliveryReport(I)V
-Lcom/google/android/mms/pdu/SendReq;->setExpiry(J)V
-Lcom/google/android/mms/pdu/SendReq;->setMessageClass([B)V
-Lcom/google/android/mms/pdu/SendReq;->setMessageSize(J)V
-Lcom/google/android/mms/pdu/SendReq;->setReadReport(I)V
-Lcom/google/android/mms/pdu/SendReq;->setTo([Lcom/google/android/mms/pdu/EncodedStringValue;)V
-Lcom/google/android/mms/pdu/SendReq;->setTransactionId([B)V
-Lcom/google/android/mms/util/AbstractCache;-><init>()V
-Lcom/google/android/mms/util/AbstractCache;->get(Ljava/lang/Object;)Ljava/lang/Object;
-Lcom/google/android/mms/util/AbstractCache;->purge(Ljava/lang/Object;)Ljava/lang/Object;
-Lcom/google/android/mms/util/AbstractCache;->purgeAll()V
-Lcom/google/android/mms/util/AbstractCache;->put(Ljava/lang/Object;Ljava/lang/Object;)Z
-Lcom/google/android/mms/util/DownloadDrmHelper;->isDrmConvertNeeded(Ljava/lang/String;)Z
-Lcom/google/android/mms/util/DownloadDrmHelper;->modifyDrmFwLockFileExtension(Ljava/lang/String;)Ljava/lang/String;
-Lcom/google/android/mms/util/DrmConvertSession;->close(Ljava/lang/String;)I
-Lcom/google/android/mms/util/DrmConvertSession;->convert([BI)[B
-Lcom/google/android/mms/util/DrmConvertSession;->open(Landroid/content/Context;Ljava/lang/String;)Lcom/google/android/mms/util/DrmConvertSession;
-Lcom/google/android/mms/util/PduCache;-><init>()V
-Lcom/google/android/mms/util/PduCache;->getInstance()Lcom/google/android/mms/util/PduCache;
-Lcom/google/android/mms/util/PduCache;->isUpdating(Landroid/net/Uri;)Z
-Lcom/google/android/mms/util/PduCache;->purge(Landroid/net/Uri;)Lcom/google/android/mms/util/PduCacheEntry;
-Lcom/google/android/mms/util/PduCache;->purgeAll()V
-Lcom/google/android/mms/util/PduCacheEntry;-><init>(Lcom/google/android/mms/pdu/GenericPdu;IJ)V
-Lcom/google/android/mms/util/PduCacheEntry;->getMessageBox()I
-Lcom/google/android/mms/util/PduCacheEntry;->getPdu()Lcom/google/android/mms/pdu/GenericPdu;
-Lcom/google/android/mms/util/PduCacheEntry;->getThreadId()J
-Lcom/google/android/mms/util/SqliteWrapper;->checkSQLiteException(Landroid/content/Context;Landroid/database/sqlite/SQLiteException;)V
-Lcom/google/android/mms/util/SqliteWrapper;->delete(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Ljava/lang/String;[Ljava/lang/String;)I
-Lcom/google/android/mms/util/SqliteWrapper;->insert(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
-Lcom/google/android/mms/util/SqliteWrapper;->query(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
-Lcom/google/android/mms/util/SqliteWrapper;->requery(Landroid/content/Context;Landroid/database/Cursor;)Z
-Lcom/google/android/mms/util/SqliteWrapper;->update(Landroid/content/Context;Landroid/content/ContentResolver;Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I
Lcom/google/android/util/AbstractMessageParser$Token$Type;->values()[Lcom/google/android/util/AbstractMessageParser$Token$Type;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 553ef69..a6784780 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -191,6 +191,8 @@
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.net.InetAddress;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -6435,6 +6437,26 @@
NetworkSecurityConfigProvider.install(appContext);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+
+ if (isAppDebuggable) {
+ try {
+ // Load all the agents in the code_cache/startup_agents directory.
+ // We pass the absolute path to the data_dir as an argument.
+ Path startup_path = appContext.getCodeCacheDir().toPath().resolve("startup_agents");
+ if (Files.exists(startup_path)) {
+ for (Path p : Files.newDirectoryStream(startup_path)) {
+ handleAttachAgent(
+ p.toAbsolutePath().toString()
+ + "="
+ + appContext.getDataDir().toPath().toAbsolutePath().toString(),
+ data.info);
+ }
+ }
+ } catch (Exception e) {
+ // Ignored.
+ }
+ }
+
// Continue loading instrumentation.
if (ii != null) {
ApplicationInfo instrApp;
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index ed2b991..f9b96c5 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -242,7 +242,7 @@
mUiAutomationConnection.connect(mClient, flags);
mFlags = flags;
} catch (RemoteException re) {
- throw new RuntimeException("Error while connecting UiAutomation", re);
+ throw new RuntimeException("Error while connecting " + this, re);
}
synchronized (mLock) {
@@ -255,7 +255,7 @@
final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
final long remainingTimeMillis = CONNECT_TIMEOUT_MILLIS - elapsedTimeMillis;
if (remainingTimeMillis <= 0) {
- throw new RuntimeException("Error while connecting UiAutomation");
+ throw new RuntimeException("Error while connecting " + this);
}
try {
mLock.wait(remainingTimeMillis);
@@ -290,7 +290,7 @@
synchronized (mLock) {
if (mIsConnecting) {
throw new IllegalStateException(
- "Cannot call disconnect() while connecting!");
+ "Cannot call disconnect() while connecting " + this);
}
throwIfNotConnectedLocked();
mConnectionId = CONNECTION_ID_UNDEFINED;
@@ -299,7 +299,7 @@
// Calling out without a lock held.
mUiAutomationConnection.disconnect();
} catch (RemoteException re) {
- throw new RuntimeException("Error while disconnecting UiAutomation", re);
+ throw new RuntimeException("Error while disconnecting " + this, re);
} finally {
mRemoteCallbackThread.quit();
mRemoteCallbackThread = null;
@@ -1184,19 +1184,29 @@
return result;
}
+ @Override
+ public String toString() {
+ final StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.append("UiAutomation@").append(Integer.toHexString(hashCode()));
+ stringBuilder.append("[id=").append(mConnectionId);
+ stringBuilder.append(", flags=").append(mFlags);
+ stringBuilder.append("]");
+ return stringBuilder.toString();
+ }
+
private boolean isConnectedLocked() {
return mConnectionId != CONNECTION_ID_UNDEFINED;
}
private void throwIfConnectedLocked() {
if (mConnectionId != CONNECTION_ID_UNDEFINED) {
- throw new IllegalStateException("UiAutomation not connected!");
+ throw new IllegalStateException("UiAutomation not connected, " + this);
}
}
private void throwIfNotConnectedLocked() {
if (!isConnectedLocked()) {
- throw new IllegalStateException("UiAutomation not connected!");
+ throw new IllegalStateException("UiAutomation not connected, " + this);
}
}
@@ -1220,6 +1230,9 @@
mConnectionId = connectionId;
mLock.notifyAll();
}
+ if (Build.IS_DEBUGGABLE) {
+ Log.v(LOG_TAG, "Init " + UiAutomation.this);
+ }
}
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 49cfd41..ff5a043 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4991,26 +4991,60 @@
* call {@link android.security.KeyChain#choosePrivateKeyAlias} first.
*
* The grantee app will receive the {@link android.security.KeyChain#ACTION_KEY_ACCESS_CHANGED}
- * broadcast when access to a key is granted or revoked.
+ * broadcast when access to a key is granted.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
* {@code null} if calling from a delegated certificate installer.
* @param alias The alias of the key to grant access to.
* @param packageName The name of the (already installed) package to grant access to.
- * @param hasGrant Whether to grant access to the alias or revoke it.
* @return {@code true} if the grant was set successfully, {@code false} otherwise.
*
- * @throws SecurityException if the caller is not a device owner, a profile owner or
+ * @throws SecurityException if the caller is not a device owner, a profile owner or
* delegated certificate chooser.
* @throws IllegalArgumentException if {@code packageName} or {@code alias} are empty, or if
* {@code packageName} is not a name of an installed package.
+ * @see #revokeKeyPairFromApp
*/
- public boolean setKeyGrantForApp(@Nullable ComponentName admin, @NonNull String alias,
- @NonNull String packageName, boolean hasGrant) {
- throwIfParentInstance("addKeyGrant");
+ public boolean grantKeyPairToApp(@Nullable ComponentName admin, @NonNull String alias,
+ @NonNull String packageName) {
+ throwIfParentInstance("grantKeyPairToApp");
try {
return mService.setKeyGrantForApp(
- admin, mContext.getPackageName(), alias, packageName, hasGrant);
+ admin, mContext.getPackageName(), alias, packageName, true);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+
+ /**
+ * Called by a device or profile owner, or delegated certificate chooser (an app that has been
+ * delegated the {@link #DELEGATION_CERT_SELECTION} privilege), to revoke an application's
+ * grant to a KeyChain key pair.
+ * Calls by the application to {@link android.security.KeyChain#getPrivateKey}
+ * will fail after the grant is revoked.
+ *
+ * The grantee app will receive the {@link android.security.KeyChain#ACTION_KEY_ACCESS_CHANGED}
+ * broadcast when access to a key is revoked.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
+ * {@code null} if calling from a delegated certificate installer.
+ * @param alias The alias of the key to revoke access from.
+ * @param packageName The name of the (already installed) package to revoke access from.
+ * @return {@code true} if the grant was revoked successfully, {@code false} otherwise.
+ *
+ * @throws SecurityException if the caller is not a device owner, a profile owner or
+ * delegated certificate chooser.
+ * @throws IllegalArgumentException if {@code packageName} or {@code alias} are empty, or if
+ * {@code packageName} is not a name of an installed package.
+ * @see #grantKeyPairToApp
+ */
+ public boolean revokeKeyPairFromApp(@Nullable ComponentName admin, @NonNull String alias,
+ @NonNull String packageName) {
+ throwIfParentInstance("revokeKeyPairFromApp");
+ try {
+ return mService.setKeyGrantForApp(
+ admin, mContext.getPackageName(), alias, packageName, false);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java
index e4114c6..ac40150 100644
--- a/core/java/android/companion/AssociationRequest.java
+++ b/core/java/android/companion/AssociationRequest.java
@@ -127,6 +127,12 @@
public Builder() {}
/**
+ * Whether only a single device should match the provided filter.
+ *
+ * When scanning for a single device with a specifc {@link BluetoothDeviceFilter} mac
+ * address, bonded devices are also searched among. This allows to obtain the necessary app
+ * privileges even if the device is already paired.
+ *
* @param singleDevice if true, scanning for a device will stop as soon as at least one
* fitting device is found
*/
diff --git a/services/core/java/com/android/server/compat/IPlatformCompat.aidl b/core/java/android/compat/IPlatformCompat.aidl
similarity index 90%
rename from services/core/java/com/android/server/compat/IPlatformCompat.aidl
rename to core/java/android/compat/IPlatformCompat.aidl
index 8ab08f9..3d8a9d5 100644
--- a/services/core/java/com/android/server/compat/IPlatformCompat.aidl
+++ b/core/java/android/compat/IPlatformCompat.aidl
@@ -14,12 +14,16 @@
* limitations under the License.
*/
-package com.android.server.compat;
+package android.compat;
import android.content.pm.ApplicationInfo;
/**
- * System private API for talking with the PlatformCompat service.
+ * Platform private API for talking with the PlatformCompat service.
+ *
+ * <p> Should be used for gating and logging from non-app processes.
+ * For app processes please use android.compat.Compatibility API.
+ *
* {@hide}
*/
interface IPlatformCompat
diff --git a/core/java/android/content/AutofillOptions.java b/core/java/android/content/AutofillOptions.java
index 8fb9501..082663e 100644
--- a/core/java/android/content/AutofillOptions.java
+++ b/core/java/android/content/AutofillOptions.java
@@ -21,6 +21,8 @@
import android.app.ActivityThread;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.view.autofill.AutofillManager;
@@ -62,6 +64,18 @@
@Nullable
public ArraySet<ComponentName> whitelistedActivitiesForAugmentedAutofill;
+ /**
+ * The package disable expiration by autofill service.
+ */
+ public long appDisabledExpiration;
+
+ /**
+ * The disabled Activities of the package. key is component name string, value is when they
+ * will be enabled.
+ */
+ @Nullable
+ public ArrayMap<String, Long> disabledActivities;
+
public AutofillOptions(int loggingLevel, boolean compatModeEnabled) {
this.loggingLevel = loggingLevel;
this.compatModeEnabled = compatModeEnabled;
@@ -82,6 +96,27 @@
}
/**
+ * Returns if autofill is disabled by service to the given activity.
+ */
+ public boolean isAutofillDisabledLocked(@NonNull ComponentName componentName) {
+ final long elapsedTime = SystemClock.elapsedRealtime();
+ final String component = componentName.flattenToString();
+ // Check app first.
+ if (appDisabledExpiration >= elapsedTime) return true;
+
+ // Then check activities.
+ if (disabledActivities != null) {
+ final Long expiration = disabledActivities.get(component);
+ if (expiration != null) {
+ if (expiration >= elapsedTime) return true;
+ disabledActivities.remove(component);
+ }
+ }
+ appDisabledExpiration = 0;
+ return false;
+ }
+
+ /**
* @hide
*/
@TestApi
@@ -110,7 +145,8 @@
@Override
public String toString() {
return "AutofillOptions [loggingLevel=" + loggingLevel + ", compatMode=" + compatModeEnabled
- + ", augmentedAutofillEnabled=" + augmentedAutofillEnabled + "]";
+ + ", augmentedAutofillEnabled=" + augmentedAutofillEnabled
+ + ", appDisabledExpiration=" + appDisabledExpiration + "]";
}
/** @hide */
@@ -122,6 +158,11 @@
pw.print(", whitelistedActivitiesForAugmentedAutofill=");
pw.print(whitelistedActivitiesForAugmentedAutofill);
}
+ pw.print(", appDisabledExpiration="); pw.print(appDisabledExpiration);
+ if (disabledActivities != null) {
+ pw.print(", disabledActivities=");
+ pw.print(disabledActivities);
+ }
}
@Override
@@ -135,6 +176,16 @@
parcel.writeBoolean(compatModeEnabled);
parcel.writeBoolean(augmentedAutofillEnabled);
parcel.writeArraySet(whitelistedActivitiesForAugmentedAutofill);
+ parcel.writeLong(appDisabledExpiration);
+ final int size = disabledActivities != null ? disabledActivities.size() : 0;
+ parcel.writeInt(size);
+ if (size > 0) {
+ for (int i = 0; i < size; i++) {
+ final String key = disabledActivities.keyAt(i);
+ parcel.writeString(key);
+ parcel.writeLong(disabledActivities.get(key));
+ }
+ }
}
public static final @android.annotation.NonNull Parcelable.Creator<AutofillOptions> CREATOR =
@@ -148,6 +199,14 @@
options.augmentedAutofillEnabled = parcel.readBoolean();
options.whitelistedActivitiesForAugmentedAutofill =
(ArraySet<ComponentName>) parcel.readArraySet(null);
+ options.appDisabledExpiration = parcel.readLong();
+ final int size = parcel.readInt();
+ if (size > 0) {
+ options.disabledActivities = new ArrayMap<>();
+ for (int i = 0; i < size; i++) {
+ options.disabledActivities.put(parcel.readString(), parcel.readLong());
+ }
+ }
return options;
}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 4ea3726..f297c06 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -17,6 +17,7 @@
package android.content;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.MODE_ERRORED;
@@ -645,9 +646,11 @@
}
boolean checkUser(int pid, int uid, Context context) {
- return UserHandle.getUserId(uid) == context.getUserId()
- || mSingleUser
- || context.checkPermission(INTERACT_ACROSS_USERS, pid, uid)
+ if (UserHandle.getUserId(uid) == context.getUserId() || mSingleUser) {
+ return true;
+ }
+ return context.checkPermission(INTERACT_ACROSS_USERS, pid, uid) == PERMISSION_GRANTED
+ || context.checkPermission(INTERACT_ACROSS_USERS_FULL, pid, uid)
== PERMISSION_GRANTED;
}
@@ -1030,10 +1033,12 @@
/** @hide */
public final void setTransportLoggingEnabled(boolean enabled) {
- if (enabled) {
- mTransport.mInterface = new LoggingContentInterface(getClass().getSimpleName(), this);
- } else {
- mTransport.mInterface = this;
+ if (mTransport != null) {
+ if (enabled) {
+ mTransport.mInterface = new LoggingContentInterface(getClass().getSimpleName(), this);
+ } else {
+ mTransport.mInterface = this;
+ }
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index bb5ced5..2c53faa 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -38,6 +38,7 @@
import android.app.IApplicationThread;
import android.app.IServiceConnection;
import android.app.VrManager;
+import android.compat.IPlatformCompat;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
@@ -3228,6 +3229,7 @@
ROLE_SERVICE,
//@hide ROLE_CONTROLLER_SERVICE,
CAMERA_SERVICE,
+ //@hide: PLATFORM_COMPAT_SERVICE,
PRINT_SERVICE,
CONSUMER_IR_SERVICE,
//@hide: TRUST_SERVICE,
@@ -4597,6 +4599,13 @@
public static final String STATS_MANAGER = "stats";
/**
+ * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+ * {@link IPlatformCompat} IBinder for communicating with the platform compat service.
+ * @hide
+ */
+ public static final String PLATFORM_COMPAT_SERVICE = "platform_compat";
+
+ /**
* Service to capture a bugreport.
* @see #getSystemService(String)
* @see android.os.BugreportManager
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 895eba6..c561013 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -38,6 +38,8 @@
import android.app.PackageInstallObserver;
import android.app.admin.DevicePolicyManager;
import android.app.usage.StorageStatsManager;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -3369,6 +3371,17 @@
*/
public static final int VERSION_CODE_HIGHEST = -1;
+ /**
+ * Apps targeting Android R and above will need to declare the packages and intents they intend
+ * to use to get details about other apps on a device. Such declarations must be made via the
+ * {@code <queries>} tag in the manifest.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ public static final long FILTER_APPLICATION_QUERY = 135549675L;
+
/** {@hide} */
public int getUserId() {
return UserHandle.myUserId();
diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java
index 73b8a48..1609f53 100644
--- a/core/java/android/content/rollback/RollbackManager.java
+++ b/core/java/android/content/rollback/RollbackManager.java
@@ -74,7 +74,10 @@
}
/**
- * Returns a list of all currently available rollbacks.
+ * Returns a list of all currently available rollbacks. This includes ones for very recently
+ * installed packages (even if onFinished has not yet been called). As a result, packages that
+ * very recently failed to install may also be included, but those rollbacks will fail with
+ * 'rollback not available'.
*
* @throws SecurityException if the caller does not have appropriate permissions.
*/
diff --git a/core/java/android/gesture/GestureLibraries.java b/core/java/android/gesture/GestureLibraries.java
index 611d9ab..5e31ce6 100644
--- a/core/java/android/gesture/GestureLibraries.java
+++ b/core/java/android/gesture/GestureLibraries.java
@@ -16,14 +16,16 @@
package android.gesture;
+import android.annotation.NonNull;
import android.annotation.RawRes;
+import android.os.ParcelFileDescriptor;
import android.util.Log;
import static android.gesture.GestureConstants.*;
import android.content.Context;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileOutputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.InputStream;
@@ -41,6 +43,11 @@
return new FileGestureLibrary(path);
}
+ @NonNull
+ public static GestureLibrary fromFileDescriptor(@NonNull ParcelFileDescriptor pfd) {
+ return new FileGestureLibrary(pfd.getFileDescriptor());
+ }
+
public static GestureLibrary fromPrivateFile(Context context, String name) {
return fromFile(context.getFileStreamPath(name));
}
@@ -50,55 +57,83 @@
}
private static class FileGestureLibrary extends GestureLibrary {
+ // Either a file or an fd is used
private final File mPath;
+ private final FileDescriptor mFd;
public FileGestureLibrary(File path) {
mPath = path;
+ mFd = null;
}
+ public FileGestureLibrary(FileDescriptor fd) {
+ mPath = null;
+ mFd = fd;
+ }
+
+ /**
+ * <p>If this GestureLibrary was created using a FileDescriptor,
+ * this method will always return false.
+ */
@Override
public boolean isReadOnly() {
- return !mPath.canWrite();
+ if (mPath != null) {
+ return !mPath.canWrite();
+ }
+ return false;
}
public boolean save() {
if (!mStore.hasChanged()) return true;
+ boolean result = false;
- final File file = mPath;
+ if (mPath != null) {
+ final File file = mPath;
- final File parentFile = file.getParentFile();
- if (!parentFile.exists()) {
- if (!parentFile.mkdirs()) {
- return false;
+ final File parentFile = file.getParentFile();
+ if (!parentFile.exists()) {
+ if (!parentFile.mkdirs()) {
+ return false;
+ }
+ }
+
+ try {
+ //noinspection ResultOfMethodCallIgnored
+ file.createNewFile();
+ mStore.save(new FileOutputStream(file), true);
+ result = true;
+ } catch (IOException e) {
+ Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e);
+ }
+ } else {
+ try {
+ mStore.save(new FileOutputStream(mFd), true);
+ result = true;
+ } catch (IOException e) {
+ Log.d(LOG_TAG, "Could not save the gesture library", e);
}
}
-
- boolean result = false;
- try {
- //noinspection ResultOfMethodCallIgnored
- file.createNewFile();
- mStore.save(new FileOutputStream(file), true);
- result = true;
- } catch (FileNotFoundException e) {
- Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e);
- } catch (IOException e) {
- Log.d(LOG_TAG, "Could not save the gesture library in " + mPath, e);
- }
-
return result;
}
public boolean load() {
boolean result = false;
- final File file = mPath;
- if (file.exists() && file.canRead()) {
+ if (mPath != null) {
+ final File file = mPath;
+ if (file.exists() && file.canRead()) {
+ try {
+ mStore.load(new FileInputStream(file), true);
+ result = true;
+ } catch (IOException e) {
+ Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e);
+ }
+ }
+ } else {
try {
- mStore.load(new FileInputStream(file), true);
+ mStore.load(new FileInputStream(mFd), true);
result = true;
- } catch (FileNotFoundException e) {
- Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e);
} catch (IOException e) {
- Log.d(LOG_TAG, "Could not load the gesture library from " + mPath, e);
+ Log.d(LOG_TAG, "Could not load the gesture library", e);
}
}
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 1142a07..fb6b231 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -100,9 +100,12 @@
/**
* @hide
*/
- public static final int DISMISSED_REASON_POSITIVE = 1;
+ public static final int DISMISSED_REASON_CONFIRMED = 1;
/**
+ * Dialog is done animating away after user clicked on the button set via
+ * {@link BiometricPrompt.Builder#setNegativeButton(CharSequence, Executor,
+ * DialogInterface.OnClickListener)}.
* @hide
*/
public static final int DISMISSED_REASON_NEGATIVE = 2;
@@ -112,6 +115,25 @@
*/
public static final int DISMISSED_REASON_USER_CANCEL = 3;
+ /**
+ * Authenticated, confirmation not required. Dialog animated away.
+ * @hide
+ */
+ public static final int DISMISSED_REASON_CONFIRM_NOT_REQUIRED = 4;
+
+ /**
+ * Error message shown on SystemUI. When BiometricService receives this, the UI is already
+ * gone.
+ * @hide
+ */
+ public static final int DISMISSED_REASON_ERROR = 5;
+
+ /**
+ * Dialog dismissal requested by BiometricService.
+ * @hide
+ */
+ public static final int DISMISSED_REASON_SERVER_REQUESTED = 6;
+
private static class ButtonInfo {
Executor executor;
DialogInterface.OnClickListener listener;
@@ -362,7 +384,7 @@
@Override
public void onDialogDismissed(int reason) throws RemoteException {
// Check the reason and invoke OnClickListener(s) if necessary
- if (reason == DISMISSED_REASON_POSITIVE) {
+ if (reason == DISMISSED_REASON_CONFIRMED) {
mPositiveButtonInfo.executor.execute(() -> {
mPositiveButtonInfo.listener.onClick(null, DialogInterface.BUTTON_POSITIVE);
});
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
index 180daaf..ca6114e 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiverInternal.aidl
@@ -27,8 +27,8 @@
// Notify BiometricService that authentication was successful. If user confirmation is required,
// the auth token must be submitted into KeyStore.
void onAuthenticationSucceeded(boolean requireConfirmation, in byte[] token);
- // Notify BiometricService that an error has occurred.
- void onAuthenticationFailed(int cookie, boolean requireConfirmation);
+ // Notify BiometricService authentication was rejected.
+ void onAuthenticationFailed();
// Notify BiometricService than an error has occured. Forward to the correct receiver depending
// on the cookie.
void onError(int cookie, int error, String message);
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index 0fb93e5..beff0f7 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -211,10 +211,12 @@
@Override
public void onProgramListUpdated(ProgramList.Chunk chunk) {
- synchronized (mLock) {
- if (mProgramList == null) return;
- mProgramList.apply(Objects.requireNonNull(chunk));
- }
+ mHandler.post(() -> {
+ synchronized (mLock) {
+ if (mProgramList == null) return;
+ mProgramList.apply(Objects.requireNonNull(chunk));
+ }
+ });
}
@Override
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index 0513fee..356b344 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.app.Dialog;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.graphics.Rect;
import android.os.Debug;
import android.os.IBinder;
@@ -50,6 +51,7 @@
final int mWindowType;
final int mGravity;
final boolean mTakesFocus;
+ final boolean mAutomotiveHideNavBarForKeyboard;
private final Rect mBounds = new Rect();
@Retention(SOURCE)
@@ -134,6 +136,8 @@
mWindowType = windowType;
mGravity = gravity;
mTakesFocus = takesFocus;
+ mAutomotiveHideNavBarForKeyboard = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
initDockWindow();
}
@@ -247,6 +251,11 @@
windowModFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
}
+ if (isAutomotive() && mAutomotiveHideNavBarForKeyboard) {
+ windowSetFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+ windowModFlags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+ }
+
getWindow().setFlags(windowSetFlags, windowModFlags);
}
@@ -338,6 +347,10 @@
mWindowState = newState;
}
+ private boolean isAutomotive() {
+ return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+
private static String stateToString(@SoftInputWindowState int state) {
switch (state) {
case SoftInputWindowState.TOKEN_PENDING:
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index e4f88c5..77fd946 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -20,6 +20,7 @@
import android.annotation.SystemService;
import android.content.Context;
import android.gsi.GsiProgress;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
/**
@@ -52,22 +53,39 @@
/** The DynamicSystemManager.Session represents a started session for the installation. */
public class Session {
private Session() {}
+
/**
- * Write a chunk of the DynamicSystem system image
+ * Set the file descriptor that points to a ashmem which will be used
+ * to fetch data during the submitFromAshmem.
*
- * @return {@code true} if the call succeeds. {@code false} if there is any native runtime
- * error.
+ * @param ashmem fd that points to a ashmem
+ * @param size size of the ashmem file
*/
@RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
- public boolean write(byte[] buf) {
+ public boolean setAshmem(ParcelFileDescriptor ashmem, long size) {
try {
- return mService.write(buf);
+ return mService.setAshmem(ashmem, size);
} catch (RemoteException e) {
throw new RuntimeException(e.toString());
}
}
/**
+ * Submit bytes to the DSU partition from the ashmem previously set with
+ * setAshmem.
+ *
+ * @param size Number of bytes
+ * @return true on success, false otherwise.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+ public boolean submitFromAshmem(int size) {
+ try {
+ return mService.submitFromAshmem(size);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+ /**
* Finish write and make device to boot into the it after reboot.
*
* @return {@code true} if the call succeeds. {@code false} if there is any native runtime
diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl
index 2f4ab2d..a6de170 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -79,10 +79,20 @@
boolean setEnable(boolean enable, boolean oneShot);
/**
- * Write a chunk of the DynamicSystem system image
+ * Set the file descriptor that points to a ashmem which will be used
+ * to fetch data during the submitFromAshmem.
*
- * @return true if the call succeeds
+ * @param fd fd that points to a ashmem
+ * @param size size of the ashmem file
*/
- boolean write(in byte[] buf);
+ boolean setAshmem(in ParcelFileDescriptor fd, long size);
+ /**
+ * Submit bytes to the DSU partition from the ashmem previously set with
+ * setAshmem.
+ *
+ * @param bytes number of bytes that can be read from stream.
+ * @return true on success, false otherwise.
+ */
+ boolean submitFromAshmem(long bytes);
}
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 2299aad..a959913f 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -3592,10 +3592,23 @@
}
/** @hide */
+ public static Uri scanFile(ContentProviderClient client, File file) {
+ return scan(client, SCAN_FILE_CALL, file, false);
+ }
+
+ /** @hide */
private static Uri scan(Context context, String method, File file,
boolean originatedFromShell) {
final ContentResolver resolver = context.getContentResolver();
try (ContentProviderClient client = resolver.acquireContentProviderClient(AUTHORITY)) {
+ return scan(client, method, file, originatedFromShell);
+ }
+ }
+
+ /** @hide */
+ private static Uri scan(ContentProviderClient client, String method, File file,
+ boolean originatedFromShell) {
+ try {
final Bundle in = new Bundle();
in.putParcelable(Intent.EXTRA_STREAM, Uri.fromFile(file));
in.putBoolean(EXTRA_ORIGINATED_FROM_SHELL, originatedFromShell);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9abb64f..e486235 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16,16 +16,19 @@
package android.provider;
-import static android.provider.SettingsValidators.ANY_INTEGER_VALIDATOR;
-import static android.provider.SettingsValidators.ANY_STRING_VALIDATOR;
-import static android.provider.SettingsValidators.BOOLEAN_VALIDATOR;
-import static android.provider.SettingsValidators.COMPONENT_NAME_VALIDATOR;
-import static android.provider.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR;
-import static android.provider.SettingsValidators.LOCALE_VALIDATOR;
-import static android.provider.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
-import static android.provider.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR;
-import static android.provider.SettingsValidators.PACKAGE_NAME_VALIDATOR;
-import static android.provider.SettingsValidators.URI_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.JSON_OBJECT_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.LENIENT_IP_ADDRESS_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.NULLABLE_COMPONENT_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.TILE_LIST_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.TTS_LIST_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.URI_VALIDATOR;
import android.Manifest;
import android.annotation.IntDef;
@@ -80,7 +83,12 @@
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.UserHandle;
-import android.provider.SettingsValidators.Validator;
+import android.provider.settings.validators.ComponentNameListValidator;
+import android.provider.settings.validators.DiscreteValueValidator;
+import android.provider.settings.validators.InclusiveFloatRangeValidator;
+import android.provider.settings.validators.InclusiveIntegerRangeValidator;
+import android.provider.settings.validators.PackageNameListValidator;
+import android.provider.settings.validators.Validator;
import android.speech.tts.TextToSpeech;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
@@ -3149,7 +3157,7 @@
public static final String END_BUTTON_BEHAVIOR = "end_button_behavior";
private static final Validator END_BUTTON_BEHAVIOR_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+ new InclusiveIntegerRangeValidator(0, 3);
/**
* END_BUTTON_BEHAVIOR value for "go home".
@@ -3351,7 +3359,7 @@
"bluetooth_discoverability";
private static final Validator BLUETOOTH_DISCOVERABILITY_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+ new InclusiveIntegerRangeValidator(0, 2);
/**
* Bluetooth discoverability timeout. If this value is nonzero, then
@@ -3495,7 +3503,7 @@
public static final String PEAK_REFRESH_RATE = "peak_refresh_rate";
private static final Validator PEAK_REFRESH_RATE_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(24f, Float.MAX_VALUE);
+ new InclusiveFloatRangeValidator(24f, Float.MAX_VALUE);
/**
* The amount of time in milliseconds before the device goes to sleep or begins
@@ -3524,7 +3532,7 @@
public static final String SCREEN_BRIGHTNESS_FOR_VR = "screen_brightness_for_vr";
private static final Validator SCREEN_BRIGHTNESS_FOR_VR_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 255);
+ new InclusiveIntegerRangeValidator(0, 255);
/**
* Control whether to enable automatic brightness mode.
@@ -3542,7 +3550,7 @@
public static final String SCREEN_AUTO_BRIGHTNESS_ADJ = "screen_auto_brightness_adj";
private static final Validator SCREEN_AUTO_BRIGHTNESS_ADJ_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(-1, 1);
+ new InclusiveFloatRangeValidator(-1, 1);
/**
* SCREEN_BRIGHTNESS_MODE value for manual mode.
@@ -3676,7 +3684,7 @@
"haptic_feedback_intensity";
private static final Validator VIBRATION_INTENSITY_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+ new InclusiveIntegerRangeValidator(0, 3);
/**
* Ringer volume. This is used internally, changing this value will not
@@ -3766,7 +3774,7 @@
public static final String MASTER_BALANCE = "master_balance";
private static final Validator MASTER_BALANCE_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(-1.f, 1.f);
+ new InclusiveFloatRangeValidator(-1.f, 1.f);
/**
* Whether the notifications should use the ring volume (value of 1) or
@@ -4004,7 +4012,7 @@
/** @hide */
public static final Validator TIME_12_24_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"12", "24", null});
+ new DiscreteValueValidator(new String[] {"12", "24", null});
/**
* Date format string
@@ -4090,7 +4098,7 @@
/** @hide */
public static final Validator USER_ROTATION_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+ new InclusiveIntegerRangeValidator(0, 3);
/**
* Control whether the rotation lock toggle in the System UI should be hidden.
@@ -4179,7 +4187,7 @@
/** @hide */
public static final Validator TTY_MODE_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 3);
+ new InclusiveIntegerRangeValidator(0, 3);
/**
* Whether the sounds effects (key clicks, lid open ...) are enabled. The value is
@@ -4381,7 +4389,7 @@
/** @hide */
public static final Validator SIP_CALL_OPTIONS_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(
+ new DiscreteValueValidator(
new String[] {"SIP_ALWAYS", "SIP_ADDRESS_ONLY"});
/**
@@ -4428,7 +4436,7 @@
/** @hide */
public static final Validator POINTER_SPEED_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(-7, 7);
+ new InclusiveFloatRangeValidator(-7, 7);
/**
* Whether lock-to-app will be triggered by long-press on recents.
@@ -6352,7 +6360,7 @@
public static final String LOCK_SCREEN_CUSTOM_CLOCK_FACE = "lock_screen_custom_clock_face";
private static final Validator LOCK_SCREEN_CUSTOM_CLOCK_FACE_VALIDATOR =
- SettingsValidators.JSON_OBJECT_VALIDATOR;
+ JSON_OBJECT_VALIDATOR;
/**
* Indicates which clock face to show on lock screen and AOD while docked.
@@ -6509,7 +6517,7 @@
"enabled_accessibility_services";
private static final Validator ENABLED_ACCESSIBILITY_SERVICES_VALIDATOR =
- new SettingsValidators.ComponentNameListValidator(":");
+ new ComponentNameListValidator(":");
/**
* List of the accessibility services to which the user has granted
@@ -6521,7 +6529,7 @@
"touch_exploration_granted_accessibility_services";
private static final Validator TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES_VALIDATOR =
- new SettingsValidators.ComponentNameListValidator(":");
+ new ComponentNameListValidator(":");
/**
* Whether the Global Actions Panel is enabled.
@@ -6696,7 +6704,7 @@
"accessibility_display_magnification_scale";
private static final Validator ACCESSIBILITY_DISPLAY_MAGNIFICATION_SCALE_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE);
+ new InclusiveFloatRangeValidator(1.0f, Float.MAX_VALUE);
/**
* Unused mangnification setting
@@ -6780,7 +6788,7 @@
"accessibility_captioning_preset";
private static final Validator ACCESSIBILITY_CAPTIONING_PRESET_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[]{"-1", "0", "1", "2",
+ new DiscreteValueValidator(new String[]{"-1", "0", "1", "2",
"3", "4"});
/**
@@ -6824,7 +6832,7 @@
"accessibility_captioning_edge_type";
private static final Validator ACCESSIBILITY_CAPTIONING_EDGE_TYPE_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2"});
+ new DiscreteValueValidator(new String[]{"0", "1", "2"});
/**
* Integer property that specifes the edge color for captions as a
@@ -6870,7 +6878,7 @@
"accessibility_captioning_typeface";
private static final Validator ACCESSIBILITY_CAPTIONING_TYPEFACE_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[]{"DEFAULT",
+ new DiscreteValueValidator(new String[]{"DEFAULT",
"MONOSPACE", "SANS_SERIF", "SERIF"});
/**
@@ -6882,7 +6890,7 @@
"accessibility_captioning_font_scale";
private static final Validator ACCESSIBILITY_CAPTIONING_FONT_SCALE_VALIDATOR =
- new SettingsValidators.InclusiveFloatRangeValidator(0.5f, 2.0f);
+ new InclusiveFloatRangeValidator(0.5f, 2.0f);
/**
* Setting that specifies whether display color inversion is enabled.
@@ -6923,7 +6931,7 @@
"accessibility_display_daltonizer";
private static final Validator ACCESSIBILITY_DISPLAY_DALTONIZER_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(
+ new DiscreteValueValidator(
new String[] {"-1", "0", "11", "12", "13"});
/**
@@ -7112,8 +7120,7 @@
*/
public static final String TTS_DEFAULT_LOCALE = "tts_default_locale";
- private static final Validator TTS_DEFAULT_LOCALE_VALIDATOR =
- new SettingsValidators.TTSListValidator();
+ private static final Validator TTS_DEFAULT_LOCALE_VALIDATOR = TTS_LIST_VALIDATOR;
/**
* Space delimited list of plugin packages that are enabled.
@@ -7121,7 +7128,7 @@
public static final String TTS_ENABLED_PLUGINS = "tts_enabled_plugins";
private static final Validator TTS_ENABLED_PLUGINS_VALIDATOR =
- new SettingsValidators.PackageNameListValidator(" ");
+ new PackageNameListValidator(" ");
/**
* @deprecated Use {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON}
@@ -7315,7 +7322,7 @@
"preferred_tty_mode";
private static final Validator PREFERRED_TTY_MODE_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1", "2", "3"});
+ new DiscreteValueValidator(new String[]{"0", "1", "2", "3"});
/**
* Whether the enhanced voice privacy mode is enabled.
@@ -7630,7 +7637,7 @@
public static final String INCALL_POWER_BUTTON_BEHAVIOR = "incall_power_button_behavior";
private static final Validator INCALL_POWER_BUTTON_BEHAVIOR_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[]{"1", "2"});
+ new DiscreteValueValidator(new String[]{"1", "2"});
/**
* INCALL_POWER_BUTTON_BEHAVIOR value for "turn off screen".
@@ -7851,7 +7858,7 @@
public static final String UI_NIGHT_MODE = "ui_night_mode";
private static final Validator UI_NIGHT_MODE_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+ new InclusiveIntegerRangeValidator(0, 2);
/**
* Whether screensavers are enabled.
@@ -7871,7 +7878,7 @@
public static final String SCREENSAVER_COMPONENTS = "screensaver_components";
private static final Validator SCREENSAVER_COMPONENTS_VALIDATOR =
- new SettingsValidators.ComponentNameListValidator(",");
+ new ComponentNameListValidator(",");
/**
* If screensavers are enabled, whether the screensaver should be automatically launched
@@ -8023,7 +8030,7 @@
"enabled_notification_assistant";
private static final Validator ENABLED_NOTIFICATION_ASSISTANT_VALIDATOR =
- new SettingsValidators.ComponentNameListValidator(":");
+ new ComponentNameListValidator(":");
/**
* Read only list of the service components that the current user has explicitly allowed to
@@ -8038,7 +8045,7 @@
public static final String ENABLED_NOTIFICATION_LISTENERS = "enabled_notification_listeners";
private static final Validator ENABLED_NOTIFICATION_LISTENERS_VALIDATOR =
- new SettingsValidators.ComponentNameListValidator(":");
+ new ComponentNameListValidator(":");
/**
* Read only list of the packages that the current user has explicitly allowed to
@@ -8053,7 +8060,7 @@
"enabled_notification_policy_access_packages";
private static final Validator ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES_VALIDATOR =
- new SettingsValidators.PackageNameListValidator(":");
+ new PackageNameListValidator(":");
/**
* Defines whether managed profile ringtones should be synced from it's parent profile
@@ -8350,16 +8357,6 @@
BOOLEAN_VALIDATOR;
/**
- * Whether or not the face unlock education screen has been shown to the user.
- * @hide
- */
- public static final String FACE_UNLOCK_EDUCATION_INFO_DISPLAYED =
- "face_unlock_education_info_displayed";
-
- private static final Validator FACE_UNLOCK_EDUCATION_INFO_DISPLAYED_VALIDATOR =
- BOOLEAN_VALIDATOR;
-
- /**
* Whether or not debugging is enabled.
* @hide
*/
@@ -8446,7 +8443,7 @@
public static final String NIGHT_DISPLAY_AUTO_MODE = "night_display_auto_mode";
private static final Validator NIGHT_DISPLAY_AUTO_MODE_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 2);
+ new InclusiveIntegerRangeValidator(0, 2);
/**
* Control the color temperature of Night Display, represented in Kelvin.
@@ -8507,7 +8504,7 @@
public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
private static final Validator ENABLED_VR_LISTENERS_VALIDATOR =
- new SettingsValidators.ComponentNameListValidator(":");
+ new ComponentNameListValidator(":");
/**
* Behavior of the display while in VR mode.
@@ -8519,7 +8516,7 @@
public static final String VR_DISPLAY_MODE = "vr_display_mode";
private static final Validator VR_DISPLAY_MODE_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[]{"0", "1"});
+ new DiscreteValueValidator(new String[]{"0", "1"});
/**
* Lower the display persistence while the system is in VR mode.
@@ -8638,8 +8635,7 @@
*/
public static final String QS_TILES = "sysui_qs_tiles";
- private static final Validator QS_TILES_VALIDATOR =
- new SettingsValidators.TileListValidator();
+ private static final Validator QS_TILES_VALIDATOR = TILE_LIST_VALIDATOR;
/**
* Specifies whether the web action API is enabled.
@@ -8705,8 +8701,7 @@
*/
public static final String QS_AUTO_ADDED_TILES = "qs_auto_tiles";
- private static final Validator QS_AUTO_ADDED_TILES_VALIDATOR =
- new SettingsValidators.TileListValidator();
+ private static final Validator QS_AUTO_ADDED_TILES_VALIDATOR = TILE_LIST_VALIDATOR;
/**
* Whether the Lockdown button should be shown in the power menu.
@@ -8867,7 +8862,7 @@
"theme_customization_overlay_packages";
private static final Validator THEME_CUSTOMIZATION_OVERLAY_PACKAGES_VALIDATOR =
- SettingsValidators.JSON_OBJECT_VALIDATOR;
+ JSON_OBJECT_VALIDATOR;
/**
* Navigation bar mode.
@@ -8879,7 +8874,7 @@
public static final String NAVIGATION_MODE =
"navigation_mode";
private static final Validator NAVIGATION_MODE_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2"});
+ new DiscreteValueValidator(new String[] {"0", "1", "2"});
/**
* Controls whether aware is enabled.
@@ -9182,8 +9177,6 @@
VALIDATORS.put(FACE_UNLOCK_APP_ENABLED, FACE_UNLOCK_APP_ENABLED_VALIDATOR);
VALIDATORS.put(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION,
FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION_VALIDATOR);
- VALIDATORS.put(FACE_UNLOCK_EDUCATION_INFO_DISPLAYED,
- FACE_UNLOCK_EDUCATION_INFO_DISPLAYED_VALIDATOR);
VALIDATORS.put(ASSIST_GESTURE_ENABLED, ASSIST_GESTURE_ENABLED_VALIDATOR);
VALIDATORS.put(ASSIST_GESTURE_SILENCE_ALERTS_ENABLED,
ASSIST_GESTURE_SILENCE_ALERTS_ENABLED_VALIDATOR);
@@ -10583,22 +10576,6 @@
BOOLEAN_VALIDATOR;
/**
- * Whether to notify the user of carrier networks.
- * <p>
- * If not connected and the scan results have a carrier network, we will
- * put this notification up. If we attempt to connect to a network or
- * the carrier network(s) disappear, we remove the notification. When we
- * show the notification, we will not show it again for
- * {@link android.provider.Settings.Global#WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY} time.
- * @hide
- */
- public static final String WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON =
- "wifi_carrier_networks_available_notification_on";
-
- private static final Validator WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR =
- BOOLEAN_VALIDATOR;
-
- /**
* {@hide}
*/
public static final String WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON =
@@ -10733,7 +10710,7 @@
"network_recommendations_enabled";
private static final Validator NETWORK_RECOMMENDATIONS_ENABLED_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"-1", "0", "1"});
+ new DiscreteValueValidator(new String[] {"-1", "0", "1"});
/**
* Which package name to use for network recommendations. If null, network recommendations
@@ -11058,12 +11035,6 @@
public static final String WIFI_P2P_DEVICE_NAME = "wifi_p2p_device_name";
/**
- * The min time between wifi disable and wifi enable
- * @hide
- */
- public static final String WIFI_REENABLE_DELAY_MS = "wifi_reenable_delay";
-
- /**
* Timeout for ephemeral networks when all known BSSIDs go out of range. We will disconnect
* from an ephemeral network if there is no BSSID for that network with a non-null score that
* has been seen in this time period.
@@ -12590,7 +12561,7 @@
public static final String EMERGENCY_TONE = "emergency_tone";
private static final Validator EMERGENCY_TONE_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2"});
+ new DiscreteValueValidator(new String[] {"0", "1", "2"});
/**
* CDMA only settings
@@ -12620,7 +12591,7 @@
"enable_automatic_system_server_heap_dumps";
private static final Validator ENABLE_AUTOMATIC_SYSTEM_SERVER_HEAP_DUMPS_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"});
+ new DiscreteValueValidator(new String[] {"0", "1"});
/**
* See RIL_PreferredNetworkType in ril.h
@@ -12814,7 +12785,7 @@
"low_power_sticky_auto_disable_level";
private static final Validator LOW_POWER_MODE_STICKY_AUTO_DISABLE_LEVEL_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
+ new InclusiveIntegerRangeValidator(0, 100);
/**
* Whether sticky battery saver should be deactivated once the battery level has reached the
@@ -12826,7 +12797,7 @@
"low_power_sticky_auto_disable_enabled";
private static final Validator LOW_POWER_MODE_STICKY_AUTO_DISABLE_ENABLED_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"});
+ new DiscreteValueValidator(new String[] {"0", "1"});
/**
* Battery level [1-100] at which low power mode automatically turns on.
@@ -12841,7 +12812,7 @@
public static final String LOW_POWER_MODE_TRIGGER_LEVEL = "low_power_trigger_level";
private static final Validator LOW_POWER_MODE_TRIGGER_LEVEL_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
+ new InclusiveIntegerRangeValidator(0, 100);
/**
* Whether battery saver is currently set to trigger based on percentage, dynamic power
@@ -12854,7 +12825,7 @@
public static final String AUTOMATIC_POWER_SAVE_MODE = "automatic_power_save_mode";
private static final Validator AUTOMATIC_POWER_SAVE_MODE_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1"});
+ new DiscreteValueValidator(new String[] {"0", "1"});
/**
* The setting that backs the disable threshold for the setPowerSavingsWarning api in
@@ -12867,7 +12838,7 @@
public static final String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD =
"dynamic_power_savings_disable_threshold";
private static final Validator DYNAMIC_POWER_SAVINGS_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 100);
+ new InclusiveIntegerRangeValidator(0, 100);
/**
* The setting which backs the setDynamicPowerSaveHint api in PowerManager.
@@ -13017,7 +12988,7 @@
public static final String ENCODED_SURROUND_OUTPUT = "encoded_surround_output";
private static final Validator ENCODED_SURROUND_OUTPUT_VALIDATOR =
- new SettingsValidators.DiscreteValueValidator(new String[] {"0", "1", "2", "3"});
+ new DiscreteValueValidator(new String[] {"0", "1", "2", "3"});
/**
* Surround sounds formats that are enabled when ENCODED_SURROUND_OUTPUT is set to
@@ -13768,7 +13739,7 @@
public static final String POWER_BUTTON_LONG_PRESS =
"power_button_long_press";
private static final Validator POWER_BUTTON_LONG_PRESS_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 5);
+ new InclusiveIntegerRangeValidator(0, 5);
/**
* Overrides internal R.integer.config_veryLongPressOnPowerBehavior.
@@ -13779,7 +13750,7 @@
public static final String POWER_BUTTON_VERY_LONG_PRESS =
"power_button_very_long_press";
private static final Validator POWER_BUTTON_VERY_LONG_PRESS_VALIDATOR =
- new SettingsValidators.InclusiveIntegerRangeValidator(0, 1);
+ new InclusiveIntegerRangeValidator(0, 1);
/**
* Settings to backup. This is here so that it's in the same place as the settings
@@ -13814,7 +13785,6 @@
NETWORK_RECOMMENDATIONS_ENABLED,
WIFI_WAKEUP_ENABLED,
WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
- WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON,
USE_OPEN_WIFI_PACKAGE,
WIFI_WATCHDOG_POOR_NETWORK_TEST_ENABLED,
EMERGENCY_TONE,
@@ -13883,8 +13853,6 @@
VALIDATORS.put(PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_VALIDATOR);
VALIDATORS.put(PRIVATE_DNS_SPECIFIER, PRIVATE_DNS_SPECIFIER_VALIDATOR);
VALIDATORS.put(SOFT_AP_TIMEOUT_ENABLED, SOFT_AP_TIMEOUT_ENABLED_VALIDATOR);
- VALIDATORS.put(WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON,
- WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON_VALIDATOR);
VALIDATORS.put(WIFI_SCAN_THROTTLE_ENABLED, WIFI_SCAN_THROTTLE_ENABLED_VALIDATOR);
VALIDATORS.put(APP_AUTO_RESTRICTION_ENABLED, APP_AUTO_RESTRICTION_ENABLED_VALIDATOR);
VALIDATORS.put(ZEN_DURATION, ZEN_DURATION_VALIDATOR);
diff --git a/core/java/android/provider/SettingsValidators.java b/core/java/android/provider/SettingsValidators.java
deleted file mode 100644
index e4cf9fd..0000000
--- a/core/java/android/provider/SettingsValidators.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.provider;
-
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.net.Uri;
-import android.text.TextUtils;
-
-import com.android.internal.util.ArrayUtils;
-
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.Locale;
-
-/**
- * This class provides both interface for validation and common validators
- * used to ensure Settings have meaningful values.
- *
- * @hide
- */
-public class SettingsValidators {
-
- public static final Validator BOOLEAN_VALIDATOR =
- new DiscreteValueValidator(new String[] {"0", "1"});
-
- public static final Validator ANY_STRING_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- return true;
- }
- };
-
- public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- try {
- return Integer.parseInt(value) >= 0;
- } catch (NumberFormatException e) {
- return false;
- }
- }
- };
-
- public static final Validator ANY_INTEGER_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- try {
- Integer.parseInt(value);
- return true;
- } catch (NumberFormatException e) {
- return false;
- }
- }
- };
-
- public static final Validator URI_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- try {
- Uri.decode(value);
- return true;
- } catch (IllegalArgumentException e) {
- return false;
- }
- }
- };
-
- /**
- * Does not allow a setting to have a null {@link ComponentName}. Use {@link
- * SettingsValidators#NULLABLE_COMPONENT_NAME_VALIDATOR} instead if a setting can have a
- * nullable {@link ComponentName}.
- */
- public static final Validator COMPONENT_NAME_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- return value != null && ComponentName.unflattenFromString(value) != null;
- }
- };
-
- /**
- * Allows a setting to have a null {@link ComponentName}.
- */
- public static final Validator NULLABLE_COMPONENT_NAME_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- return value == null || COMPONENT_NAME_VALIDATOR.validate(value);
- }
- };
-
- public static final Validator PACKAGE_NAME_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- return value != null && isStringPackageName(value);
- }
-
- private boolean isStringPackageName(String value) {
- // The name may contain uppercase or lowercase letters ('A' through 'Z'), numbers,
- // and underscores ('_'). However, individual package name parts may only
- // start with letters.
- // (https://developer.android.com/guide/topics/manifest/manifest-element.html#package)
- if (value == null) {
- return false;
- }
- String[] subparts = value.split("\\.");
- boolean isValidPackageName = true;
- for (String subpart : subparts) {
- isValidPackageName &= isSubpartValidForPackageName(subpart);
- if (!isValidPackageName) break;
- }
- return isValidPackageName;
- }
-
- private boolean isSubpartValidForPackageName(String subpart) {
- if (subpart.length() == 0) return false;
- boolean isValidSubpart = Character.isLetter(subpart.charAt(0));
- for (int i = 1; i < subpart.length(); i++) {
- isValidSubpart &= (Character.isLetterOrDigit(subpart.charAt(i))
- || (subpart.charAt(i) == '_'));
- if (!isValidSubpart) break;
- }
- return isValidSubpart;
- }
- };
-
- public static final Validator LENIENT_IP_ADDRESS_VALIDATOR = new Validator() {
- private static final int MAX_IPV6_LENGTH = 45;
-
- @Override
- public boolean validate(@Nullable String value) {
- if (value == null) {
- return false;
- }
- return value.length() <= MAX_IPV6_LENGTH;
- }
- };
-
- public static final Validator LOCALE_VALIDATOR = new Validator() {
- @Override
- public boolean validate(@Nullable String value) {
- if (value == null) {
- return false;
- }
- Locale[] validLocales = Locale.getAvailableLocales();
- for (Locale locale : validLocales) {
- if (value.equals(locale.toString())) {
- return true;
- }
- }
- return false;
- }
- };
-
- /** {@link Validator} that checks whether a value is a valid {@link JSONObject}. */
- public static final Validator JSON_OBJECT_VALIDATOR = (value) -> {
- if (TextUtils.isEmpty(value)) {
- return false;
- }
- try {
- new JSONObject(value);
- return true;
- } catch (JSONException e) {
- return false;
- }
- };
-
- public interface Validator {
- /**
- * Returns whether the input value is valid. Subclasses should handle the case where the
- * input value is {@code null}.
- */
- boolean validate(@Nullable String value);
- }
-
- public static final class DiscreteValueValidator implements Validator {
- private final String[] mValues;
-
- public DiscreteValueValidator(String[] values) {
- mValues = values;
- }
-
- @Override
- public boolean validate(@Nullable String value) {
- return ArrayUtils.contains(mValues, value);
- }
- }
-
- public static final class InclusiveIntegerRangeValidator implements Validator {
- private final int mMin;
- private final int mMax;
-
- public InclusiveIntegerRangeValidator(int min, int max) {
- mMin = min;
- mMax = max;
- }
-
- @Override
- public boolean validate(@Nullable String value) {
- try {
- final int intValue = Integer.parseInt(value);
- return intValue >= mMin && intValue <= mMax;
- } catch (NumberFormatException e) {
- return false;
- }
- }
- }
-
- public static final class InclusiveFloatRangeValidator implements Validator {
- private final float mMin;
- private final float mMax;
-
- public InclusiveFloatRangeValidator(float min, float max) {
- mMin = min;
- mMax = max;
- }
-
- @Override
- public boolean validate(@Nullable String value) {
- try {
- final float floatValue = Float.parseFloat(value);
- return floatValue >= mMin && floatValue <= mMax;
- } catch (NumberFormatException | NullPointerException e) {
- return false;
- }
- }
- }
-
- public static final class ComponentNameListValidator implements Validator {
- private final String mSeparator;
-
- public ComponentNameListValidator(String separator) {
- mSeparator = separator;
- }
-
- @Override
- public boolean validate(@Nullable String value) {
- if (value == null) {
- return false;
- }
- String[] elements = value.split(mSeparator);
- for (String element : elements) {
- if (!COMPONENT_NAME_VALIDATOR.validate(element)) {
- return false;
- }
- }
- return true;
- }
- }
-
- public static final class PackageNameListValidator implements Validator {
- private final String mSeparator;
-
- public PackageNameListValidator(String separator) {
- mSeparator = separator;
- }
-
- @Override
- public boolean validate(@Nullable String value) {
- if (value == null) {
- return false;
- }
- String[] elements = value.split(mSeparator);
- for (String element : elements) {
- if (!PACKAGE_NAME_VALIDATOR.validate(element)) {
- return false;
- }
- }
- return true;
- }
- }
-
- private abstract static class ListValidator implements Validator {
- public boolean validate(@Nullable String value) {
- if (!isEntryValid(value)) {
- return false;
- }
- String[] items = value.split(",");
- for (String item : items) {
- if (!isItemValid(item)) {
- return false;
- }
- }
- return true;
- }
-
- protected abstract boolean isEntryValid(String entry);
-
- protected abstract boolean isItemValid(String item);
- }
-
- /** Ensure a restored value is a string in the format the text-to-speech system handles */
- public static final class TTSListValidator extends ListValidator {
- protected boolean isEntryValid(String entry) {
- return entry != null && entry.length() > 0;
- }
-
- protected boolean isItemValid(String item) {
- String[] parts = item.split(":");
- // Replaces any old language separator (-) with the new one (_)
- return ((parts.length == 2)
- && (parts[0].length() > 0)
- && ANY_STRING_VALIDATOR.validate(parts[0])
- && LOCALE_VALIDATOR.validate(parts[1].replace('-', '_')));
- }
- }
-
- /** Ensure a restored value is suitable to be used as a tile name */
- public static final class TileListValidator extends ListValidator {
- protected boolean isEntryValid(String entry) {
- return entry != null;
- }
-
- protected boolean isItemValid(String item) {
- return item.length() > 0 && ANY_STRING_VALIDATOR.validate(item);
- }
- }
-}
diff --git a/core/java/android/provider/settings/validators/ComponentNameListValidator.java b/core/java/android/provider/settings/validators/ComponentNameListValidator.java
new file mode 100644
index 0000000..b6b867a
--- /dev/null
+++ b/core/java/android/provider/settings/validators/ComponentNameListValidator.java
@@ -0,0 +1,40 @@
+/*
+ * 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 android.provider.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.COMPONENT_NAME_VALIDATOR;
+
+/**
+ * Validate a list of compoments.
+ *
+ * @hide
+ */
+public final class ComponentNameListValidator extends ListValidator {
+ public ComponentNameListValidator(String separator) {
+ super(separator);
+ }
+
+ @Override
+ protected boolean isEntryValid(String entry) {
+ return entry != null;
+ }
+
+ @Override
+ protected boolean isItemValid(String item) {
+ return COMPONENT_NAME_VALIDATOR.validate(item);
+ }
+}
diff --git a/core/java/android/provider/settings/validators/DiscreteValueValidator.java b/core/java/android/provider/settings/validators/DiscreteValueValidator.java
new file mode 100644
index 0000000..183651f
--- /dev/null
+++ b/core/java/android/provider/settings/validators/DiscreteValueValidator.java
@@ -0,0 +1,39 @@
+/*
+ * 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 android.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * Validate a value exists in an array of known good values
+ *
+ * @hide
+ */
+public final class DiscreteValueValidator implements Validator {
+ private final String[] mValues;
+
+ public DiscreteValueValidator(String[] values) {
+ mValues = values;
+ }
+
+ @Override
+ public boolean validate(@Nullable String value) {
+ return ArrayUtils.contains(mValues, value);
+ }
+}
diff --git a/core/java/android/provider/settings/validators/InclusiveFloatRangeValidator.java b/core/java/android/provider/settings/validators/InclusiveFloatRangeValidator.java
new file mode 100644
index 0000000..38400ac
--- /dev/null
+++ b/core/java/android/provider/settings/validators/InclusiveFloatRangeValidator.java
@@ -0,0 +1,44 @@
+/*
+ * 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 android.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Validate a float value lies within a given (boundary inclusive) range.
+ *
+ * @hide
+ */
+public final class InclusiveFloatRangeValidator implements Validator {
+ private final float mMin;
+ private final float mMax;
+
+ public InclusiveFloatRangeValidator(float min, float max) {
+ mMin = min;
+ mMax = max;
+ }
+
+ @Override
+ public boolean validate(@Nullable String value) {
+ try {
+ final float floatValue = Float.parseFloat(value);
+ return floatValue >= mMin && floatValue <= mMax;
+ } catch (NumberFormatException | NullPointerException e) {
+ return false;
+ }
+ }
+}
diff --git a/core/java/android/provider/settings/validators/InclusiveIntegerRangeValidator.java b/core/java/android/provider/settings/validators/InclusiveIntegerRangeValidator.java
new file mode 100644
index 0000000..e53c252
--- /dev/null
+++ b/core/java/android/provider/settings/validators/InclusiveIntegerRangeValidator.java
@@ -0,0 +1,44 @@
+/*
+ * 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 android.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Validate an integer value lies within a given (boundary inclusive) range.
+ *
+ * @hide
+ */
+public final class InclusiveIntegerRangeValidator implements Validator {
+ private final int mMin;
+ private final int mMax;
+
+ public InclusiveIntegerRangeValidator(int min, int max) {
+ mMin = min;
+ mMax = max;
+ }
+
+ @Override
+ public boolean validate(@Nullable String value) {
+ try {
+ final int intValue = Integer.parseInt(value);
+ return intValue >= mMin && intValue <= mMax;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+}
diff --git a/core/java/android/provider/settings/validators/ListValidator.java b/core/java/android/provider/settings/validators/ListValidator.java
new file mode 100644
index 0000000..a6001d2
--- /dev/null
+++ b/core/java/android/provider/settings/validators/ListValidator.java
@@ -0,0 +1,51 @@
+/*
+ * 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 android.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Validate the elements in a list.
+ *
+ * @hide
+ */
+abstract class ListValidator implements Validator {
+
+ private String mListSplitRegex;
+
+ ListValidator(String listSplitRegex) {
+ mListSplitRegex = listSplitRegex;
+ }
+
+ public boolean validate(@Nullable String value) {
+ if (!isEntryValid(value)) {
+ return false;
+ }
+ String[] items = value.split(",");
+ for (String item : items) {
+ if (!isItemValid(item)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected abstract boolean isEntryValid(String entry);
+
+ protected abstract boolean isItemValid(String item);
+}
+
diff --git a/core/java/android/provider/settings/validators/PackageNameListValidator.java b/core/java/android/provider/settings/validators/PackageNameListValidator.java
new file mode 100644
index 0000000..bc7fc13
--- /dev/null
+++ b/core/java/android/provider/settings/validators/PackageNameListValidator.java
@@ -0,0 +1,40 @@
+/*
+ * 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 android.provider.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.PACKAGE_NAME_VALIDATOR;
+
+/**
+ * Validate a list of package names.
+ *
+ * @hide
+ */
+public final class PackageNameListValidator extends ListValidator {
+ public PackageNameListValidator(String separator) {
+ super(separator);
+ }
+
+ @Override
+ protected boolean isEntryValid(String entry) {
+ return entry != null;
+ }
+
+ @Override
+ protected boolean isItemValid(String item) {
+ return PACKAGE_NAME_VALIDATOR.validate(item);
+ }
+}
diff --git a/core/java/android/provider/settings/validators/SettingsValidators.java b/core/java/android/provider/settings/validators/SettingsValidators.java
new file mode 100644
index 0000000..562c638
--- /dev/null
+++ b/core/java/android/provider/settings/validators/SettingsValidators.java
@@ -0,0 +1,183 @@
+/*
+ * 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 android.provider.settings.validators;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Locale;
+
+/**
+ * This class provides both interface for validation and common validators
+ * used to ensure Settings have meaningful values.
+ *
+ * @hide
+ */
+public class SettingsValidators {
+
+ public static final Validator BOOLEAN_VALIDATOR =
+ new DiscreteValueValidator(new String[] {"0", "1"});
+
+ public static final Validator ANY_STRING_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ return true;
+ }
+ };
+
+ public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ try {
+ return Integer.parseInt(value) >= 0;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+ };
+
+ public static final Validator ANY_INTEGER_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ try {
+ Integer.parseInt(value);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+ };
+
+ public static final Validator URI_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ try {
+ Uri.decode(value);
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+ };
+
+ /**
+ * Does not allow a setting to have a null {@link ComponentName}. Use {@link
+ * SettingsValidators#NULLABLE_COMPONENT_NAME_VALIDATOR} instead if a setting can have a
+ * nullable {@link ComponentName}.
+ */
+ public static final Validator COMPONENT_NAME_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ return value != null && ComponentName.unflattenFromString(value) != null;
+ }
+ };
+
+ /**
+ * Allows a setting to have a null {@link ComponentName}.
+ */
+ public static final Validator NULLABLE_COMPONENT_NAME_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ return value == null || COMPONENT_NAME_VALIDATOR.validate(value);
+ }
+ };
+
+ public static final Validator PACKAGE_NAME_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ return value != null && isStringPackageName(value);
+ }
+
+ private boolean isStringPackageName(String value) {
+ // The name may contain uppercase or lowercase letters ('A' through 'Z'), numbers,
+ // and underscores ('_'). However, individual package name parts may only
+ // start with letters.
+ // (https://developer.android.com/guide/topics/manifest/manifest-element.html#package)
+ if (value == null) {
+ return false;
+ }
+ String[] subparts = value.split("\\.");
+ boolean isValidPackageName = true;
+ for (String subpart : subparts) {
+ isValidPackageName &= isSubpartValidForPackageName(subpart);
+ if (!isValidPackageName) break;
+ }
+ return isValidPackageName;
+ }
+
+ private boolean isSubpartValidForPackageName(String subpart) {
+ if (subpart.length() == 0) return false;
+ boolean isValidSubpart = Character.isLetter(subpart.charAt(0));
+ for (int i = 1; i < subpart.length(); i++) {
+ isValidSubpart &= (Character.isLetterOrDigit(subpart.charAt(i))
+ || (subpart.charAt(i) == '_'));
+ if (!isValidSubpart) break;
+ }
+ return isValidSubpart;
+ }
+ };
+
+ public static final Validator LENIENT_IP_ADDRESS_VALIDATOR = new Validator() {
+ private static final int MAX_IPV6_LENGTH = 45;
+
+ @Override
+ public boolean validate(@Nullable String value) {
+ if (value == null) {
+ return false;
+ }
+ return value.length() <= MAX_IPV6_LENGTH;
+ }
+ };
+
+ public static final Validator LOCALE_VALIDATOR = new Validator() {
+ @Override
+ public boolean validate(@Nullable String value) {
+ if (value == null) {
+ return false;
+ }
+ Locale[] validLocales = Locale.getAvailableLocales();
+ for (Locale locale : validLocales) {
+ if (value.equals(locale.toString())) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ /** {@link Validator} that checks whether a value is a valid {@link JSONObject}. */
+ public static final Validator JSON_OBJECT_VALIDATOR = (value) -> {
+ if (TextUtils.isEmpty(value)) {
+ return false;
+ }
+ try {
+ new JSONObject(value);
+ return true;
+ } catch (JSONException e) {
+ return false;
+ }
+ };
+
+ public static final Validator TTS_LIST_VALIDATOR = new TTSListValidator();
+
+ public static final Validator TILE_LIST_VALIDATOR = new TileListValidator();
+}
diff --git a/core/java/android/provider/settings/validators/TTSListValidator.java b/core/java/android/provider/settings/validators/TTSListValidator.java
new file mode 100644
index 0000000..6c73471
--- /dev/null
+++ b/core/java/android/provider/settings/validators/TTSListValidator.java
@@ -0,0 +1,45 @@
+/*
+ * 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 android.provider.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
+import static android.provider.settings.validators.SettingsValidators.LOCALE_VALIDATOR;
+
+/**
+ * Ensure a restored value is a string in the format the text-to-speech system handles
+ *
+ * @hide
+ */
+final class TTSListValidator extends ListValidator {
+
+ TTSListValidator() {
+ super(",");
+ }
+
+ protected boolean isEntryValid(String entry) {
+ return entry != null && entry.length() > 0;
+ }
+
+ protected boolean isItemValid(String item) {
+ String[] parts = item.split(":");
+ // Replaces any old language separator (-) with the new one (_)
+ return ((parts.length == 2)
+ && (parts[0].length() > 0)
+ && ANY_STRING_VALIDATOR.validate(parts[0])
+ && LOCALE_VALIDATOR.validate(parts[1].replace('-', '_')));
+ }
+}
diff --git a/core/java/android/provider/settings/validators/TileListValidator.java b/core/java/android/provider/settings/validators/TileListValidator.java
new file mode 100644
index 0000000..c696442
--- /dev/null
+++ b/core/java/android/provider/settings/validators/TileListValidator.java
@@ -0,0 +1,38 @@
+/*
+ * 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 android.provider.settings.validators;
+
+import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
+
+/**
+ * Ensure a restored value is suitable to be used as a tile name
+ *
+ * @hide
+ */
+final class TileListValidator extends ListValidator {
+ TileListValidator() {
+ super(",");
+ }
+
+ protected boolean isEntryValid(String entry) {
+ return entry != null;
+ }
+
+ protected boolean isItemValid(String item) {
+ return item.length() > 0 && ANY_STRING_VALIDATOR.validate(item);
+ }
+}
diff --git a/core/java/android/provider/settings/validators/Validator.java b/core/java/android/provider/settings/validators/Validator.java
new file mode 100644
index 0000000..393a03d
--- /dev/null
+++ b/core/java/android/provider/settings/validators/Validator.java
@@ -0,0 +1,32 @@
+/*
+ * 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 android.provider.settings.validators;
+
+import android.annotation.Nullable;
+
+/**
+ * Interface for a settings value validator.
+ *
+ * @hide
+ */
+public interface Validator {
+ /**
+ * Returns whether the input value is valid. Subclasses should handle the case where the
+ * input value is {@code null}.
+ */
+ boolean validate(@Nullable String value);
+}
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 100774c..44446ad0 100644
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -16,6 +16,7 @@
package android.speech.tts;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RawRes;
import android.annotation.SdkConstant;
@@ -858,23 +859,20 @@
}
// Post connection case
- runActionNoReconnect(new Action<Void>() {
- @Override
- public Void run(ITextToSpeechService service) throws RemoteException {
- service.setCallback(getCallerIdentity(), null);
- service.stop(getCallerIdentity());
- mServiceConnection.disconnect();
- // Context#unbindService does not result in a call to
- // ServiceConnection#onServiceDisconnected. As a result, the
- // service ends up being destroyed (if there are no other open
- // connections to it) but the process lives on and the
- // ServiceConnection continues to refer to the destroyed service.
- //
- // This leads to tons of log spam about SynthThread being dead.
- mServiceConnection = null;
- mCurrentEngine = null;
- return null;
- }
+ runActionNoReconnect((ITextToSpeechService service) -> {
+ service.setCallback(getCallerIdentity(), null);
+ service.stop(getCallerIdentity());
+ mServiceConnection.disconnect();
+ // Context#unbindService does not result in a call to
+ // ServiceConnection#onServiceDisconnected. As a result, the
+ // service ends up being destroyed (if there are no other open
+ // connections to it) but the process lives on and the
+ // ServiceConnection continues to refer to the destroyed service.
+ //
+ // This leads to tons of log spam about SynthThread being dead.
+ mServiceConnection = null;
+ mCurrentEngine = null;
+ return null;
}, null, "shutdown", false);
}
@@ -1105,17 +1103,14 @@
final int queueMode,
final Bundle params,
final String utteranceId) {
- return runAction(new Action<Integer>() {
- @Override
- public Integer run(ITextToSpeechService service) throws RemoteException {
- Uri utteranceUri = mUtterances.get(text);
- if (utteranceUri != null) {
- return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
- getParams(params), utteranceId);
- } else {
- return service.speak(getCallerIdentity(), text, queueMode, getParams(params),
- utteranceId);
- }
+ return runAction((ITextToSpeechService service) -> {
+ Uri utteranceUri = mUtterances.get(text);
+ if (utteranceUri != null) {
+ return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
+ getParams(params), utteranceId);
+ } else {
+ return service.speak(getCallerIdentity(), text, queueMode, getParams(params),
+ utteranceId);
}
}, ERROR, "speak");
}
@@ -1178,16 +1173,13 @@
*/
public int playEarcon(final String earcon, final int queueMode,
final Bundle params, final String utteranceId) {
- return runAction(new Action<Integer>() {
- @Override
- public Integer run(ITextToSpeechService service) throws RemoteException {
- Uri earconUri = mEarcons.get(earcon);
- if (earconUri == null) {
- return ERROR;
- }
- return service.playAudio(getCallerIdentity(), earconUri, queueMode,
- getParams(params), utteranceId);
+ return runAction((ITextToSpeechService service) -> {
+ Uri earconUri = mEarcons.get(earcon);
+ if (earconUri == null) {
+ return ERROR;
}
+ return service.playAudio(getCallerIdentity(), earconUri, queueMode,
+ getParams(params), utteranceId);
}, ERROR, "playEarcon");
}
@@ -1242,12 +1234,9 @@
*/
public int playSilentUtterance(final long durationInMs, final int queueMode,
final String utteranceId) {
- return runAction(new Action<Integer>() {
- @Override
- public Integer run(ITextToSpeechService service) throws RemoteException {
- return service.playSilence(getCallerIdentity(), durationInMs,
- queueMode, utteranceId);
- }
+ return runAction((ITextToSpeechService service) -> {
+ return service.playSilence(getCallerIdentity(), durationInMs,
+ queueMode, utteranceId);
}, ERROR, "playSilentUtterance");
}
@@ -1302,26 +1291,23 @@
*/
@Deprecated
public Set<String> getFeatures(final Locale locale) {
- return runAction(new Action<Set<String>>() {
- @Override
- public Set<String> run(ITextToSpeechService service) throws RemoteException {
- String[] features = null;
- try {
- features = service.getFeaturesForLanguage(
- locale.getISO3Language(), locale.getISO3Country(), locale.getVariant());
- } catch(MissingResourceException e) {
- Log.w(TAG, "Couldn't retrieve 3 letter ISO 639-2/T language and/or ISO 3166 " +
- "country code for locale: " + locale, e);
- return null;
- }
-
- if (features != null) {
- final Set<String> featureSet = new HashSet<String>();
- Collections.addAll(featureSet, features);
- return featureSet;
- }
+ return runAction((ITextToSpeechService service) -> {
+ String[] features = null;
+ try {
+ features = service.getFeaturesForLanguage(
+ locale.getISO3Language(), locale.getISO3Country(), locale.getVariant());
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve 3 letter ISO 639-2/T language and/or ISO 3166 "
+ + "country code for locale: " + locale, e);
return null;
}
+
+ if (features != null) {
+ final Set<String> featureSet = new HashSet<String>();
+ Collections.addAll(featureSet, features);
+ return featureSet;
+ }
+ return null;
}, null, "getFeatures");
}
@@ -1334,11 +1320,8 @@
* @return {@code true} if the TTS engine is speaking.
*/
public boolean isSpeaking() {
- return runAction(new Action<Boolean>() {
- @Override
- public Boolean run(ITextToSpeechService service) throws RemoteException {
- return service.isSpeaking();
- }
+ return runAction((ITextToSpeechService service) -> {
+ return service.isSpeaking();
}, false, "isSpeaking");
}
@@ -1349,11 +1332,8 @@
* @return {@link #ERROR} or {@link #SUCCESS}.
*/
public int stop() {
- return runAction(new Action<Integer>() {
- @Override
- public Integer run(ITextToSpeechService service) throws RemoteException {
- return service.stop(getCallerIdentity());
- }
+ return runAction((ITextToSpeechService service) -> {
+ return service.stop(getCallerIdentity());
}, ERROR, "stop");
}
@@ -1447,13 +1427,10 @@
*/
@Deprecated
public Locale getDefaultLanguage() {
- return runAction(new Action<Locale>() {
- @Override
- public Locale run(ITextToSpeechService service) throws RemoteException {
- String[] defaultLanguage = service.getClientDefaultLanguage();
+ return runAction((ITextToSpeechService service) -> {
+ String[] defaultLanguage = service.getClientDefaultLanguage();
- return new Locale(defaultLanguage[0], defaultLanguage[1], defaultLanguage[2]);
- }
+ return new Locale(defaultLanguage[0], defaultLanguage[1], defaultLanguage[2]);
}, null, "getDefaultLanguage");
}
@@ -1474,83 +1451,80 @@
* {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
*/
public int setLanguage(final Locale loc) {
- return runAction(new Action<Integer>() {
- @Override
- public Integer run(ITextToSpeechService service) throws RemoteException {
- if (loc == null) {
- return LANG_NOT_SUPPORTED;
- }
- String language = null, country = null;
- try {
- language = loc.getISO3Language();
- } catch (MissingResourceException e) {
- Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
- return LANG_NOT_SUPPORTED;
- }
-
- try {
- country = loc.getISO3Country();
- } catch (MissingResourceException e) {
- Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
- return LANG_NOT_SUPPORTED;
- }
-
- String variant = loc.getVariant();
-
- // As of API level 21, setLanguage is implemented using setVoice.
- // (which, in the default implementation, will call loadLanguage on the service
- // interface).
-
- // Sanitize locale using isLanguageAvailable.
- int result = service.isLanguageAvailable(language, country, variant);
- if (result >= LANG_AVAILABLE) {
- // Get the default voice for the locale.
- String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
- if (TextUtils.isEmpty(voiceName)) {
- Log.w(TAG, "Couldn't find the default voice for " + language + "-" +
- country + "-" + variant);
- return LANG_NOT_SUPPORTED;
- }
-
- // Load it.
- if (service.loadVoice(getCallerIdentity(), voiceName) == TextToSpeech.ERROR) {
- Log.w(TAG, "The service claimed " + language + "-" + country + "-"
- + variant + " was available with voice name " + voiceName
- + " but loadVoice returned ERROR");
- return LANG_NOT_SUPPORTED;
- }
-
- // Set the language/country/variant of the voice, so #getLanguage will return
- // the currently set voice locale when called.
- Voice voice = getVoice(service, voiceName);
- if (voice == null) {
- Log.w(TAG, "getDefaultVoiceNameFor returned " + voiceName + " for locale "
- + language + "-" + country + "-" + variant
- + " but getVoice returns null");
- return LANG_NOT_SUPPORTED;
- }
- String voiceLanguage = "";
- try {
- voiceLanguage = voice.getLocale().getISO3Language();
- } catch (MissingResourceException e) {
- Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " +
- voice.getLocale(), e);
- }
-
- String voiceCountry = "";
- try {
- voiceCountry = voice.getLocale().getISO3Country();
- } catch (MissingResourceException e) {
- Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " +
- voice.getLocale(), e);
- }
- mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voiceName);
- mParams.putString(Engine.KEY_PARAM_LANGUAGE, voiceLanguage);
- mParams.putString(Engine.KEY_PARAM_COUNTRY, voiceCountry);
- mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
- }
- return result;
+ return runAction((ITextToSpeechService service) -> {
+ if (loc == null) {
+ return LANG_NOT_SUPPORTED;
}
+ String language = null, country = null;
+ try {
+ language = loc.getISO3Language();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
+ return LANG_NOT_SUPPORTED;
+ }
+
+ try {
+ country = loc.getISO3Country();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
+ return LANG_NOT_SUPPORTED;
+ }
+
+ String variant = loc.getVariant();
+
+ // As of API level 21, setLanguage is implemented using setVoice.
+ // (which, in the default implementation, will call loadLanguage on the service
+ // interface).
+
+ // Sanitize locale using isLanguageAvailable.
+ int result = service.isLanguageAvailable(language, country, variant);
+ if (result >= LANG_AVAILABLE) {
+ // Get the default voice for the locale.
+ String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
+ if (TextUtils.isEmpty(voiceName)) {
+ Log.w(TAG, "Couldn't find the default voice for " + language + "-"
+ + country + "-" + variant);
+ return LANG_NOT_SUPPORTED;
+ }
+
+ // Load it.
+ if (service.loadVoice(getCallerIdentity(), voiceName) == TextToSpeech.ERROR) {
+ Log.w(TAG, "The service claimed " + language + "-" + country + "-"
+ + variant + " was available with voice name " + voiceName
+ + " but loadVoice returned ERROR");
+ return LANG_NOT_SUPPORTED;
+ }
+
+ // Set the language/country/variant of the voice, so #getLanguage will return
+ // the currently set voice locale when called.
+ Voice voice = getVoice(service, voiceName);
+ if (voice == null) {
+ Log.w(TAG, "getDefaultVoiceNameFor returned " + voiceName + " for locale "
+ + language + "-" + country + "-" + variant
+ + " but getVoice returns null");
+ return LANG_NOT_SUPPORTED;
+ }
+ String voiceLanguage = "";
+ try {
+ voiceLanguage = voice.getLocale().getISO3Language();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: "
+ + voice.getLocale(), e);
+ }
+
+ String voiceCountry = "";
+ try {
+ voiceCountry = voice.getLocale().getISO3Country();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: "
+ + voice.getLocale(), e);
+ }
+ mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voiceName);
+ mParams.putString(Engine.KEY_PARAM_LANGUAGE, voiceLanguage);
+ mParams.putString(Engine.KEY_PARAM_COUNTRY, voiceCountry);
+ mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
+ }
+ return result;
}, LANG_NOT_SUPPORTED, "setLanguage");
}
@@ -1582,16 +1556,13 @@
*/
@Deprecated
public Locale getLanguage() {
- return runAction(new Action<Locale>() {
- @Override
- public Locale run(ITextToSpeechService service) {
- /* No service call, but we're accessing mParams, hence need for
- wrapping it as an Action instance */
- String lang = mParams.getString(Engine.KEY_PARAM_LANGUAGE, "");
- String country = mParams.getString(Engine.KEY_PARAM_COUNTRY, "");
- String variant = mParams.getString(Engine.KEY_PARAM_VARIANT, "");
- return new Locale(lang, country, variant);
- }
+ return runAction((ITextToSpeechService service) -> {
+ /* No service call, but we're accessing mParams, hence need for
+ wrapping it as an Action instance */
+ String lang = mParams.getString(Engine.KEY_PARAM_LANGUAGE, "");
+ String country = mParams.getString(Engine.KEY_PARAM_COUNTRY, "");
+ String variant = mParams.getString(Engine.KEY_PARAM_VARIANT, "");
+ return new Locale(lang, country, variant);
}, null, "getLanguage");
}
@@ -1599,19 +1570,16 @@
* Query the engine about the set of available languages.
*/
public Set<Locale> getAvailableLanguages() {
- return runAction(new Action<Set<Locale>>() {
- @Override
- public Set<Locale> run(ITextToSpeechService service) throws RemoteException {
- List<Voice> voices = service.getVoices();
- if (voices == null) {
- return new HashSet<Locale>();
- }
- HashSet<Locale> locales = new HashSet<Locale>();
- for (Voice voice : voices) {
- locales.add(voice.getLocale());
- }
- return locales;
+ return runAction((ITextToSpeechService service) -> {
+ List<Voice> voices = service.getVoices();
+ if (voices == null) {
+ return new HashSet<Locale>();
}
+ HashSet<Locale> locales = new HashSet<Locale>();
+ for (Voice voice : voices) {
+ locales.add(voice.getLocale());
+ }
+ return locales;
}, null, "getAvailableLanguages");
}
@@ -1625,12 +1593,9 @@
* @see Voice
*/
public Set<Voice> getVoices() {
- return runAction(new Action<Set<Voice>>() {
- @Override
- public Set<Voice> run(ITextToSpeechService service) throws RemoteException {
- List<Voice> voices = service.getVoices();
- return (voices != null) ? new HashSet<Voice>(voices) : new HashSet<Voice>();
- }
+ return runAction((ITextToSpeechService service) -> {
+ List<Voice> voices = service.getVoices();
+ return (voices != null) ? new HashSet<Voice>(voices) : new HashSet<Voice>();
}, null, "getVoices");
}
@@ -1645,36 +1610,33 @@
* @see Voice
*/
public int setVoice(final Voice voice) {
- return runAction(new Action<Integer>() {
- @Override
- public Integer run(ITextToSpeechService service) throws RemoteException {
- int result = service.loadVoice(getCallerIdentity(), voice.getName());
- if (result == SUCCESS) {
- mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voice.getName());
+ return runAction((ITextToSpeechService service) -> {
+ int result = service.loadVoice(getCallerIdentity(), voice.getName());
+ if (result == SUCCESS) {
+ mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voice.getName());
- // Set the language/country/variant, so #getLanguage will return the voice
- // locale when called.
- String language = "";
- try {
- language = voice.getLocale().getISO3Language();
- } catch (MissingResourceException e) {
- Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " +
- voice.getLocale(), e);
- }
-
- String country = "";
- try {
- country = voice.getLocale().getISO3Country();
- } catch (MissingResourceException e) {
- Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " +
- voice.getLocale(), e);
- }
- mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
- mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
- mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
+ // Set the language/country/variant, so #getLanguage will return the voice
+ // locale when called.
+ String language = "";
+ try {
+ language = voice.getLocale().getISO3Language();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: "
+ + voice.getLocale(), e);
}
- return result;
+
+ String country = "";
+ try {
+ country = voice.getLocale().getISO3Country();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: "
+ + voice.getLocale(), e);
+ }
+ mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
+ mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
+ mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
}
+ return result;
}, LANG_NOT_SUPPORTED, "setVoice");
}
@@ -1689,15 +1651,12 @@
* @see Voice
*/
public Voice getVoice() {
- return runAction(new Action<Voice>() {
- @Override
- public Voice run(ITextToSpeechService service) throws RemoteException {
- String voiceName = mParams.getString(Engine.KEY_PARAM_VOICE_NAME, "");
- if (TextUtils.isEmpty(voiceName)) {
- return null;
- }
- return getVoice(service, voiceName);
+ return runAction((ITextToSpeechService service) -> {
+ String voiceName = mParams.getString(Engine.KEY_PARAM_VOICE_NAME, "");
+ if (TextUtils.isEmpty(voiceName)) {
+ return null;
}
+ return getVoice(service, voiceName);
}, null, "getVoice");
}
@@ -1730,45 +1689,42 @@
* on error.
*/
public Voice getDefaultVoice() {
- return runAction(new Action<Voice>() {
- @Override
- public Voice run(ITextToSpeechService service) throws RemoteException {
+ return runAction((ITextToSpeechService service) -> {
- String[] defaultLanguage = service.getClientDefaultLanguage();
+ String[] defaultLanguage = service.getClientDefaultLanguage();
- if (defaultLanguage == null || defaultLanguage.length == 0) {
- Log.e(TAG, "service.getClientDefaultLanguage() returned empty array");
- return null;
- }
- String language = defaultLanguage[0];
- String country = (defaultLanguage.length > 1) ? defaultLanguage[1] : "";
- String variant = (defaultLanguage.length > 2) ? defaultLanguage[2] : "";
-
- // Sanitize the locale using isLanguageAvailable.
- int result = service.isLanguageAvailable(language, country, variant);
- if (result < LANG_AVAILABLE) {
- // The default language is not supported.
- return null;
- }
-
- // Get the default voice name
- String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
- if (TextUtils.isEmpty(voiceName)) {
- return null;
- }
-
- // Find it
- List<Voice> voices = service.getVoices();
- if (voices == null) {
- return null;
- }
- for (Voice voice : voices) {
- if (voice.getName().equals(voiceName)) {
- return voice;
- }
- }
+ if (defaultLanguage == null || defaultLanguage.length == 0) {
+ Log.e(TAG, "service.getClientDefaultLanguage() returned empty array");
return null;
}
+ String language = defaultLanguage[0];
+ String country = (defaultLanguage.length > 1) ? defaultLanguage[1] : "";
+ String variant = (defaultLanguage.length > 2) ? defaultLanguage[2] : "";
+
+ // Sanitize the locale using isLanguageAvailable.
+ int result = service.isLanguageAvailable(language, country, variant);
+ if (result < LANG_AVAILABLE) {
+ // The default language is not supported.
+ return null;
+ }
+
+ // Get the default voice name
+ String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
+ if (TextUtils.isEmpty(voiceName)) {
+ return null;
+ }
+
+ // Find it
+ List<Voice> voices = service.getVoices();
+ if (voices == null) {
+ return null;
+ }
+ for (Voice voice : voices) {
+ if (voice.getName().equals(voiceName)) {
+ return voice;
+ }
+ }
+ return null;
}, null, "getDefaultVoice");
}
@@ -1784,31 +1740,55 @@
* {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
*/
public int isLanguageAvailable(final Locale loc) {
- return runAction(new Action<Integer>() {
- @Override
- public Integer run(ITextToSpeechService service) throws RemoteException {
- String language = null, country = null;
+ return runAction((ITextToSpeechService service) -> {
+ String language = null, country = null;
- try {
- language = loc.getISO3Language();
- } catch (MissingResourceException e) {
- Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
- return LANG_NOT_SUPPORTED;
- }
-
- try {
- country = loc.getISO3Country();
- } catch (MissingResourceException e) {
- Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
- return LANG_NOT_SUPPORTED;
- }
-
- return service.isLanguageAvailable(language, country, loc.getVariant());
+ try {
+ language = loc.getISO3Language();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
+ return LANG_NOT_SUPPORTED;
}
+
+ try {
+ country = loc.getISO3Country();
+ } catch (MissingResourceException e) {
+ Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
+ return LANG_NOT_SUPPORTED;
+ }
+
+ return service.isLanguageAvailable(language, country, loc.getVariant());
}, LANG_NOT_SUPPORTED, "isLanguageAvailable");
}
/**
+ * Synthesizes the given text to a ParcelFileDescriptor using the specified parameters.
+ * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
+ * requests and then returns. The synthesis might not have finished (or even started!) at the
+ * time when this method returns. In order to reliably detect errors during synthesis,
+ * we recommend setting an utterance progress listener (see
+ * {@link #setOnUtteranceProgressListener}).
+ *
+ * @param text The text that should be synthesized. No longer than
+ * {@link #getMaxSpeechInputLength()} characters.
+ * @param params Parameters for the request. Can be null.
+ * Engine specific parameters may be passed in but the parameter keys
+ * must be prefixed by the name of the engine they are intended for. For example
+ * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the engine
+ * named "com.svox.pico" if it is being used.
+ * @param fileDescriptor ParcelFileDescriptor to write the generated audio data to.
+ * @param utteranceId An unique identifier for this request.
+ * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
+ */
+ public int synthesizeToFile(@NonNull final CharSequence text, @NonNull final Bundle params,
+ @NonNull final ParcelFileDescriptor fileDescriptor, @NonNull final String utteranceId) {
+ return runAction((ITextToSpeechService service) -> {
+ return service.synthesizeToFileDescriptor(getCallerIdentity(), text,
+ fileDescriptor, getParams(params), utteranceId);
+ }, ERROR, "synthesizeToFile");
+ }
+
+ /**
* Synthesizes the given text to a file using the specified parameters.
* This method is asynchronous, i.e. the method just adds the request to the queue of TTS
* requests and then returns. The synthesis might not have finished (or even started!) at the
@@ -1829,33 +1809,26 @@
*/
public int synthesizeToFile(final CharSequence text, final Bundle params,
final File file, final String utteranceId) {
- return runAction(new Action<Integer>() {
- @Override
- public Integer run(ITextToSpeechService service) throws RemoteException {
- ParcelFileDescriptor fileDescriptor;
- int returnValue;
- try {
- if(file.exists() && !file.canWrite()) {
- Log.e(TAG, "Can't write to " + file);
- return ERROR;
- }
- fileDescriptor = ParcelFileDescriptor.open(file,
- ParcelFileDescriptor.MODE_WRITE_ONLY |
- ParcelFileDescriptor.MODE_CREATE |
- ParcelFileDescriptor.MODE_TRUNCATE);
- returnValue = service.synthesizeToFileDescriptor(getCallerIdentity(), text,
- fileDescriptor, getParams(params), utteranceId);
- fileDescriptor.close();
- return returnValue;
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Opening file " + file + " failed", e);
- return ERROR;
- } catch (IOException e) {
- Log.e(TAG, "Closing file " + file + " failed", e);
- return ERROR;
- }
- }
- }, ERROR, "synthesizeToFile");
+ if (file.exists() && !file.canWrite()) {
+ Log.e(TAG, "Can't write to " + file);
+ return ERROR;
+ }
+ try (
+ ParcelFileDescriptor fileDescriptor = ParcelFileDescriptor.open(file,
+ ParcelFileDescriptor.MODE_WRITE_ONLY
+ | ParcelFileDescriptor.MODE_CREATE
+ | ParcelFileDescriptor.MODE_TRUNCATE);
+ ) {
+ int returnValue = synthesizeToFile(text, params, fileDescriptor, utteranceId);
+ fileDescriptor.close();
+ return returnValue;
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Opening file " + file + " failed", e);
+ return ERROR;
+ } catch (IOException e) {
+ Log.e(TAG, "Closing file " + file + " failed", e);
+ return ERROR;
+ }
}
/**
diff --git a/core/java/android/util/Half.java b/core/java/android/util/Half.java
index 70d049a..fe536a6 100644
--- a/core/java/android/util/Half.java
+++ b/core/java/android/util/Half.java
@@ -20,6 +20,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import libcore.util.FP16;
+
/**
* <p>The {@code Half} class is a wrapper and a utility class to manipulate half-precision 16-bit
* <a href="https://en.wikipedia.org/wiki/Half-precision_floating-point_format">IEEE 754</a>
@@ -148,25 +150,6 @@
*/
public static final @HalfFloat short POSITIVE_ZERO = (short) 0x0000;
- private static final int FP16_SIGN_SHIFT = 15;
- private static final int FP16_SIGN_MASK = 0x8000;
- private static final int FP16_EXPONENT_SHIFT = 10;
- private static final int FP16_EXPONENT_MASK = 0x1f;
- private static final int FP16_SIGNIFICAND_MASK = 0x3ff;
- private static final int FP16_EXPONENT_BIAS = 15;
- private static final int FP16_COMBINED = 0x7fff;
- private static final int FP16_EXPONENT_MAX = 0x7c00;
-
- private static final int FP32_SIGN_SHIFT = 31;
- private static final int FP32_EXPONENT_SHIFT = 23;
- private static final int FP32_EXPONENT_MASK = 0xff;
- private static final int FP32_SIGNIFICAND_MASK = 0x7fffff;
- private static final int FP32_EXPONENT_BIAS = 127;
- private static final int FP32_QNAN_MASK = 0x400000;
-
- private static final int FP32_DENORMAL_MAGIC = 126 << 23;
- private static final float FP32_DENORMAL_FLOAT = Float.intBitsToFloat(FP32_DENORMAL_MAGIC);
-
private final @HalfFloat short mValue;
/**
@@ -414,16 +397,7 @@
* than {@code y}
*/
public static int compare(@HalfFloat short x, @HalfFloat short y) {
- if (less(x, y)) return -1;
- if (greater(x, y)) return 1;
-
- // Collapse NaNs, akin to halfToIntBits(), but we want to keep
- // (signed) short value types to preserve the ordering of -0.0
- // and +0.0
- short xBits = (x & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : x;
- short yBits = (y & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : y;
-
- return (xBits == yBits ? 0 : (xBits < yBits ? -1 : 1));
+ return FP16.compare(x, y);
}
/**
@@ -440,7 +414,7 @@
* @see #halfToIntBits(short)
*/
public static @HalfFloat short halfToShortBits(@HalfFloat short h) {
- return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h;
+ return (h & FP16.EXPONENT_SIGNIFICAND_MASK) > FP16.POSITIVE_INFINITY ? NaN : h;
}
/**
@@ -459,7 +433,7 @@
* @see #intBitsToHalf(int)
*/
public static int halfToIntBits(@HalfFloat short h) {
- return (h & FP16_COMBINED) > FP16_EXPONENT_MAX ? NaN : h & 0xffff;
+ return (h & FP16.EXPONENT_SIGNIFICAND_MASK) > FP16.POSITIVE_INFINITY ? NaN : h & 0xffff;
}
/**
@@ -505,7 +479,7 @@
* of the second parameter
*/
public static @HalfFloat short copySign(@HalfFloat short magnitude, @HalfFloat short sign) {
- return (short) ((sign & FP16_SIGN_MASK) | (magnitude & FP16_COMBINED));
+ return (short) ((sign & FP16.SIGN_MASK) | (magnitude & FP16.EXPONENT_SIGNIFICAND_MASK));
}
/**
@@ -523,7 +497,7 @@
* @return The absolute value of the specified half-precision float
*/
public static @HalfFloat short abs(@HalfFloat short h) {
- return (short) (h & FP16_COMBINED);
+ return (short) (h & FP16.EXPONENT_SIGNIFICAND_MASK);
}
/**
@@ -538,26 +512,18 @@
* the result is zero (with the same sign)</li>
* </ul>
*
+ * <p class=note>
+ * <strong>Note:</strong> Unlike the identically named
+ * <code class=prettyprint>int java.lang.Math.round(float)</code> method,
+ * this returns a Half value stored in a short, <strong>not</strong> an
+ * actual short integer result.
+ *
* @param h A half-precision float value
* @return The value of the specified half-precision float rounded to the nearest
* half-precision float value
*/
public static @HalfFloat short round(@HalfFloat short h) {
- int bits = h & 0xffff;
- int e = bits & 0x7fff;
- int result = bits;
-
- if (e < 0x3c00) {
- result &= FP16_SIGN_MASK;
- result |= (0x3c00 & (e >= 0x3800 ? 0xffff : 0x0));
- } else if (e < 0x6400) {
- e = 25 - (e >> 10);
- int mask = (1 << e) - 1;
- result += (1 << (e - 1));
- result &= ~mask;
- }
-
- return (short) result;
+ return FP16.rint(h);
}
/**
@@ -577,21 +543,7 @@
* greater than or equal to the specified half-precision float value
*/
public static @HalfFloat short ceil(@HalfFloat short h) {
- int bits = h & 0xffff;
- int e = bits & 0x7fff;
- int result = bits;
-
- if (e < 0x3c00) {
- result &= FP16_SIGN_MASK;
- result |= 0x3c00 & -(~(bits >> 15) & (e != 0 ? 1 : 0));
- } else if (e < 0x6400) {
- e = 25 - (e >> 10);
- int mask = (1 << e) - 1;
- result += mask & ((bits >> 15) - 1);
- result &= ~mask;
- }
-
- return (short) result;
+ return FP16.ceil(h);
}
/**
@@ -611,21 +563,7 @@
* less than or equal to the specified half-precision float value
*/
public static @HalfFloat short floor(@HalfFloat short h) {
- int bits = h & 0xffff;
- int e = bits & 0x7fff;
- int result = bits;
-
- if (e < 0x3c00) {
- result &= FP16_SIGN_MASK;
- result |= 0x3c00 & (bits > 0x8000 ? 0xffff : 0x0);
- } else if (e < 0x6400) {
- e = 25 - (e >> 10);
- int mask = (1 << e) - 1;
- result += mask & -(bits >> 15);
- result &= ~mask;
- }
-
- return (short) result;
+ return FP16.floor(h);
}
/**
@@ -644,19 +582,7 @@
* half-precision float value
*/
public static @HalfFloat short trunc(@HalfFloat short h) {
- int bits = h & 0xffff;
- int e = bits & 0x7fff;
- int result = bits;
-
- if (e < 0x3c00) {
- result &= FP16_SIGN_MASK;
- } else if (e < 0x6400) {
- e = 25 - (e >> 10);
- int mask = (1 << e) - 1;
- result &= ~mask;
- }
-
- return (short) result;
+ return FP16.trunc(h);
}
/**
@@ -672,15 +598,7 @@
* @return The smaller of the two specified half-precision values
*/
public static @HalfFloat short min(@HalfFloat short x, @HalfFloat short y) {
- if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
- if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
-
- if ((x & FP16_COMBINED) == 0 && (y & FP16_COMBINED) == 0) {
- return (x & FP16_SIGN_MASK) != 0 ? x : y;
- }
-
- return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
- ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
+ return FP16.min(x, y);
}
/**
@@ -697,15 +615,7 @@
* @return The larger of the two specified half-precision values
*/
public static @HalfFloat short max(@HalfFloat short x, @HalfFloat short y) {
- if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
- if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return NaN;
-
- if ((x & FP16_COMBINED) == 0 && (y & FP16_COMBINED) == 0) {
- return (x & FP16_SIGN_MASK) != 0 ? y : x;
- }
-
- return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
- ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff) ? x : y;
+ return FP16.max(x, y);
}
/**
@@ -719,11 +629,7 @@
* @return True if x is less than y, false otherwise
*/
public static boolean less(@HalfFloat short x, @HalfFloat short y) {
- if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
- if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
- return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <
- ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ return FP16.less(x, y);
}
/**
@@ -737,11 +643,7 @@
* @return True if x is less than or equal to y, false otherwise
*/
public static boolean lessEquals(@HalfFloat short x, @HalfFloat short y) {
- if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
- if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
- return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) <=
- ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ return FP16.lessEquals(x, y);
}
/**
@@ -755,11 +657,7 @@
* @return True if x is greater than y, false otherwise
*/
public static boolean greater(@HalfFloat short x, @HalfFloat short y) {
- if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
- if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
- return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >
- ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ return FP16.greater(x, y);
}
/**
@@ -773,11 +671,7 @@
* @return True if x is greater than y, false otherwise
*/
public static boolean greaterEquals(@HalfFloat short x, @HalfFloat short y) {
- if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
- if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
- return ((x & FP16_SIGN_MASK) != 0 ? 0x8000 - (x & 0xffff) : x & 0xffff) >=
- ((y & FP16_SIGN_MASK) != 0 ? 0x8000 - (y & 0xffff) : y & 0xffff);
+ return FP16.greaterEquals(x, y);
}
/**
@@ -791,10 +685,7 @@
* @return True if x is equal to y, false otherwise
*/
public static boolean equals(@HalfFloat short x, @HalfFloat short y) {
- if ((x & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
- if ((y & FP16_COMBINED) > FP16_EXPONENT_MAX) return false;
-
- return x == y || ((x | y) & FP16_COMBINED) == 0;
+ return FP16.equals(x, y);
}
/**
@@ -804,7 +695,7 @@
* @return 1 if the value is positive, -1 if the value is negative
*/
public static int getSign(@HalfFloat short h) {
- return (h & FP16_SIGN_MASK) == 0 ? 1 : -1;
+ return (h & FP16.SIGN_MASK) == 0 ? 1 : -1;
}
/**
@@ -818,7 +709,7 @@
* @return The unbiased exponent of the specified value
*/
public static int getExponent(@HalfFloat short h) {
- return ((h >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK) - FP16_EXPONENT_BIAS;
+ return ((h >>> FP16.EXPONENT_SHIFT) & FP16.SHIFTED_EXPONENT_MASK) - FP16.EXPONENT_BIAS;
}
/**
@@ -829,7 +720,7 @@
* @return The significand, or significand, of the specified vlaue
*/
public static int getSignificand(@HalfFloat short h) {
- return h & FP16_SIGNIFICAND_MASK;
+ return h & FP16.SIGNIFICAND_MASK;
}
/**
@@ -841,7 +732,7 @@
* false otherwise
*/
public static boolean isInfinite(@HalfFloat short h) {
- return (h & FP16_COMBINED) == FP16_EXPONENT_MAX;
+ return FP16.isInfinite(h);
}
/**
@@ -852,7 +743,7 @@
* @return True if the value is a NaN, false otherwise
*/
public static boolean isNaN(@HalfFloat short h) {
- return (h & FP16_COMBINED) > FP16_EXPONENT_MAX;
+ return FP16.isNaN(h);
}
/**
@@ -866,7 +757,7 @@
* @return True if the value is normalized, false otherwise
*/
public static boolean isNormalized(@HalfFloat short h) {
- return (h & FP16_EXPONENT_MAX) != 0 && (h & FP16_EXPONENT_MAX) != FP16_EXPONENT_MAX;
+ return FP16.isNormalized(h);
}
/**
@@ -885,35 +776,7 @@
* @return A normalized single-precision float value
*/
public static float toFloat(@HalfFloat short h) {
- int bits = h & 0xffff;
- int s = bits & FP16_SIGN_MASK;
- int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK;
- int m = (bits ) & FP16_SIGNIFICAND_MASK;
-
- int outE = 0;
- int outM = 0;
-
- if (e == 0) { // Denormal or 0
- if (m != 0) {
- // Convert denorm fp16 into normalized fp32
- float o = Float.intBitsToFloat(FP32_DENORMAL_MAGIC + m);
- o -= FP32_DENORMAL_FLOAT;
- return s == 0 ? o : -o;
- }
- } else {
- outM = m << 13;
- if (e == 0x1f) { // Infinite or NaN
- outE = 0xff;
- if (outM != 0) { // SNaNs are quieted
- outM |= FP32_QNAN_MASK;
- }
- } else {
- outE = e - FP16_EXPONENT_BIAS + FP32_EXPONENT_BIAS;
- }
- }
-
- int out = (s << 16) | (outE << FP32_EXPONENT_SHIFT) | outM;
- return Float.intBitsToFloat(out);
+ return FP16.toFloat(h);
}
/**
@@ -940,44 +803,7 @@
*/
@SuppressWarnings("StatementWithEmptyBody")
public static @HalfFloat short toHalf(float f) {
- int bits = Float.floatToRawIntBits(f);
- int s = (bits >>> FP32_SIGN_SHIFT );
- int e = (bits >>> FP32_EXPONENT_SHIFT) & FP32_EXPONENT_MASK;
- int m = (bits ) & FP32_SIGNIFICAND_MASK;
-
- int outE = 0;
- int outM = 0;
-
- if (e == 0xff) { // Infinite or NaN
- outE = 0x1f;
- outM = m != 0 ? 0x200 : 0;
- } else {
- e = e - FP32_EXPONENT_BIAS + FP16_EXPONENT_BIAS;
- if (e >= 0x1f) { // Overflow
- outE = 0x31;
- } else if (e <= 0) { // Underflow
- if (e < -10) {
- // The absolute fp32 value is less than MIN_VALUE, flush to +/-0
- } else {
- // The fp32 value is a normalized float less than MIN_NORMAL,
- // we convert to a denorm fp16
- m = (m | 0x800000) >> (1 - e);
- if ((m & 0x1000) != 0) m += 0x2000;
- outM = m >> 13;
- }
- } else {
- outE = e;
- outM = m >> 13;
- if ((m & 0x1000) != 0) {
- // Round to nearest "0.5" up
- int out = (outE << FP16_EXPONENT_SHIFT) | outM;
- out++;
- return (short) (out | (s << FP16_SIGN_SHIFT));
- }
- }
- }
-
- return (short) ((s << FP16_SIGN_SHIFT) | (outE << FP16_EXPONENT_SHIFT) | outM);
+ return FP16.toHalf(f);
}
/**
@@ -1073,40 +899,6 @@
*/
@NonNull
public static String toHexString(@HalfFloat short h) {
- StringBuilder o = new StringBuilder();
-
- int bits = h & 0xffff;
- int s = (bits >>> FP16_SIGN_SHIFT );
- int e = (bits >>> FP16_EXPONENT_SHIFT) & FP16_EXPONENT_MASK;
- int m = (bits ) & FP16_SIGNIFICAND_MASK;
-
- if (e == 0x1f) { // Infinite or NaN
- if (m == 0) {
- if (s != 0) o.append('-');
- o.append("Infinity");
- } else {
- o.append("NaN");
- }
- } else {
- if (s == 1) o.append('-');
- if (e == 0) {
- if (m == 0) {
- o.append("0x0.0p0");
- } else {
- o.append("0x0.");
- String significand = Integer.toHexString(m);
- o.append(significand.replaceFirst("0{2,}$", ""));
- o.append("p-14");
- }
- } else {
- o.append("0x1.");
- String significand = Integer.toHexString(m);
- o.append(significand.replaceFirst("0{2,}$", ""));
- o.append('p');
- o.append(Integer.toString(e - FP16_EXPONENT_BIAS));
- }
- }
-
- return o.toString();
+ return FP16.toHexString(h);
}
}
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index 8b5659b..3a1df3e 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -60,9 +60,19 @@
}
public synchronized void dump(PrintWriter pw) {
+ dump("", pw);
+ }
+
+ /**
+ * Dumps the content of local log to print writer with each log entry predeced with indent
+ *
+ * @param indent indent that precedes each log entry
+ * @param pw printer writer to write into
+ */
+ public synchronized void dump(String indent, PrintWriter pw) {
Iterator<String> itr = mLog.iterator();
while (itr.hasNext()) {
- pw.println(itr.next());
+ pw.printf("%s%s\n", indent, itr.next());
}
}
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index f8b38e9..07cecd3 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -62,9 +62,9 @@
}
/**
- * Tries to return a frozen ICU time zone that would have had the specified offset
- * and DST value at the specified moment in the specified country.
- * Returns null if no suitable zone could be found.
+ * Returns a frozen ICU time zone that has / would have had the specified offset and DST value
+ * at the specified moment in the specified country. Returns null if no suitable zone could be
+ * found.
*/
private static android.icu.util.TimeZone getIcuTimeZone(
int offset, boolean dst, long when, String country) {
@@ -73,8 +73,15 @@
}
android.icu.util.TimeZone bias = android.icu.util.TimeZone.getDefault();
- return TimeZoneFinder.getInstance()
- .lookupTimeZoneByCountryAndOffset(country, offset, dst, when, bias);
+ CountryTimeZones countryTimeZones =
+ TimeZoneFinder.getInstance().lookupCountryTimeZones(country);
+ if (countryTimeZones == null) {
+ return null;
+ }
+
+ CountryTimeZones.OffsetResult offsetResult =
+ countryTimeZones.lookupByOffsetWithBias(offset, dst, when, bias);
+ return offsetResult != null ? offsetResult.mTimeZone : null;
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 42275a3..d7d7e21 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -856,6 +856,8 @@
mScrollY = record.mScrollY;
mMaxScrollX = record.mMaxScrollX;
mMaxScrollY = record.mMaxScrollY;
+ mScrollDeltaX = record.mScrollDeltaX;
+ mScrollDeltaY = record.mScrollDeltaY;
mAddedCount = record.mAddedCount;
mRemovedCount = record.mRemovedCount;
mClassName = record.mClassName;
@@ -882,6 +884,8 @@
mScrollY = 0;
mMaxScrollX = 0;
mMaxScrollY = 0;
+ mScrollDeltaX = UNDEFINED;
+ mScrollDeltaY = UNDEFINED;
mAddedCount = UNDEFINED;
mRemovedCount = UNDEFINED;
mClassName = null;
@@ -921,6 +925,8 @@
append(builder, "ScrollY", mScrollY);
append(builder, "MaxScrollX", mMaxScrollX);
append(builder, "MaxScrollY", mMaxScrollY);
+ append(builder, "ScrollDeltaX", mScrollDeltaX);
+ append(builder, "ScrollDeltaY", mScrollDeltaY);
append(builder, "AddedCount", mAddedCount);
append(builder, "RemovedCount", mRemovedCount);
append(builder, "ParcelableData", mParcelableData);
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 1f7ae0e..3bbd321 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -43,6 +43,7 @@
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.service.autofill.AutofillService;
import android.service.autofill.FillEventHistory;
import android.service.autofill.UserData;
@@ -1737,6 +1738,26 @@
final SyncResultReceiver receiver = new SyncResultReceiver(SYNC_CALLS_TIMEOUT_MS);
final ComponentName componentName = client.autofillClientGetComponentName();
+
+ if (!mEnabledForAugmentedAutofillOnly && mOptions != null
+ && mOptions.isAutofillDisabledLocked(componentName)) {
+ if (mOptions.isAugmentedAutofillEnabled(mContext)) {
+ if (sDebug) {
+ Log.d(TAG, "startSession(" + componentName + "): disabled by service but "
+ + "whitelisted for augmented autofill");
+ flags |= FLAG_ADD_CLIENT_ENABLED_FOR_AUGMENTED_AUTOFILL_ONLY;
+ }
+ } else {
+ if (sDebug) {
+ Log.d(TAG, "startSession(" + componentName + "): ignored because "
+ + "disabled by service and not whitelisted for augmented autofill");
+ }
+ setSessionFinished(AutofillManager.STATE_DISABLED_BY_SERVICE, null);
+ client.autofillClientResetableStateAvailable();
+ return;
+ }
+ }
+
mService.startSession(client.autofillClientGetActivityToken(),
mServiceClient.asBinder(), id, bounds, value, mContext.getUserId(),
mCallback != null, flags, componentName,
@@ -2067,6 +2088,7 @@
mServiceClientCleaner.clean();
mServiceClientCleaner = null;
}
+ notifyReenableAutofill();
}
}
sDebug = (flags & SET_STATE_FLAG_DEBUG) != 0;
@@ -2403,6 +2425,37 @@
}
}
+ private void notifyDisableAutofill(long disableDuration, ComponentName componentName) {
+ synchronized (mLock) {
+ if (mOptions == null) {
+ return;
+ }
+ long expiration = SystemClock.elapsedRealtime() + disableDuration;
+ // Protect it against overflow
+ if (expiration < 0) {
+ expiration = Long.MAX_VALUE;
+ }
+ if (componentName != null) {
+ if (mOptions.disabledActivities == null) {
+ mOptions.disabledActivities = new ArrayMap<>();
+ }
+ mOptions.disabledActivities.put(componentName.flattenToString(), expiration);
+ } else {
+ mOptions.appDisabledExpiration = expiration;
+ }
+ }
+ }
+
+ void notifyReenableAutofill() {
+ synchronized (mLock) {
+ if (mOptions == null) {
+ return;
+ }
+ mOptions.appDisabledExpiration = 0;
+ mOptions.disabledActivities = null;
+ }
+ }
+
private void notifyNoFillUi(int sessionId, AutofillId id, int sessionFinishedState) {
if (sVerbose) {
Log.v(TAG, "notifyNoFillUi(): sessionId=" + sessionId + ", autofillId=" + id
@@ -3181,6 +3234,15 @@
}
@Override
+ public void notifyDisableAutofill(long disableDuration, ComponentName componentName)
+ throws RemoteException {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.post(() -> afm.notifyDisableAutofill(disableDuration, componentName));
+ }
+ }
+
+ @Override
public void dispatchUnhandledKey(int sessionId, AutofillId id, KeyEvent fullScreen) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 85def7f..84949c8 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -18,6 +18,7 @@
import java.util.List;
+import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentSender;
import android.graphics.Rect;
@@ -111,4 +112,8 @@
*/
void getAugmentedAutofillClient(in IResultReceiver result);
+ /**
+ * Notifies disables autofill for the app or activity.
+ */
+ void notifyDisableAutofill(long disableDuration, in ComponentName componentName);
}
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index cee7943..1e7440b 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -480,6 +480,8 @@
return;
}
+ mNextFlushForTextChanged = false;
+
final int numberEvents = mEvents.size();
final String reasonString = getFlushReasonAsString(reason);
if (sDebug) {
@@ -495,10 +497,6 @@
try {
mHandler.removeMessages(MSG_FLUSH);
- if (reason == FLUSH_REASON_TEXT_CHANGE_TIMEOUT) {
- mNextFlushForTextChanged = false;
- }
-
final ParceledListSlice<ContentCaptureEvent> events = clearEvents();
mDirectServiceInterface.sendEvents(events, reason, mManager.mOptions);
} catch (RemoteException e) {
diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java
index 18ec334..ac145b1 100644
--- a/core/java/android/webkit/PermissionRequest.java
+++ b/core/java/android/webkit/PermissionRequest.java
@@ -32,7 +32,7 @@
* avoid unintentionally granting requests for new permissions, you should pass the
* specific permissions you intend to grant to {@link #grant(String[]) grant()},
* and avoid writing code like this example:
- * <pre>
+ * <pre class="prettyprint">
* permissionRequest.grant(permissionRequest.getResources()) // This is wrong!!!
* </pre>
* See the WebView's release notes for information about new protected resources.
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index 95fe963..4db6308 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -519,15 +519,17 @@
* may not be supported and applications wishing to support these sources
* or more advanced file operations should build their own Intent.
*
- * <pre>
- * How to use:
- * 1. Build an intent using {@link #createIntent}
- * 2. Fire the intent using {@link android.app.Activity#startActivityForResult}.
- * 3. Check for ActivityNotFoundException and take a user friendly action if thrown.
- * 4. Listen the result using {@link android.app.Activity#onActivityResult}
- * 5. Parse the result using {@link #parseResult} only if media capture was not requested.
- * 6. Send the result using filePathCallback of {@link WebChromeClient#onShowFileChooser}
- * </pre>
+ * <p>How to use:
+ * <ol>
+ * <li>Build an intent using {@link #createIntent}</li>
+ * <li>Fire the intent using {@link android.app.Activity#startActivityForResult}.</li>
+ * <li>Check for ActivityNotFoundException and take a user friendly action if thrown.</li>
+ * <li>Listen the result using {@link android.app.Activity#onActivityResult}</li>
+ * <li>Parse the result using {@link #parseResult} only if media capture was not
+ * requested.</li>
+ * <li>Send the result using filePathCallback of {@link
+ * WebChromeClient#onShowFileChooser}</li>
+ * </ol>
*
* @return an Intent that supports basic file chooser sources.
*/
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index a35659e..e4b5eaa 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -762,7 +762,7 @@
* encoded. If the data is base64 encoded, the value of the encoding
* parameter must be {@code "base64"}. HTML can be encoded with {@link
* android.util.Base64#encodeToString(byte[],int)} like so:
- * <pre>
+ * <pre class="prettyprint">
* String unencodedHtml =
* "<html><body>'%28' is the code for '('</body></html>";
* String encodedHtml = Base64.encodeToString(unencodedHtml.getBytes(), Base64.NO_PADDING);
@@ -1854,7 +1854,7 @@
* important security note below for implications.
* <p> Note that injected objects will not appear in JavaScript until the page is next
* (re)loaded. JavaScript should be enabled before injecting the object. For example:
- * <pre>
+ * <pre class="prettyprint">
* class JsObject {
* {@literal @}JavascriptInterface
* public String toString() { return "injectedObject"; }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 00206fc..faeecda 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -164,6 +164,8 @@
public static final String LAUNCH_LOCATON_DIRECT_SHARE = "direct_share";
private static final int APP_PREDICTION_SHARE_TARGET_QUERY_PACKAGE_LIMIT = 20;
public static final String APP_PREDICTION_INTENT_FILTER_KEY = "intent_filter";
+
+ private boolean mIsAppPredictorComponentAvailable;
private AppPredictor mAppPredictor;
private AppPredictor.Callback mAppPredictorCallback;
private Map<ChooserTarget, AppTarget> mDirectShareAppTargetCache;
@@ -617,6 +619,9 @@
.addTaggedData(MetricsEvent.FIELD_SHARESHEET_MIMETYPE, target.getType())
.addTaggedData(MetricsEvent.FIELD_TIME_TO_APP_TARGETS, systemCost));
+ // This is the only place this value is being set. Effectively final.
+ mIsAppPredictorComponentAvailable = isAppPredictionServiceAvailable();
+
AppPredictor appPredictor = getAppPredictorForDirectShareIfEnabled();
if (appPredictor != null) {
mDirectShareAppTargetCache = new HashMap<>();
@@ -707,6 +712,32 @@
}
/**
+ * Returns true if app prediction service is defined and the component exists on device.
+ */
+ private boolean isAppPredictionServiceAvailable() {
+ final String appPredictionServiceName =
+ getString(R.string.config_defaultAppPredictionService);
+ if (appPredictionServiceName == null) {
+ return false;
+ }
+ final ComponentName appPredictionComponentName =
+ ComponentName.unflattenFromString(appPredictionServiceName);
+ if (appPredictionComponentName == null) {
+ return false;
+ }
+
+ // Check if the app prediction component actually exists on the device.
+ Intent intent = new Intent();
+ intent.setComponent(appPredictionComponentName);
+ if (getPackageManager().resolveService(intent, PackageManager.MATCH_ALL) == null) {
+ Log.e(TAG, "App prediction service is defined, but does not exist: "
+ + appPredictionServiceName);
+ return false;
+ }
+ return true;
+ }
+
+ /**
* Check if the profile currently used is a work profile.
* @return true if it is work profile, false if it is parent profile (or no work profile is
* set up)
@@ -1693,6 +1724,9 @@
@Nullable
private AppPredictor getAppPredictor() {
+ if (!mIsAppPredictorComponentAvailable) {
+ return null;
+ }
if (mAppPredictor == null
&& getPackageManager().getAppPredictionServicePackageName() != null) {
final IntentFilter filter = getTargetIntentFilter();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 88d0e85..8503ab8 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4750,11 +4750,11 @@
final long uptime = mClocks.uptimeMillis();
boolean updateHistory = false;
- if (isScreenDoze(state)) {
+ if (isScreenDoze(state) && !isScreenDoze(oldState)) {
mHistoryCur.states |= HistoryItem.STATE_SCREEN_DOZE_FLAG;
mScreenDozeTimer.startRunningLocked(elapsedRealtime);
updateHistory = true;
- } else if (isScreenDoze(oldState)) {
+ } else if (isScreenDoze(oldState) && !isScreenDoze(state)) {
mHistoryCur.states &= ~HistoryItem.STATE_SCREEN_DOZE_FLAG;
mScreenDozeTimer.stopRunningLocked(elapsedRealtime);
updateHistory = true;
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index cf0394d..9441825 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -153,7 +153,7 @@
// Used to show the dialog when BiometricService starts authentication
void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
- boolean requireConfirmation, int userId);
+ boolean requireConfirmation, int userId, String opPackageName);
// Used to hide the dialog when a biometric is authenticated
void onBiometricAuthenticated(boolean authenticated, String failureReason);
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index 85ae18e..4c3a177 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -101,7 +101,7 @@
// Used to show the dialog when BiometricService starts authentication
void showBiometricDialog(in Bundle bundle, IBiometricServiceReceiverInternal receiver, int type,
- boolean requireConfirmation, int userId);
+ boolean requireConfirmation, int userId, String opPackageName);
// Used to hide the dialog when a biometric is authenticated
void onBiometricAuthenticated(boolean authenticated, String failureReason);
// Used to set a temporary message, e.g. fingerprint not recognized, finger moved too fast, etc
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 98162af..ec91cbf 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -78,14 +78,18 @@
return nullptr;
}
-static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream, jobject source) {
+static jobject native_create(JNIEnv* env, std::unique_ptr<SkStream> stream,
+ jobject source, jboolean preferAnimation) {
if (!stream.get()) {
return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to create a stream",
nullptr, source);
}
std::unique_ptr<ImageDecoder> decoder(new ImageDecoder);
SkCodec::Result result;
- auto codec = SkCodec::MakeFromStream(std::move(stream), &result, decoder->mPeeker.get());
+ auto codec = SkCodec::MakeFromStream(
+ std::move(stream), &result, decoder->mPeeker.get(),
+ preferAnimation ? SkCodec::SelectionPolicy::kPreferAnimation
+ : SkCodec::SelectionPolicy::kPreferStillImage);
if (jthrowable jexception = get_and_clear_exception(env)) {
return throw_exception(env, ImageDecoder::kSourceException, "", jexception, source);
}
@@ -124,7 +128,7 @@
}
static jobject ImageDecoder_nCreateFd(JNIEnv* env, jobject /*clazz*/,
- jobject fileDescriptor, jobject source) {
+ jobject fileDescriptor, jboolean preferAnimation, jobject source) {
int descriptor = jniGetFDFromFileDescriptor(env, fileDescriptor);
struct stat fdStat;
@@ -142,11 +146,11 @@
}
std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file));
- return native_create(env, std::move(fileStream), source);
+ return native_create(env, std::move(fileStream), source, preferAnimation);
}
static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/,
- jobject is, jbyteArray storage, jobject source) {
+ jobject is, jbyteArray storage, jboolean preferAnimation, jobject source) {
std::unique_ptr<SkStream> stream(CreateJavaInputStreamAdaptor(env, is, storage, false));
if (!stream.get()) {
@@ -157,31 +161,33 @@
std::unique_ptr<SkStream> bufferedStream(
SkFrontBufferedStream::Make(std::move(stream),
SkCodec::MinBufferedBytesNeeded()));
- return native_create(env, std::move(bufferedStream), source);
+ return native_create(env, std::move(bufferedStream), source, preferAnimation);
}
-static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/, jlong assetPtr,
- jobject source) {
+static jobject ImageDecoder_nCreateAsset(JNIEnv* env, jobject /*clazz*/,
+ jlong assetPtr, jboolean preferAnimation, jobject source) {
Asset* asset = reinterpret_cast<Asset*>(assetPtr);
std::unique_ptr<SkStream> stream(new AssetStreamAdaptor(asset));
- return native_create(env, std::move(stream), source);
+ return native_create(env, std::move(stream), source, preferAnimation);
}
-static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/, jobject jbyteBuffer,
- jint initialPosition, jint limit, jobject source) {
+static jobject ImageDecoder_nCreateByteBuffer(JNIEnv* env, jobject /*clazz*/,
+ jobject jbyteBuffer, jint initialPosition, jint limit,
+ jboolean preferAnimation, jobject source) {
std::unique_ptr<SkStream> stream = CreateByteBufferStreamAdaptor(env, jbyteBuffer,
initialPosition, limit);
if (!stream) {
return throw_exception(env, ImageDecoder::kSourceMalformedData, "Failed to read ByteBuffer",
nullptr, source);
}
- return native_create(env, std::move(stream), source);
+ return native_create(env, std::move(stream), source, preferAnimation);
}
-static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/, jbyteArray byteArray,
- jint offset, jint length, jobject source) {
+static jobject ImageDecoder_nCreateByteArray(JNIEnv* env, jobject /*clazz*/,
+ jbyteArray byteArray, jint offset, jint length,
+ jboolean preferAnimation, jobject source) {
std::unique_ptr<SkStream> stream(CreateByteArrayStreamAdaptor(env, byteArray, offset, length));
- return native_create(env, std::move(stream), source);
+ return native_create(env, std::move(stream), source, preferAnimation);
}
jint postProcessAndRelease(JNIEnv* env, jobject jimageDecoder, std::unique_ptr<Canvas> canvas) {
@@ -514,11 +520,11 @@
}
static const JNINativeMethod gImageDecoderMethods[] = {
- { "nCreate", "(JLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset },
- { "nCreate", "(Ljava/nio/ByteBuffer;IILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
- { "nCreate", "([BIILandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
- { "nCreate", "(Ljava/io/InputStream;[BLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
- { "nCreate", "(Ljava/io/FileDescriptor;Landroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
+ { "nCreate", "(JZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateAsset },
+ { "nCreate", "(Ljava/nio/ByteBuffer;IIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteBuffer },
+ { "nCreate", "([BIIZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateByteArray },
+ { "nCreate", "(Ljava/io/InputStream;[BZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateInputStream },
+ { "nCreate", "(Ljava/io/FileDescriptor;ZLandroid/graphics/ImageDecoder$Source;)Landroid/graphics/ImageDecoder;", (void*) ImageDecoder_nCreateFd },
{ "nDecodeBitmap", "(JLandroid/graphics/ImageDecoder;ZIILandroid/graphics/Rect;ZIZZZJZ)Landroid/graphics/Bitmap;",
(void*) ImageDecoder_nDecodeBitmap },
{ "nGetSampledSize","(JI)Landroid/util/Size;", (void*) ImageDecoder_nGetSampledSize },
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 7e3b083..8553a2c 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -210,8 +210,7 @@
if (parcel) {
bool isInitialized = parcel->readInt32();
if (isInitialized) {
- InputChannel* inputChannel = new InputChannel();
- inputChannel->read(*parcel);
+ sp<InputChannel> inputChannel = InputChannel::read(*parcel);
NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 5cbf81c..096bf69 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -497,11 +497,8 @@
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
- std::vector<uint8_t> byteData(parcel->dataSize());
- memcpy(byteData.data(), parcel->data(), parcel->dataSize());
-
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
- transaction->setMetadata(ctrl, id, std::move(byteData));
+ transaction->setMetadata(ctrl, id, *parcel);
}
static void nativeSetColor(JNIEnv* env, jclass clazz, jlong transactionObj,
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 795f7ab..d5b875b 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -117,7 +117,9 @@
static pid_t gSystemServerPid = 0;
+static constexpr const char* kPropFuse = "persist.sys.fuse";
static constexpr const char* kZygoteClassName = "com/android/internal/os/Zygote";
+
static jclass gZygoteClass;
static jmethodID gCallPostForkSystemServerHooks;
static jmethodID gCallPostForkChildHooks;
@@ -704,15 +706,24 @@
return;
}
- const std::string& storage_source = ExternalStorageViews[mount_mode];
-
- BindMount(storage_source, "/storage", fail_fn);
-
- // Mount user-specific symlink helper into place
- userid_t user_id = multiuser_get_user_id(uid);
+ const userid_t user_id = multiuser_get_user_id(uid);
const std::string user_source = StringPrintf("/mnt/user/%d", user_id);
+ bool isFuse = GetBoolProperty(kPropFuse, false);
+
CreateDir(user_source, 0751, AID_ROOT, AID_ROOT, fail_fn);
- BindMount(user_source, "/storage/self", fail_fn);
+
+ if (isFuse) {
+ // TODO(b/135341433): Bind mount the appropriate storage view for the app given its permissions
+ // media and media_location permission access. This should prevent the kernel from incorrectly
+ // sharing a cache across permission buckets
+ BindMount(user_source, "/storage", fail_fn);
+ } else {
+ const std::string& storage_source = ExternalStorageViews[mount_mode];
+ BindMount(storage_source, "/storage", fail_fn);
+
+ // Mount user-specific symlink helper into place
+ BindMount(user_source, "/storage/self", fail_fn);
+ }
}
static bool NeedsNoRandomizeWorkaround() {
diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto
index 0070694..f2ca0a4 100644
--- a/core/proto/android/providers/settings/global.proto
+++ b/core/proto/android/providers/settings/global.proto
@@ -995,7 +995,7 @@
optional SettingProto display_certification_on = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto display_wps_config = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto networks_available_notification_on = 6 [ (android.privacy).dest = DEST_AUTOMATIC ];
- optional SettingProto carrier_networks_available_notification_on = 7 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ reserved 7; reserved "carrier_networks_available_notification_on";
optional SettingProto networks_available_repeat_delay = 8 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto country_code = 9;
optional SettingProto framework_scan_interval_ms = 10 [ (android.privacy).dest = DEST_AUTOMATIC ];
@@ -1013,7 +1013,7 @@
optional SettingProto watchdog_poor_network_test_enabled = 22 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto suspend_optimizations_enabled = 23 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto verbose_logging_enabled = 24 [ (android.privacy).dest = DEST_AUTOMATIC ];
- reserved 25; // connected_mac_randomization_enabled
+ reserved 25; reserved "connected_mac_randomization_enabled";
optional SettingProto max_dhcp_retry_count = 26 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto mobile_data_transition_wakelock_timeout_ms = 27 [ (android.privacy).dest = DEST_AUTOMATIC ];
// Controls whether WiFi configurations created by a Device Owner app should
@@ -1025,7 +1025,7 @@
optional SettingProto device_owner_configs_lockdown = 28 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto frequency_band = 29 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto p2p_device_name = 30;
- optional SettingProto reenable_delay_ms = 31 [ (android.privacy).dest = DEST_AUTOMATIC ];
+ reserved 31; reserved "reenable_delay_ms";
optional SettingProto ephemeral_out_of_range_timeout_ms = 32 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto on_when_proxy_disconnected = 33 [ (android.privacy).dest = DEST_AUTOMATIC ];
optional SettingProto bounce_delay_override_ms = 34 [ (android.privacy).dest = DEST_AUTOMATIC ];
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index a1ff9b9..d54b6b0 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -367,10 +367,8 @@
optional int32 id = 3;
optional bool is_proc = 4;
optional bool has_activities = 5;
- oneof ss_kb {
- int64 pss_kb = 6;
- int64 rss_kb = 9;
- }
+ optional int64 pss_kb = 6;
+ optional int64 rss_kb = 9;
optional int64 swap_pss_kb = 7;
repeated MemItem sub_items = 8;
}
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index aac144c..79167ab 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -308,6 +308,8 @@
// Whether or not TimeController should skip setting wakeup alarms for jobs that aren't
// ready now.
reserved 1; // skip_not_ready_jobs
+ // Whether or not TimeController will use a non-wakeup alarm for delay constraints.
+ optional bool use_non_wakeup_alarm_for_delay = 2;
}
optional TimeController time_controller = 25;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index aee1233..c0a8650 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4458,12 +4458,12 @@
<!-- @SystemApi Allows to access all app shortcuts.
@hide -->
<permission android:name="android.permission.ACCESS_SHORTCUTS"
- android:protectionLevel="signature|textClassifier" />
+ android:protectionLevel="signature|appPredictor" />
<!-- @SystemApi Allows unlimited calls to shortcut mutation APIs.
@hide -->
<permission android:name="android.permission.UNLIMITED_SHORTCUTS_API_CALLS"
- android:protectionLevel="signature|textClassifier" />
+ android:protectionLevel="signature|appPredictor" />
<!-- @SystemApi Allows an application to read the runtime profiles of other apps.
@hide <p>Not for use by third-party applications. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fa4e085..ceccd0d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1707,6 +1707,7 @@
manifest. -->
<string-array name="config_forceQueryablePackages" translatable="false">
<item>com.android.settings</item>
+ <item>com.android.providers.settings</item>
<!-- Add packages here -->
</string-array>
@@ -4285,4 +4286,9 @@
<!-- The list of packages to automatically opt out of refresh rates higher than 60hz because
of known compatibility issues. -->
<string-array name="config_highRefreshRateBlacklist"></string-array>
+
+ <!-- Whether or not to hide the navigation bar when the soft keyboard is visible in order to
+ create additional screen real estate outside beyond the keyboard. Note that the user needs
+ to have a confirmed way to dismiss the keyboard when desired. -->
+ <bool name="config_automotiveHideNavBarForKeyboard">false</bool>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index d1d7bf5..cdfa0439 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3377,8 +3377,6 @@
<!-- Notification title for a nearby open wireless network.-->
<string name="wifi_available_title">Connect to open Wi\u2011Fi network</string>
- <!-- Notification title for a nearby carrier wireless network.-->
- <string name="wifi_available_carrier_network_title">Connect to carrier Wi\u2011Fi network</string>
<!-- Notification title when the system is connecting to the specified network. The network name is specified in the notification content. -->
<string name="wifi_available_title_connecting">Connecting to Wi\u2011Fi network</string>
<!-- Notification title when the system has connected to the network. The network name is specified in the notification content. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f7ae453..c37a457 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2070,7 +2070,6 @@
<java-symbol type="plurals" name="wifi_available" />
<java-symbol type="plurals" name="wifi_available_detailed" />
<java-symbol type="string" name="wifi_available_title" />
- <java-symbol type="string" name="wifi_available_carrier_network_title" />
<java-symbol type="string" name="wifi_available_title_connecting" />
<java-symbol type="string" name="wifi_available_title_connected" />
<java-symbol type="string" name="wifi_available_title_failed_to_connect" />
@@ -3847,4 +3846,6 @@
<java-symbol type="string" name="config_factoryResetPackage" />
<java-symbol type="array" name="config_highRefreshRateBlacklist" />
+ <java-symbol type="bool" name="config_automotiveHideNavBarForKeyboard" />
+
</resources>
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
index 3d9a1d9..7c6271c 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/StartProgramListUpdatesFanoutTest.java
@@ -19,6 +19,7 @@
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,6 +44,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
+import org.mockito.verification.VerificationWithTimeout;
import java.util.Arrays;
import java.util.HashSet;
@@ -56,6 +58,8 @@
public class StartProgramListUpdatesFanoutTest {
private static final String TAG = "BroadcastRadioTests.hal2.StartProgramListUpdatesFanout";
+ private static final VerificationWithTimeout CB_TIMEOUT = timeout(100);
+
// Mocks
@Mock IBroadcastRadio mBroadcastRadioMock;
@Mock ITunerSession mHalTunerSessionMock;
@@ -200,10 +204,10 @@
// Adding mDabEnsembleInfo should not update any client.
updateHalProgramInfo(false, Arrays.asList(mDabEnsembleInfo), null);
- verify(mAidlTunerCallbackMocks[0], times(1)).onProgramListUpdated(any());
- verify(mAidlTunerCallbackMocks[1], times(2)).onProgramListUpdated(any());
- verify(mAidlTunerCallbackMocks[2], times(1)).onProgramListUpdated(any());
- verify(mAidlTunerCallbackMocks[3], times(2)).onProgramListUpdated(any());
+ verify(mAidlTunerCallbackMocks[0], CB_TIMEOUT.times(1)).onProgramListUpdated(any());
+ verify(mAidlTunerCallbackMocks[1], CB_TIMEOUT.times(2)).onProgramListUpdated(any());
+ verify(mAidlTunerCallbackMocks[2], CB_TIMEOUT.times(1)).onProgramListUpdated(any());
+ verify(mAidlTunerCallbackMocks[3], CB_TIMEOUT.times(2)).onProgramListUpdated(any());
}
@Test
@@ -240,7 +244,7 @@
// Update the HAL with mModifiedAmFmInfo, and verify only the remaining client is updated.
updateHalProgramInfo(true, Arrays.asList(mModifiedAmFmInfo), null);
- verify(mAidlTunerCallbackMocks[0], times(1)).onProgramListUpdated(any());
+ verify(mAidlTunerCallbackMocks[0], CB_TIMEOUT.times(1)).onProgramListUpdated(any());
verifyAidlClientReceivedChunk(mAidlTunerCallbackMocks[1], true,
Arrays.asList(mModifiedAmFmInfo), null);
@@ -313,6 +317,6 @@
}
ProgramList.Chunk expectedChunk = new ProgramList.Chunk(purge, true, modifiedSet,
removedSet);
- verify(clientMock).onProgramListUpdated(expectedChunk);
+ verify(clientMock, CB_TIMEOUT).onProgramListUpdated(expectedChunk);
}
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index bdbf368..97b7ae9 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -544,7 +544,6 @@
Settings.Global.WIFI_ON,
Settings.Global.WIFI_P2P_DEVICE_NAME,
Settings.Global.WIFI_P2P_PENDING_FACTORY_RESET,
- Settings.Global.WIFI_REENABLE_DELAY_MS,
Settings.Global.WIFI_RTT_BACKGROUND_EXEC_GAP_MS,
Settings.Global.WIFI_SAVED_STATE,
Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
@@ -721,7 +720,6 @@
Settings.Secure.BIOMETRIC_DEBUG_ENABLED,
Settings.Secure.FACE_UNLOCK_ATTENTION_REQUIRED,
Settings.Secure.FACE_UNLOCK_DIVERSITY_REQUIRED,
- Settings.Secure.FACE_UNLOCK_EDUCATION_INFO_DISPLAYED,
Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED);
@Test
diff --git a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java b/core/tests/coretests/src/android/provider/settings/validators/SettingsValidatorsTest.java
similarity index 85%
rename from core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
rename to core/tests/coretests/src/android/provider/settings/validators/SettingsValidatorsTest.java
index eea8c83..5f042d3 100644
--- a/core/tests/coretests/src/android/provider/SettingsValidatorsTest.java
+++ b/core/tests/coretests/src/android/provider/settings/validators/SettingsValidatorsTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * 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.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.provider;
+package android.provider.settings.validators;
import static com.google.common.truth.Truth.assertThat;
@@ -23,7 +23,7 @@
import static org.junit.Assert.fail;
import android.platform.test.annotations.Presubmit;
-import android.provider.SettingsValidators.Validator;
+import android.provider.Settings;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -138,7 +138,7 @@
@Test
public void testDiscreteValueValidator() {
String[] beerTypes = new String[]{"Ale", "American IPA", "Stout"};
- Validator v = new SettingsValidators.DiscreteValueValidator(beerTypes);
+ Validator v = new DiscreteValueValidator(beerTypes);
assertTrue(v.validate("Ale"));
assertTrue(v.validate("American IPA"));
assertTrue(v.validate("Stout"));
@@ -148,14 +148,14 @@
@Test
public void testDiscreteValueValidator_onNullValue_returnsFalse() {
String[] discreteTypes = new String[]{"Type1", "Type2"};
- Validator v = new SettingsValidators.DiscreteValueValidator(discreteTypes);
+ Validator v = new DiscreteValueValidator(discreteTypes);
assertFalse(v.validate(null));
}
@Test
public void testInclusiveIntegerRangeValidator() {
- Validator v = new SettingsValidators.InclusiveIntegerRangeValidator(0, 5);
+ Validator v = new InclusiveIntegerRangeValidator(0, 5);
assertTrue(v.validate("0"));
assertTrue(v.validate("2"));
assertTrue(v.validate("5"));
@@ -165,14 +165,14 @@
@Test
public void testInclusiveIntegerRangeValidator_onNullValue_returnsFalse() {
- Validator v = new SettingsValidators.InclusiveIntegerRangeValidator(0, 5);
+ Validator v = new InclusiveIntegerRangeValidator(0, 5);
assertFalse(v.validate(null));
}
@Test
public void testInclusiveFloatRangeValidator() {
- Validator v = new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 5.0f);
+ Validator v = new InclusiveFloatRangeValidator(0.0f, 5.0f);
assertTrue(v.validate("0.0"));
assertTrue(v.validate("2.0"));
assertTrue(v.validate("5.0"));
@@ -182,14 +182,14 @@
@Test
public void testInclusiveFloatRangeValidator_onNullValue_returnsFalse() {
- Validator v = new SettingsValidators.InclusiveFloatRangeValidator(0.0f, 5.0f);
+ Validator v = new InclusiveFloatRangeValidator(0.0f, 5.0f);
assertFalse(v.validate(null));
}
@Test
public void testComponentNameListValidator() {
- Validator v = new SettingsValidators.ComponentNameListValidator(",");
+ Validator v = new ComponentNameListValidator(",");
assertTrue(v.validate("com.android.localtransport/.LocalTransport,"
+ "com.google.android.gms/.backup.migrate.service.D2dTransport"));
assertFalse(v.validate("com.google.5android,android"));
@@ -197,21 +197,21 @@
@Test
public void testComponentNameListValidator_onNullValue_returnsFalse() {
- Validator v = new SettingsValidators.ComponentNameListValidator(",");
+ Validator v = new ComponentNameListValidator(",");
assertFalse(v.validate(null));
}
@Test
public void testPackageNameListValidator() {
- Validator v = new SettingsValidators.PackageNameListValidator(",");
+ Validator v = new PackageNameListValidator(",");
assertTrue(v.validate("com.android.localtransport.LocalTransport,com.google.android.gms"));
assertFalse(v.validate("5com.android.internal.backup.LocalTransport,android"));
}
@Test
public void testPackageNameListValidator_onNullValue_returnsFalse() {
- Validator v = new SettingsValidators.PackageNameListValidator(",");
+ Validator v = new PackageNameListValidator(",");
assertFalse(v.validate(null));
}
@@ -256,51 +256,41 @@
@Test
public void testTTSListValidator_withValidInput_returnsTrue() {
- Validator v = new SettingsValidators.TTSListValidator();
-
- assertTrue(v.validate("com.foo.ttsengine:en-US,com.bar.ttsengine:es_ES"));
+ assertTrue(
+ SettingsValidators.TTS_LIST_VALIDATOR.validate(
+ "com.foo.ttsengine:en-US,com.bar.ttsengine:es_ES"));
}
@Test
public void testTTSListValidator_withInvalidInput_returnsFalse() {
- Validator v = new SettingsValidators.TTSListValidator();
-
- assertFalse(v.validate("com.foo.ttsengine:eng-USA,INVALID"));
+ assertFalse(
+ SettingsValidators.TTS_LIST_VALIDATOR.validate(
+ "com.foo.ttsengine:eng-USA,INVALID"));
}
@Test
public void testTTSListValidator_withEmptyInput_returnsFalse() {
- Validator v = new SettingsValidators.TTSListValidator();
-
- assertFalse(v.validate(""));
+ assertFalse(SettingsValidators.TTS_LIST_VALIDATOR.validate(""));
}
@Test
public void testTTSListValidator_withNullInput_returnsFalse() {
- Validator v = new SettingsValidators.TTSListValidator();
-
- assertFalse(v.validate(null));
+ assertFalse(SettingsValidators.TTS_LIST_VALIDATOR.validate(null));
}
@Test
public void testTileListValidator_withValidInput_returnsTrue() {
- Validator v = new SettingsValidators.TileListValidator();
-
- assertTrue(v.validate("1,2,3,4"));
+ assertTrue(SettingsValidators.TILE_LIST_VALIDATOR.validate("1,2,3,4"));
}
@Test
public void testTileListValidator_withMissingValue_returnsFalse() {
- Validator v = new SettingsValidators.TileListValidator();
-
- assertFalse(v.validate("1,,3"));
+ assertFalse(SettingsValidators.TILE_LIST_VALIDATOR.validate("1,,3"));
}
@Test
public void testTileListValidator_withNullInput_returnsFalse() {
- Validator v = new SettingsValidators.TileListValidator();
-
- assertFalse(v.validate(null));
+ assertFalse(SettingsValidators.TILE_LIST_VALIDATOR.validate(null));
}
@Test
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 226c269..4187c80 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -24,6 +24,7 @@
<permission name="android.permission.PROVIDE_RESOLVER_RANKER_SERVICE" />
<permission name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE" />
<permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
+ <permission name="android.permission.INTERACT_ACROSS_USERS" />
</privapp-permissions>
<privapp-permissions package="com.android.apps.tag">
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 2d5babc..150a941 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -214,7 +214,7 @@
/* @hide */
@NonNull
- abstract ImageDecoder createImageDecoder() throws IOException;
+ abstract ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException;
};
private static class ByteArraySource extends Source {
@@ -228,8 +228,8 @@
private final int mLength;
@Override
- public ImageDecoder createImageDecoder() throws IOException {
- return nCreate(mData, mOffset, mLength, this);
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
+ return nCreate(mData, mOffset, mLength, preferAnimation, this);
}
}
@@ -240,14 +240,14 @@
private final ByteBuffer mBuffer;
@Override
- public ImageDecoder createImageDecoder() throws IOException {
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
if (!mBuffer.isDirect() && mBuffer.hasArray()) {
int offset = mBuffer.arrayOffset() + mBuffer.position();
int length = mBuffer.limit() - mBuffer.position();
- return nCreate(mBuffer.array(), offset, length, this);
+ return nCreate(mBuffer.array(), offset, length, preferAnimation, this);
}
ByteBuffer buffer = mBuffer.slice();
- return nCreate(buffer, buffer.position(), buffer.limit(), this);
+ return nCreate(buffer, buffer.position(), buffer.limit(), preferAnimation, this);
}
}
@@ -267,7 +267,7 @@
Resources getResources() { return mResources; }
@Override
- public ImageDecoder createImageDecoder() throws IOException {
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
AssetFileDescriptor assetFd = null;
try {
if (mUri.getScheme() == ContentResolver.SCHEME_CONTENT) {
@@ -284,26 +284,26 @@
throw new FileNotFoundException(mUri.toString());
}
- return createFromStream(is, true, this);
+ return createFromStream(is, true, preferAnimation, this);
}
- return createFromAssetFileDescriptor(assetFd, this);
+ return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
}
}
@NonNull
private static ImageDecoder createFromFile(@NonNull File file,
- @NonNull Source source) throws IOException {
+ boolean preferAnimation, @NonNull Source source) throws IOException {
FileInputStream stream = new FileInputStream(file);
FileDescriptor fd = stream.getFD();
try {
Os.lseek(fd, 0, SEEK_CUR);
} catch (ErrnoException e) {
- return createFromStream(stream, true, source);
+ return createFromStream(stream, true, preferAnimation, source);
}
ImageDecoder decoder = null;
try {
- decoder = nCreate(fd, source);
+ decoder = nCreate(fd, preferAnimation, source);
} finally {
if (decoder == null) {
IoUtils.closeQuietly(stream);
@@ -317,12 +317,12 @@
@NonNull
private static ImageDecoder createFromStream(@NonNull InputStream is,
- boolean closeInputStream, Source source) throws IOException {
+ boolean closeInputStream, boolean preferAnimation, Source source) throws IOException {
// Arbitrary size matches BitmapFactory.
byte[] storage = new byte[16 * 1024];
ImageDecoder decoder = null;
try {
- decoder = nCreate(is, storage, source);
+ decoder = nCreate(is, storage, preferAnimation, source);
} finally {
if (decoder == null) {
if (closeInputStream) {
@@ -340,7 +340,7 @@
@NonNull
private static ImageDecoder createFromAssetFileDescriptor(@NonNull AssetFileDescriptor assetFd,
- Source source) throws IOException {
+ boolean preferAnimation, Source source) throws IOException {
final FileDescriptor fd = assetFd.getFileDescriptor();
final long offset = assetFd.getStartOffset();
@@ -348,9 +348,9 @@
try {
try {
Os.lseek(fd, offset, SEEK_SET);
- decoder = nCreate(fd, source);
+ decoder = nCreate(fd, preferAnimation, source);
} catch (ErrnoException e) {
- decoder = createFromStream(new FileInputStream(fd), true, source);
+ decoder = createFromStream(new FileInputStream(fd), true, preferAnimation, source);
}
} finally {
if (decoder == null) {
@@ -388,7 +388,7 @@
public int getDensity() { return mInputDensity; }
@Override
- public ImageDecoder createImageDecoder() throws IOException {
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
synchronized (this) {
if (mInputStream == null) {
@@ -396,7 +396,7 @@
}
InputStream is = mInputStream;
mInputStream = null;
- return createFromStream(is, false, this);
+ return createFromStream(is, false, preferAnimation, this);
}
}
}
@@ -434,14 +434,14 @@
}
@Override
- public ImageDecoder createImageDecoder() throws IOException {
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
synchronized (this) {
if (mAssetInputStream == null) {
throw new IOException("Cannot reuse AssetInputStreamSource");
}
AssetInputStream ais = mAssetInputStream;
mAssetInputStream = null;
- return createFromAsset(ais, this);
+ return createFromAsset(ais, preferAnimation, this);
}
}
}
@@ -469,7 +469,7 @@
}
@Override
- public ImageDecoder createImageDecoder() throws IOException {
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
TypedValue value = new TypedValue();
// This is just used in order to access the underlying Asset and
// keep it alive.
@@ -483,7 +483,7 @@
}
}
- return createFromAsset((AssetInputStream) is, this);
+ return createFromAsset((AssetInputStream) is, preferAnimation, this);
}
}
@@ -491,11 +491,11 @@
* ImageDecoder will own the AssetInputStream.
*/
private static ImageDecoder createFromAsset(AssetInputStream ais,
- Source source) throws IOException {
+ boolean preferAnimation, Source source) throws IOException {
ImageDecoder decoder = null;
try {
long asset = ais.getNativeAsset();
- decoder = nCreate(asset, source);
+ decoder = nCreate(asset, preferAnimation, source);
} finally {
if (decoder == null) {
IoUtils.closeQuietly(ais);
@@ -517,9 +517,9 @@
private final String mFileName;
@Override
- public ImageDecoder createImageDecoder() throws IOException {
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
InputStream is = mAssets.open(mFileName);
- return createFromAsset((AssetInputStream) is, this);
+ return createFromAsset((AssetInputStream) is, preferAnimation, this);
}
}
@@ -531,8 +531,8 @@
private final File mFile;
@Override
- public ImageDecoder createImageDecoder() throws IOException {
- return createFromFile(mFile, this);
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
+ return createFromFile(mFile, preferAnimation, this);
}
}
@@ -544,7 +544,7 @@
private final Callable<AssetFileDescriptor> mCallable;
@Override
- public ImageDecoder createImageDecoder() throws IOException {
+ public ImageDecoder createImageDecoder(boolean preferAnimation) throws IOException {
AssetFileDescriptor assetFd = null;
try {
assetFd = mCallable.call();
@@ -555,7 +555,7 @@
throw new IOException(e);
}
}
- return createFromAssetFileDescriptor(assetFd, this);
+ return createFromAssetFileDescriptor(assetFd, preferAnimation, this);
}
}
@@ -1740,7 +1740,7 @@
@NonNull
private static Drawable decodeDrawableImpl(@NonNull Source src,
@Nullable OnHeaderDecodedListener listener) throws IOException {
- try (ImageDecoder decoder = src.createImageDecoder()) {
+ try (ImageDecoder decoder = src.createImageDecoder(true /*preferAnimation*/)) {
decoder.mSource = src;
decoder.callHeaderDecoded(listener, src);
@@ -1844,7 +1844,7 @@
@NonNull
private static Bitmap decodeBitmapImpl(@NonNull Source src,
@Nullable OnHeaderDecodedListener listener) throws IOException {
- try (ImageDecoder decoder = src.createImageDecoder()) {
+ try (ImageDecoder decoder = src.createImageDecoder(false /*preferAnimation*/)) {
decoder.mSource = src;
decoder.callHeaderDecoded(listener, src);
@@ -1971,15 +1971,17 @@
}
}
- private static native ImageDecoder nCreate(long asset, Source src) throws IOException;
- private static native ImageDecoder nCreate(ByteBuffer buffer, int position,
- int limit, Source src) throws IOException;
+ private static native ImageDecoder nCreate(long asset,
+ boolean preferAnimation, Source src) throws IOException;
+ private static native ImageDecoder nCreate(ByteBuffer buffer, int position, int limit,
+ boolean preferAnimation, Source src) throws IOException;
private static native ImageDecoder nCreate(byte[] data, int offset, int length,
- Source src) throws IOException;
+ boolean preferAnimation, Source src) throws IOException;
private static native ImageDecoder nCreate(InputStream is, byte[] storage,
- Source src) throws IOException;
+ boolean preferAnimation, Source src) throws IOException;
// The fd must be seekable.
- private static native ImageDecoder nCreate(FileDescriptor fd, Source src) throws IOException;
+ private static native ImageDecoder nCreate(FileDescriptor fd,
+ boolean preferAnimation, Source src) throws IOException;
@NonNull
private static native Bitmap nDecodeBitmap(long nativePtr,
@NonNull ImageDecoder decoder,
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 5100165..35abc57 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -138,14 +138,14 @@
err = mCreateInstance(&instance_create, nullptr, &mInstance);
LOG_ALWAYS_FATAL_IF(err < 0);
+ GET_INST_PROC(CreateDevice);
GET_INST_PROC(DestroyInstance);
+ GET_INST_PROC(EnumerateDeviceExtensionProperties);
GET_INST_PROC(EnumeratePhysicalDevices);
- GET_INST_PROC(GetPhysicalDeviceProperties);
- GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
GET_INST_PROC(GetPhysicalDeviceFeatures2);
GET_INST_PROC(GetPhysicalDeviceImageFormatProperties2);
- GET_INST_PROC(CreateDevice);
- GET_INST_PROC(EnumerateDeviceExtensionProperties);
+ GET_INST_PROC(GetPhysicalDeviceProperties);
+ GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
uint32_t gpuCount;
LOG_ALWAYS_FATAL_IF(mEnumeratePhysicalDevices(mInstance, &gpuCount, nullptr));
@@ -312,29 +312,27 @@
LOG_ALWAYS_FATAL_IF(mCreateDevice(mPhysicalDevice, &deviceInfo, nullptr, &mDevice));
- GET_DEV_PROC(GetDeviceQueue);
- GET_DEV_PROC(DeviceWaitIdle);
- GET_DEV_PROC(DestroyDevice);
- GET_DEV_PROC(CreateCommandPool);
- GET_DEV_PROC(DestroyCommandPool);
GET_DEV_PROC(AllocateCommandBuffers);
- GET_DEV_PROC(FreeCommandBuffers);
- GET_DEV_PROC(ResetCommandBuffer);
GET_DEV_PROC(BeginCommandBuffer);
- GET_DEV_PROC(EndCommandBuffer);
GET_DEV_PROC(CmdPipelineBarrier);
+ GET_DEV_PROC(CreateCommandPool);
+ GET_DEV_PROC(CreateFence);
+ GET_DEV_PROC(CreateSemaphore);
+ GET_DEV_PROC(DestroyCommandPool);
+ GET_DEV_PROC(DestroyDevice);
+ GET_DEV_PROC(DestroyFence);
+ GET_DEV_PROC(DestroySemaphore);
+ GET_DEV_PROC(DeviceWaitIdle);
+ GET_DEV_PROC(EndCommandBuffer);
+ GET_DEV_PROC(FreeCommandBuffers);
GET_DEV_PROC(GetDeviceQueue);
+ GET_DEV_PROC(GetSemaphoreFdKHR);
+ GET_DEV_PROC(ImportSemaphoreFdKHR);
GET_DEV_PROC(QueueSubmit);
GET_DEV_PROC(QueueWaitIdle);
- GET_DEV_PROC(DeviceWaitIdle);
- GET_DEV_PROC(CreateSemaphore);
- GET_DEV_PROC(DestroySemaphore);
- GET_DEV_PROC(ImportSemaphoreFdKHR);
- GET_DEV_PROC(GetSemaphoreFdKHR);
- GET_DEV_PROC(CreateFence);
- GET_DEV_PROC(DestroyFence);
- GET_DEV_PROC(WaitForFences);
+ GET_DEV_PROC(ResetCommandBuffer);
GET_DEV_PROC(ResetFences);
+ GET_DEV_PROC(WaitForFences);
}
void VulkanManager::initialize() {
diff --git a/libs/input/Android.bp b/libs/input/Android.bp
index 89d3cc4..16f2917 100644
--- a/libs/input/Android.bp
+++ b/libs/input/Android.bp
@@ -20,6 +20,7 @@
],
shared_libs: [
+ "libbinder",
"libcutils",
"liblog",
"libutils",
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index c1868d3..fd386e9 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -245,7 +245,8 @@
if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
|| (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
| DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
- | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
+ | DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID
+ | DIRTY_ICON_STYLE))))) {
needApplyTransaction = true;
if (wantSurfaceVisibleAndDrawn
@@ -274,6 +275,21 @@
update.state.transformationMatrix.dtdy);
}
+ if (wantSurfaceVisibleAndDrawn
+ && (becomingVisible
+ || (update.state.dirty & (DIRTY_HOTSPOT | DIRTY_ICON_STYLE)))) {
+ Parcel p;
+ p.writeInt32(update.state.icon.style);
+ p.writeFloat(update.state.icon.hotSpotX);
+ p.writeFloat(update.state.icon.hotSpotY);
+
+ // Pass cursor metadata in the sprite surface so that when Android is running as a
+ // client OS (e.g. ARC++) the host OS can get the requested cursor metadata and
+ // update mouse cursor in the host OS.
+ t.setMetadata(
+ update.state.surfaceControl, METADATA_MOUSE_CURSOR, p);
+ }
+
int32_t surfaceLayer = mOverlayLayer + update.state.layer;
if (wantSurfaceVisibleAndDrawn
&& (becomingVisible || (update.state.dirty & DIRTY_LAYER))) {
@@ -397,9 +413,14 @@
} else {
dirty = DIRTY_BITMAP;
}
+
+ if (mLocked.state.icon.style != icon.style) {
+ mLocked.state.icon.style = icon.style;
+ dirty |= DIRTY_ICON_STYLE;
+ }
} else if (mLocked.state.icon.isValid()) {
mLocked.state.icon.bitmap.reset();
- dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
+ dirty = DIRTY_BITMAP | DIRTY_HOTSPOT | DIRTY_ICON_STYLE;
} else {
return; // setting to invalid icon and already invalid so nothing to do
}
diff --git a/libs/input/SpriteController.h b/libs/input/SpriteController.h
index 5b216f5..79a904f 100644
--- a/libs/input/SpriteController.h
+++ b/libs/input/SpriteController.h
@@ -55,11 +55,12 @@
* Icon that a sprite displays, including its hotspot.
*/
struct SpriteIcon {
- inline SpriteIcon() : hotSpotX(0), hotSpotY(0) { }
- inline SpriteIcon(const SkBitmap& bitmap, float hotSpotX, float hotSpotY) :
- bitmap(bitmap), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
+ inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { }
+ inline SpriteIcon(const SkBitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) :
+ bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
SkBitmap bitmap;
+ int32_t style;
float hotSpotX;
float hotSpotY;
@@ -69,11 +70,12 @@
bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(),
0, 0);
}
- return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY);
+ return SpriteIcon(bitmapCopy, style, hotSpotX, hotSpotY);
}
inline void reset() {
bitmap.reset();
+ style = 0;
hotSpotX = 0;
hotSpotY = 0;
}
@@ -149,15 +151,15 @@
SpriteController(const sp<Looper>& looper, int32_t overlayLayer);
/* Creates a new sprite, initially invisible. */
- sp<Sprite> createSprite();
+ virtual sp<Sprite> createSprite();
/* Opens or closes a transaction to perform a batch of sprite updates as part of
* a single operation such as setPosition and setAlpha. It is not necessary to
* open a transaction when updating a single property.
* Calls to openTransaction() nest and must be matched by an equal number
* of calls to closeTransaction(). */
- void openTransaction();
- void closeTransaction();
+ virtual void openTransaction();
+ virtual void closeTransaction();
private:
enum {
@@ -174,6 +176,7 @@
DIRTY_VISIBILITY = 1 << 5,
DIRTY_HOTSPOT = 1 << 6,
DIRTY_DISPLAY_ID = 1 << 7,
+ DIRTY_ICON_STYLE = 1 << 8,
};
/* Describes the state of a sprite.
diff --git a/libs/input/TEST_MAPPING b/libs/input/TEST_MAPPING
new file mode 100644
index 0000000..fe74c62
--- /dev/null
+++ b/libs/input/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libinputservice_test"
+ }
+ ]
+}
diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp
new file mode 100644
index 0000000..e83b2a7
--- /dev/null
+++ b/libs/input/tests/Android.bp
@@ -0,0 +1,45 @@
+// 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.
+
+cc_test {
+ name: "libinputservice_test",
+ srcs: [
+ "PointerController_test.cpp",
+ ],
+ shared_libs: [
+ "libinputservice",
+ "libgui",
+ "libhwui",
+ "libutils",
+ ],
+ static_libs: [
+ "libgmock",
+ "libgtest",
+ ],
+ header_libs: [
+ "libbase_headers",
+ "libinputflinger_headers",
+ ],
+ include_dirs: [
+ "frameworks/base/libs",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wextra",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+}
diff --git a/libs/input/tests/PointerController_test.cpp b/libs/input/tests/PointerController_test.cpp
new file mode 100644
index 0000000..92efb4e
--- /dev/null
+++ b/libs/input/tests/PointerController_test.cpp
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+
+#include "mocks/MockSprite.h"
+#include "mocks/MockSpriteController.h"
+
+#include <input/PointerController.h>
+#include <input/SpriteController.h>
+
+#include <atomic>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+namespace android {
+
+enum TestCursorType {
+ CURSOR_TYPE_DEFAULT = 0,
+ CURSOR_TYPE_HOVER,
+ CURSOR_TYPE_TOUCH,
+ CURSOR_TYPE_ANCHOR,
+ CURSOR_TYPE_ADDITIONAL_1,
+ CURSOR_TYPE_ADDITIONAL_2,
+ CURSOR_TYPE_CUSTOM = -1,
+};
+
+using ::testing::AllOf;
+using ::testing::Field;
+using ::testing::NiceMock;
+using ::testing::Mock;
+using ::testing::Return;
+using ::testing::Test;
+
+std::pair<float, float> getHotSpotCoordinatesForType(int32_t type) {
+ return std::make_pair(type * 10, type * 10 + 5);
+}
+
+class MockPointerControllerPolicyInterface : public PointerControllerPolicyInterface {
+public:
+ virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) override;
+ virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) override;
+ virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
+ std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) override;
+ virtual int32_t getDefaultPointerIconId() override;
+ virtual int32_t getCustomPointerIconId() override;
+
+private:
+ void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
+};
+
+void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
+ loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
+}
+
+void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
+ int32_t) {
+ loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
+ loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
+ loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
+}
+
+void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
+ std::map<int32_t, SpriteIcon>* outResources,
+ std::map<int32_t, PointerAnimation>* outAnimationResources,
+ int32_t) {
+ SpriteIcon icon;
+ PointerAnimation anim;
+
+ for (int32_t cursorType : {CURSOR_TYPE_ADDITIONAL_1, CURSOR_TYPE_ADDITIONAL_2}) {
+ loadPointerIconForType(&icon, cursorType);
+ anim.animationFrames.push_back(icon);
+ anim.durationPerFrame = 10;
+ (*outResources)[cursorType] = icon;
+ (*outAnimationResources)[cursorType] = anim;
+ }
+}
+
+int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
+ return CURSOR_TYPE_DEFAULT;
+}
+
+int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() {
+ return CURSOR_TYPE_CUSTOM;
+}
+
+void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
+ icon->style = type;
+ std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
+ icon->hotSpotX = hotSpot.first;
+ icon->hotSpotY = hotSpot.second;
+}
+
+class PointerControllerTest : public Test {
+protected:
+ PointerControllerTest();
+ ~PointerControllerTest();
+
+ sp<MockSprite> mPointerSprite;
+ sp<MockPointerControllerPolicyInterface> mPolicy;
+ sp<MockSpriteController> mSpriteController;
+ sp<PointerController> mPointerController;
+
+private:
+ void loopThread();
+
+ std::atomic<bool> mRunning = true;
+ class MyLooper : public Looper {
+ public:
+ MyLooper() : Looper(false) {}
+ ~MyLooper() = default;
+ };
+ sp<MyLooper> mLooper;
+ std::thread mThread;
+};
+
+PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
+ mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
+
+ mSpriteController = new NiceMock<MockSpriteController>(mLooper);
+ mPolicy = new MockPointerControllerPolicyInterface();
+
+ EXPECT_CALL(*mSpriteController, createSprite())
+ .WillOnce(Return(mPointerSprite));
+
+ mPointerController = new PointerController(mPolicy, mLooper, mSpriteController);
+
+ DisplayViewport viewport;
+ viewport.displayId = ADISPLAY_ID_DEFAULT;
+ viewport.logicalRight = 1600;
+ viewport.logicalBottom = 1200;
+ viewport.physicalRight = 800;
+ viewport.physicalBottom = 600;
+ viewport.deviceWidth = 400;
+ viewport.deviceHeight = 300;
+ mPointerController->setDisplayViewport(viewport);
+}
+
+PointerControllerTest::~PointerControllerTest() {
+ mRunning.store(false, std::memory_order_relaxed);
+ mThread.join();
+}
+
+void PointerControllerTest::loopThread() {
+ Looper::setForThread(mLooper);
+
+ while (mRunning.load(std::memory_order_relaxed)) {
+ mLooper->pollOnce(100);
+ }
+}
+
+TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
+ mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+ std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
+ EXPECT_CALL(*mPointerSprite, setVisible(true));
+ EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+ EXPECT_CALL(*mPointerSprite, setIcon(
+ AllOf(
+ Field(&SpriteIcon::style, CURSOR_TYPE_DEFAULT),
+ Field(&SpriteIcon::hotSpotX, hotspot.first),
+ Field(&SpriteIcon::hotSpotY, hotspot.second))));
+ mPointerController->reloadPointerResources();
+}
+
+TEST_F(PointerControllerTest, updatePointerIcon) {
+ mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+ int32_t type = CURSOR_TYPE_ADDITIONAL_1;
+ std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
+ EXPECT_CALL(*mPointerSprite, setVisible(true));
+ EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+ EXPECT_CALL(*mPointerSprite, setIcon(
+ AllOf(
+ Field(&SpriteIcon::style, type),
+ Field(&SpriteIcon::hotSpotX, hotspot.first),
+ Field(&SpriteIcon::hotSpotY, hotspot.second))));
+ mPointerController->updatePointerIcon(type);
+}
+
+TEST_F(PointerControllerTest, setCustomPointerIcon) {
+ mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
+
+ int32_t style = CURSOR_TYPE_CUSTOM;
+ float hotSpotX = 15;
+ float hotSpotY = 20;
+
+ SpriteIcon icon;
+ icon.style = style;
+ icon.hotSpotX = hotSpotX;
+ icon.hotSpotY = hotSpotY;
+
+ EXPECT_CALL(*mPointerSprite, setVisible(true));
+ EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
+ EXPECT_CALL(*mPointerSprite, setIcon(
+ AllOf(
+ Field(&SpriteIcon::style, style),
+ Field(&SpriteIcon::hotSpotX, hotSpotX),
+ Field(&SpriteIcon::hotSpotY, hotSpotY))));
+ mPointerController->setCustomPointerIcon(icon);
+}
+
+} // namespace android
diff --git a/libs/input/tests/mocks/MockSprite.h b/libs/input/tests/mocks/MockSprite.h
new file mode 100644
index 0000000..013b79c
--- /dev/null
+++ b/libs/input/tests/mocks/MockSprite.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef _MOCK_SPRITE_H
+#define _MOCK_SPRITE_H
+
+#include <input/SpriteController.h>
+
+#include <gmock/gmock.h>
+
+namespace android {
+
+class MockSprite : public Sprite {
+public:
+ virtual ~MockSprite() = default;
+
+ MOCK_METHOD(void, setIcon, (const SpriteIcon& icon), (override));
+ MOCK_METHOD(void, setVisible, (bool), (override));
+ MOCK_METHOD(void, setPosition, (float, float), (override));
+ MOCK_METHOD(void, setLayer, (int32_t), (override));
+ MOCK_METHOD(void, setAlpha, (float), (override));
+ MOCK_METHOD(void, setTransformationMatrix, (const SpriteTransformationMatrix&), (override));
+ MOCK_METHOD(void, setDisplayId, (int32_t), (override));
+};
+
+} // namespace android
+
+#endif // _MOCK_SPRITE_H
diff --git a/libs/input/tests/mocks/MockSpriteController.h b/libs/input/tests/mocks/MockSpriteController.h
new file mode 100644
index 0000000..a034f66
--- /dev/null
+++ b/libs/input/tests/mocks/MockSpriteController.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef _MOCK_SPRITE_CONTROLLER_H
+#define _MOCK_SPRITE_CONTROLLER_H
+
+#include "MockSprite.h"
+
+#include <input/SpriteController.h>
+
+namespace android {
+
+class MockSpriteController : public SpriteController {
+
+public:
+ MockSpriteController(sp<Looper> looper) : SpriteController(looper, 0) {}
+ ~MockSpriteController() {}
+
+ MOCK_METHOD(sp<Sprite>, createSprite, (), (override));
+ MOCK_METHOD(void, openTransaction, (), (override));
+ MOCK_METHOD(void, closeTransaction, (), (override));
+};
+
+} // namespace android
+
+#endif // _MOCK_SPRITE_CONTROLLER_H
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 148ffaf..55583d5 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -37,6 +37,7 @@
import libcore.io.Streams;
import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInput;
import java.io.DataInputStream;
@@ -1866,14 +1867,17 @@
FileInputStream in = null;
FileOutputStream out = null;
+ File originalFile = null;
+ if (mFilename != null) {
+ originalFile = new File(mFilename);
+ }
File tempFile = null;
try {
// Move the original file to temporary file.
if (mFilename != null) {
tempFile = new File(mFilename + ".tmp");
- File originalFile = new File(mFilename);
if (!originalFile.renameTo(tempFile)) {
- throw new IOException("Could'nt rename to " + tempFile.getAbsolutePath());
+ throw new IOException("Couldn't rename to " + tempFile.getAbsolutePath());
}
} else if (mSeekableFileDescriptor != null) {
tempFile = File.createTempFile("temp", "jpg");
@@ -1882,8 +1886,8 @@
out = new FileOutputStream(tempFile);
Streams.copy(in, out);
}
- } catch (ErrnoException e) {
- throw e.rethrowAsIOException();
+ } catch (Exception e) {
+ throw new IOException("Failed to copy original file to temp file", e);
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
@@ -1900,9 +1904,18 @@
Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET);
out = new FileOutputStream(mSeekableFileDescriptor);
}
- saveJpegAttributes(in, out);
- } catch (ErrnoException e) {
- throw e.rethrowAsIOException();
+ try (BufferedInputStream bufferedIn = new BufferedInputStream(in);
+ BufferedOutputStream bufferedOut = new BufferedOutputStream(out)) {
+ saveJpegAttributes(bufferedIn, bufferedOut);
+ }
+ } catch (Exception e) {
+ if (mFilename != null) {
+ if (!tempFile.renameTo(originalFile)) {
+ throw new IOException("Couldn't restore original file: "
+ + originalFile.getAbsolutePath());
+ }
+ }
+ throw new IOException("Failed to save new file", e);
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
diff --git a/media/java/android/media/MediaScannerConnection.java b/media/java/android/media/MediaScannerConnection.java
index 471fa2c..7eec8d9 100644
--- a/media/java/android/media/MediaScannerConnection.java
+++ b/media/java/android/media/MediaScannerConnection.java
@@ -16,17 +16,20 @@
package android.media;
+import android.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
+import android.content.ContentProviderClient;
import android.content.Context;
-import android.content.Intent;
import android.content.ServiceConnection;
-import android.media.IMediaScannerListener;
-import android.media.IMediaScannerService;
import android.net.Uri;
+import android.os.Build;
import android.os.IBinder;
-import android.os.RemoteException;
+import android.provider.MediaStore;
import android.util.Log;
+import com.android.internal.os.BackgroundThread;
+
+import java.io.File;
/**
* MediaScannerConnection provides a way for applications to pass a
@@ -38,20 +41,24 @@
* to the client of the MediaScannerConnection class.
*/
public class MediaScannerConnection implements ServiceConnection {
-
private static final String TAG = "MediaScannerConnection";
- private Context mContext;
- private MediaScannerConnectionClient mClient;
- private IMediaScannerService mService;
- private boolean mConnected; // true if connect() has been called since last disconnect()
+ private final Context mContext;
+ private final MediaScannerConnectionClient mClient;
+ private ContentProviderClient mProvider;
+
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+ private IMediaScannerService mService;
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+ private boolean mConnected;
+ @Deprecated
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
private final IMediaScannerListener.Stub mListener = new IMediaScannerListener.Stub() {
+ @Override
public void scanCompleted(String path, Uri uri) {
- MediaScannerConnectionClient client = mClient;
- if (client != null) {
- client.onScanCompleted(path, uri);
- }
}
};
@@ -81,15 +88,6 @@
* MediaScanner service has been established.
*/
public void onMediaScannerConnected();
-
- /**
- * Called to notify the client when the media scanner has finished
- * scanning a file.
- * @param path the path to the file that has been scanned.
- * @param uri the Uri for the file if the scanning operation succeeded
- * and the file was added to the media database, or null if scanning failed.
- */
- public void onScanCompleted(String path, Uri uri);
}
/**
@@ -111,13 +109,12 @@
*/
public void connect() {
synchronized (this) {
- if (!mConnected) {
- Intent intent = new Intent(IMediaScannerService.class.getName());
- intent.setComponent(
- new ComponentName("com.android.providers.media",
- "com.android.providers.media.MediaScannerService"));
- mContext.bindService(intent, this, Context.BIND_AUTO_CREATE);
- mConnected = true;
+ if (mProvider == null) {
+ mProvider = mContext.getContentResolver()
+ .acquireContentProviderClient(MediaStore.AUTHORITY);
+ if (mClient != null) {
+ mClient.onMediaScannerConnected();
+ }
}
}
}
@@ -127,22 +124,9 @@
*/
public void disconnect() {
synchronized (this) {
- if (mConnected) {
- if (false) {
- Log.v(TAG, "Disconnecting from Media Scanner");
- }
- try {
- mContext.unbindService(this);
- if (mClient instanceof ClientProxy) {
- mClient = null;
- }
- mService = null;
- } catch (IllegalArgumentException ex) {
- if (false) {
- Log.v(TAG, "disconnect failed: " + ex);
- }
- }
- mConnected = false;
+ if (mProvider != null) {
+ mProvider.close();
+ mProvider = null;
}
}
}
@@ -152,7 +136,7 @@
* @return true if we are connected, false otherwise
*/
public synchronized boolean isConnected() {
- return (mService != null && mConnected);
+ return (mProvider != null);
}
/**
@@ -166,55 +150,15 @@
*/
public void scanFile(String path, String mimeType) {
synchronized (this) {
- if (mService == null || !mConnected) {
+ if (mProvider == null) {
throw new IllegalStateException("not connected to MediaScannerService");
}
- try {
- if (false) {
- Log.v(TAG, "Scanning file " + path);
+ BackgroundThread.getExecutor().execute(() -> {
+ final Uri uri = scanFileQuietly(mProvider, new File(path));
+ if (mClient != null) {
+ mClient.onScanCompleted(path, uri);
}
- mService.requestScanFile(path, mimeType, mListener);
- } catch (RemoteException e) {
- if (false) {
- Log.d(TAG, "Failed to scan file " + path);
- }
- }
- }
- }
-
- static class ClientProxy implements MediaScannerConnectionClient {
- final String[] mPaths;
- final String[] mMimeTypes;
- final OnScanCompletedListener mClient;
- MediaScannerConnection mConnection;
- int mNextPath;
-
- ClientProxy(String[] paths, String[] mimeTypes, OnScanCompletedListener client) {
- mPaths = paths;
- mMimeTypes = mimeTypes;
- mClient = client;
- }
-
- public void onMediaScannerConnected() {
- scanNextPath();
- }
-
- public void onScanCompleted(String path, Uri uri) {
- if (mClient != null) {
- mClient.onScanCompleted(path, uri);
- }
- scanNextPath();
- }
-
- void scanNextPath() {
- if (mNextPath >= mPaths.length) {
- mConnection.disconnect();
- mConnection = null;
- return;
- }
- String mimeType = mMimeTypes != null ? mMimeTypes[mNextPath] : null;
- mConnection.scanFile(mPaths[mNextPath], mimeType);
- mNextPath++;
+ });
}
}
@@ -237,36 +181,76 @@
*/
public static void scanFile(Context context, String[] paths, String[] mimeTypes,
OnScanCompletedListener callback) {
- ClientProxy client = new ClientProxy(paths, mimeTypes, callback);
- MediaScannerConnection connection = new MediaScannerConnection(context, client);
- client.mConnection = connection;
- connection.connect();
- }
-
- /**
- * Part of the ServiceConnection interface. Do not call.
- */
- public void onServiceConnected(ComponentName className, IBinder service) {
- if (false) {
- Log.v(TAG, "Connected to Media Scanner");
- }
- synchronized (this) {
- mService = IMediaScannerService.Stub.asInterface(service);
- if (mService != null && mClient != null) {
- mClient.onMediaScannerConnected();
+ BackgroundThread.getExecutor().execute(() -> {
+ try (ContentProviderClient client = context.getContentResolver()
+ .acquireContentProviderClient(MediaStore.AUTHORITY)) {
+ for (String path : paths) {
+ final Uri uri = scanFileQuietly(client, new File(path));
+ if (callback != null) {
+ callback.onScanCompleted(path, uri);
+ }
+ }
}
+ });
+ }
+
+ private static Uri scanFileQuietly(ContentProviderClient client, File file) {
+ Uri uri = null;
+ try {
+ uri = MediaStore.scanFile(client, file);
+ Log.d(TAG, "Scanned " + file + " to " + uri);
+ } catch (Exception e) {
+ Log.w(TAG, "Failed to scan " + file + ": " + e);
+ }
+ return uri;
+ }
+
+ @Deprecated
+ static class ClientProxy implements MediaScannerConnectionClient {
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+ final String[] mPaths;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+ final String[] mMimeTypes;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+ final OnScanCompletedListener mClient;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+ MediaScannerConnection mConnection;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+ int mNextPath;
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+ ClientProxy(String[] paths, String[] mimeTypes, OnScanCompletedListener client) {
+ mPaths = paths;
+ mMimeTypes = mimeTypes;
+ mClient = client;
+ }
+
+ @Override
+ public void onMediaScannerConnected() {
+ }
+
+ @Override
+ public void onScanCompleted(String path, Uri uri) {
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.O)
+ void scanNextPath() {
}
}
/**
* Part of the ServiceConnection interface. Do not call.
*/
+ @Override
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ // No longer needed
+ }
+
+ /**
+ * Part of the ServiceConnection interface. Do not call.
+ */
+ @Override
public void onServiceDisconnected(ComponentName className) {
- if (false) {
- Log.v(TAG, "Disconnected from Media Scanner");
- }
- synchronized (this) {
- mService = null;
- }
+ // No longer needed
}
}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 97fbea6..7ea83f5 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -32,7 +32,10 @@
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.inputmethodservice.InputMethodService;
+import android.os.IBinder;
import android.util.Log;
+import android.view.Display;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -87,8 +90,7 @@
/**
* A status bar (and navigation bar) tailored for the automotive use case.
*/
-public class CarStatusBar extends StatusBar implements
- CarBatteryController.BatteryViewHandler {
+public class CarStatusBar extends StatusBar implements CarBatteryController.BatteryViewHandler {
private static final String TAG = "CarStatusBar";
// used to calculate how fast to open or close the window
private static final float DEFAULT_FLING_VELOCITY = 0;
@@ -169,6 +171,9 @@
private boolean mIsSwipingVerticallyToClose;
// Whether heads-up notifications should be shown when shade is open.
private boolean mEnableHeadsUpNotificationWhenNotificationShadeOpen;
+ // If the nav bar should be hidden when the soft keyboard is visible.
+ private boolean mHideNavBarForKeyboard;
+ private boolean mBottomNavBarVisible;
private final CarPowerStateListener mCarPowerStateListener =
(int state) -> {
@@ -190,6 +195,17 @@
// builds the nav bar
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
+
+ // Keyboard related setup, before nav bars are created.
+ mHideNavBarForKeyboard = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard);
+ mBottomNavBarVisible = false;
+
+ // Need to initialize screen lifecycle before calling super.start - before switcher is
+ // created.
+ mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
+ mScreenLifecycle.addObserver(mScreenObserver);
+
super.start();
mTaskStackListener = new TaskStackListenerImpl();
mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
@@ -236,9 +252,6 @@
mPowerManagerHelper.connectToCarService();
mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
-
- mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
- mScreenLifecycle.addObserver(mScreenObserver);
}
/**
@@ -720,6 +733,13 @@
buildNavBarContent();
attachNavBarWindows();
+ // Try setting up the initial state of the nav bar if applicable.
+ if (result != null) {
+ setImeWindowStatus(Display.DEFAULT_DISPLAY, result.mImeToken,
+ result.mImeWindowVis, result.mImeBackDisposition,
+ result.mShowImeSwitcher);
+ }
+
// There has been a car customized nav bar on the default display, so just create nav bars
// on external displays.
mNavigationBarController.createNavigationBars(false /* includeDefaultDisplay */, result);
@@ -758,22 +778,33 @@
}
- private void attachNavBarWindows() {
-
- if (mShowBottom) {
- WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
- LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
- WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
- WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
- | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
- | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
- PixelFormat.TRANSLUCENT);
- lp.setTitle("CarNavigationBar");
- lp.windowAnimations = 0;
- mWindowManager.addView(mNavigationBarWindow, lp);
+ /**
+ * We register for soft keyboard visibility events such that we can hide the navigation bar
+ * giving more screen space to the IME. Note: this is optional and controlled by
+ * {@code com.android.internal.R.bool.config_automotiveHideNavBarForKeyboard}.
+ */
+ @Override
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+ boolean showImeSwitcher) {
+ if (!mHideNavBarForKeyboard) {
+ return;
}
+ if (mContext.getDisplay().getDisplayId() != displayId) {
+ return;
+ }
+
+ boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
+ if (!isKeyboardVisible) {
+ attachBottomNavBarWindow();
+ } else {
+ detachBottomNavBarWindow();
+ }
+ }
+
+ private void attachNavBarWindows() {
+ attachBottomNavBarWindow();
+
if (mShowLeft) {
int width = mContext.getResources().getDimensionPixelSize(
R.dimen.car_left_navigation_bar_width);
@@ -808,7 +839,49 @@
rightlp.gravity = Gravity.RIGHT;
mWindowManager.addView(mRightNavigationBarWindow, rightlp);
}
+ }
+ /**
+ * Attaches the bottom nav bar window. Can be extended to modify the specific behavior of
+ * attaching the bottom nav bar.
+ */
+ protected void attachBottomNavBarWindow() {
+ if (!mShowBottom) {
+ return;
+ }
+
+ if (mBottomNavBarVisible) {
+ return;
+ }
+ mBottomNavBarVisible = true;
+
+ WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+ | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+ PixelFormat.TRANSLUCENT);
+ lp.setTitle("CarNavigationBar");
+ lp.windowAnimations = 0;
+ mWindowManager.addView(mNavigationBarWindow, lp);
+ }
+
+ /**
+ * Detaches the bottom nav bar window. Can be extended to modify the specific behavior of
+ * detaching the bottom nav bar.
+ */
+ protected void detachBottomNavBarWindow() {
+ if (!mShowBottom) {
+ return;
+ }
+
+ if (!mBottomNavBarVisible) {
+ return;
+ }
+ mBottomNavBarVisible = false;
+ mWindowManager.removeView(mNavigationBarWindow);
}
private void buildBottomBar(int layout) {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 077f7ec..cf286bd 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -20,6 +20,8 @@
import android.gsi.GsiProgress;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.MemoryFile;
+import android.os.ParcelFileDescriptor;
import android.os.image.DynamicSystemManager;
import android.util.Log;
import android.webkit.URLUtil;
@@ -28,11 +30,9 @@
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
-import java.util.Arrays;
import java.util.Locale;
import java.util.zip.GZIPInputStream;
-
class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> {
private static final String TAG = "InstallationAsyncTask";
@@ -125,28 +125,26 @@
Thread.sleep(10);
}
-
if (mInstallationSession == null) {
- throw new IOException("Failed to start installation with requested size: "
- + (mSystemSize + mUserdataSize));
+ throw new IOException(
+ "Failed to start installation with requested size: "
+ + (mSystemSize + mUserdataSize));
}
installedSize = mUserdataSize;
+ MemoryFile memoryFile = new MemoryFile("dsu", READ_BUFFER_SIZE);
byte[] bytes = new byte[READ_BUFFER_SIZE];
-
+ mInstallationSession.setAshmem(
+ new ParcelFileDescriptor(memoryFile.getFileDescriptor()), READ_BUFFER_SIZE);
int numBytesRead;
-
Log.d(TAG, "Start installation loop");
while ((numBytesRead = mStream.read(bytes, 0, READ_BUFFER_SIZE)) != -1) {
+ memoryFile.writeBytes(bytes, 0, 0, numBytesRead);
if (isCancelled()) {
break;
}
-
- byte[] writeBuffer = numBytesRead == READ_BUFFER_SIZE
- ? bytes : Arrays.copyOf(bytes, numBytesRead);
-
- if (!mInstallationSession.write(writeBuffer)) {
+ if (!mInstallationSession.submitFromAshmem(numBytesRead)) {
throw new IOException("Failed write() to DynamicSystem");
}
@@ -157,7 +155,6 @@
reportedInstalledSize = installedSize;
}
}
-
return null;
} catch (Exception e) {
diff --git a/packages/SettingsLib/res/values/styles_support_preference.xml b/packages/SettingsLib/res/values/styles_support_preference.xml
index 5d787f8..6e61196 100644
--- a/packages/SettingsLib/res/values/styles_support_preference.xml
+++ b/packages/SettingsLib/res/values/styles_support_preference.xml
@@ -26,7 +26,7 @@
<item name="allowDividerAbove">true</item>
</style>
- <style name="PreferenceThemeOverlay.SettingsBase" parent="@style/PreferenceThemeOverlay.v14.Material">
+ <style name="PreferenceThemeOverlay.SettingsBase" parent="@style/PreferenceThemeOverlay">
<item name="footerPreferenceStyle">@style/Preference.FooterPreference.SettingsBase</item>
</style>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 2286f4c..36e945f 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -34,7 +34,7 @@
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.provider.Settings;
-import android.provider.SettingsValidators.Validator;
+import android.provider.settings.validators.Validator;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.BackupUtils;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index a3b0e6b..00b2563 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1538,9 +1538,6 @@
Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
GlobalSettingsProto.Wifi.NETWORKS_AVAILABLE_NOTIFICATION_ON);
dumpSetting(s, p,
- Settings.Global.WIFI_CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON,
- GlobalSettingsProto.Wifi.CARRIER_NETWORKS_AVAILABLE_NOTIFICATION_ON);
- dumpSetting(s, p,
Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY,
GlobalSettingsProto.Wifi.NETWORKS_AVAILABLE_REPEAT_DELAY);
dumpSetting(s, p,
@@ -1607,9 +1604,6 @@
Settings.Global.WIFI_P2P_DEVICE_NAME,
GlobalSettingsProto.Wifi.P2P_DEVICE_NAME);
dumpSetting(s, p,
- Settings.Global.WIFI_REENABLE_DELAY_MS,
- GlobalSettingsProto.Wifi.REENABLE_DELAY_MS);
- dumpSetting(s, p,
Settings.Global.WIFI_EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS,
GlobalSettingsProto.Wifi.EPHEMERAL_OUT_OF_RANGE_TIMEOUT_MS);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 7016d30..e492e28 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -70,7 +70,7 @@
import android.provider.Settings;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
-import android.provider.SettingsValidators;
+import android.provider.settings.validators.Validator;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -314,11 +314,6 @@
public boolean onCreate() {
Settings.setInSystemServer();
- // fail to boot if there're any backed up settings that don't have a non-null validator
- ensureAllBackedUpSystemSettingsHaveValidators();
- ensureAllBackedUpGlobalSettingsHaveValidators();
- ensureAllBackedUpSecureSettingsHaveValidators();
-
synchronized (mLock) {
mUserManager = UserManager.get(getContext());
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
@@ -338,57 +333,6 @@
return true;
}
- private void ensureAllBackedUpSystemSettingsHaveValidators() {
- String offenders = getOffenders(concat(Settings.System.SETTINGS_TO_BACKUP,
- Settings.System.LEGACY_RESTORE_SETTINGS), Settings.System.VALIDATORS);
-
- failToBootIfOffendersPresent(offenders, "Settings.System");
- }
-
- private void ensureAllBackedUpGlobalSettingsHaveValidators() {
- String offenders = getOffenders(concat(Settings.Global.SETTINGS_TO_BACKUP,
- Settings.Global.LEGACY_RESTORE_SETTINGS), Settings.Global.VALIDATORS);
-
- failToBootIfOffendersPresent(offenders, "Settings.Global");
- }
-
- private void ensureAllBackedUpSecureSettingsHaveValidators() {
- String offenders = getOffenders(concat(Settings.Secure.SETTINGS_TO_BACKUP,
- Settings.Secure.LEGACY_RESTORE_SETTINGS), Settings.Secure.VALIDATORS);
-
- failToBootIfOffendersPresent(offenders, "Settings.Secure");
- }
-
- private void failToBootIfOffendersPresent(String offenders, String settingsType) {
- if (offenders.length() > 0) {
- throw new RuntimeException("All " + settingsType + " settings that are backed up"
- + " have to have a non-null validator, but those don't: " + offenders);
- }
- }
-
- private String getOffenders(String[] settingsToBackup, Map<String,
- SettingsValidators.Validator> validators) {
- StringBuilder offenders = new StringBuilder();
- for (String setting : settingsToBackup) {
- if (validators.get(setting) == null) {
- offenders.append(setting).append(" ");
- }
- }
- return offenders.toString();
- }
-
- private final String[] concat(String[] first, String[] second) {
- if (second == null || second.length == 0) {
- return first;
- }
- final int firstLen = first.length;
- final int secondLen = second.length;
- String[] both = new String[firstLen + secondLen];
- System.arraycopy(first, 0, both, 0, firstLen);
- System.arraycopy(second, 0, both, firstLen, secondLen);
- return both;
- }
-
@Override
public Bundle call(String method, String name, Bundle args) {
final int requestingUserId = getRequestingUserId(args);
@@ -1773,7 +1717,7 @@
}
private void validateSystemSettingValue(String name, String value) {
- SettingsValidators.Validator validator = Settings.System.VALIDATORS.get(name);
+ Validator validator = Settings.System.VALIDATORS.get(name);
if (validator != null && !validator.validate(value)) {
throw new IllegalArgumentException("Invalid value: " + value
+ " for setting: " + name);
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 58e1d47..8c0108d 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -352,10 +352,10 @@
private final BugreportInfo mInfo;
- BugreportCallbackImpl(String name) {
+ BugreportCallbackImpl(String name, @Nullable String title, @Nullable String description) {
// pid not used in this workflow, so setting default = 0
mInfo = new BugreportInfo(mContext, 0 /* pid */, name,
- 100 /* max progress*/);
+ 100 /* max progress*/, title, description);
}
@Override
@@ -578,6 +578,8 @@
}
int bugreportType = intent.getIntExtra(EXTRA_BUGREPORT_TYPE,
BugreportParams.BUGREPORT_MODE_INTERACTIVE);
+ String shareTitle = intent.getStringExtra(EXTRA_TITLE);
+ String shareDescription = intent.getStringExtra(EXTRA_DESCRIPTION);
ParcelFileDescriptor screenshotFd = createReadWriteFile(BUGREPORT_DIR,
bugreportName + ".png");
@@ -595,7 +597,8 @@
+ " bugreport file fd: " + bugreportFd
+ " screenshot file fd: " + screenshotFd);
- BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName);
+ BugreportCallbackImpl bugreportCallback = new BugreportCallbackImpl(bugreportName,
+ shareTitle, shareDescription);
try {
mBugreportManager.startBugreport(bugreportFd, screenshotFd,
new BugreportParams(bugreportType), executor, bugreportCallback);
@@ -982,7 +985,10 @@
}
screenshotFile = null;
}
- onBugreportFinished(id, bugreportFile, screenshotFile, info.title, info.description, max);
+ // TODO: Since we are passing id to the function, it should be able to find the info linked
+ // to the id and therefore use the value of shareTitle and shareDescription.
+ onBugreportFinished(id, bugreportFile, screenshotFile, info.shareTitle,
+ info.shareDescription, max);
}
@@ -1844,6 +1850,14 @@
String title;
/**
+ * One-line summary of the bug; when set, will be used as the subject of the
+ * {@link Intent#ACTION_SEND_MULTIPLE} intent. This is the predefined title which is
+ * set initially when the request to take a bugreport is made. This overrides any changes
+ * in the title that the user makes after the bugreport starts.
+ */
+ String shareTitle;
+
+ /**
* User-provided, detailed description of the bugreport; when set, will be added to the body
* of the {@link Intent#ACTION_SEND_MULTIPLE} intent.
*/
@@ -1906,7 +1920,9 @@
int screenshotCounter;
/**
- * Descriptive text that will be shown to the user in the notification message.
+ * Descriptive text that will be shown to the user in the notification message. This is the
+ * predefined description which is set initially when the request to take a bugreport is
+ * made.
*/
String shareDescription;
@@ -1914,18 +1930,21 @@
* Constructor for tracked bugreports - typically called upon receiving BUGREPORT_STARTED.
*/
BugreportInfo(Context context, int id, int pid, String name, int max) {
- this(context, pid, name, max);
+ this(context, pid, name, max, null, null);
this.id = id;
}
/**
* Constructor for tracked bugreports - typically called upon receiving BUGREPORT_REQUESTED.
*/
- BugreportInfo(Context context, int pid, String name, int max) {
+ BugreportInfo(Context context, int pid, String name, int max, @Nullable String shareTitle,
+ @Nullable String shareDescription) {
this.context = context;
this.pid = pid;
this.name = name;
this.max = this.realMax = max;
+ this.shareTitle = shareTitle == null ? "" : shareTitle;
+ this.shareDescription = shareDescription == null ? "" : shareDescription;
}
/**
@@ -2019,6 +2038,7 @@
.append("\n\taddingDetailsToZip: ").append(addingDetailsToZip)
.append(" addedDetailsToZip: ").append(addedDetailsToZip)
.append("\n\tshareDescription: ").append(shareDescription)
+ .append("\n\tshareTitle: ").append(shareTitle)
.toString();
}
@@ -2046,6 +2066,7 @@
finished = in.readInt() == 1;
screenshotCounter = in.readInt();
shareDescription = in.readString();
+ shareTitle = in.readString();
}
@Override
@@ -2071,6 +2092,7 @@
dest.writeInt(finished ? 1 : 0);
dest.writeInt(screenshotCounter);
dest.writeString(shareDescription);
+ dest.writeString(shareTitle);
}
@Override
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index fab7242..19e682b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1386,6 +1386,8 @@
buttons</string>
<string name="screen_pinning_toast_recents_invisible">To unpin this screen, touch & hold Back
and Home buttons</string>
+ <!-- Notify (in toast) user how to unpin screen in gesture navigation mode [CHAR LIMIT=NONE] -->
+ <string name="screen_pinning_toast_gesture_nav">To unpin this screen, swipe up & hold</string>
<!-- Screen pinning positive response. -->
<string name="screen_pinning_positive">Got it</string>
<!-- Screen pinning negative response. -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index de20972..8c5374a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1446,6 +1446,7 @@
protected void handleStartedGoingToSleep(int arg1) {
checkIsHandlerThread();
+ mLockIconPressed = false;
clearBiometricRecognized();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1481,7 +1482,6 @@
private void handleScreenTurnedOff() {
checkIsHandlerThread();
- mLockIconPressed = false;
mHardwareFingerprintUnavailableRetryCount = 0;
mHardwareFaceUnavailableRetryCount = 0;
for (int i = 0; i < mCallbacks.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialog.java
new file mode 100644
index 0000000..d4baefd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialog.java
@@ -0,0 +1,108 @@
+/*
+ * 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.systemui.biometrics;
+
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+import com.android.systemui.biometrics.ui.BiometricDialogView;
+
+/**
+ * Interface for the biometric dialog UI.
+ */
+public interface BiometricDialog {
+
+ // TODO: Clean up save/restore state
+ String[] KEYS_TO_BACKUP = {
+ BiometricPrompt.KEY_TITLE,
+ BiometricPrompt.KEY_USE_DEFAULT_TITLE,
+ BiometricPrompt.KEY_SUBTITLE,
+ BiometricPrompt.KEY_DESCRIPTION,
+ BiometricPrompt.KEY_POSITIVE_TEXT,
+ BiometricPrompt.KEY_NEGATIVE_TEXT,
+ BiometricPrompt.KEY_REQUIRE_CONFIRMATION,
+ BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL,
+ BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL,
+
+ BiometricDialogView.KEY_TRY_AGAIN_VISIBILITY,
+ BiometricDialogView.KEY_CONFIRM_VISIBILITY,
+ BiometricDialogView.KEY_CONFIRM_ENABLED,
+ BiometricDialogView.KEY_STATE,
+ BiometricDialogView.KEY_ERROR_TEXT_VISIBILITY,
+ BiometricDialogView.KEY_ERROR_TEXT_STRING,
+ BiometricDialogView.KEY_ERROR_TEXT_IS_TEMPORARY,
+ BiometricDialogView.KEY_ERROR_TEXT_COLOR,
+ };
+
+ /**
+ * Show the dialog.
+ * @param wm
+ * @param skipIntroAnimation
+ */
+ void show(WindowManager wm, boolean skipIntroAnimation);
+
+ /**
+ * Dismiss the dialog without sending a callback.
+ */
+ void dismissWithoutCallback(boolean animate);
+
+ /**
+ * Dismiss the dialog. Animate away.
+ */
+ void dismissFromSystemServer();
+
+ /**
+ * Biometric authenticated. May be pending user confirmation, or completed.
+ */
+ void onAuthenticationSucceeded();
+
+ /**
+ * Authentication failed (reject, timeout). Dialog stays showing.
+ * @param failureReason
+ */
+ void onAuthenticationFailed(String failureReason);
+
+ /**
+ * Authentication rejected, or help message received.
+ * @param help
+ */
+ void onHelp(String help);
+
+ /**
+ * Authentication failed. Dialog going away.
+ * @param error
+ */
+ void onError(String error);
+
+ /**
+ * Save the current state.
+ * @param outState
+ */
+ void onSaveState(Bundle outState);
+
+ /**
+ * Restore a previous state.
+ * @param savedState
+ */
+ void restoreState(Bundle savedState);
+
+ /**
+ * Get the client's package name
+ */
+ String getOpPackageName();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
index e66a8fa..a8e5722 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogImpl.java
@@ -16,137 +16,156 @@
package com.android.systemui.biometrics;
+import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
+import android.app.IActivityTaskManager;
+import android.app.TaskStackListener;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
-import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.IBiometricServiceReceiverInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
-import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.view.WindowManager;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
-import com.android.systemui.Dependency;
import com.android.systemui.SystemUI;
-import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.biometrics.ui.BiometricDialogView;
import com.android.systemui.statusbar.CommandQueue;
+import java.util.List;
+
/**
- * Receives messages sent from AuthenticationClient and shows the appropriate biometric UI (e.g.
- * BiometricDialogView).
+ * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the
+ * appropriate biometric UI (e.g. BiometricDialogView).
*/
-public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callbacks {
+public class BiometricDialogImpl extends SystemUI implements CommandQueue.Callbacks,
+ DialogViewCallback {
private static final String TAG = "BiometricDialogImpl";
private static final boolean DEBUG = true;
- private static final int MSG_SHOW_DIALOG = 1;
- private static final int MSG_BIOMETRIC_AUTHENTICATED = 2;
- private static final int MSG_BIOMETRIC_HELP = 3;
- private static final int MSG_BIOMETRIC_ERROR = 4;
- private static final int MSG_HIDE_DIALOG = 5;
- private static final int MSG_BUTTON_NEGATIVE = 6;
- private static final int MSG_USER_CANCELED = 7;
- private static final int MSG_BUTTON_POSITIVE = 8;
- private static final int MSG_TRY_AGAIN_PRESSED = 9;
+ private final Injector mInjector;
+ // TODO: These should just be saved from onSaveState
private SomeArgs mCurrentDialogArgs;
- private BiometricDialogView mCurrentDialog;
+ @VisibleForTesting
+ BiometricDialog mCurrentDialog;
+
+ private Handler mHandler = new Handler(Looper.getMainLooper());
private WindowManager mWindowManager;
- private IBiometricServiceReceiverInternal mReceiver;
- private boolean mDialogShowing;
- private Callback mCallback = new Callback();
- private WakefulnessLifecycle mWakefulnessLifecycle;
+ @VisibleForTesting
+ IActivityTaskManager mActivityTaskManager;
+ @VisibleForTesting
+ BiometricTaskStackListener mTaskStackListener;
+ @VisibleForTesting
+ IBiometricServiceReceiverInternal mReceiver;
- private Handler mHandler = new Handler(Looper.getMainLooper()) {
+ public class BiometricTaskStackListener extends TaskStackListener {
@Override
- public void handleMessage(Message msg) {
- switch(msg.what) {
- case MSG_SHOW_DIALOG:
- handleShowDialog((SomeArgs) msg.obj, false /* skipAnimation */,
- null /* savedState */);
- break;
- case MSG_BIOMETRIC_AUTHENTICATED: {
- SomeArgs args = (SomeArgs) msg.obj;
- handleBiometricAuthenticated((boolean) args.arg1 /* authenticated */,
- (String) args.arg2 /* failureReason */);
- args.recycle();
- break;
- }
- case MSG_BIOMETRIC_HELP: {
- SomeArgs args = (SomeArgs) msg.obj;
- handleBiometricHelp((String) args.arg1 /* message */);
- args.recycle();
- break;
- }
- case MSG_BIOMETRIC_ERROR:
- handleBiometricError((String) msg.obj);
- break;
- case MSG_HIDE_DIALOG:
- handleHideDialog((Boolean) msg.obj);
- break;
- case MSG_BUTTON_NEGATIVE:
- handleButtonNegative();
- break;
- case MSG_USER_CANCELED:
- handleUserCanceled();
- break;
- case MSG_BUTTON_POSITIVE:
- handleButtonPositive();
- break;
- case MSG_TRY_AGAIN_PRESSED:
- handleTryAgainPressed();
- break;
- default:
- Log.w(TAG, "Unknown message: " + msg.what);
- break;
- }
- }
- };
-
- private class Callback implements DialogViewCallback {
- @Override
- public void onUserCanceled() {
- mHandler.obtainMessage(MSG_USER_CANCELED).sendToTarget();
- }
-
- @Override
- public void onErrorShown() {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_HIDE_DIALOG,
- false /* userCanceled */), BiometricPrompt.HIDE_DIALOG_DELAY);
- }
-
- @Override
- public void onNegativePressed() {
- mHandler.obtainMessage(MSG_BUTTON_NEGATIVE).sendToTarget();
- }
-
- @Override
- public void onPositivePressed() {
- mHandler.obtainMessage(MSG_BUTTON_POSITIVE).sendToTarget();
- }
-
- @Override
- public void onTryAgainPressed() {
- mHandler.obtainMessage(MSG_TRY_AGAIN_PRESSED).sendToTarget();
+ public void onTaskStackChanged() {
+ mHandler.post(mTaskStackChangedRunnable);
}
}
- final WakefulnessLifecycle.Observer mWakefulnessObserver = new WakefulnessLifecycle.Observer() {
- @Override
- public void onStartedGoingToSleep() {
- if (mDialogShowing) {
- if (DEBUG) Log.d(TAG, "User canceled due to screen off");
- mHandler.obtainMessage(MSG_USER_CANCELED).sendToTarget();
+ private final Runnable mTaskStackChangedRunnable = () -> {
+ if (mCurrentDialog != null) {
+ try {
+ final String clientPackage = mCurrentDialog.getOpPackageName();
+ Log.w(TAG, "Task stack changed, current client: " + clientPackage);
+ final List<ActivityManager.RunningTaskInfo> runningTasks =
+ mActivityTaskManager.getTasks(1);
+ if (!runningTasks.isEmpty()) {
+ final String topPackage = runningTasks.get(0).topActivity.getPackageName();
+ if (!topPackage.contentEquals(clientPackage)) {
+ Log.w(TAG, "Evicting client due to: " + topPackage);
+ mCurrentDialog.dismissWithoutCallback(true /* animate */);
+ mCurrentDialog = null;
+ mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ mReceiver = null;
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote exception", e);
}
}
};
@Override
+ public void onTryAgainPressed() {
+ try {
+ mReceiver.onTryAgainPressed();
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException when handling try again", e);
+ }
+ }
+
+ @Override
+ public void onDismissed(@DismissedReason int reason) {
+ switch (reason) {
+ case DialogViewCallback.DISMISSED_USER_CANCELED:
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ break;
+
+ case DialogViewCallback.DISMISSED_BUTTON_NEGATIVE:
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+ break;
+
+ case DialogViewCallback.DISMISSED_BUTTON_POSITIVE:
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+ break;
+
+ case DialogViewCallback.DISMISSED_AUTHENTICATED:
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+ break;
+
+ case DialogViewCallback.DISMISSED_ERROR:
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_ERROR);
+ break;
+
+ case DialogViewCallback.DISMISSED_BY_SYSTEM_SERVER:
+ sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
+ break;
+
+ default:
+ Log.e(TAG, "Unhandled reason: " + reason);
+ break;
+ }
+ }
+
+ private void sendResultAndCleanUp(@DismissedReason int reason) {
+ if (mReceiver == null) {
+ Log.e(TAG, "Receiver is null");
+ return;
+ }
+ try {
+ mReceiver.onDialogDismissed(reason);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Remote exception", e);
+ }
+ onDialogDismissed(reason);
+ }
+
+ public static class Injector {
+ IActivityTaskManager getActivityTaskManager() {
+ return ActivityTaskManager.getService();
+ }
+ }
+
+ public BiometricDialogImpl() {
+ this(new Injector());
+ }
+
+ @VisibleForTesting
+ BiometricDialogImpl(Injector injector) {
+ mInjector = injector;
+ }
+
+ @Override
public void start() {
final PackageManager pm = mContext.getPackageManager();
if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
@@ -154,30 +173,38 @@
|| pm.hasSystemFeature(PackageManager.FEATURE_IRIS)) {
getComponent(CommandQueue.class).addCallback(this);
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- mWakefulnessLifecycle = Dependency.get(WakefulnessLifecycle.class);
- mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+ mActivityTaskManager = mInjector.getActivityTaskManager();
+
+ try {
+ mTaskStackListener = new BiometricTaskStackListener();
+ mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to register task stack listener", e);
+ }
}
}
@Override
public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int type, boolean requireConfirmation, int userId) {
+ int type, boolean requireConfirmation, int userId, String opPackageName) {
if (DEBUG) {
Log.d(TAG, "showBiometricDialog, type: " + type
+ ", requireConfirmation: " + requireConfirmation);
}
- // Remove these messages as they are part of the previous client
- mHandler.removeMessages(MSG_BIOMETRIC_ERROR);
- mHandler.removeMessages(MSG_BIOMETRIC_HELP);
- mHandler.removeMessages(MSG_BIOMETRIC_AUTHENTICATED);
- mHandler.removeMessages(MSG_HIDE_DIALOG);
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
args.arg2 = receiver;
args.argi1 = type;
args.arg3 = requireConfirmation;
args.argi2 = userId;
- mHandler.obtainMessage(MSG_SHOW_DIALOG, args).sendToTarget();
+ args.arg4 = opPackageName;
+
+ boolean skipAnimation = false;
+ if (mCurrentDialog != null) {
+ Log.w(TAG, "mCurrentDialog: " + mCurrentDialog);
+ skipAnimation = true;
+ }
+ showDialog(args, skipAnimation, null /* savedState */);
}
@Override
@@ -185,185 +212,111 @@
if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: " + authenticated
+ " reason: " + failureReason);
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = authenticated;
- args.arg2 = failureReason;
- mHandler.obtainMessage(MSG_BIOMETRIC_AUTHENTICATED, args).sendToTarget();
- }
-
- @Override
- public void onBiometricHelp(String message) {
- if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = message;
- mHandler.obtainMessage(MSG_BIOMETRIC_HELP, args).sendToTarget();
- }
-
- @Override
- public void onBiometricError(String error) {
- if (DEBUG) Log.d(TAG, "onBiometricError: " + error);
- mHandler.obtainMessage(MSG_BIOMETRIC_ERROR, error).sendToTarget();
- }
-
- @Override
- public void hideBiometricDialog() {
- if (DEBUG) Log.d(TAG, "hideBiometricDialog");
- mHandler.obtainMessage(MSG_HIDE_DIALOG, false /* userCanceled */).sendToTarget();
- }
-
- private void handleShowDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
- mCurrentDialogArgs = args;
- final int type = args.argi1;
-
- // Create a new dialog but do not replace the current one yet.
- BiometricDialogView newDialog;
- if (type == BiometricAuthenticator.TYPE_FINGERPRINT) {
- newDialog = new FingerprintDialogView(mContext, mCallback);
- } else if (type == BiometricAuthenticator.TYPE_FACE) {
- newDialog = new FaceDialogView(mContext, mCallback);
- } else {
- Log.e(TAG, "Unsupported type: " + type);
- return;
- }
-
- if (DEBUG) Log.d(TAG, "handleShowDialog, "
- + " savedState: " + savedState
- + " mCurrentDialog: " + mCurrentDialog
- + " newDialog: " + newDialog
- + " type: " + type);
-
- if (savedState != null) {
- // SavedState is only non-null if it's from onConfigurationChanged. Restore the state
- // even though it may be removed / re-created again
- newDialog.restoreState(savedState);
- } else if (mCurrentDialog != null && mDialogShowing) {
- // If somehow we're asked to show a dialog, the old one doesn't need to be animated
- // away. This can happen if the app cancels and re-starts auth during configuration
- // change. This is ugly because we also have to do things on onConfigurationChanged
- // here.
- mCurrentDialog.forceRemove();
- }
-
- mReceiver = (IBiometricServiceReceiverInternal) args.arg2;
- newDialog.setBundle((Bundle) args.arg1);
- newDialog.setRequireConfirmation((boolean) args.arg3);
- newDialog.setUserId(args.argi2);
- newDialog.setSkipIntro(skipAnimation);
- mCurrentDialog = newDialog;
- mWindowManager.addView(mCurrentDialog, mCurrentDialog.getLayoutParams());
- mDialogShowing = true;
- }
-
- private void handleBiometricAuthenticated(boolean authenticated, String failureReason) {
- if (DEBUG) Log.d(TAG, "handleBiometricAuthenticated: " + authenticated);
-
if (authenticated) {
- mCurrentDialog.announceForAccessibility(
- mContext.getResources()
- .getText(mCurrentDialog.getAuthenticatedAccessibilityResourceId()));
- if (mCurrentDialog.requiresConfirmation()) {
- mCurrentDialog.updateState(BiometricDialogView.STATE_PENDING_CONFIRMATION);
- } else {
- mCurrentDialog.updateState(BiometricDialogView.STATE_AUTHENTICATED);
- mHandler.postDelayed(() -> {
- handleHideDialog(false /* userCanceled */);
- }, mCurrentDialog.getDelayAfterAuthenticatedDurationMs());
- }
+ mCurrentDialog.onAuthenticationSucceeded();
} else {
mCurrentDialog.onAuthenticationFailed(failureReason);
}
}
- private void handleBiometricHelp(String message) {
- if (DEBUG) Log.d(TAG, "handleBiometricHelp: " + message);
- mCurrentDialog.onHelpReceived(message);
+ @Override
+ public void onBiometricHelp(String message) {
+ if (DEBUG) Log.d(TAG, "onBiometricHelp: " + message);
+
+ mCurrentDialog.onHelp(message);
}
- private void handleBiometricError(String error) {
- if (DEBUG) Log.d(TAG, "handleBiometricError: " + error);
- if (!mDialogShowing) {
- if (DEBUG) Log.d(TAG, "Dialog already dismissed");
- return;
- }
- mCurrentDialog.onErrorReceived(error);
+ @Override
+ public void onBiometricError(String error) {
+ if (DEBUG) Log.d(TAG, "onBiometricError: " + error);
+ mCurrentDialog.onError(error);
}
- private void handleHideDialog(boolean userCanceled) {
- if (DEBUG) Log.d(TAG, "handleHideDialog, userCanceled: " + userCanceled);
- if (!mDialogShowing) {
- // This can happen if there's a race and we get called from both
- // onAuthenticated and onError, etc.
- Log.w(TAG, "Dialog already dismissed, userCanceled: " + userCanceled);
+ @Override
+ public void hideBiometricDialog() {
+ if (DEBUG) Log.d(TAG, "hideBiometricDialog");
+
+ mCurrentDialog.dismissFromSystemServer();
+ }
+
+ private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
+ mCurrentDialogArgs = args;
+ final int type = args.argi1;
+ final Bundle biometricPromptBundle = (Bundle) args.arg1;
+ final boolean requireConfirmation = (boolean) args.arg3;
+ final int userId = args.argi2;
+ final String opPackageName = (String) args.arg4;
+
+ // Create a new dialog but do not replace the current one yet.
+ final BiometricDialog newDialog = buildDialog(
+ biometricPromptBundle,
+ requireConfirmation,
+ userId,
+ type,
+ opPackageName);
+
+ if (newDialog == null) {
+ Log.e(TAG, "Unsupported type: " + type);
return;
}
- if (userCanceled) {
- try {
- mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException when hiding dialog", e);
- }
+
+ if (DEBUG) {
+ Log.d(TAG, "showDialog, "
+ + " savedState: " + savedState
+ + " mCurrentDialog: " + mCurrentDialog
+ + " newDialog: " + newDialog
+ + " type: " + type);
+ }
+
+ if (savedState != null) {
+ // SavedState is only non-null if it's from onConfigurationChanged. Restore the state
+ // even though it may be removed / re-created again
+ newDialog.restoreState(savedState);
+ } else if (mCurrentDialog != null) {
+ // If somehow we're asked to show a dialog, the old one doesn't need to be animated
+ // away. This can happen if the app cancels and re-starts auth during configuration
+ // change. This is ugly because we also have to do things on onConfigurationChanged
+ // here.
+ mCurrentDialog.dismissWithoutCallback(false /* animate */);
+ }
+
+ mReceiver = (IBiometricServiceReceiverInternal) args.arg2;
+ mCurrentDialog = newDialog;
+ mCurrentDialog.show(mWindowManager, skipAnimation);
+ }
+
+ private void onDialogDismissed(@DismissedReason int reason) {
+ if (DEBUG) Log.d(TAG, "onDialogDismissed: " + reason);
+ if (mCurrentDialog == null) {
+ Log.w(TAG, "Dialog already dismissed");
}
mReceiver = null;
- mDialogShowing = false;
- mCurrentDialog.startDismiss();
- }
-
- private void handleButtonNegative() {
- if (mReceiver == null) {
- Log.e(TAG, "Receiver is null");
- return;
- }
- try {
- mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
- } catch (RemoteException e) {
- Log.e(TAG, "Remote exception when handling negative button", e);
- }
- handleHideDialog(false /* userCanceled */);
- }
-
- private void handleButtonPositive() {
- if (mReceiver == null) {
- Log.e(TAG, "Receiver is null");
- return;
- }
- try {
- mReceiver.onDialogDismissed(BiometricPrompt.DISMISSED_REASON_POSITIVE);
- } catch (RemoteException e) {
- Log.e(TAG, "Remote exception when handling positive button", e);
- }
- handleHideDialog(false /* userCanceled */);
- }
-
- private void handleUserCanceled() {
- handleHideDialog(true /* userCanceled */);
- }
-
- private void handleTryAgainPressed() {
- try {
- mReceiver.onTryAgainPressed();
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException when handling try again", e);
- }
+ mCurrentDialog = null;
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- final boolean wasShowing = mDialogShowing;
// Save the state of the current dialog (buttons showing, etc)
- final Bundle savedState = new Bundle();
if (mCurrentDialog != null) {
+ final Bundle savedState = new Bundle();
mCurrentDialog.onSaveState(savedState);
- }
+ mCurrentDialog.dismissWithoutCallback(false /* animate */);
+ mCurrentDialog = null;
- if (mDialogShowing) {
- mCurrentDialog.forceRemove();
- mDialogShowing = false;
+ showDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
}
+ }
- if (wasShowing) {
- handleShowDialog(mCurrentDialogArgs, true /* skipAnimation */, savedState);
- }
+ protected BiometricDialog buildDialog(Bundle biometricPromptBundle,
+ boolean requireConfirmation, int userId, int type, String opPackageName) {
+ return new BiometricDialogView.Builder(mContext)
+ .setCallback(this)
+ .setBiometricPromptBundle(biometricPromptBundle)
+ .setRequireConfirmation(requireConfirmation)
+ .setUserId(userId)
+ .setOpPackageName(opPackageName)
+ .build(type);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
index 24fd22e..b65d1e8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/DialogViewCallback.java
@@ -16,36 +16,38 @@
package com.android.systemui.biometrics;
+import android.annotation.IntDef;
+
/**
* Callback interface for dialog views. These should be implemented by the controller (e.g.
* FingerprintDialogImpl) and passed into their views (e.g. FingerprintDialogView).
*/
public interface DialogViewCallback {
- /**
- * Invoked when the user cancels authentication by tapping outside the prompt, etc. The dialog
- * should be dismissed.
- */
- void onUserCanceled();
+
+ int DISMISSED_USER_CANCELED = 1;
+ int DISMISSED_BUTTON_NEGATIVE = 2;
+ int DISMISSED_BUTTON_POSITIVE = 3;
+
+ int DISMISSED_AUTHENTICATED = 4;
+ int DISMISSED_ERROR = 5;
+ int DISMISSED_BY_SYSTEM_SERVER = 6;
+
+ @IntDef({DISMISSED_USER_CANCELED,
+ DISMISSED_BUTTON_NEGATIVE,
+ DISMISSED_BUTTON_POSITIVE,
+ DISMISSED_AUTHENTICATED,
+ DISMISSED_ERROR,
+ DISMISSED_BY_SYSTEM_SERVER})
+ @interface DismissedReason {}
/**
- * Invoked when an error is shown. The dialog should be dismissed after a set amount of time.
+ * Invoked when the dialog is dismissed
+ * @param reason
*/
- void onErrorShown();
+ void onDismissed(@DismissedReason int reason);
/**
- * Invoked when the negative button is pressed. The client should be notified and the dialog
- * should be dismissed.
- */
- void onNegativePressed();
-
- /**
- * Invoked when the positive button is pressed. The client should be notified and the dialog
- * should be dismissed.
- */
- void onPositivePressed();
-
- /**
- * Invoked when the "try again" button is pressed.
+ * Invoked when the "try again" button is clicked
*/
void onTryAgainPressed();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java
similarity index 70%
rename from packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java
index ce67577..2b4dde5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/BiometricDialogView.java
@@ -11,10 +11,10 @@
* 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
+ * limitations under the License.
*/
-package com.android.systemui.biometrics;
+package com.android.systemui.biometrics.ui;
import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE;
@@ -23,6 +23,7 @@
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricPrompt;
import android.os.Binder;
import android.os.Bundle;
@@ -46,25 +47,30 @@
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.Dependency;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
+import com.android.systemui.biometrics.BiometricDialog;
+import com.android.systemui.biometrics.DialogViewCallback;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.util.leak.RotationUtils;
/**
* Abstract base class. Shows a dialog for BiometricPrompt.
*/
-public abstract class BiometricDialogView extends LinearLayout {
+public abstract class BiometricDialogView extends LinearLayout implements BiometricDialog {
private static final String TAG = "BiometricDialogView";
- private static final String KEY_TRY_AGAIN_VISIBILITY = "key_try_again_visibility";
- private static final String KEY_CONFIRM_VISIBILITY = "key_confirm_visibility";
- private static final String KEY_CONFIRM_ENABLED = "key_confirm_enabled";
- private static final String KEY_STATE = "key_state";
- private static final String KEY_ERROR_TEXT_VISIBILITY = "key_error_text_visibility";
- private static final String KEY_ERROR_TEXT_STRING = "key_error_text_string";
- private static final String KEY_ERROR_TEXT_IS_TEMPORARY = "key_error_text_is_temporary";
- private static final String KEY_ERROR_TEXT_COLOR = "key_error_text_color";
+ public static final String KEY_TRY_AGAIN_VISIBILITY = "key_try_again_visibility";
+ public static final String KEY_CONFIRM_VISIBILITY = "key_confirm_visibility";
+ public static final String KEY_CONFIRM_ENABLED = "key_confirm_enabled";
+ public static final String KEY_STATE = "key_state";
+ public static final String KEY_ERROR_TEXT_VISIBILITY = "key_error_text_visibility";
+ public static final String KEY_ERROR_TEXT_STRING = "key_error_text_string";
+ public static final String KEY_ERROR_TEXT_IS_TEMPORARY = "key_error_text_is_temporary";
+ public static final String KEY_ERROR_TEXT_COLOR = "key_error_text_color";
private static final int ANIMATION_DURATION_SHOW = 250; // ms
private static final int ANIMATION_DURATION_AWAY = 350; // ms
@@ -77,6 +83,8 @@
protected static final int STATE_PENDING_CONFIRMATION = 3;
protected static final int STATE_AUTHENTICATED = 4;
+ @VisibleForTesting
+ final WakefulnessLifecycle mWakefulnessLifecycle;
private final AccessibilityManager mAccessibilityManager;
private final IBinder mWindowToken = new Binder();
private final Interpolator mLinearOutSlowIn;
@@ -90,22 +98,30 @@
protected final ViewGroup mLayout;
protected final LinearLayout mDialog;
- protected final TextView mTitleText;
- protected final TextView mSubtitleText;
- protected final TextView mDescriptionText;
- protected final ImageView mBiometricIcon;
- protected final TextView mErrorText;
- protected final Button mPositiveButton;
- protected final Button mNegativeButton;
- protected final Button mTryAgainButton;
+ @VisibleForTesting
+ final TextView mTitleText;
+ @VisibleForTesting
+ final TextView mSubtitleText;
+ @VisibleForTesting
+ final TextView mDescriptionText;
+ @VisibleForTesting
+ final ImageView mBiometricIcon;
+ @VisibleForTesting
+ final TextView mErrorText;
+ @VisibleForTesting
+ final Button mPositiveButton;
+ @VisibleForTesting
+ final Button mNegativeButton;
+ @VisibleForTesting
+ final Button mTryAgainButton;
protected final int mTextColor;
private Bundle mBundle;
private Bundle mRestoredState;
+ private String mOpPackageName;
private int mState = STATE_IDLE;
- private boolean mAnimatingAway;
private boolean mWasForceRemoved;
private boolean mSkipIntro;
protected boolean mRequireConfirmation;
@@ -141,6 +157,15 @@
}
};
+ @VisibleForTesting
+ final WakefulnessLifecycle.Observer mWakefulnessObserver =
+ new WakefulnessLifecycle.Observer() {
+ @Override
+ public void onStartedGoingToSleep() {
+ animateAway(DialogViewCallback.DISMISSED_USER_CANCELED);
+ }
+ };
+
protected Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -155,8 +180,80 @@
}
};
- public BiometricDialogView(Context context, DialogViewCallback callback) {
+ /**
+ * Builds the dialog with specified parameters.
+ */
+ public static class Builder {
+ public static final int TYPE_FINGERPRINT = BiometricAuthenticator.TYPE_FINGERPRINT;
+ public static final int TYPE_FACE = BiometricAuthenticator.TYPE_FACE;
+
+ private Context mContext;
+ private DialogViewCallback mCallback;
+ private Bundle mBundle;
+ private boolean mRequireConfirmation;
+ private int mUserId;
+ private String mOpPackageName;
+
+ public Builder(Context context) {
+ mContext = context;
+ }
+
+ public Builder setCallback(DialogViewCallback callback) {
+ mCallback = callback;
+ return this;
+ }
+
+ public Builder setBiometricPromptBundle(Bundle bundle) {
+ mBundle = bundle;
+ return this;
+ }
+
+ public Builder setRequireConfirmation(boolean requireConfirmation) {
+ mRequireConfirmation = requireConfirmation;
+ return this;
+ }
+
+ public Builder setUserId(int userId) {
+ mUserId = userId;
+ return this;
+ }
+
+ public Builder setOpPackageName(String opPackageName) {
+ mOpPackageName = opPackageName;
+ return this;
+ }
+
+ public BiometricDialogView build(int type) {
+ return build(type, new Injector());
+ }
+
+ public BiometricDialogView build(int type, Injector injector) {
+ BiometricDialogView dialog;
+ if (type == TYPE_FINGERPRINT) {
+ dialog = new FingerprintDialogView(mContext, mCallback, injector);
+ } else if (type == TYPE_FACE) {
+ dialog = new FaceDialogView(mContext, mCallback, injector);
+ } else {
+ return null;
+ }
+ dialog.setBundle(mBundle);
+ dialog.setRequireConfirmation(mRequireConfirmation);
+ dialog.setUserId(mUserId);
+ dialog.setOpPackageName(mOpPackageName);
+ return dialog;
+ }
+ }
+
+ public static class Injector {
+ public WakefulnessLifecycle getWakefulnessLifecycle() {
+ return Dependency.get(WakefulnessLifecycle.class);
+ }
+ }
+
+ protected BiometricDialogView(Context context, DialogViewCallback callback, Injector injector) {
super(context);
+ mWakefulnessLifecycle = injector.getWakefulnessLifecycle();
+
mCallback = callback;
mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
@@ -178,19 +275,13 @@
addView(mLayout);
mLayout.setOnKeyListener(new View.OnKeyListener() {
- boolean downPressed = false;
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode != KeyEvent.KEYCODE_BACK) {
return false;
}
- if (event.getAction() == KeyEvent.ACTION_DOWN && downPressed == false) {
- downPressed = true;
- } else if (event.getAction() == KeyEvent.ACTION_DOWN) {
- downPressed = false;
- } else if (event.getAction() == KeyEvent.ACTION_UP && downPressed == true) {
- downPressed = false;
- mCallback.onUserCanceled();
+ if (event.getAction() == KeyEvent.ACTION_UP) {
+ animateAway(DialogViewCallback.DISMISSED_USER_CANCELED);
}
return true;
}
@@ -219,16 +310,16 @@
mNegativeButton.setOnClickListener((View v) -> {
if (mState == STATE_PENDING_CONFIRMATION || mState == STATE_AUTHENTICATED) {
- mCallback.onUserCanceled();
+ animateAway(DialogViewCallback.DISMISSED_USER_CANCELED);
} else {
- mCallback.onNegativePressed();
+ animateAway(DialogViewCallback.DISMISSED_BUTTON_NEGATIVE);
}
});
mPositiveButton.setOnClickListener((View v) -> {
updateState(STATE_AUTHENTICATED);
mHandler.postDelayed(() -> {
- mCallback.onPositivePressed();
+ animateAway(DialogViewCallback.DISMISSED_BUTTON_POSITIVE);
}, getDelayAfterAuthenticatedDurationMs());
});
@@ -248,21 +339,12 @@
mLayout.requestFocus();
}
- public void onSaveState(Bundle bundle) {
- bundle.putInt(KEY_TRY_AGAIN_VISIBILITY, mTryAgainButton.getVisibility());
- bundle.putInt(KEY_CONFIRM_VISIBILITY, mPositiveButton.getVisibility());
- bundle.putBoolean(KEY_CONFIRM_ENABLED, mPositiveButton.isEnabled());
- bundle.putInt(KEY_STATE, mState);
- bundle.putInt(KEY_ERROR_TEXT_VISIBILITY, mErrorText.getVisibility());
- bundle.putCharSequence(KEY_ERROR_TEXT_STRING, mErrorText.getText());
- bundle.putBoolean(KEY_ERROR_TEXT_IS_TEMPORARY, mHandler.hasMessages(MSG_RESET_MESSAGE));
- bundle.putInt(KEY_ERROR_TEXT_COLOR, mErrorText.getCurrentTextColor());
- }
-
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
+ mWakefulnessLifecycle.addObserver(mWakefulnessObserver);
+
final ImageView backgroundView = mLayout.findViewById(R.id.background);
if (mUserManager.isManagedProfile(mUserId)) {
@@ -278,6 +360,7 @@
}
mNegativeButton.setVisibility(View.VISIBLE);
+ mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
if (RotationUtils.getRotation(mContext) != RotationUtils.ROTATION_NONE) {
mDialog.getLayoutParams().width = (int) mDialogWidth;
@@ -285,7 +368,6 @@
if (mRestoredState == null) {
updateState(STATE_AUTHENTICATING);
- mNegativeButton.setText(mBundle.getCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT));
final int hint = getHintStringResourceId();
if (hint != 0) {
mErrorText.setText(hint);
@@ -346,34 +428,49 @@
mSkipIntro = false;
}
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+
+ mWakefulnessLifecycle.removeObserver(mWakefulnessObserver);
+ }
+
private void setDismissesDialog(View v) {
v.setClickable(true);
v.setOnClickListener(v1 -> {
if (mState != STATE_AUTHENTICATED && shouldGrayAreaDismissDialog()) {
- mCallback.onUserCanceled();
+ animateAway(DialogViewCallback.DISMISSED_USER_CANCELED);
}
});
}
- public void startDismiss() {
+ private void animateAway(@DialogViewCallback.DismissedReason int reason) {
+ animateAway(true /* sendReason */, reason);
+ }
+
+ /**
+ * Animate the dialog away
+ * @param reason one of the {@link DialogViewCallback} codes
+ */
+ private void animateAway(boolean sendReason, @DialogViewCallback.DismissedReason int reason) {
if (!mCompletedAnimatingIn) {
Log.w(TAG, "startDismiss(): waiting for onDialogAnimatedIn");
mPendingDismissDialog = true;
return;
}
- mAnimatingAway = true;
-
// This is where final cleanup should occur.
final Runnable endActionRunnable = new Runnable() {
@Override
public void run() {
mWindowManager.removeView(BiometricDialogView.this);
- mAnimatingAway = false;
// Set the icons / text back to normal state
handleResetMessage();
showTryAgainButton(false /* show */);
updateState(STATE_IDLE);
+ if (sendReason) {
+ mCallback.onDismissed(reason);
+ }
}
};
@@ -398,47 +495,30 @@
}
/**
- * Force remove the window, cancelling any animation that's happening. This should only be
- * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method
- * will cause the dialog to show without an animation the next time it's attached.
- */
- public void forceRemove() {
- mLayout.animate().cancel();
- mDialog.animate().cancel();
- mWindowManager.removeView(BiometricDialogView.this);
- mAnimatingAway = false;
- mWasForceRemoved = true;
- }
-
- /**
* Skip the intro animation
*/
- public void setSkipIntro(boolean skip) {
+ private void setSkipIntro(boolean skip) {
mSkipIntro = skip;
}
- public boolean isAnimatingAway() {
- return mAnimatingAway;
- }
-
- public void setBundle(Bundle bundle) {
+ private void setBundle(Bundle bundle) {
mBundle = bundle;
}
- public void setRequireConfirmation(boolean requireConfirmation) {
+ private void setRequireConfirmation(boolean requireConfirmation) {
mRequireConfirmation = requireConfirmation;
}
- public boolean requiresConfirmation() {
+ protected boolean requiresConfirmation() {
return mRequireConfirmation;
}
- public void setUserId(int userId) {
+ private void setUserId(int userId) {
mUserId = userId;
}
- public ViewGroup getLayout() {
- return mLayout;
+ private void setOpPackageName(String opPackageName) {
+ mOpPackageName = opPackageName;
}
// Shows an error/help message
@@ -452,17 +532,63 @@
BiometricPrompt.HIDE_DIALOG_DELAY);
}
+ @Override
+ public void show(WindowManager wm, boolean skipIntroAnimation) {
+ setSkipIntro(skipIntroAnimation);
+ wm.addView(this, getLayoutParams(mWindowToken));
+ }
+
+ /**
+ * Force remove the window, cancelling any animation that's happening. This should only be
+ * called if we want to quickly show the dialog again (e.g. on rotation). Calling this method
+ * will cause the dialog to show without an animation the next time it's attached.
+ */
+ @Override
+ public void dismissWithoutCallback(boolean animate) {
+ if (animate) {
+ animateAway(false /* sendReason */, 0 /* reason */);
+ } else {
+ mLayout.animate().cancel();
+ mDialog.animate().cancel();
+ mWindowManager.removeView(BiometricDialogView.this);
+ mWasForceRemoved = true;
+ }
+ }
+
+ @Override
+ public void dismissFromSystemServer() {
+ animateAway(DialogViewCallback.DISMISSED_BY_SYSTEM_SERVER);
+ }
+
+ @Override
+ public void onAuthenticationSucceeded() {
+ announceForAccessibility(getResources().getText(getAuthenticatedAccessibilityResourceId()));
+
+ if (requiresConfirmation()) {
+ updateState(STATE_PENDING_CONFIRMATION);
+ } else {
+ mHandler.postDelayed(() -> {
+ animateAway(DialogViewCallback.DISMISSED_AUTHENTICATED);
+ }, getDelayAfterAuthenticatedDurationMs());
+
+ updateState(STATE_AUTHENTICATED);
+ }
+ }
+
+
+ @Override
+ public void onAuthenticationFailed(String message) {
+ updateState(STATE_ERROR);
+ showTemporaryMessage(message);
+ }
+
/**
* Transient help message (acquire) is received, dialog stays showing. Sensor stays in
* "authenticating" state.
* @param message
*/
- public void onHelpReceived(String message) {
- updateState(STATE_ERROR);
- showTemporaryMessage(message);
- }
-
- public void onAuthenticationFailed(String message) {
+ @Override
+ public void onHelp(String message) {
updateState(STATE_ERROR);
showTemporaryMessage(message);
}
@@ -471,14 +597,61 @@
* Hard error is received, dialog will be dismissed soon.
* @param error
*/
- public void onErrorReceived(String error) {
+ @Override
+ public void onError(String error) {
updateState(STATE_ERROR);
showTemporaryMessage(error);
showTryAgainButton(false /* show */);
- mCallback.onErrorShown(); // TODO: Split between fp and face
+
+ mHandler.postDelayed(() -> {
+ animateAway(DialogViewCallback.DISMISSED_ERROR);
+ }, BiometricPrompt.HIDE_DIALOG_DELAY);
}
- public void updateState(int newState) {
+
+ @Override
+ public void onSaveState(Bundle bundle) {
+ bundle.putInt(KEY_TRY_AGAIN_VISIBILITY, mTryAgainButton.getVisibility());
+ bundle.putInt(KEY_CONFIRM_VISIBILITY, mPositiveButton.getVisibility());
+ bundle.putBoolean(KEY_CONFIRM_ENABLED, mPositiveButton.isEnabled());
+ bundle.putInt(KEY_STATE, mState);
+ bundle.putInt(KEY_ERROR_TEXT_VISIBILITY, mErrorText.getVisibility());
+ bundle.putCharSequence(KEY_ERROR_TEXT_STRING, mErrorText.getText());
+ bundle.putBoolean(KEY_ERROR_TEXT_IS_TEMPORARY, mHandler.hasMessages(MSG_RESET_MESSAGE));
+ bundle.putInt(KEY_ERROR_TEXT_COLOR, mErrorText.getCurrentTextColor());
+ }
+
+ @Override
+ public void restoreState(Bundle bundle) {
+ mRestoredState = bundle;
+ final int tryAgainVisibility = bundle.getInt(KEY_TRY_AGAIN_VISIBILITY);
+ mTryAgainButton.setVisibility(tryAgainVisibility);
+ final int confirmVisibility = bundle.getInt(KEY_CONFIRM_VISIBILITY);
+ mPositiveButton.setVisibility(confirmVisibility);
+ final boolean confirmEnabled = bundle.getBoolean(KEY_CONFIRM_ENABLED);
+ mPositiveButton.setEnabled(confirmEnabled);
+ mState = bundle.getInt(KEY_STATE);
+ mErrorText.setText(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
+ mErrorText.setContentDescription(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
+ final int errorTextVisibility = bundle.getInt(KEY_ERROR_TEXT_VISIBILITY);
+ mErrorText.setVisibility(errorTextVisibility);
+ if (errorTextVisibility == View.INVISIBLE || tryAgainVisibility == View.INVISIBLE
+ || confirmVisibility == View.INVISIBLE) {
+ announceAccessibilityEvent();
+ }
+ mErrorText.setTextColor(bundle.getInt(KEY_ERROR_TEXT_COLOR));
+ if (bundle.getBoolean(KEY_ERROR_TEXT_IS_TEMPORARY)) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESET_MESSAGE),
+ BiometricPrompt.HIDE_DIALOG_DELAY);
+ }
+ }
+
+ @Override
+ public String getOpPackageName() {
+ return mOpPackageName;
+ }
+
+ protected void updateState(int newState) {
if (newState == STATE_PENDING_CONFIRMATION) {
mHandler.removeMessages(MSG_RESET_MESSAGE);
mErrorText.setTextColor(mTextColor);
@@ -505,48 +678,24 @@
mState = newState;
}
- public void showTryAgainButton(boolean show) {
+ protected void showTryAgainButton(boolean show) {
}
- public void onDialogAnimatedIn() {
+ protected void onDialogAnimatedIn() {
mCompletedAnimatingIn = true;
if (mPendingDismissDialog) {
Log.d(TAG, "onDialogAnimatedIn(): mPendingDismissDialog=true, dismissing now");
- startDismiss();
+ animateAway(false /* sendReason */, 0);
mPendingDismissDialog = false;
}
}
- public void restoreState(Bundle bundle) {
- mRestoredState = bundle;
- final int tryAgainVisibility = bundle.getInt(KEY_TRY_AGAIN_VISIBILITY);
- mTryAgainButton.setVisibility(tryAgainVisibility);
- final int confirmVisibility = bundle.getInt(KEY_CONFIRM_VISIBILITY);
- mPositiveButton.setVisibility(confirmVisibility);
- final boolean confirmEnabled = bundle.getBoolean(KEY_CONFIRM_ENABLED);
- mPositiveButton.setEnabled(confirmEnabled);
- mState = bundle.getInt(KEY_STATE);
- mErrorText.setText(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
- mErrorText.setContentDescription(bundle.getCharSequence(KEY_ERROR_TEXT_STRING));
- final int errorTextVisibility = bundle.getInt(KEY_ERROR_TEXT_VISIBILITY);
- mErrorText.setVisibility(errorTextVisibility);
- if (errorTextVisibility == View.INVISIBLE || tryAgainVisibility == View.INVISIBLE
- || confirmVisibility == View.INVISIBLE) {
- announceAccessibilityEvent();
- }
- mErrorText.setTextColor(bundle.getInt(KEY_ERROR_TEXT_COLOR));
- if (bundle.getBoolean(KEY_ERROR_TEXT_IS_TEMPORARY)) {
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_RESET_MESSAGE),
- BiometricPrompt.HIDE_DIALOG_DELAY);
- }
- }
-
- protected int getState() {
- return mState;
- }
-
- public WindowManager.LayoutParams getLayoutParams() {
+ /**
+ * @param windowToken token for the window
+ * @return
+ */
+ public static WindowManager.LayoutParams getLayoutParams(IBinder windowToken) {
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT,
@@ -555,7 +704,7 @@
PixelFormat.TRANSLUCENT);
lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
lp.setTitle("BiometricDialogView");
- lp.token = mWindowToken;
+ lp.token = windowToken;
return lp;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java
similarity index 97%
rename from packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java
index ae6cb5c..bd87148 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FaceDialogView.java
@@ -11,10 +11,10 @@
* 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
+ * limitations under the License.
*/
-package com.android.systemui.biometrics;
+package com.android.systemui.biometrics.ui;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -33,7 +33,9 @@
import android.view.View;
import android.view.ViewOutlineProvider;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
+import com.android.systemui.biometrics.DialogViewCallback;
/**
* This class loads the view for the system-provided dialog. The view consists of:
@@ -52,7 +54,8 @@
private static final int TEXT_ANIMATE_DISTANCE = 32; // dp
private static final int SIZE_UNKNOWN = 0;
- private static final int SIZE_SMALL = 1;
+ @VisibleForTesting
+ static final int SIZE_SMALL = 1;
private static final int SIZE_GROWING = 2;
private static final int SIZE_BIG = 3;
@@ -152,13 +155,13 @@
announceAccessibilityEvent();
};
- public FaceDialogView(Context context,
- DialogViewCallback callback) {
- super(context, callback);
+ protected FaceDialogView(Context context, DialogViewCallback callback, Injector injector) {
+ super(context, callback, injector);
mIconController = new IconController();
}
- private void updateSize(int newSize) {
+ @VisibleForTesting
+ void updateSize(int newSize) {
final float padding = dpToPixels(IMPLICIT_Y_PADDING);
final float iconSmallPositionY = mDialog.getHeight() - mBiometricIcon.getHeight() - padding;
@@ -339,8 +342,8 @@
}
@Override
- public void onErrorReceived(String error) {
- super.onErrorReceived(error);
+ public void onError(String error) {
+ super.onError(error);
// All error messages will cause the dialog to go from small -> big. Error messages
// are messages such as lockout, auth failed, etc.
if (mSize == SIZE_SMALL) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
rename to packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java
index 183933e..e597080 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/FingerprintDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/FingerprintDialogView.java
@@ -11,10 +11,10 @@
* 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
+ * limitations under the License.
*/
-package com.android.systemui.biometrics;
+package com.android.systemui.biometrics.ui;
import android.content.Context;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -22,6 +22,7 @@
import android.util.Log;
import com.android.systemui.R;
+import com.android.systemui.biometrics.DialogViewCallback;
/**
* This class loads the view for the system-provided dialog. The view consists of:
@@ -32,9 +33,9 @@
private static final String TAG = "FingerprintDialogView";
- public FingerprintDialogView(Context context,
- DialogViewCallback callback) {
- super(context, callback);
+ protected FingerprintDialogView(Context context, DialogViewCallback callback,
+ Injector injector) {
+ super(context, callback, injector);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
index c58b7db..82ae30a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/ZigZagClassifier.java
@@ -39,8 +39,8 @@
// most swipes will follow somewhat of a 'C' or 'S' shape, we allow more deviance along the
// `SECONDARY` axis.
private static final float MAX_X_PRIMARY_DEVIANCE = .05f;
- private static final float MAX_Y_PRIMARY_DEVIANCE = .05f;
- private static final float MAX_X_SECONDARY_DEVIANCE = .3f;
+ private static final float MAX_Y_PRIMARY_DEVIANCE = .1f;
+ private static final float MAX_X_SECONDARY_DEVIANCE = .6f;
private static final float MAX_Y_SECONDARY_DEVIANCE = .3f;
private final float mMaxXPrimaryDeviance;
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
index d7411260..aac721e 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
@@ -146,7 +146,13 @@
* @return true if EglSurface is ready.
*/
public boolean createEglSurface(SurfaceHolder surfaceHolder) {
- mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null, 0);
+ if (hasEglDisplay()) {
+ mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null, 0);
+ } else {
+ Log.w(TAG, "mEglDisplay is null");
+ return false;
+ }
+
if (mEglSurface == null || mEglSurface == EGL_NO_SURFACE) {
Log.w(TAG, "createWindowSurface failed: " + GLUtils.getEGLErrorString(eglGetError()));
return false;
@@ -186,7 +192,13 @@
public boolean createEglContext() {
int[] attrib_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_NONE};
- mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
+ if (hasEglDisplay()) {
+ mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attrib_list, 0);
+ } else {
+ Log.w(TAG, "mEglDisplay is null");
+ return false;
+ }
+
if (mEglContext == EGL_NO_CONTEXT) {
Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()));
return false;
@@ -213,6 +225,14 @@
}
/**
+ * Check if we have EglDisplay.
+ * @return true if EglDisplay is ready.
+ */
+ public boolean hasEglDisplay() {
+ return mEglDisplay != null;
+ }
+
+ /**
* Swap buffer to display.
* @return true if swap successfully.
*/
@@ -235,7 +255,9 @@
if (hasEglContext()) {
destroyEglContext();
}
- eglTerminate(mEglDisplay);
+ if (hasEglDisplay()) {
+ eglTerminate(mEglDisplay);
+ }
mEglReady = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index fa0fe13..134d4b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -271,7 +271,7 @@
default void onRotationProposal(int rotation, boolean isValid) { }
default void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int type, boolean requireConfirmation, int userId) { }
+ int type, boolean requireConfirmation, int userId, String opPackageName) { }
default void onBiometricAuthenticated(boolean authenticated, String failureReason) { }
default void onBiometricHelp(String message) { }
default void onBiometricError(String error) { }
@@ -741,7 +741,7 @@
@Override
public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int type, boolean requireConfirmation, int userId) {
+ int type, boolean requireConfirmation, int userId, String opPackageName) {
synchronized (mLock) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = bundle;
@@ -749,6 +749,7 @@
args.argi1 = type;
args.arg3 = requireConfirmation;
args.argi2 = userId;
+ args.arg4 = opPackageName;
mHandler.obtainMessage(MSG_BIOMETRIC_SHOW, args)
.sendToTarget();
}
@@ -1036,7 +1037,8 @@
(IBiometricServiceReceiverInternal) someArgs.arg2,
someArgs.argi1 /* type */,
(boolean) someArgs.arg3 /* requireConfirmation */,
- someArgs.argi2 /* userId */);
+ someArgs.argi2 /* userId */,
+ (String) someArgs.arg4 /* opPackageName */);
}
someArgs.recycle();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
index 48d6de9..276afa7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/PulseExpansionHandler.kt
@@ -41,7 +41,6 @@
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.ShadeController
-import com.android.systemui.statusbar.policy.HeadsUpManager
import javax.inject.Inject
import javax.inject.Singleton
@@ -52,11 +51,13 @@
*/
@Singleton
class PulseExpansionHandler @Inject
-constructor(context: Context,
- private val wakeUpCoordinator: NotificationWakeUpCoordinator,
- private val bypassController: KeyguardBypassController,
- private val headsUpManager: HeadsUpManagerPhone,
- private val roundnessManager: NotificationRoundnessManager) : Gefingerpoken {
+constructor(
+ context: Context,
+ private val wakeUpCoordinator: NotificationWakeUpCoordinator,
+ private val bypassController: KeyguardBypassController,
+ private val headsUpManager: HeadsUpManagerPhone,
+ private val roundnessManager: NotificationRoundnessManager
+) : Gefingerpoken {
companion object {
private val RUBBERBAND_FACTOR_STATIC = 0.25f
private val SPRING_BACK_ANIMATION_LENGTH_MS = 375
@@ -124,8 +125,8 @@
}
private fun maybeStartExpansion(event: MotionEvent): Boolean {
- if (!wakeUpCoordinator.canShowPulsingHuns || qsExpanded
- || bouncerShowing) {
+ if (!wakeUpCoordinator.canShowPulsingHuns || qsExpanded ||
+ bouncerShowing) {
return false
}
if (velocityTracker == null) {
@@ -160,18 +161,18 @@
}
MotionEvent.ACTION_UP -> {
- recycleVelocityTracker();
+ recycleVelocityTracker()
}
MotionEvent.ACTION_CANCEL -> {
- recycleVelocityTracker();
+ recycleVelocityTracker()
}
}
return false
}
private fun recycleVelocityTracker() {
- velocityTracker?.recycle();
+ velocityTracker?.recycle()
velocityTracker = null
}
@@ -216,7 +217,7 @@
"com.android.systemui:PULSEDRAG")
}
shadeController.goToLockedShade(mStartingChild)
- leavingLockscreen = true;
+ leavingLockscreen = true
isExpanding = false
if (mStartingChild is ExpandableNotificationRow) {
val row = mStartingChild as ExpandableNotificationRow?
@@ -227,7 +228,7 @@
private fun updateExpansionHeight(height: Float) {
var expansionHeight = max(height, 0.0f)
if (!mReachedWakeUpHeight && height > mWakeUpHeight) {
- mReachedWakeUpHeight = true;
+ mReachedWakeUpHeight = true
}
if (mStartingChild != null) {
val child = mStartingChild!!
@@ -317,9 +318,11 @@
} else null
}
- fun setUp(stackScroller: NotificationStackScrollLayout,
- expansionCallback: ExpansionCallback,
- shadeController: ShadeController) {
+ fun setUp(
+ stackScroller: NotificationStackScrollLayout,
+ expansionCallback: ExpansionCallback,
+ shadeController: ShadeController
+ ) {
this.expansionCallback = expansionCallback
this.shadeController = shadeController
this.stackScroller = stackScroller
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
index d9328fa..e4bd4fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/DynamicPrivacyController.java
@@ -107,7 +107,7 @@
*/
public boolean isInLockedDownShade() {
if (!mStatusBarKeyguardViewManager.isShowing()
- || !mStatusBarKeyguardViewManager.isSecure()) {
+ || !mUnlockMethodCache.isMethodSecure()) {
return false;
}
int state = mStateController.getState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index fdee278..3485f23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.phone;
+import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
+
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_OVERVIEW_DISABLED;
@@ -955,7 +957,8 @@
}
public void showPinningEscapeToast() {
- mScreenPinningNotify.showEscapeToast(isRecentsButtonVisible());
+ mScreenPinningNotify.showEscapeToast(
+ mNavBarMode == NAV_BAR_MODE_GESTURAL, isRecentsButtonVisible());
}
public boolean isVertical() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java
index f8731b4..071e00d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenPinningNotify.java
@@ -51,7 +51,7 @@
}
/** Show a toast that describes the gesture the user should use to escape pinned mode. */
- public void showEscapeToast(boolean isRecentsButtonVisible) {
+ public void showEscapeToast(boolean isGestureNavEnabled, boolean isRecentsButtonVisible) {
long showToastTime = SystemClock.elapsedRealtime();
if ((showToastTime - mLastShowToastTime) < SHOW_TOAST_MINIMUM_INTERVAL) {
Slog.i(TAG, "Ignore toast since it is requested in very short interval.");
@@ -60,9 +60,11 @@
if (mLastToast != null) {
mLastToast.cancel();
}
- mLastToast = makeAllUserToastAndShow(isRecentsButtonVisible
- ? R.string.screen_pinning_toast
- : R.string.screen_pinning_toast_recents_invisible);
+ mLastToast = makeAllUserToastAndShow(isGestureNavEnabled
+ ? R.string.screen_pinning_toast_gesture_nav
+ : isRecentsButtonVisible
+ ? R.string.screen_pinning_toast
+ : R.string.screen_pinning_toast_recents_invisible);
mLastShowToastTime = showToastTime;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index e75365e..7acf4fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -537,8 +537,14 @@
return mConfig.nr5GIconMap.get(Config.NR_CONNECTED);
}
} else if (nrState == NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED) {
- if (mConfig.nr5GIconMap.containsKey(Config.NR_NOT_RESTRICTED)) {
- return mConfig.nr5GIconMap.get(Config.NR_NOT_RESTRICTED);
+ if (mCurrentState.activityDormant) {
+ if (mConfig.nr5GIconMap.containsKey(Config.NR_NOT_RESTRICTED_RRC_IDLE)) {
+ return mConfig.nr5GIconMap.get(Config.NR_NOT_RESTRICTED_RRC_IDLE);
+ }
+ } else {
+ if (mConfig.nr5GIconMap.containsKey(Config.NR_NOT_RESTRICTED_RRC_CON)) {
+ return mConfig.nr5GIconMap.get(Config.NR_NOT_RESTRICTED_RRC_CON);
+ }
}
} else if (nrState == NetworkRegistrationInfo.NR_STATE_RESTRICTED) {
if (mConfig.nr5GIconMap.containsKey(Config.NR_RESTRICTED)) {
@@ -559,6 +565,8 @@
|| activity == TelephonyManager.DATA_ACTIVITY_IN;
mCurrentState.activityOut = activity == TelephonyManager.DATA_ACTIVITY_INOUT
|| activity == TelephonyManager.DATA_ACTIVITY_OUT;
+ mCurrentState.activityDormant = activity == TelephonyManager.DATA_ACTIVITY_DORMANT;
+
notifyListenersIfNecessary();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 04f96a4..292571e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -1112,8 +1112,9 @@
static class Config {
static final int NR_CONNECTED_MMWAVE = 1;
static final int NR_CONNECTED = 2;
- static final int NR_NOT_RESTRICTED = 3;
- static final int NR_RESTRICTED = 4;
+ static final int NR_NOT_RESTRICTED_RRC_IDLE = 3;
+ static final int NR_NOT_RESTRICTED_RRC_CON = 4;
+ static final int NR_RESTRICTED = 5;
Map<Integer, MobileIconGroup> nr5GIconMap = new HashMap<>();
@@ -1133,10 +1134,11 @@
*/
private static final Map<String, Integer> NR_STATUS_STRING_TO_INDEX;
static {
- NR_STATUS_STRING_TO_INDEX = new HashMap<>(4);
+ NR_STATUS_STRING_TO_INDEX = new HashMap<>(5);
NR_STATUS_STRING_TO_INDEX.put("connected_mmwave", NR_CONNECTED_MMWAVE);
NR_STATUS_STRING_TO_INDEX.put("connected", NR_CONNECTED);
- NR_STATUS_STRING_TO_INDEX.put("not_restricted", NR_NOT_RESTRICTED);
+ NR_STATUS_STRING_TO_INDEX.put("not_restricted_rrc_idle", NR_NOT_RESTRICTED_RRC_IDLE);
+ NR_STATUS_STRING_TO_INDEX.put("not_restricted_rrc_con", NR_NOT_RESTRICTED_RRC_CON);
NR_STATUS_STRING_TO_INDEX.put("restricted", NR_RESTRICTED);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
index 9ec30d4..abe3f2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java
@@ -258,6 +258,7 @@
boolean enabled;
boolean activityIn;
boolean activityOut;
+ public boolean activityDormant;
int level;
IconGroup iconGroup;
int inetCondition;
@@ -274,6 +275,7 @@
inetCondition = state.inetCondition;
activityIn = state.activityIn;
activityOut = state.activityOut;
+ activityDormant = state.activityDormant;
rssi = state.rssi;
time = state.time;
}
@@ -297,6 +299,7 @@
.append("iconGroup=").append(iconGroup).append(',')
.append("activityIn=").append(activityIn).append(',')
.append("activityOut=").append(activityOut).append(',')
+ .append("activityDormant=").append(activityDormant).append(',')
.append("rssi=").append(rssi).append(',')
.append("lastModified=").append(DateFormat.format("MM-dd HH:mm:ss", time));
}
@@ -314,6 +317,7 @@
&& other.iconGroup == iconGroup
&& other.activityIn == activityIn
&& other.activityOut == activityOut
+ && other.activityDormant == activityDormant
&& other.rssi == rssi;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogImplTest.java
new file mode 100644
index 0000000..8f2f8b1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricDialogImplTest.java
@@ -0,0 +1,329 @@
+/*
+ * 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.systemui.biometrics;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.app.IActivityTaskManager;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.os.Bundle;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper.RunWithLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class BiometricDialogImplTest extends SysuiTestCase {
+
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private IBiometricServiceReceiverInternal mReceiver;
+ @Mock
+ private BiometricDialog mDialog1;
+ @Mock
+ private BiometricDialog mDialog2;
+
+ private TestableBiometricDialogImpl mBiometricDialogImpl;
+
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+
+ TestableContext context = spy(mContext);
+
+ mContext.putComponent(StatusBar.class, mock(StatusBar.class));
+ mContext.putComponent(CommandQueue.class, mock(CommandQueue.class));
+
+ when(context.getPackageManager()).thenReturn(mPackageManager);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE))
+ .thenReturn(true);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
+ .thenReturn(true);
+
+ when(mDialog1.getOpPackageName()).thenReturn("Dialog1");
+ when(mDialog2.getOpPackageName()).thenReturn("Dialog2");
+
+ mBiometricDialogImpl = new TestableBiometricDialogImpl(new MockInjector());
+ mBiometricDialogImpl.mContext = context;
+ mBiometricDialogImpl.mComponents = mContext.getComponents();
+
+ mBiometricDialogImpl.start();
+ }
+
+ // Callback tests
+
+ @Test
+ public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_USER_CANCELED);
+ verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ }
+
+ @Test
+ public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_BUTTON_NEGATIVE);
+ verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+ }
+
+ @Test
+ public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_BUTTON_POSITIVE);
+ verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+ }
+
+ @Test
+ public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_AUTHENTICATED);
+ verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+ }
+
+ @Test
+ public void testSendsReasonError_whenDismissedByError() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_ERROR);
+ verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR);
+ }
+
+ @Test
+ public void testSendsReasonDismissedBySystemServer_whenDismissedByServer() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ mBiometricDialogImpl.onDismissed(DialogViewCallback.DISMISSED_BY_SYSTEM_SERVER);
+ verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
+ }
+
+ // Statusbar tests
+
+ @Test
+ public void testShowInvoked_whenSystemRequested()
+ throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ verify(mDialog1).show(any(), eq(false) /* skipIntro */);
+ }
+
+ @Test
+ public void testOnAuthenticationSucceededInvoked_whenSystemRequested() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ mBiometricDialogImpl.onBiometricAuthenticated(true, null /* failureReason */);
+ verify(mDialog1).onAuthenticationSucceeded();
+ }
+
+ @Test
+ public void testOnAuthenticationFailedInvoked_whenSystemRequested() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ final String failureReason = "failure reason";
+ mBiometricDialogImpl.onBiometricAuthenticated(false, failureReason);
+
+ ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+ verify(mDialog1).onAuthenticationFailed(captor.capture());
+
+ assertEquals(captor.getValue(), failureReason);
+ }
+
+ @Test
+ public void testOnHelpInvoked_whenSystemRequested() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ final String helpMessage = "help";
+ mBiometricDialogImpl.onBiometricHelp(helpMessage);
+
+ ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+ verify(mDialog1).onHelp(captor.capture());
+
+ assertEquals(captor.getValue(), helpMessage);
+ }
+
+ @Test
+ public void testOnErrorInvoked_whenSystemRequested() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ final String errMessage = "error message";
+ mBiometricDialogImpl.onBiometricError(errMessage);
+
+ ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
+ verify(mDialog1).onError(captor.capture());
+
+ assertEquals(captor.getValue(), errMessage);
+ }
+
+ @Test
+ public void testDismissWithoutCallbackInvoked_whenSystemRequested() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ mBiometricDialogImpl.hideBiometricDialog();
+ verify(mDialog1).dismissFromSystemServer();
+ }
+
+ @Test
+ public void testClientNotified_whenDismissedBySystemServer() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ mBiometricDialogImpl.hideBiometricDialog();
+ verify(mDialog1).dismissFromSystemServer();
+
+ assertNotNull(mBiometricDialogImpl.mCurrentDialog);
+ assertNotNull(mBiometricDialogImpl.mReceiver);
+ }
+
+ // Corner case tests
+
+ @Test
+ public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ verify(mDialog1).show(any(), eq(false) /* skipIntro */);
+
+ showDialog(BiometricPrompt.TYPE_FACE);
+
+ // First dialog should be dismissed without animation
+ verify(mDialog1).dismissWithoutCallback(eq(false) /* animate */);
+
+ // Second dialog should be shown without animation
+ verify(mDialog2).show(any(), eq(true)) /* skipIntro */;
+ }
+
+ @Test
+ public void testConfigurationPersists_whenOnConfigurationChanged() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+ verify(mDialog1).show(any(), eq(false) /* skipIntro */);
+
+ mBiometricDialogImpl.onConfigurationChanged(new Configuration());
+
+ ArgumentCaptor<Bundle> captor = ArgumentCaptor.forClass(Bundle.class);
+ verify(mDialog1).onSaveState(captor.capture());
+
+ // Old dialog doesn't animate
+ verify(mDialog1).dismissWithoutCallback(eq(false /* animate */));
+
+ // Saved state is restored into new dialog
+ ArgumentCaptor<Bundle> captor2 = ArgumentCaptor.forClass(Bundle.class);
+ verify(mDialog2).restoreState(captor2.capture());
+
+ // Dialog for new configuration skips intro
+ verify(mDialog2).show(any(), eq(true) /* skipIntro */);
+
+ // TODO: This should check all values we want to save/restore
+ assertEquals(captor.getValue(), captor2.getValue());
+ }
+
+ @Test
+ public void testClientNotified_whenTaskStackChangesDuringAuthentication() throws Exception {
+ showDialog(BiometricPrompt.TYPE_FACE);
+
+ List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>();
+ ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
+ taskInfo.topActivity = mock(ComponentName.class);
+ when(taskInfo.topActivity.getPackageName()).thenReturn("other_package");
+ tasks.add(taskInfo);
+ when(mBiometricDialogImpl.mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
+
+ mBiometricDialogImpl.mTaskStackListener.onTaskStackChanged();
+ waitForIdleSync();
+
+ assertNull(mBiometricDialogImpl.mCurrentDialog);
+ assertNull(mBiometricDialogImpl.mReceiver);
+ verify(mDialog1).dismissWithoutCallback(true /* animate */);
+ verify(mReceiver).onDialogDismissed(eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL));
+ }
+
+ // Helpers
+
+ private void showDialog(int type) {
+ mBiometricDialogImpl.showBiometricDialog(createTestDialogBundle(),
+ mReceiver /* receiver */,
+ type,
+ true /* requireConfirmation */,
+ 0 /* userId */,
+ "testPackage");
+ }
+
+ private Bundle createTestDialogBundle() {
+ Bundle bundle = new Bundle();
+
+ bundle.putCharSequence(BiometricPrompt.KEY_TITLE, "Title");
+ bundle.putCharSequence(BiometricPrompt.KEY_SUBTITLE, "Subtitle");
+ bundle.putCharSequence(BiometricPrompt.KEY_DESCRIPTION, "Description");
+ bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, "Negative Button");
+
+ // RequireConfirmation is a hint to BiometricService. This can be forced to be required
+ // by user settings, and should be tested in BiometricService.
+ bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true);
+
+ return bundle;
+ }
+
+ private final class TestableBiometricDialogImpl extends BiometricDialogImpl {
+ private int mBuildCount = 0;
+
+ public TestableBiometricDialogImpl(Injector injector) {
+ super(injector);
+ }
+
+ @Override
+ protected BiometricDialog buildDialog(Bundle biometricPromptBundle,
+ boolean requireConfirmation, int userId, int type, String opPackageName) {
+ BiometricDialog dialog;
+ if (mBuildCount == 0) {
+ dialog = mDialog1;
+ } else if (mBuildCount == 1) {
+ dialog = mDialog2;
+ } else {
+ dialog = null;
+ }
+ mBuildCount++;
+ return dialog;
+ }
+ }
+
+ private final class MockInjector extends BiometricDialogImpl.Injector {
+ @Override
+ IActivityTaskManager getActivityTaskManager() {
+ return mock(IActivityTaskManager.class);
+ }
+ }
+}
+
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/BiometricDialogViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/BiometricDialogViewTest.java
new file mode 100644
index 0000000..bbdd837
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/BiometricDialogViewTest.java
@@ -0,0 +1,204 @@
+/*
+ * 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.systemui.biometrics.ui;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNotSame;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.spy;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper.RunWithLooper;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.DialogViewCallback;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
+@SmallTest
+public class BiometricDialogViewTest extends SysuiTestCase {
+
+ FaceDialogView mFaceDialogView;
+
+ private static final String TITLE = "Title";
+ private static final String SUBTITLE = "Subtitle";
+ private static final String DESCRIPTION = "Description";
+ private static final String NEGATIVE_BUTTON = "Negative Button";
+
+ private static final String TEST_HELP = "Help";
+
+ TestableContext mTestableContext;
+ @Mock
+ private DialogViewCallback mCallback;
+ @Mock
+ private UserManager mUserManager;
+ @Mock
+ private DevicePolicyManager mDpm;
+
+ private static class Injector extends BiometricDialogView.Injector {
+ @Override
+ public WakefulnessLifecycle getWakefulnessLifecycle() {
+ final WakefulnessLifecycle lifecycle = new WakefulnessLifecycle();
+ lifecycle.dispatchFinishedWakingUp();
+ return lifecycle;
+ }
+ }
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mTestableContext = spy(mContext);
+ mTestableContext.addMockSystemService(UserManager.class, mUserManager);
+ mTestableContext.addMockSystemService(DevicePolicyManager.class, mDpm);
+ }
+
+ @Test
+ public void testContentStates_confirmationRequired_authenticated() {
+ mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+ true /* requireConfirmation */);
+ mFaceDialogView.onAttachedToWindow();
+
+ // When starting authentication
+ assertEquals(View.VISIBLE, mFaceDialogView.mTitleText.getVisibility());
+ assertEquals(View.VISIBLE, mFaceDialogView.mSubtitleText.getVisibility());
+ assertEquals(View.VISIBLE, mFaceDialogView.mDescriptionText.getVisibility());
+ assertEquals(View.INVISIBLE, mFaceDialogView.mErrorText.getVisibility());
+ assertEquals(View.VISIBLE, mFaceDialogView.mPositiveButton.getVisibility());
+ assertEquals(View.VISIBLE, mFaceDialogView.mNegativeButton.getVisibility());
+ assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
+
+ // Contents are as expected
+ assertTrue(TITLE.contentEquals(mFaceDialogView.mTitleText.getText()));
+ assertTrue(SUBTITLE.contentEquals(mFaceDialogView.mSubtitleText.getText()));
+ assertTrue(DESCRIPTION.contentEquals(mFaceDialogView.mDescriptionText.getText()));
+ assertTrue(mFaceDialogView.mPositiveButton.getText().toString()
+ .contentEquals(mContext.getString(R.string.biometric_dialog_confirm)));
+ assertTrue(NEGATIVE_BUTTON.contentEquals(mFaceDialogView.mNegativeButton.getText()));
+ assertTrue(mFaceDialogView.mTryAgainButton.getText().toString()
+ .contentEquals(mContext.getString(R.string.biometric_dialog_try_again)));
+
+ // When help message is received
+ mFaceDialogView.onHelp(TEST_HELP);
+ assertEquals(mFaceDialogView.mErrorText.getVisibility(), View.VISIBLE);
+ assertTrue(TEST_HELP.contentEquals(mFaceDialogView.mErrorText.getText()));
+
+ // When authenticated, confirm button comes out
+ mFaceDialogView.onAuthenticationSucceeded();
+ assertEquals(View.VISIBLE, mFaceDialogView.mPositiveButton.getVisibility());
+ assertEquals(true, mFaceDialogView.mPositiveButton.isEnabled());
+ }
+
+ @Test
+ public void testContentStates_confirmationNotRequired_authenticated() {
+ mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+ false /* requireConfirmation */);
+ mFaceDialogView.onAttachedToWindow();
+ mFaceDialogView.updateSize(FaceDialogView.SIZE_SMALL);
+
+ assertEquals(View.INVISIBLE, mFaceDialogView.mTitleText.getVisibility());
+ assertNotSame(View.VISIBLE, mFaceDialogView.mSubtitleText.getVisibility());
+ assertNotSame(View.VISIBLE, mFaceDialogView.mDescriptionText.getVisibility());
+ assertEquals(View.INVISIBLE, mFaceDialogView.mErrorText.getVisibility());
+ assertEquals(View.GONE, mFaceDialogView.mPositiveButton.getVisibility());
+ assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
+ assertEquals(View.GONE, mFaceDialogView.mTryAgainButton.getVisibility());
+ }
+
+ @Test
+ public void testContentStates_confirmationNotRequired_help() {
+ mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+ false /* requireConfirmation */);
+ mFaceDialogView.onAttachedToWindow();
+
+ mFaceDialogView.onHelp(TEST_HELP);
+ assertEquals(mFaceDialogView.mErrorText.getVisibility(), View.VISIBLE);
+ assertTrue(TEST_HELP.contentEquals(mFaceDialogView.mErrorText.getText()));
+ }
+
+ @Test
+ public void testBack_sendsUserCanceled() {
+ // TODO: Need robolectric framework to wait for handler to complete
+ }
+
+ @Test
+ public void testScreenOff_sendsUserCanceled() {
+ // TODO: Need robolectric framework to wait for handler to complete
+ }
+
+ @Test
+ public void testRestoreState_contentStatesCorrect() {
+ mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+ false /* requireConfirmation */);
+ mFaceDialogView.onAttachedToWindow();
+ mFaceDialogView.onAuthenticationFailed(TEST_HELP);
+
+ final Bundle bundle = new Bundle();
+ mFaceDialogView.onSaveState(bundle);
+
+ mFaceDialogView = buildFaceDialogView(mTestableContext, mCallback,
+ false /* requireConfirmation */);
+ mFaceDialogView.restoreState(bundle);
+ mFaceDialogView.onAttachedToWindow();
+
+ assertEquals(View.VISIBLE, mFaceDialogView.mTryAgainButton.getVisibility());
+ }
+
+ private FaceDialogView buildFaceDialogView(Context context, DialogViewCallback callback,
+ boolean requireConfirmation) {
+ return (FaceDialogView) new BiometricDialogView.Builder(context)
+ .setCallback(callback)
+ .setBiometricPromptBundle(createTestDialogBundle())
+ .setRequireConfirmation(requireConfirmation)
+ .setUserId(0)
+ .setOpPackageName("test_package")
+ .build(BiometricDialogView.Builder.TYPE_FACE, new Injector());
+ }
+
+ private Bundle createTestDialogBundle() {
+ Bundle bundle = new Bundle();
+
+ bundle.putCharSequence(BiometricPrompt.KEY_TITLE, TITLE);
+ bundle.putCharSequence(BiometricPrompt.KEY_SUBTITLE, SUBTITLE);
+ bundle.putCharSequence(BiometricPrompt.KEY_DESCRIPTION, DESCRIPTION);
+ bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, NEGATIVE_BUTTON);
+
+ // RequireConfirmation is a hint to BiometricService. This can be forced to be required
+ // by user settings, and should be tested in BiometricService.
+ bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true);
+
+ return bundle;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
index 25a1a75..fb4c1ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/brightline/ZigZagClassifierTest.java
@@ -133,8 +133,8 @@
// This test looks just like testPass_horizontalZigZagVerticalStraight but with
// a shorter y range, making it look more crooked.
appendMoveEvent(0, 0);
- appendMoveEvent(5, 10);
- appendMoveEvent(-5, 20);
+ appendMoveEvent(6, 10);
+ appendMoveEvent(-6, 20);
assertThat(mClassifier.isFalseTouch(), is(true));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
new file mode 100644
index 0000000..b4a60d6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/EglHelperTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.systemui.glwallpaper;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.RETURNS_DEFAULTS;
+import static org.mockito.Mockito.mock;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.SurfaceHolder;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class EglHelperTest extends SysuiTestCase {
+
+ @Mock
+ private EglHelper mEglHelper;
+ @Mock
+ private SurfaceHolder mSurfaceHolder;
+
+ @Before
+ public void setUp() throws Exception {
+ mEglHelper = mock(EglHelper.class, RETURNS_DEFAULTS);
+ mSurfaceHolder = mock(SurfaceHolder.class, RETURNS_DEFAULTS);
+ }
+
+ @Test
+ public void testInit_finish() {
+ mEglHelper.init(mSurfaceHolder);
+ mEglHelper.finish();
+ }
+
+ @Test
+ public void testFinish_shouldNotCrash() {
+ assertFalse(mEglHelper.hasEglDisplay());
+ assertFalse(mEglHelper.hasEglSurface());
+ assertFalse(mEglHelper.hasEglContext());
+
+ mEglHelper.finish();
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 1e1f2156..b252a0d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -365,4 +365,45 @@
waitForIdleSync();
verify(mCallbacks).onRecentsAnimationStateChanged(eq(true));
}
+
+ @Test
+ public void testShowBiometricDialog() {
+ Bundle bundle = new Bundle();
+ String packageName = "test";
+ mCommandQueue.showBiometricDialog(bundle, null /* receiver */, 1, true, 3, packageName);
+ waitForIdleSync();
+ verify(mCallbacks).showBiometricDialog(eq(bundle), eq(null), eq(1), eq(true), eq(3),
+ eq(packageName));
+ }
+
+ @Test
+ public void testOnBiometricAuthenticated() {
+ String failureReason = "test_failure_reason";
+ mCommandQueue.onBiometricAuthenticated(true /* authenticated */, failureReason);
+ waitForIdleSync();
+ verify(mCallbacks).onBiometricAuthenticated(eq(true), eq(failureReason));
+ }
+
+ @Test
+ public void testOnBiometricHelp() {
+ String helpMessage = "test_help_message";
+ mCommandQueue.onBiometricHelp(helpMessage);
+ waitForIdleSync();
+ verify(mCallbacks).onBiometricHelp(eq(helpMessage));
+ }
+
+ @Test
+ public void testOnBiometricError() {
+ String errorMessage = "test_error_message";
+ mCommandQueue.onBiometricError(errorMessage);
+ waitForIdleSync();
+ verify(mCallbacks).onBiometricError(eq(errorMessage));
+ }
+
+ @Test
+ public void testHideBiometricDialog() {
+ mCommandQueue.hideBiometricDialog();
+ waitForIdleSync();
+ verify(mCallbacks).hideBiometricDialog();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 9ae9ceb..e691b7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -228,6 +228,18 @@
NetworkControllerImpl.Config.add5GIconMapping("connected:5g", mConfig);
}
+ public void setupNr5GIconConfigurationForNotRestrictedRrcCon() {
+ NetworkControllerImpl.Config.add5GIconMapping("connected_mmwave:5g_plus", mConfig);
+ NetworkControllerImpl.Config.add5GIconMapping("connected:5g_plus", mConfig);
+ NetworkControllerImpl.Config.add5GIconMapping("not_restricted_rrc_con:5g", mConfig);
+ }
+
+ public void setupNr5GIconConfigurationForNotRestrictedRrcIdle() {
+ NetworkControllerImpl.Config.add5GIconMapping("connected_mmwave:5g_plus", mConfig);
+ NetworkControllerImpl.Config.add5GIconMapping("connected:5g_plus", mConfig);
+ NetworkControllerImpl.Config.add5GIconMapping("not_restricted_rrc_idle:5g", mConfig);
+ }
+
public void setConnectivityViaBroadcast(
int networkType, boolean validated, boolean isConnected) {
setConnectivityCommon(networkType, validated, isConnected);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index 5128675..f0394da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -175,6 +175,35 @@
}
@Test
+ public void testNr5GIcon_NrNotRestrictedRrcCon_show5GIcon() {
+ setupNr5GIconConfigurationForNotRestrictedRrcCon();
+ setupDefaultSignal();
+ updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
+ TelephonyManager.NETWORK_TYPE_LTE);
+ updateDataActivity(TelephonyManager.DATA_ACTIVITY_INOUT);
+ ServiceState ss = Mockito.mock(ServiceState.class);
+ doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(ss).getNrState();
+ mPhoneStateListener.onServiceStateChanged(ss);
+
+ verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, TelephonyIcons.ICON_5G,
+ true, DEFAULT_QS_SIGNAL_STRENGTH, TelephonyIcons.ICON_5G, true, true);
+ }
+
+ @Test
+ public void testNr5GIcon_NrNotRestrictedRrcIdle_show5GIcon() {
+ setupNr5GIconConfigurationForNotRestrictedRrcIdle();
+ setupDefaultSignal();
+ updateDataConnectionState(TelephonyManager.DATA_CONNECTED,
+ TelephonyManager.NETWORK_TYPE_LTE);
+ updateDataActivity(TelephonyManager.DATA_ACTIVITY_DORMANT);
+ ServiceState ss = Mockito.mock(ServiceState.class);
+ doReturn(NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED).when(ss).getNrState();
+ mPhoneStateListener.onServiceStateChanged(ss);
+
+ verifyDataIndicators(TelephonyIcons.ICON_5G);
+ }
+
+ @Test
public void testNr5GIcon_NrConnectedWithoutMMWave_show5GIcon() {
setupDefaultNr5GIconConfiguration();
setupDefaultSignal();
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 1356157..5e9c08b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -348,13 +348,13 @@
}
}
- protected abstract boolean isCalledForCurrentUserLocked();
+ protected abstract boolean hasRightsToCurrentUserLocked();
@Override
public List<AccessibilityWindowInfo> getWindows() {
ensureWindowsAvailableTimed();
synchronized (mLock) {
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return null;
}
final boolean permissionGranted =
@@ -387,7 +387,7 @@
public AccessibilityWindowInfo getWindow(int windowId) {
ensureWindowsAvailableTimed();
synchronized (mLock) {
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return null;
}
final boolean permissionGranted =
@@ -420,7 +420,7 @@
MagnificationSpec spec;
synchronized (mLock) {
mUsesAccessibilityCache = true;
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return null;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -481,7 +481,7 @@
MagnificationSpec spec;
synchronized (mLock) {
mUsesAccessibilityCache = true;
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return null;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -542,7 +542,7 @@
MagnificationSpec spec;
synchronized (mLock) {
mUsesAccessibilityCache = true;
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return null;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -602,7 +602,7 @@
Region partialInteractiveRegion = Region.obtain();
MagnificationSpec spec;
synchronized (mLock) {
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return null;
}
resolvedWindowId = resolveAccessibilityWindowIdForFindFocusLocked(
@@ -663,7 +663,7 @@
Region partialInteractiveRegion = Region.obtain();
MagnificationSpec spec;
synchronized (mLock) {
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return null;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -728,7 +728,7 @@
throws RemoteException {
final int resolvedWindowId;
synchronized (mLock) {
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return false;
}
resolvedWindowId = resolveAccessibilityWindowIdLocked(accessibilityWindowId);
@@ -748,7 +748,7 @@
@Override
public boolean performGlobalAction(int action) {
synchronized (mLock) {
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return false;
}
}
@@ -771,7 +771,7 @@
@Override
public float getMagnificationScale(int displayId) {
synchronized (mLock) {
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return 1.0f;
}
}
@@ -787,7 +787,7 @@
public Region getMagnificationRegion(int displayId) {
synchronized (mLock) {
final Region region = Region.obtain();
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return region;
}
MagnificationController magnificationController =
@@ -810,7 +810,7 @@
@Override
public float getMagnificationCenterX(int displayId) {
synchronized (mLock) {
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return 0.0f;
}
MagnificationController magnificationController =
@@ -832,7 +832,7 @@
@Override
public float getMagnificationCenterY(int displayId) {
synchronized (mLock) {
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return 0.0f;
}
MagnificationController magnificationController =
@@ -864,7 +864,7 @@
@Override
public boolean resetMagnification(int displayId, boolean animate) {
synchronized (mLock) {
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return false;
}
if (!mSecurityPolicy.canControlMagnification(this)) {
@@ -886,7 +886,7 @@
public boolean setMagnificationScaleAndCenter(int displayId, float scale, float centerX,
float centerY, boolean animate) {
synchronized (mLock) {
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return false;
}
if (!mSecurityPolicy.canControlMagnification(this)) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 18b6f90..c733d3b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -29,8 +29,8 @@
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityEvent;
-import com.android.server.accessibility.gestures.TouchExplorer;
import com.android.server.LocalServices;
+import com.android.server.accessibility.gestures.TouchExplorer;
import com.android.server.policy.WindowManagerPolicy;
import java.util.ArrayList;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index d058b9a..893e4e4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2531,10 +2531,9 @@
// to create event handler per display. The events should be handled by the
// display which is overlaid by it.
final Display display = displays[i];
- if (display.getType() == Display.TYPE_OVERLAY) {
- continue;
+ if (isValidDisplay(display)) {
+ mDisplaysList.add(display);
}
- mDisplaysList.add(display);
}
}
}
@@ -2542,7 +2541,7 @@
@Override
public void onDisplayAdded(int displayId) {
final Display display = mDisplayManager.getDisplay(displayId);
- if (display == null || display.getType() == Display.TYPE_OVERLAY) {
+ if (!isValidDisplay(display)) {
return;
}
@@ -2593,6 +2592,19 @@
public void onDisplayChanged(int displayId) {
/* do nothing */
}
+
+ private boolean isValidDisplay(@Nullable Display display) {
+ if (display == null || display.getType() == Display.TYPE_OVERLAY) {
+ return false;
+ }
+ // Private virtual displays are created by the ap and is not allowed to access by other
+ // aps. We assume we could ignore them.
+ if ((display.getType() == Display.TYPE_VIRTUAL
+ && (display.getFlags() & Display.FLAG_PRIVATE) != 0)) {
+ return false;
+ }
+ return true;
+ }
}
/** Represents an {@link AccessibilityManager} */
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
index 315d6fa..b88b24e 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilitySecurityPolicy.java
@@ -468,7 +468,7 @@
}
}
- private boolean hasPermission(String permission) {
+ boolean hasPermission(String permission) {
return mContext.checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index 02f7821..d7f61e5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -18,6 +18,7 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.accessibilityservice.IAccessibilityServiceClient;
import android.content.ComponentName;
@@ -27,6 +28,7 @@
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.provider.Settings;
@@ -211,19 +213,31 @@
}
@Override
- protected boolean isCalledForCurrentUserLocked() {
+ protected boolean hasRightsToCurrentUserLocked() {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
// performs the current profile parent resolution.
- final int resolvedUserId = mSecurityPolicy
- .resolveCallingUserIdEnforcingPermissionsLocked(UserHandle.USER_CURRENT);
- return resolvedUserId == mSystemSupport.getCurrentUserIdLocked();
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid == Process.ROOT_UID
+ || callingUid == Process.SYSTEM_UID
+ || callingUid == Process.SHELL_UID) {
+ return true;
+ }
+ if (mSecurityPolicy.resolveProfileParentLocked(UserHandle.getUserId(callingUid))
+ == mSystemSupport.getCurrentUserIdLocked()) {
+ return true;
+ }
+ if (mSecurityPolicy.hasPermission(Manifest.permission.INTERACT_ACROSS_USERS)
+ || mSecurityPolicy.hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL)) {
+ return true;
+ }
+ return false;
}
@Override
public boolean setSoftKeyboardShowMode(int showMode) {
synchronized (mLock) {
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return false;
}
final UserState userState = mUserStateWeakReference.get();
@@ -241,7 +255,7 @@
@Override
public boolean isAccessibilityButtonAvailable() {
synchronized (mLock) {
- if (!isCalledForCurrentUserLocked()) {
+ if (!hasRightsToCurrentUserLocked()) {
return false;
}
UserState userState = mUserStateWeakReference.get();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 9687098..c9efe36 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -872,8 +872,8 @@
RemoteAccessibilityConnection wrapper = new RemoteAccessibilityConnection(
windowId, connection, packageName, resolvedUid, resolvedUserId);
wrapper.linkToDeath();
- getInteractionConnectionsForUserLocked(userId).put(windowId, wrapper);
- getWindowTokensForUserLocked(userId).put(windowId, windowToken.asBinder());
+ getInteractionConnectionsForUserLocked(resolvedUserId).put(windowId, wrapper);
+ getWindowTokensForUserLocked(resolvedUserId).put(windowId, windowToken.asBinder());
if (DEBUG) {
Slog.i(LOG_TAG, "Added user connection for pid:" + Binder.getCallingPid()
+ " with windowId: " + windowId
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index 2698b72..79d975d 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -66,6 +66,7 @@
mUiAutomationServiceOwner.unlinkToDeath(this, 0);
mUiAutomationServiceOwner = null;
destroyUiAutomationService();
+ Slog.v(LOG_TAG, "UiAutomation service owner died");
}
};
@@ -263,7 +264,7 @@
}
@Override
- protected boolean isCalledForCurrentUserLocked() {
+ protected boolean hasRightsToCurrentUserLocked() {
// Allow UiAutomation to work for any user
return true;
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index a64f4e4..6b7c3e6 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -807,6 +807,7 @@
packageName, versionCode, userId);
final AutofillOptions options = new AutofillOptions(loggingLevel, compatModeEnabled);
mAugmentedAutofillState.injectAugmentedAutofillInfo(options, userId, packageName);
+ injectDisableAppInfo(options, userId, packageName);
return options;
}
@@ -820,6 +821,19 @@
}
return false;
}
+
+ private void injectDisableAppInfo(@NonNull AutofillOptions options, int userId,
+ String packageName) {
+ synchronized (mLock) {
+ final AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ options.appDisabledExpiration = service.getAppDisabledExpirationLocked(
+ packageName);
+ options.disabledActivities = service.getAppDisabledActivitiesLocked(
+ packageName);
+ }
+ }
+ }
}
/**
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 1e1e07d..d7ed2e9 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -980,12 +980,12 @@
for (int i = 0; i < size; i++) {
final String packageName = mDisabledApps.keyAt(i);
final long expiration = mDisabledApps.valueAt(i);
- builder.append(prefix).append(prefix)
- .append(i).append(". ").append(packageName).append(": ");
- TimeUtils.formatDuration((expiration - now), builder);
- builder.append('\n');
- }
- pw.println(builder);
+ builder.append(prefix).append(prefix)
+ .append(i).append(". ").append(packageName).append(": ");
+ TimeUtils.formatDuration((expiration - now), builder);
+ builder.append('\n');
+ }
+ pw.println(builder);
}
pw.print(prefix); pw.print("Disabled activities: ");
@@ -1000,12 +1000,12 @@
for (int i = 0; i < size; i++) {
final ComponentName component = mDisabledActivities.keyAt(i);
final long expiration = mDisabledActivities.valueAt(i);
- builder.append(prefix).append(prefix)
- .append(i).append(". ").append(component).append(": ");
- TimeUtils.formatDuration((expiration - now), builder);
- builder.append('\n');
- }
- pw.println(builder);
+ builder.append(prefix).append(prefix)
+ .append(i).append(". ").append(component).append(": ");
+ TimeUtils.formatDuration((expiration - now), builder);
+ builder.append('\n');
+ }
+ pw.println(builder);
}
final int size = mSessions.size();
@@ -1418,6 +1418,36 @@
}
}
+ // Called by AutofillManagerService
+ long getAppDisabledExpirationLocked(@NonNull String packageName) {
+ if (mDisabledApps == null) {
+ return 0;
+ }
+ final Long expiration = mDisabledApps.get(packageName);
+ return expiration != null ? expiration : 0;
+ }
+
+ // Called by AutofillManagerService
+ @Nullable
+ ArrayMap<String, Long> getAppDisabledActivitiesLocked(@NonNull String packageName) {
+ if (mDisabledActivities != null) {
+ final int size = mDisabledActivities.size();
+ ArrayMap<String, Long> disabledList = null;
+ for (int i = 0; i < size; i++) {
+ final ComponentName component = mDisabledActivities.keyAt(i);
+ if (packageName.equals(component.getPackageName())) {
+ if (disabledList == null) {
+ disabledList = new ArrayMap<>();
+ }
+ final long expiration = mDisabledActivities.valueAt(i);
+ disabledList.put(component.flattenToShortString(), expiration);
+ }
+ }
+ return disabledList;
+ }
+ return null;
+ }
+
/**
* Checks if autofill is disabled by service to the given activity.
*/
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 8032e1b..48f16ac 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -301,6 +301,11 @@
+ "mForAugmentedAutofillOnly: %s", mForAugmentedAutofillOnly);
return;
}
+ if (mCurrentViewId == null) {
+ Slog.w(TAG, "No current view id - session might have finished");
+ return;
+ }
+
final AssistStructure structure = resultData.getParcelable(ASSIST_KEY_STRUCTURE);
if (structure == null) {
Slog.e(TAG, "No assist structure - app might have crashed providing it");
@@ -767,13 +772,19 @@
final long disableDuration = response.getDisableDuration();
if (disableDuration > 0) {
final int flags = response.getFlags();
- if ((flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0) {
+ final boolean disableActivityOnly =
+ (flags & FillResponse.FLAG_DISABLE_ACTIVITY_ONLY) != 0;
+ notifyDisableAutofillToClient(disableDuration,
+ disableActivityOnly ? mComponentName : null);
+
+ if (disableActivityOnly) {
mService.disableAutofillForActivity(mComponentName, disableDuration,
id, mCompatMode);
} else {
mService.disableAutofillForApp(mComponentName.getPackageName(), disableDuration,
id, mCompatMode);
}
+
// Although "standard" autofill is disabled, it might still trigger augmented autofill
if (triggerAugmentedAutofillLocked() != null) {
mForAugmentedAutofillOnly = true;
@@ -2562,6 +2573,17 @@
}
}
+ private void notifyDisableAutofillToClient(long disableDuration, ComponentName componentName) {
+ synchronized (mLock) {
+ if (mCurrentViewId == null) return;
+ try {
+ mClient.notifyDisableAutofill(disableDuration, componentName);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error notifying client disable autofill: id=" + mCurrentViewId, e);
+ }
+ }
+ }
+
@GuardedBy("mLock")
private void updateTrackedIdsLocked() {
// Only track the views of the last response as only those are reported back to the
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 695fccc..222a6f2 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2014 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.
@@ -18,29 +18,56 @@
import static com.android.internal.util.Preconditions.checkNotNull;
+import static java.util.Collections.emptySet;
+
+import android.Manifest;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.app.admin.DevicePolicyManager;
import android.app.backup.BackupManager;
+import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.IRestoreSession;
+import android.app.backup.ISelectBackupTransportCallback;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
+import com.android.server.SystemConfig;
import com.android.server.SystemService;
+import com.android.server.backup.utils.RandomAccessFileUtils;
+import java.io.File;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.io.PrintWriter;
+import java.util.Set;
/**
* Definition of the system service that performs backup/restore operations.
@@ -48,8 +75,20 @@
* <p>This class is responsible for handling user-aware operations and acts as a delegator, routing
* incoming calls to the appropriate per-user {@link UserBackupManagerService} to handle the
* corresponding backup/restore operation.
+ *
+ * <p>It also determines whether the backup service is available. It can be disabled in the
+ * following two ways:
+ *
+ * <ul>
+ * <li>Temporary - call {@link #setBackupServiceActive(int, boolean)}, or
+ * <li>Permanent - set the system property {@link #BACKUP_DISABLE_PROPERTY} to true.
+ * </ul>
+ *
+ * Temporary disabling is controlled by {@link #setBackupServiceActive(int, boolean)} through
+ * privileged callers (currently {@link DevicePolicyManager}). If called on {@link
+ * UserHandle#USER_SYSTEM}, backup is disabled for all users.
*/
-public class BackupManagerService {
+public class BackupManagerService extends IBackupManager.Stub {
public static final String TAG = "BackupManagerService";
public static final boolean DEBUG = true;
public static final boolean MORE_DEBUG = false;
@@ -58,50 +97,581 @@
@VisibleForTesting
static final String DUMP_RUNNING_USERS_MESSAGE = "Backup Manager is running for users:";
- private final Context mContext;
- private final Trampoline mTrampoline;
- private final SparseArray<UserBackupManagerService> mServiceUsers;
+ /**
+ * Name of file that disables the backup service. If this file exists, then backup is disabled
+ * for all users.
+ */
+ private static final String BACKUP_SUPPRESS_FILENAME = "backup-suppress";
- /** Instantiate a new instance of {@link BackupManagerService}. */
- public BackupManagerService(
- Context context,
- Trampoline trampoline,
- SparseArray<UserBackupManagerService> userServices) {
- mContext = checkNotNull(context);
- mTrampoline = checkNotNull(trampoline);
- // TODO(b/135661048): Remove
- mServiceUsers = userServices;
+ /**
+ * Name of file for non-system users that enables the backup service for the user. Backup is
+ * disabled by default in non-system users.
+ */
+ private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated";
+
+ /**
+ * Name of file for non-system users that remembers whether backup was explicitly activated or
+ * deactivated with a call to setBackupServiceActive.
+ */
+ private static final String REMEMBER_ACTIVATED_FILENAME = "backup-remember-activated";
+
+ // Product-level suppression of backup/restore.
+ private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
+
+ private static final String BACKUP_THREAD = "backup";
+
+ static BackupManagerService sInstance;
+
+ static BackupManagerService getInstance() {
+ return checkNotNull(sInstance);
+ }
+
+ private final Context mContext;
+ private final UserManager mUserManager;
+
+ private final boolean mGlobalDisable;
+ // Lock to write backup suppress files.
+ // TODD(b/121198006): remove this object and synchronized all methods on "this".
+ private final Object mStateLock = new Object();
+
+ private final Handler mHandler;
+ private final Set<ComponentName> mTransportWhitelist;
+
+ /** Keeps track of all unlocked users registered with this service. Indexed by user id. */
+ private final SparseArray<UserBackupManagerService> mUserServices;
+
+ private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId > 0) { // for only non system users
+ mHandler.post(() -> onRemovedNonSystemUser(userId));
+ }
+ }
+ }
+ };
+
+ public BackupManagerService(Context context) {
+ this(context, new SparseArray<>());
+ }
+
+ @VisibleForTesting
+ BackupManagerService(Context context, SparseArray<UserBackupManagerService> userServices) {
+ mContext = context;
+ mGlobalDisable = isBackupDisabled();
+ HandlerThread handlerThread =
+ new HandlerThread(BACKUP_THREAD, Process.THREAD_PRIORITY_BACKGROUND);
+ handlerThread.start();
+ mHandler = new Handler(handlerThread.getLooper());
+ mUserManager = UserManager.get(context);
+ mUserServices = userServices;
+ Set<ComponentName> transportWhitelist =
+ SystemConfig.getInstance().getBackupTransportWhitelist();
+ mTransportWhitelist = (transportWhitelist == null) ? emptySet() : transportWhitelist;
+ mContext.registerReceiver(
+ mUserRemovedReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
+ }
+
+ // TODO: Remove this when we implement DI by injecting in the construtor.
+ @VisibleForTesting
+ Handler getBackupHandler() {
+ return mHandler;
+ }
+
+ protected boolean isBackupDisabled() {
+ return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
+ }
+
+ protected int binderGetCallingUserId() {
+ return Binder.getCallingUserHandle().getIdentifier();
+ }
+
+ protected int binderGetCallingUid() {
+ return Binder.getCallingUid();
+ }
+
+ /** Stored in the system user's directory. */
+ protected File getSuppressFileForSystemUser() {
+ return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
+ BACKUP_SUPPRESS_FILENAME);
+ }
+
+ /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+ protected File getRememberActivatedFileForNonSystemUser(int userId) {
+ return UserBackupManagerFiles.getStateFileInSystemDir(REMEMBER_ACTIVATED_FILENAME, userId);
+ }
+
+ /** Stored in the system user's directory and the file is indexed by the user it refers to. */
+ protected File getActivatedFileForNonSystemUser(int userId) {
+ return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId);
}
/**
- * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
- * If the user is not registered with the service (either the user is locked or not eligible for
- * the backup service) then return {@code null}.
- *
- * @param userId The id of the user to retrieve its instance of {@link
- * UserBackupManagerService}.
- * @param caller A {@link String} identifying the caller for logging purposes.
- * @throws SecurityException if {@code userId} is different from the calling user id and the
- * caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ * Remove backup state for non system {@code userId} when the user is removed from the device.
+ * For non system users, backup state is stored in both the user's own dir and the system dir.
+ * When the user is removed, the user's own dir gets removed by the OS. This method ensures that
+ * the part of the user backup state which is in the system dir also gets removed.
*/
- @Nullable
- @VisibleForTesting
- UserBackupManagerService getServiceForUserIfCallerHasPermission(
- @UserIdInt int userId, String caller) {
- return mTrampoline.getServiceForUserIfCallerHasPermission(userId, caller);
+ private void onRemovedNonSystemUser(int userId) {
+ Slog.i(TAG, "Removing state for non system user " + userId);
+ File dir = UserBackupManagerFiles.getStateDirInSystemDir(userId);
+ if (!FileUtils.deleteContentsAndDir(dir)) {
+ Slog.w(TAG, "Failed to delete state dir for removed user: " + userId);
+ }
}
- /*
- * The following methods are implementations of IBackupManager methods called from Trampoline.
- * They delegate to the appropriate per-user instance of UserBackupManagerService to perform the
- * action on the passed in user. Currently this is a straight redirection (see TODO).
+ // TODO (b/124359804) move to util method in FileUtils
+ private void createFile(File file) throws IOException {
+ if (file.exists()) {
+ return;
+ }
+
+ file.getParentFile().mkdirs();
+ if (!file.createNewFile()) {
+ Slog.w(TAG, "Failed to create file " + file.getPath());
+ }
+ }
+
+ // TODO (b/124359804) move to util method in FileUtils
+ private void deleteFile(File file) {
+ if (!file.exists()) {
+ return;
+ }
+
+ if (!file.delete()) {
+ Slog.w(TAG, "Failed to delete file " + file.getPath());
+ }
+ }
+
+ /**
+ * Deactivates the backup service for user {@code userId}. If this is the system user, it
+ * creates a suppress file which disables backup for all users. If this is a non-system user, it
+ * only deactivates backup for that user by deleting its activate file.
*/
- // TODO (b/118520567): Stop hardcoding system user when we pass in user id as a parameter
+ @GuardedBy("mStateLock")
+ private void deactivateBackupForUserLocked(int userId) throws IOException {
+ if (userId == UserHandle.USER_SYSTEM) {
+ createFile(getSuppressFileForSystemUser());
+ } else {
+ deleteFile(getActivatedFileForNonSystemUser(userId));
+ }
+ }
+
+ /**
+ * Enables the backup service for user {@code userId}. If this is the system user, it deletes
+ * the suppress file. If this is a non-system user, it creates the user's activate file. Note,
+ * deleting the suppress file does not automatically enable backup for non-system users, they
+ * need their own activate file in order to participate in the service.
+ */
+ @GuardedBy("mStateLock")
+ private void activateBackupForUserLocked(int userId) throws IOException {
+ if (userId == UserHandle.USER_SYSTEM) {
+ deleteFile(getSuppressFileForSystemUser());
+ } else {
+ createFile(getActivatedFileForNonSystemUser(userId));
+ }
+ }
+
+ // This method should not perform any I/O (e.g. do not call isBackupActivatedForUser),
+ // it's used in multiple places where I/O waits would cause system lock-ups.
+ private boolean isUserReadyForBackup(int userId) {
+ return mUserServices.get(UserHandle.USER_SYSTEM) != null
+ && mUserServices.get(userId) != null;
+ }
+
+ /**
+ * Backup is activated for the system user if the suppress file does not exist. Backup is
+ * activated for non-system users if the suppress file does not exist AND the user's activated
+ * file exists.
+ */
+ private boolean isBackupActivatedForUser(int userId) {
+ if (getSuppressFileForSystemUser().exists()) {
+ return false;
+ }
+
+ return userId == UserHandle.USER_SYSTEM
+ || getActivatedFileForNonSystemUser(userId).exists();
+ }
+
+ protected Context getContext() {
+ return mContext;
+ }
+
+ protected UserManager getUserManager() {
+ return mUserManager;
+ }
+
+ protected void postToHandler(Runnable runnable) {
+ mHandler.post(runnable);
+ }
+
+ /**
+ * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked.
+ * Starts the backup service for this user if backup is active for this user. Offloads work onto
+ * the handler thread {@link #mHandlerThread} to keep unlock time low since backup is not
+ * essential for device functioning.
+ */
+ void onUnlockUser(int userId) {
+ postToHandler(() -> startServiceForUser(userId));
+ }
+
+ /**
+ * Starts the backup service for user {@code userId} by creating a new instance of {@link
+ * UserBackupManagerService} and registering it with this service.
+ */
+ @VisibleForTesting
+ void startServiceForUser(int userId) {
+ // We know that the user is unlocked here because it is called from setBackupServiceActive
+ // and unlockUser which have these guarantees. So we can check if the file exists.
+ if (mGlobalDisable) {
+ Slog.i(TAG, "Backup service not supported");
+ return;
+ }
+ if (!isBackupActivatedForUser(userId)) {
+ Slog.i(TAG, "Backup not activated for user " + userId);
+ return;
+ }
+ if (mUserServices.get(userId) != null) {
+ Slog.i(TAG, "userId " + userId + " already started, so not starting again");
+ return;
+ }
+ Slog.i(TAG, "Starting service for user: " + userId);
+ UserBackupManagerService userBackupManagerService =
+ UserBackupManagerService.createAndInitializeService(
+ userId, mContext, this, mTransportWhitelist);
+ startServiceForUser(userId, userBackupManagerService);
+ }
+
+ /**
+ * Starts the backup service for user {@code userId} by registering its instance of {@link
+ * UserBackupManagerService} with this service and setting enabled state.
+ */
+ void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) {
+ mUserServices.put(userId, userBackupManagerService);
+
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
+ userBackupManagerService.initializeBackupEnableState();
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Stops the backup service for user {@code userId} when the user is stopped. */
+ @VisibleForTesting
+ protected void stopServiceForUser(int userId) {
+ UserBackupManagerService userBackupManagerService = mUserServices.removeReturnOld(userId);
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.tearDownService();
+
+ KeyValueBackupJob.cancel(userId, mContext);
+ FullBackupJob.cancel(userId, mContext);
+ }
+ }
+
+ /**
+ * Returns a list of users currently unlocked that have a {@link UserBackupManagerService}
+ * registered.
+ *
+ * Warning: Do NOT modify returned object as it's used inside.
+ *
+ * TODO: Return a copy or only expose read-only information through other means.
+ */
+ @VisibleForTesting
+ SparseArray<UserBackupManagerService> getUserServices() {
+ return mUserServices;
+ }
+
+ /**
+ * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is stopped.
+ * Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low.
+ */
+ void onStopUser(int userId) {
+ postToHandler(
+ () -> {
+ if (!mGlobalDisable) {
+ Slog.i(TAG, "Stopping service for user: " + userId);
+ stopServiceForUser(userId);
+ }
+ });
+ }
+
+ /** Returns {@link UserBackupManagerService} for user {@code userId}. */
+ @Nullable
+ public UserBackupManagerService getUserService(int userId) {
+ return mUserServices.get(userId);
+ }
+
+ /**
+ * The system user and managed profiles can only be acted on by callers in the system or root
+ * processes. Other users can be acted on by callers who have both android.permission.BACKUP and
+ * android.permission.INTERACT_ACROSS_USERS_FULL permissions.
+ */
+ private void enforcePermissionsOnUser(int userId) throws SecurityException {
+ boolean isRestrictedUser =
+ userId == UserHandle.USER_SYSTEM
+ || getUserManager().getUserInfo(userId).isManagedProfile();
+
+ if (isRestrictedUser) {
+ int caller = binderGetCallingUid();
+ if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) {
+ throw new SecurityException("No permission to configure backup activity");
+ }
+ } else {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.BACKUP, "No permission to configure backup activity");
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "No permission to configure backup activity");
+ }
+ }
+
+ /**
+ * Only privileged callers should be changing the backup state. Deactivating backup in the
+ * system user also deactivates backup in all users. We are not guaranteed that {@code userId}
+ * is unlocked at this point yet, so handle both cases.
+ */
+ public void setBackupServiceActive(int userId, boolean makeActive) {
+ enforcePermissionsOnUser(userId);
+
+ // In Q, backup is OFF by default for non-system users. In the future, we will change that
+ // to ON unless backup was explicitly deactivated with a (permissioned) call to
+ // setBackupServiceActive.
+ // Therefore, remember this for use in the future. Basically the default in the future will
+ // be: rememberFile.exists() ? rememberFile.value() : ON
+ // Note that this has to be done right after the permission checks and before any other
+ // action since we need to remember that a permissioned call was made irrespective of
+ // whether the call changes the state or not.
+ if (userId != UserHandle.USER_SYSTEM) {
+ try {
+ File rememberFile = getRememberActivatedFileForNonSystemUser(userId);
+ createFile(rememberFile);
+ RandomAccessFileUtils.writeBoolean(rememberFile, makeActive);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to persist backup service activity", e);
+ }
+ }
+
+ if (mGlobalDisable) {
+ Slog.i(TAG, "Backup service not supported");
+ return;
+ }
+
+ synchronized (mStateLock) {
+ Slog.i(TAG, "Making backup " + (makeActive ? "" : "in") + "active");
+ if (makeActive) {
+ try {
+ activateBackupForUserLocked(userId);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to persist backup service activity");
+ }
+
+ // If the user is unlocked, we can start the backup service for it. Otherwise we
+ // will start the service when the user is unlocked as part of its unlock callback.
+ if (getUserManager().isUserUnlocked(userId)) {
+ // Clear calling identity as initialization enforces the system identity but we
+ // can be coming from shell.
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ startServiceForUser(userId);
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+ } else {
+ try {
+ //TODO(b/121198006): what if this throws an exception?
+ deactivateBackupForUserLocked(userId);
+ } catch (IOException e) {
+ Slog.e(TAG, "Unable to persist backup service inactivity");
+ }
+ //TODO(b/121198006): loop through active users that have work profile and
+ // stop them as well.
+ onStopUser(userId);
+ }
+ }
+ }
+
+ // IBackupManager binder API
+
+ /**
+ * Querying activity state of backup service.
+ *
+ * @param userId The user in which the activity state of backup service is queried.
+ * @return true if the service is active.
+ */
+ @Override
+ public boolean isBackupServiceActive(int userId) {
+ synchronized (mStateLock) {
+ return !mGlobalDisable && isBackupActivatedForUser(userId);
+ }
+ }
+
+ @Override
+ public void dataChangedForUser(int userId, String packageName) throws RemoteException {
+ if (isUserReadyForBackup(userId)) {
+ dataChanged(userId, packageName);
+ }
+ }
+
+ @Override
+ public void dataChanged(String packageName) throws RemoteException {
+ dataChangedForUser(binderGetCallingUserId(), packageName);
+ }
+
+ /**
+ * An app's backup agent calls this method to let the service know that there's new data to
+ * backup for their app {@code packageName}. Only used for apps participating in key-value
+ * backup.
+ */
+ public void dataChanged(@UserIdInt int userId, String packageName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "dataChanged()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.dataChanged(packageName);
+ }
+ }
// ---------------------------------------------
- // SETTINGS OPERATIONS
+ // TRANSPORT OPERATIONS
// ---------------------------------------------
+ @Override
+ public void initializeTransportsForUser(
+ int userId, String[] transportNames, IBackupObserver observer) throws RemoteException {
+ if (isUserReadyForBackup(userId)) {
+ initializeTransports(userId, transportNames, observer);
+ }
+ }
+
+ /** Run an initialize operation for the given transports {@code transportNames}. */
+ public void initializeTransports(
+ @UserIdInt int userId, String[] transportNames, IBackupObserver observer) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "initializeTransports()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.initializeTransports(transportNames, observer);
+ }
+ }
+
+ @Override
+ public void clearBackupDataForUser(int userId, String transportName, String packageName)
+ throws RemoteException {
+ if (isUserReadyForBackup(userId)) {
+ clearBackupData(userId, transportName, packageName);
+ }
+ }
+
+ /**
+ * Clear the given package {@code packageName}'s backup data from the transport {@code
+ * transportName}.
+ */
+ public void clearBackupData(@UserIdInt int userId, String transportName, String packageName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "clearBackupData()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.clearBackupData(transportName, packageName);
+ }
+ }
+
+ @Override
+ public void clearBackupData(String transportName, String packageName)
+ throws RemoteException {
+ clearBackupDataForUser(binderGetCallingUserId(), transportName, packageName);
+ }
+
+ @Override
+ public void agentConnectedForUser(int userId, String packageName, IBinder agent)
+ throws RemoteException {
+ if (isUserReadyForBackup(userId)) {
+ agentConnected(userId, packageName, agent);
+ }
+ }
+
+ @Override
+ public void agentConnected(String packageName, IBinder agent) throws RemoteException {
+ agentConnectedForUser(binderGetCallingUserId(), packageName, agent);
+ }
+
+ /**
+ * Callback: a requested backup agent has been instantiated. This should only be called from the
+ * {@link ActivityManager}.
+ */
+ public void agentConnected(@UserIdInt int userId, String packageName, IBinder agentBinder) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "agentConnected()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.agentConnected(packageName, agentBinder);
+ }
+ }
+
+ @Override
+ public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException {
+ if (isUserReadyForBackup(userId)) {
+ agentDisconnected(userId, packageName);
+ }
+ }
+
+ @Override
+ public void agentDisconnected(String packageName) throws RemoteException {
+ agentDisconnectedForUser(binderGetCallingUserId(), packageName);
+ }
+
+ /**
+ * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
+ * called from the {@link ActivityManager}.
+ */
+ public void agentDisconnected(@UserIdInt int userId, String packageName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.agentDisconnected(packageName);
+ }
+ }
+
+ @Override
+ public void restoreAtInstallForUser(int userId, String packageName, int token)
+ throws RemoteException {
+ if (isUserReadyForBackup(userId)) {
+ restoreAtInstall(userId, packageName, token);
+ }
+ }
+
+ @Override
+ public void restoreAtInstall(String packageName, int token) throws RemoteException {
+ restoreAtInstallForUser(binderGetCallingUserId(), packageName, token);
+ }
+
+ /**
+ * Used to run a restore pass for an application that is being installed. This should only be
+ * called from the {@link PackageManager}.
+ */
+ public void restoreAtInstall(@UserIdInt int userId, String packageName, int token) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "restoreAtInstall()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.restoreAtInstall(packageName, token);
+ }
+ }
+
+ @Override
+ public void setBackupEnabledForUser(@UserIdInt int userId, boolean isEnabled)
+ throws RemoteException {
+ if (isUserReadyForBackup(userId)) {
+ setBackupEnabled(userId, isEnabled);
+ }
+ }
+
+ @Override
+ public void setBackupEnabled(boolean isEnabled) throws RemoteException {
+ setBackupEnabledForUser(binderGetCallingUserId(), isEnabled);
+ }
+
/** Enable/disable the backup service. This is user-configurable via backup settings. */
public void setBackupEnabled(@UserIdInt int userId, boolean enable) {
UserBackupManagerService userBackupManagerService =
@@ -112,6 +682,18 @@
}
}
+ @Override
+ public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException {
+ if (isUserReadyForBackup(userId)) {
+ setAutoRestore(userId, doAutoRestore);
+ }
+ }
+
+ @Override
+ public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
+ setAutoRestoreForUser(binderGetCallingUserId(), doAutoRestore);
+ }
+
/** Enable/disable automatic restore of app data at install time. */
public void setAutoRestore(@UserIdInt int userId, boolean autoRestore) {
UserBackupManagerService userBackupManagerService =
@@ -122,6 +704,16 @@
}
}
+ @Override
+ public boolean isBackupEnabledForUser(@UserIdInt int userId) throws RemoteException {
+ return isUserReadyForBackup(userId) && isBackupEnabled(userId);
+ }
+
+ @Override
+ public boolean isBackupEnabled() throws RemoteException {
+ return isBackupEnabledForUser(binderGetCallingUserId());
+ }
+
/**
* Return {@code true} if the backup mechanism is currently enabled, else returns {@code false}.
*/
@@ -132,30 +724,45 @@
return userBackupManagerService != null && userBackupManagerService.isBackupEnabled();
}
- // ---------------------------------------------
- // BACKUP OPERATIONS
- // ---------------------------------------------
-
- /** Checks if the given package {@code packageName} is eligible for backup. */
- public boolean isAppEligibleForBackup(@UserIdInt int userId, String packageName) {
+ /** Sets the backup password used when running adb backup. */
+ @Override
+ public boolean setBackupPassword(String currentPassword, String newPassword) {
+ int userId = binderGetCallingUserId();
+ if (!isUserReadyForBackup(userId)) {
+ return false;
+ }
UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "isAppEligibleForBackup()");
+ getServiceForUserIfCallerHasPermission(
+ UserHandle.USER_SYSTEM, "setBackupPassword()");
return userBackupManagerService != null
- && userBackupManagerService.isAppEligibleForBackup(packageName);
+ && userBackupManagerService.setBackupPassword(currentPassword, newPassword);
}
- /**
- * Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
- */
- @Nullable
- public String[] filterAppsEligibleForBackup(@UserIdInt int userId, String[] packages) {
+ /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
+ @Override
+ public boolean hasBackupPassword() throws RemoteException {
+ int userId = binderGetCallingUserId();
+ if (!isUserReadyForBackup(userId)) {
+ return false;
+ }
UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "filterAppsEligibleForBackup()");
+ getServiceForUserIfCallerHasPermission(
+ UserHandle.USER_SYSTEM, "hasBackupPassword()");
- return userBackupManagerService == null
- ? null
- : userBackupManagerService.filterAppsEligibleForBackup(packages);
+ return userBackupManagerService != null && userBackupManagerService.hasBackupPassword();
+ }
+
+ @Override
+ public void backupNowForUser(@UserIdInt int userId) throws RemoteException {
+ if (isUserReadyForBackup(userId)) {
+ backupNow(userId);
+ }
+ }
+
+ @Override
+ public void backupNow() throws RemoteException {
+ backupNowForUser(binderGetCallingUserId());
}
/**
@@ -172,148 +779,12 @@
}
/**
- * Requests a backup for the inputted {@code packages} with a specified callback {@link
- * IBackupManagerMonitor} for receiving events during the operation.
- */
- public int requestBackup(
- @UserIdInt int userId,
- String[] packages,
- IBackupObserver observer,
- IBackupManagerMonitor monitor,
- int flags) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "requestBackup()");
-
- return userBackupManagerService == null
- ? BackupManager.ERROR_BACKUP_NOT_ALLOWED
- : userBackupManagerService.requestBackup(packages, observer, monitor, flags);
- }
-
- /** Cancel all running backup operations. */
- public void cancelBackups(@UserIdInt int userId) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "cancelBackups()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.cancelBackups();
- }
- }
-
- /**
- * Used by the {@link JobScheduler} to run a full backup when conditions are right. The model we
- * use is to perform one app backup per scheduled job execution, and to reschedule the job with
- * zero latency as long as conditions remain right and we still have work to do.
- *
- * @return Whether ongoing work will continue. The return value here will be passed along as the
- * return value to the callback {@link JobService#onStartJob(JobParameters)}.
- */
- public boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "beginFullBackup()");
-
- return userBackupManagerService != null
- && userBackupManagerService.beginFullBackup(scheduledJob);
- }
-
- /**
- * Used by the {@link JobScheduler} to end the current full backup task when conditions are no
- * longer met for running the full backup job.
- */
- public void endFullBackup(@UserIdInt int userId) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "endFullBackup()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.endFullBackup();
- }
- }
-
- /**
- * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
- */
- public void fullTransportBackup(@UserIdInt int userId, String[] packageNames) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "fullTransportBackup()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.fullTransportBackup(packageNames);
- }
- }
-
- // ---------------------------------------------
- // RESTORE OPERATIONS
- // ---------------------------------------------
-
- /**
- * Used to run a restore pass for an application that is being installed. This should only be
- * called from the {@link PackageManager}.
- */
- public void restoreAtInstall(@UserIdInt int userId, String packageName, int token) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "restoreAtInstall()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.restoreAtInstall(packageName, token);
- }
- }
-
- /**
- * Begin a restore for the specified package {@code packageName} using the specified transport
- * {@code transportName}.
- */
- @Nullable
- public IRestoreSession beginRestoreSession(
- @UserIdInt int userId, String packageName, String transportName) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()");
-
- return userBackupManagerService == null
- ? null
- : userBackupManagerService.beginRestoreSession(packageName, transportName);
- }
-
- /**
- * Get the restore-set token for the best-available restore set for this {@code packageName}:
- * the active set if possible, else the ancestral one. Returns zero if none available.
- */
- public long getAvailableRestoreToken(@UserIdInt int userId, String packageName) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "getAvailableRestoreToken()");
-
- return userBackupManagerService == null
- ? 0
- : userBackupManagerService.getAvailableRestoreToken(packageName);
- }
-
- // ---------------------------------------------
- // ADB BACKUP/RESTORE OPERATIONS
- // ---------------------------------------------
-
- /** Sets the backup password used when running adb backup. */
- public boolean setBackupPassword(String currentPassword, String newPassword) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(
- UserHandle.USER_SYSTEM, "setBackupPassword()");
-
- return userBackupManagerService != null
- && userBackupManagerService.setBackupPassword(currentPassword, newPassword);
- }
-
- /** Returns {@code true} if adb backup was run with a password, else returns {@code false}. */
- public boolean hasBackupPassword() {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(
- UserHandle.USER_SYSTEM, "hasBackupPassword()");
-
- return userBackupManagerService != null && userBackupManagerService.hasBackupPassword();
- }
-
- /**
* Used by 'adb backup' to run a backup pass for packages {@code packageNames} supplied via the
* command line, writing the resulting data stream to the supplied {@code fd}. This method is
* synchronous and does not return to the caller until the backup has been completed. It
* requires on-screen confirmation by the user.
*/
+ @Override
public void adbBackup(
@UserIdInt int userId,
ParcelFileDescriptor fd,
@@ -326,6 +797,9 @@
boolean doCompress,
boolean doKeyValue,
String[] packageNames) {
+ if (!isUserReadyForBackup(userId)) {
+ return;
+ }
UserBackupManagerService userBackupManagerService =
getServiceForUserIfCallerHasPermission(userId, "adbBackup()");
@@ -344,12 +818,36 @@
}
}
+ @Override
+ public void fullTransportBackupForUser(int userId, String[] packageNames)
+ throws RemoteException {
+ if (isUserReadyForBackup(userId)) {
+ fullTransportBackup(userId, packageNames);
+ }
+ }
+
+ /**
+ * Run a full backup pass for the given packages {@code packageNames}. Used by 'adb shell bmgr'.
+ */
+ public void fullTransportBackup(@UserIdInt int userId, String[] packageNames) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "fullTransportBackup()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.fullTransportBackup(packageNames);
+ }
+ }
+
/**
* Used by 'adb restore' to run a restore pass reading from the supplied {@code fd}. This method
* is synchronous and does not return to the caller until the restore has been completed. It
* requires on-screen confirmation by the user.
*/
+ @Override
public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) {
+ if (!isUserReadyForBackup(userId)) {
+ return;
+ }
UserBackupManagerService userBackupManagerService =
getServiceForUserIfCallerHasPermission(userId, "adbRestore()");
@@ -358,6 +856,21 @@
}
}
+ @Override
+ public void acknowledgeFullBackupOrRestoreForUser(
+ int userId,
+ int token,
+ boolean allow,
+ String curPassword,
+ String encryptionPassword,
+ IFullBackupRestoreObserver observer)
+ throws RemoteException {
+ if (isUserReadyForBackup(userId)) {
+ acknowledgeAdbBackupOrRestore(userId, token, allow,
+ curPassword, encryptionPassword, observer);
+ }
+ }
+
/**
* Confirm that the previously requested adb backup/restore operation can proceed. This is used
* to require a user-facing disclosure about the operation.
@@ -378,22 +891,574 @@
}
}
- // ---------------------------------------------
- // SERVICE OPERATIONS
- // ---------------------------------------------
+ @Override
+ public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
+ String encryptionPassword, IFullBackupRestoreObserver observer)
+ throws RemoteException {
+ acknowledgeFullBackupOrRestoreForUser(
+ binderGetCallingUserId(), token, allow, curPassword, encryptionPassword, observer);
+ }
- /** Prints service state for 'dumpsys backup'. */
+
+ @Override
+ public String getCurrentTransportForUser(int userId) throws RemoteException {
+ return (isUserReadyForBackup(userId)) ? getCurrentTransport(userId) : null;
+ }
+
+ @Override
+ public String getCurrentTransport() throws RemoteException {
+ return getCurrentTransportForUser(binderGetCallingUserId());
+ }
+
+ /** Return the name of the currently active transport. */
+ @Nullable
+ public String getCurrentTransport(@UserIdInt int userId) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "getCurrentTransport()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getCurrentTransport();
+ }
+
+ /**
+ * Returns the {@link ComponentName} of the host service of the selected transport or
+ * {@code null} if no transport selected or if the transport selected is not registered.
+ */
+ @Override
+ @Nullable
+ public ComponentName getCurrentTransportComponentForUser(int userId) {
+ return (isUserReadyForBackup(userId)) ? getCurrentTransportComponent(userId) : null;
+ }
+
+ /**
+ * Returns the {@link ComponentName} of the host service of the selected transport or {@code
+ * null} if no transport selected or if the transport selected is not registered.
+ */
+ @Nullable
+ public ComponentName getCurrentTransportComponent(@UserIdInt int userId) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "getCurrentTransportComponent()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getCurrentTransportComponent();
+ }
+
+ @Override
+ public String[] listAllTransportsForUser(int userId) throws RemoteException {
+ return (isUserReadyForBackup(userId)) ? listAllTransports(userId) : null;
+ }
+
+ /** Report all known, available backup transports by name. */
+ @Nullable
+ public String[] listAllTransports(@UserIdInt int userId) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "listAllTransports()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.listAllTransports();
+ }
+
+ @Override
+ public String[] listAllTransports() throws RemoteException {
+ return listAllTransportsForUser(binderGetCallingUserId());
+ }
+
+ @Override
+ public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException {
+ return (isUserReadyForBackup(userId))
+ ? listAllTransportComponents(userId) : null;
+ }
+
+ /** Report all known, available backup transports by {@link ComponentName}. */
+ @Nullable
+ public ComponentName[] listAllTransportComponents(@UserIdInt int userId) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "listAllTransportComponents()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.listAllTransportComponents();
+ }
+
+ @Override
+ public String[] getTransportWhitelist() {
+ int userId = binderGetCallingUserId();
+ if (!isUserReadyForBackup(userId)) {
+ return null;
+ }
+ // No permission check, intentionally.
+ String[] whitelistedTransports = new String[mTransportWhitelist.size()];
+ int i = 0;
+ for (ComponentName component : mTransportWhitelist) {
+ whitelistedTransports[i] = component.flattenToShortString();
+ i++;
+ }
+ return whitelistedTransports;
+ }
+
+ @Override
+ public void updateTransportAttributesForUser(
+ int userId,
+ ComponentName transportComponent,
+ String name,
+ @Nullable Intent configurationIntent,
+ String currentDestinationString,
+ @Nullable Intent dataManagementIntent,
+ CharSequence dataManagementLabel) {
+ if (isUserReadyForBackup(userId)) {
+ updateTransportAttributes(
+ userId,
+ transportComponent,
+ name,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ }
+ }
+
+ /**
+ * Update the attributes of the transport identified by {@code transportComponent}. If the
+ * specified transport has not been bound at least once (for registration), this call will be
+ * ignored. Only the host process of the transport can change its description, otherwise a
+ * {@link SecurityException} will be thrown.
+ *
+ * @param transportComponent The identity of the transport being described.
+ * @param name A {@link String} with the new name for the transport. This is NOT for
+ * identification. MUST NOT be {@code null}.
+ * @param configurationIntent An {@link Intent} that can be passed to {@link
+ * Context#startActivity} in order to launch the transport's configuration UI. It may be
+ * {@code null} if the transport does not offer any user-facing configuration UI.
+ * @param currentDestinationString A {@link String} describing the destination to which the
+ * transport is currently sending data. MUST NOT be {@code null}.
+ * @param dataManagementIntent An {@link Intent} that can be passed to {@link
+ * Context#startActivity} in order to launch the transport's data-management UI. It may be
+ * {@code null} if the transport does not offer any user-facing data management UI.
+ * @param dataManagementLabel A {@link CharSequence} to be used as the label for the transport's
+ * data management affordance. This MUST be {@code null} when dataManagementIntent is {@code
+ * null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
+ * @throws SecurityException If the UID of the calling process differs from the package UID of
+ * {@code transportComponent} or if the caller does NOT have BACKUP permission.
+ */
+ public void updateTransportAttributes(
+ @UserIdInt int userId,
+ ComponentName transportComponent,
+ String name,
+ @Nullable Intent configurationIntent,
+ String currentDestinationString,
+ @Nullable Intent dataManagementIntent,
+ CharSequence dataManagementLabel) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "updateTransportAttributes()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.updateTransportAttributes(
+ transportComponent,
+ name,
+ configurationIntent,
+ currentDestinationString,
+ dataManagementIntent,
+ dataManagementLabel);
+ }
+ }
+
+ @Override
+ public String selectBackupTransportForUser(int userId, String transport)
+ throws RemoteException {
+ return (isUserReadyForBackup(userId))
+ ? selectBackupTransport(userId, transport) : null;
+ }
+
+ @Override
+ public String selectBackupTransport(String transport) throws RemoteException {
+ return selectBackupTransportForUser(binderGetCallingUserId(), transport);
+ }
+
+ /**
+ * Selects transport {@code transportName} and returns the previously selected transport.
+ *
+ * @deprecated Use {@link #selectBackupTransportAsync(ComponentName,
+ * ISelectBackupTransportCallback)} instead.
+ */
+ @Deprecated
+ @Nullable
+ public String selectBackupTransport(@UserIdInt int userId, String transportName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "selectBackupTransport()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.selectBackupTransport(transportName);
+ }
+
+ @Override
+ public void selectBackupTransportAsyncForUser(int userId, ComponentName transport,
+ ISelectBackupTransportCallback listener) throws RemoteException {
+ if (isUserReadyForBackup(userId)) {
+ selectBackupTransportAsync(userId, transport, listener);
+ } else {
+ if (listener != null) {
+ try {
+ listener.onFailure(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
+ } catch (RemoteException ex) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ /**
+ * Selects transport {@code transportComponent} asynchronously and notifies {@code listener}
+ * with the result upon completion.
+ */
+ public void selectBackupTransportAsync(
+ @UserIdInt int userId,
+ ComponentName transportComponent,
+ ISelectBackupTransportCallback listener) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "selectBackupTransportAsync()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
+ }
+ }
+
+ @Override
+ public Intent getConfigurationIntentForUser(int userId, String transport)
+ throws RemoteException {
+ return isUserReadyForBackup(userId) ? getConfigurationIntent(userId, transport)
+ : null;
+ }
+
+ @Override
+ public Intent getConfigurationIntent(String transport)
+ throws RemoteException {
+ return getConfigurationIntentForUser(binderGetCallingUserId(), transport);
+ }
+
+ /**
+ * Supply the configuration intent for the given transport. If the name is not one of the
+ * available transports, or if the transport does not supply any configuration UI, the method
+ * returns {@code null}.
+ */
+ @Nullable
+ public Intent getConfigurationIntent(@UserIdInt int userId, String transportName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "getConfigurationIntent()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getConfigurationIntent(transportName);
+ }
+
+ @Override
+ public String getDestinationStringForUser(int userId, String transport) throws RemoteException {
+ return isUserReadyForBackup(userId) ? getDestinationString(userId, transport)
+ : null;
+ }
+
+ @Override
+ public String getDestinationString(String transport) throws RemoteException {
+ return getDestinationStringForUser(binderGetCallingUserId(), transport);
+ }
+
+ /**
+ * Supply the current destination string for the given transport. If the name is not one of the
+ * registered transports the method will return null.
+ *
+ * <p>This string is used VERBATIM as the summary text of the relevant Settings item.
+ *
+ * @param transportName The name of the registered transport.
+ * @return The current destination string or null if the transport is not registered.
+ */
+ @Nullable
+ public String getDestinationString(@UserIdInt int userId, String transportName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "getDestinationString()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getDestinationString(transportName);
+ }
+
+ @Override
+ public Intent getDataManagementIntentForUser(int userId, String transport)
+ throws RemoteException {
+ return isUserReadyForBackup(userId)
+ ? getDataManagementIntent(userId, transport) : null;
+ }
+
+ @Override
+ public Intent getDataManagementIntent(String transport)
+ throws RemoteException {
+ return getDataManagementIntentForUser(binderGetCallingUserId(), transport);
+ }
+
+ /** Supply the manage-data intent for the given transport. */
+ @Nullable
+ public Intent getDataManagementIntent(@UserIdInt int userId, String transportName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "getDataManagementIntent()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getDataManagementIntent(transportName);
+ }
+
+ @Override
+ public CharSequence getDataManagementLabelForUser(int userId, String transport)
+ throws RemoteException {
+ return isUserReadyForBackup(userId) ? getDataManagementLabel(userId, transport)
+ : null;
+ }
+
+ /**
+ * Supply the menu label for affordances that fire the manage-data intent for the given
+ * transport.
+ */
+ @Nullable
+ public CharSequence getDataManagementLabel(@UserIdInt int userId, String transportName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "getDataManagementLabel()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.getDataManagementLabel(transportName);
+ }
+
+ @Override
+ public IRestoreSession beginRestoreSessionForUser(
+ int userId, String packageName, String transportID) throws RemoteException {
+ return isUserReadyForBackup(userId)
+ ? beginRestoreSession(userId, packageName, transportID) : null;
+ }
+
+ /**
+ * Begin a restore for the specified package {@code packageName} using the specified transport
+ * {@code transportName}.
+ */
+ @Nullable
+ public IRestoreSession beginRestoreSession(
+ @UserIdInt int userId, String packageName, String transportName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "beginRestoreSession()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.beginRestoreSession(packageName, transportName);
+ }
+
+ @Override
+ public void opCompleteForUser(int userId, int token, long result) throws RemoteException {
+ if (isUserReadyForBackup(userId)) {
+ opComplete(userId, token, result);
+ }
+ }
+
+ @Override
+ public void opComplete(int token, long result) throws RemoteException {
+ opCompleteForUser(binderGetCallingUserId(), token, result);
+ }
+
+ /**
+ * Used by a currently-active backup agent to notify the service that it has completed its given
+ * outstanding asynchronous backup/restore operation.
+ */
+ public void opComplete(@UserIdInt int userId, int token, long result) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "opComplete()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.opComplete(token, result);
+ }
+ }
+
+ @Override
+ public long getAvailableRestoreTokenForUser(int userId, String packageName) {
+ return isUserReadyForBackup(userId) ? getAvailableRestoreToken(userId, packageName) : 0;
+ }
+
+ /**
+ * Get the restore-set token for the best-available restore set for this {@code packageName}:
+ * the active set if possible, else the ancestral one. Returns zero if none available.
+ */
+ public long getAvailableRestoreToken(@UserIdInt int userId, String packageName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "getAvailableRestoreToken()");
+
+ return userBackupManagerService == null
+ ? 0
+ : userBackupManagerService.getAvailableRestoreToken(packageName);
+ }
+
+ @Override
+ public boolean isAppEligibleForBackupForUser(int userId, String packageName) {
+ return isUserReadyForBackup(userId) && isAppEligibleForBackup(userId,
+ packageName);
+ }
+
+ /** Checks if the given package {@code packageName} is eligible for backup. */
+ public boolean isAppEligibleForBackup(@UserIdInt int userId, String packageName) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "isAppEligibleForBackup()");
+
+ return userBackupManagerService != null
+ && userBackupManagerService.isAppEligibleForBackup(packageName);
+ }
+
+ @Override
+ public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) {
+ return isUserReadyForBackup(userId) ? filterAppsEligibleForBackup(userId, packages) : null;
+ }
+
+ /**
+ * Returns from the inputted packages {@code packages}, the ones that are eligible for backup.
+ */
+ @Nullable
+ public String[] filterAppsEligibleForBackup(@UserIdInt int userId, String[] packages) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "filterAppsEligibleForBackup()");
+
+ return userBackupManagerService == null
+ ? null
+ : userBackupManagerService.filterAppsEligibleForBackup(packages);
+ }
+
+ @Override
+ public int requestBackupForUser(@UserIdInt int userId, String[] packages, IBackupObserver
+ observer, IBackupManagerMonitor monitor, int flags) throws RemoteException {
+ if (!isUserReadyForBackup(userId)) {
+ return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
+ }
+ return requestBackup(userId, packages, observer, monitor, flags);
+ }
+
+ @Override
+ public int requestBackup(String[] packages, IBackupObserver observer,
+ IBackupManagerMonitor monitor, int flags) throws RemoteException {
+ return requestBackupForUser(binderGetCallingUserId(), packages,
+ observer, monitor, flags);
+ }
+
+ /**
+ * Requests a backup for the inputted {@code packages} with a specified callback {@link
+ * IBackupManagerMonitor} for receiving events during the operation.
+ */
+ public int requestBackup(
+ @UserIdInt int userId,
+ String[] packages,
+ IBackupObserver observer,
+ IBackupManagerMonitor monitor,
+ int flags) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "requestBackup()");
+
+ return userBackupManagerService == null
+ ? BackupManager.ERROR_BACKUP_NOT_ALLOWED
+ : userBackupManagerService.requestBackup(packages, observer, monitor, flags);
+ }
+
+ @Override
+ public void cancelBackupsForUser(@UserIdInt int userId) throws RemoteException {
+ if (isUserReadyForBackup(userId)) {
+ cancelBackups(userId);
+ }
+ }
+
+ @Override
+ public void cancelBackups() throws RemoteException {
+ cancelBackupsForUser(binderGetCallingUserId());
+ }
+
+ /** Cancel all running backup operations. */
+ public void cancelBackups(@UserIdInt int userId) {
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "cancelBackups()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.cancelBackups();
+ }
+ }
+
+ /**
+ * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the
+ * serial number of its ancestral work profile or null if there is no {@link
+ * UserBackupManagerService} associated with that user.
+ *
+ * <p> The ancestral work profile is set by {@link #setAncestralSerialNumber(long)}
+ * and it corresponds to the profile that was used to restore to the callers profile.
+ */
+ @Override
+ @Nullable
+ public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
+ if (mGlobalDisable) {
+ return null;
+ }
+ int callingUserId = Binder.getCallingUserHandle().getIdentifier();
+ long oldId = Binder.clearCallingIdentity();
+ final int[] userIds;
+ try {
+ userIds =
+ mContext
+ .getSystemService(UserManager.class)
+ .getProfileIds(callingUserId, false);
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+
+ for (int userId : userIds) {
+ UserBackupManagerService userBackupManagerService = mUserServices.get(userId);
+ if (userBackupManagerService != null) {
+ if (userBackupManagerService.getAncestralSerialNumber() == ancestralSerialNumber) {
+ return UserHandle.of(userId);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Sets the ancestral work profile for the calling user.
+ *
+ * <p> The ancestral work profile corresponds to the profile that was used to restore to the
+ * callers profile.
+ */
+ @Override
+ public void setAncestralSerialNumber(long ancestralSerialNumber) {
+ if (mGlobalDisable) {
+ return;
+ }
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(
+ Binder.getCallingUserHandle().getIdentifier(),
+ "setAncestralSerialNumber()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.setAncestralSerialNumber(ancestralSerialNumber);
+ }
+ }
+
+ @Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) {
return;
}
+ int userId = binderGetCallingUserId();
+ if (!isUserReadyForBackup(userId)) {
+ pw.println("Inactive");
+ return;
+ }
if (args != null) {
for (String arg : args) {
if ("users".equals(arg.toLowerCase())) {
pw.print(DUMP_RUNNING_USERS_MESSAGE);
- for (int i = 0; i < mServiceUsers.size(); i++) {
- pw.print(" " + mServiceUsers.keyAt(i));
+ for (int i = 0; i < mUserServices.size(); i++) {
+ pw.print(" " + mUserServices.keyAt(i));
}
pw.println();
return;
@@ -409,31 +1474,103 @@
}
}
+ /**
+ * Used by the {@link JobScheduler} to run a full backup when conditions are right. The model we
+ * use is to perform one app backup per scheduled job execution, and to reschedule the job with
+ * zero latency as long as conditions remain right and we still have work to do.
+ *
+ * @return Whether ongoing work will continue. The return value here will be passed along as the
+ * return value to the callback {@link JobService#onStartJob(JobParameters)}.
+ */
+ public boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
+ if (!isUserReadyForBackup(userId)) {
+ return false;
+ }
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "beginFullBackup()");
+
+ return userBackupManagerService != null
+ && userBackupManagerService.beginFullBackup(scheduledJob);
+ }
+
+ /**
+ * Used by the {@link JobScheduler} to end the current full backup task when conditions are no
+ * longer met for running the full backup job.
+ */
+ public void endFullBackup(@UserIdInt int userId) {
+ if (!isUserReadyForBackup(userId)) {
+ return;
+ }
+ UserBackupManagerService userBackupManagerService =
+ getServiceForUserIfCallerHasPermission(userId, "endFullBackup()");
+
+ if (userBackupManagerService != null) {
+ userBackupManagerService.endFullBackup();
+ }
+ }
+
+ /**
+ * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
+ * If the user is not registered with the service (either the user is locked or not eligible for
+ * the backup service) then return {@code null}.
+ *
+ * @param userId The id of the user to retrieve its instance of {@link
+ * UserBackupManagerService}.
+ * @param caller A {@link String} identifying the caller for logging purposes.
+ * @throws SecurityException if {@code userId} is different from the calling user id and the
+ * caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Nullable
+ @VisibleForTesting
+ UserBackupManagerService getServiceForUserIfCallerHasPermission(
+ @UserIdInt int userId, String caller) {
+ enforceCallingPermissionOnUserId(userId, caller);
+ UserBackupManagerService userBackupManagerService = mUserServices.get(userId);
+ if (userBackupManagerService == null) {
+ Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
+ }
+ return userBackupManagerService;
+ }
+
+ /**
+ * If {@code userId} is different from the calling user id, then the caller must hold the
+ * android.permission.INTERACT_ACROSS_USERS_FULL permission.
+ *
+ * @param userId User id on which the backup operation is being requested.
+ * @param message A message to include in the exception if it is thrown.
+ */
+ void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) {
+ if (Binder.getCallingUserHandle().getIdentifier() != userId) {
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
+ }
+ }
+
/** Implementation to receive lifecycle event callbacks for system services. */
public static class Lifecycle extends SystemService {
public Lifecycle(Context context) {
- this(context, new Trampoline(context));
+ this(context, new BackupManagerService(context));
}
@VisibleForTesting
- Lifecycle(Context context, Trampoline trampoline) {
+ Lifecycle(Context context, BackupManagerService backupManagerService) {
super(context);
- Trampoline.sInstance = trampoline;
+ sInstance = backupManagerService;
}
@Override
public void onStart() {
- publishService(Context.BACKUP_SERVICE, Trampoline.sInstance);
+ publishService(Context.BACKUP_SERVICE, BackupManagerService.sInstance);
}
@Override
public void onUnlockUser(int userId) {
- Trampoline.sInstance.onUnlockUser(userId);
+ sInstance.onUnlockUser(userId);
}
@Override
public void onStopUser(int userId) {
- Trampoline.sInstance.onStopUser(userId);
+ sInstance.onStopUser(userId);
}
@VisibleForTesting
diff --git a/services/backup/java/com/android/server/backup/FullBackupJob.java b/services/backup/java/com/android/server/backup/FullBackupJob.java
index 19a8543..0bb25e3 100644
--- a/services/backup/java/com/android/server/backup/FullBackupJob.java
+++ b/services/backup/java/com/android/server/backup/FullBackupJob.java
@@ -91,7 +91,7 @@
mParamsForUser.put(userId, params);
}
- Trampoline service = Trampoline.getInstance();
+ BackupManagerService service = BackupManagerService.getInstance();
return service.beginFullBackup(userId, this);
}
@@ -105,7 +105,7 @@
}
}
- Trampoline service = Trampoline.getInstance();
+ BackupManagerService service = BackupManagerService.getInstance();
service.endFullBackup(userId);
return false;
diff --git a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
index 7b5dbd7..058dcae 100644
--- a/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
+++ b/services/backup/java/com/android/server/backup/KeyValueBackupJob.java
@@ -144,7 +144,7 @@
}
// Time to run a key/value backup!
- Trampoline service = Trampoline.getInstance();
+ BackupManagerService service = BackupManagerService.getInstance();
try {
service.backupNowForUser(userId);
} catch (RemoteException e) {}
diff --git a/services/backup/java/com/android/server/backup/Trampoline.java b/services/backup/java/com/android/server/backup/Trampoline.java
deleted file mode 100644
index b522c87..0000000
--- a/services/backup/java/com/android/server/backup/Trampoline.java
+++ /dev/null
@@ -1,1276 +0,0 @@
-/*
- * Copyright (C) 2014 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.backup;
-
-import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.server.backup.BackupManagerService.TAG;
-
-import static java.util.Collections.emptySet;
-
-import android.Manifest;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.app.ActivityManager;
-import android.app.admin.DevicePolicyManager;
-import android.app.backup.BackupManager;
-import android.app.backup.IBackupManager;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-import android.app.backup.IFullBackupRestoreObserver;
-import android.app.backup.IRestoreSession;
-import android.app.backup.ISelectBackupTransportCallback;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Binder;
-import android.os.FileUtils;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
-import com.android.server.SystemConfig;
-import com.android.server.backup.utils.RandomAccessFileUtils;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.Set;
-
-/**
- * A proxy to the {@link BackupManagerService} implementation.
- *
- * <p>This is an external interface to the {@link BackupManagerService} which is being accessed via
- * published binder {@link BackupManagerService.Lifecycle}. This lets us turn down the heavy
- * implementation object on the fly without disturbing binders that have been cached somewhere in
- * the system.
- *
- * <p>Trampoline determines whether the backup service is available. It can be disabled in the
- * following two ways:
- *
- * <ul>
- * <li>Temporary - create the file {@link #BACKUP_SUPPRESS_FILENAME}, or
- * <li>Permanent - set the system property {@link #BACKUP_DISABLE_PROPERTY} to true.
- * </ul>
- *
- * Temporary disabling is controlled by {@link #setBackupServiceActive(int, boolean)} through
- * privileged callers (currently {@link DevicePolicyManager}). This is called on {@link
- * UserHandle#USER_SYSTEM} and disables backup for all users.
- */
-public class Trampoline extends IBackupManager.Stub {
- /**
- * Name of file that disables the backup service. If this file exists, then backup is disabled
- * for all users.
- */
- private static final String BACKUP_SUPPRESS_FILENAME = "backup-suppress";
-
- /**
- * Name of file for non-system users that enables the backup service for the user. Backup is
- * disabled by default in non-system users.
- */
- private static final String BACKUP_ACTIVATED_FILENAME = "backup-activated";
-
- /**
- * Name of file for non-system users that remembers whether backup was explicitly activated or
- * deactivated with a call to setBackupServiceActive.
- */
- private static final String REMEMBER_ACTIVATED_FILENAME = "backup-remember-activated";
-
- // Product-level suppression of backup/restore.
- private static final String BACKUP_DISABLE_PROPERTY = "ro.backup.disable";
-
- private static final String BACKUP_THREAD = "backup";
-
- static Trampoline sInstance;
-
- static Trampoline getInstance() {
- return checkNotNull(sInstance);
- }
-
- private final Context mContext;
- private final UserManager mUserManager;
-
- private final boolean mGlobalDisable;
- // Lock to write backup suppress files.
- // TODD(b/121198006): remove this object and synchronized all methods on "this".
- private final Object mStateLock = new Object();
-
- // TODO: This is not marked as final because of test code. Since we'll merge BMS and Trampoline,
- // it doesn't make sense to refactor for final. It's never null.
- @VisibleForTesting
- protected volatile BackupManagerService mService;
- private final Handler mHandler;
- private final Set<ComponentName> mTransportWhitelist;
-
- /** Keeps track of all unlocked users registered with this service. Indexed by user id. */
- private final SparseArray<UserBackupManagerService> mUserServices;
-
- private final BroadcastReceiver mUserRemovedReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
- int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
- if (userId > 0) { // for only non system users
- mHandler.post(() -> onRemovedNonSystemUser(userId));
- }
- }
- }
- };
-
- public Trampoline(Context context) {
- this(context, new SparseArray<>());
- }
-
- @VisibleForTesting
- Trampoline(Context context, SparseArray<UserBackupManagerService> userServices) {
- mContext = context;
- mGlobalDisable = isBackupDisabled();
- HandlerThread handlerThread =
- new HandlerThread(BACKUP_THREAD, Process.THREAD_PRIORITY_BACKGROUND);
- handlerThread.start();
- mHandler = new Handler(handlerThread.getLooper());
- mUserManager = UserManager.get(context);
- mUserServices = userServices;
- mService = new BackupManagerService(mContext, this, mUserServices);
- Set<ComponentName> transportWhitelist =
- SystemConfig.getInstance().getBackupTransportWhitelist();
- mTransportWhitelist = (transportWhitelist == null) ? emptySet() : transportWhitelist;
- mContext.registerReceiver(
- mUserRemovedReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
- }
-
- // TODO: Remove this when we implement DI by injecting in the construtor.
- @VisibleForTesting
- Handler getBackupHandler() {
- return mHandler;
- }
-
- protected boolean isBackupDisabled() {
- return SystemProperties.getBoolean(BACKUP_DISABLE_PROPERTY, false);
- }
-
- protected int binderGetCallingUserId() {
- return Binder.getCallingUserHandle().getIdentifier();
- }
-
- protected int binderGetCallingUid() {
- return Binder.getCallingUid();
- }
-
- /** Stored in the system user's directory. */
- protected File getSuppressFileForSystemUser() {
- return new File(UserBackupManagerFiles.getBaseStateDir(UserHandle.USER_SYSTEM),
- BACKUP_SUPPRESS_FILENAME);
- }
-
- /** Stored in the system user's directory and the file is indexed by the user it refers to. */
- protected File getRememberActivatedFileForNonSystemUser(int userId) {
- return UserBackupManagerFiles.getStateFileInSystemDir(REMEMBER_ACTIVATED_FILENAME, userId);
- }
-
- /** Stored in the system user's directory and the file is indexed by the user it refers to. */
- protected File getActivatedFileForNonSystemUser(int userId) {
- return UserBackupManagerFiles.getStateFileInSystemDir(BACKUP_ACTIVATED_FILENAME, userId);
- }
-
- /**
- * Remove backup state for non system {@code userId} when the user is removed from the device.
- * For non system users, backup state is stored in both the user's own dir and the system dir.
- * When the user is removed, the user's own dir gets removed by the OS. This method ensures that
- * the part of the user backup state which is in the system dir also gets removed.
- */
- private void onRemovedNonSystemUser(int userId) {
- Slog.i(TAG, "Removing state for non system user " + userId);
- File dir = UserBackupManagerFiles.getStateDirInSystemDir(userId);
- if (!FileUtils.deleteContentsAndDir(dir)) {
- Slog.w(TAG, "Failed to delete state dir for removed user: " + userId);
- }
- }
-
- // TODO (b/124359804) move to util method in FileUtils
- private void createFile(File file) throws IOException {
- if (file.exists()) {
- return;
- }
-
- file.getParentFile().mkdirs();
- if (!file.createNewFile()) {
- Slog.w(TAG, "Failed to create file " + file.getPath());
- }
- }
-
- // TODO (b/124359804) move to util method in FileUtils
- private void deleteFile(File file) {
- if (!file.exists()) {
- return;
- }
-
- if (!file.delete()) {
- Slog.w(TAG, "Failed to delete file " + file.getPath());
- }
- }
-
- /**
- * Deactivates the backup service for user {@code userId}. If this is the system user, it
- * creates a suppress file which disables backup for all users. If this is a non-system user, it
- * only deactivates backup for that user by deleting its activate file.
- */
- @GuardedBy("mStateLock")
- private void deactivateBackupForUserLocked(int userId) throws IOException {
- if (userId == UserHandle.USER_SYSTEM) {
- createFile(getSuppressFileForSystemUser());
- } else {
- deleteFile(getActivatedFileForNonSystemUser(userId));
- }
- }
-
- /**
- * Enables the backup service for user {@code userId}. If this is the system user, it deletes
- * the suppress file. If this is a non-system user, it creates the user's activate file. Note,
- * deleting the suppress file does not automatically enable backup for non-system users, they
- * need their own activate file in order to participate in the service.
- */
- @GuardedBy("mStateLock")
- private void activateBackupForUserLocked(int userId) throws IOException {
- if (userId == UserHandle.USER_SYSTEM) {
- deleteFile(getSuppressFileForSystemUser());
- } else {
- createFile(getActivatedFileForNonSystemUser(userId));
- }
- }
-
- // This method should not perform any I/O (e.g. do not call isBackupActivatedForUser),
- // it's used in multiple places where I/O waits would cause system lock-ups.
- private boolean isUserReadyForBackup(int userId) {
- return mUserServices.get(UserHandle.USER_SYSTEM) != null
- && mUserServices.get(userId) != null;
- }
-
- /**
- * Backup is activated for the system user if the suppress file does not exist. Backup is
- * activated for non-system users if the suppress file does not exist AND the user's activated
- * file exists.
- */
- private boolean isBackupActivatedForUser(int userId) {
- if (getSuppressFileForSystemUser().exists()) {
- return false;
- }
-
- return userId == UserHandle.USER_SYSTEM
- || getActivatedFileForNonSystemUser(userId).exists();
- }
-
- protected Context getContext() {
- return mContext;
- }
-
- protected UserManager getUserManager() {
- return mUserManager;
- }
-
- protected void postToHandler(Runnable runnable) {
- mHandler.post(runnable);
- }
-
- /**
- * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is unlocked.
- * Starts the backup service for this user if backup is active for this user. Offloads work onto
- * the handler thread {@link #mHandlerThread} to keep unlock time low since backup is not
- * essential for device functioning.
- */
- void onUnlockUser(int userId) {
- postToHandler(() -> startServiceForUser(userId));
- }
-
- /**
- * Starts the backup service for user {@code userId} by creating a new instance of {@link
- * UserBackupManagerService} and registering it with this service.
- */
- @VisibleForTesting
- void startServiceForUser(int userId) {
- // We know that the user is unlocked here because it is called from setBackupServiceActive
- // and unlockUser which have these guarantees. So we can check if the file exists.
- if (mGlobalDisable) {
- Slog.i(TAG, "Backup service not supported");
- return;
- }
- if (!isBackupActivatedForUser(userId)) {
- Slog.i(TAG, "Backup not activated for user " + userId);
- return;
- }
- if (mUserServices.get(userId) != null) {
- Slog.i(TAG, "userId " + userId + " already started, so not starting again");
- return;
- }
- Slog.i(TAG, "Starting service for user: " + userId);
- UserBackupManagerService userBackupManagerService =
- UserBackupManagerService.createAndInitializeService(
- userId, mContext, this, mTransportWhitelist);
- startServiceForUser(userId, userBackupManagerService);
- }
-
- /**
- * Starts the backup service for user {@code userId} by registering its instance of {@link
- * UserBackupManagerService} with this service and setting enabled state.
- */
- void startServiceForUser(int userId, UserBackupManagerService userBackupManagerService) {
- mUserServices.put(userId, userBackupManagerService);
-
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "backup enable");
- userBackupManagerService.initializeBackupEnableState();
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- }
-
- /** Stops the backup service for user {@code userId} when the user is stopped. */
- @VisibleForTesting
- protected void stopServiceForUser(int userId) {
- UserBackupManagerService userBackupManagerService = mUserServices.removeReturnOld(userId);
-
- if (userBackupManagerService != null) {
- userBackupManagerService.tearDownService();
-
- KeyValueBackupJob.cancel(userId, mContext);
- FullBackupJob.cancel(userId, mContext);
- }
- }
-
- /**
- * Returns a list of users currently unlocked that have a {@link UserBackupManagerService}
- * registered.
- *
- * Warning: Do NOT modify returned object as it's used inside.
- *
- * TODO: Return a copy or only expose read-only information through other means.
- */
- @VisibleForTesting
- SparseArray<UserBackupManagerService> getUserServices() {
- return mUserServices;
- }
-
- /**
- * Called from {@link BackupManagerService.Lifecycle} when a user {@code userId} is stopped.
- * Offloads work onto the handler thread {@link #mHandlerThread} to keep stopping time low.
- */
- void onStopUser(int userId) {
- postToHandler(
- () -> {
- if (!mGlobalDisable) {
- Slog.i(TAG, "Stopping service for user: " + userId);
- stopServiceForUser(userId);
- }
- });
- }
-
- /** Returns {@link UserBackupManagerService} for user {@code userId}. */
- @Nullable
- public UserBackupManagerService getUserService(int userId) {
- return mUserServices.get(userId);
- }
-
- /**
- * The system user and managed profiles can only be acted on by callers in the system or root
- * processes. Other users can be acted on by callers who have both android.permission.BACKUP and
- * android.permission.INTERACT_ACROSS_USERS_FULL permissions.
- */
- private void enforcePermissionsOnUser(int userId) throws SecurityException {
- boolean isRestrictedUser =
- userId == UserHandle.USER_SYSTEM
- || getUserManager().getUserInfo(userId).isManagedProfile();
-
- if (isRestrictedUser) {
- int caller = binderGetCallingUid();
- if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID) {
- throw new SecurityException("No permission to configure backup activity");
- }
- } else {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.BACKUP, "No permission to configure backup activity");
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.INTERACT_ACROSS_USERS_FULL,
- "No permission to configure backup activity");
- }
- }
-
- /**
- * Only privileged callers should be changing the backup state. Deactivating backup in the
- * system user also deactivates backup in all users. We are not guaranteed that {@code userId}
- * is unlocked at this point yet, so handle both cases.
- */
- public void setBackupServiceActive(int userId, boolean makeActive) {
- enforcePermissionsOnUser(userId);
-
- // In Q, backup is OFF by default for non-system users. In the future, we will change that
- // to ON unless backup was explicitly deactivated with a (permissioned) call to
- // setBackupServiceActive.
- // Therefore, remember this for use in the future. Basically the default in the future will
- // be: rememberFile.exists() ? rememberFile.value() : ON
- // Note that this has to be done right after the permission checks and before any other
- // action since we need to remember that a permissioned call was made irrespective of
- // whether the call changes the state or not.
- if (userId != UserHandle.USER_SYSTEM) {
- try {
- File rememberFile = getRememberActivatedFileForNonSystemUser(userId);
- createFile(rememberFile);
- RandomAccessFileUtils.writeBoolean(rememberFile, makeActive);
- } catch (IOException e) {
- Slog.e(TAG, "Unable to persist backup service activity", e);
- }
- }
-
- if (mGlobalDisable) {
- Slog.i(TAG, "Backup service not supported");
- return;
- }
-
- synchronized (mStateLock) {
- Slog.i(TAG, "Making backup " + (makeActive ? "" : "in") + "active");
- if (makeActive) {
- try {
- activateBackupForUserLocked(userId);
- } catch (IOException e) {
- Slog.e(TAG, "Unable to persist backup service activity");
- }
-
- // If the user is unlocked, we can start the backup service for it. Otherwise we
- // will start the service when the user is unlocked as part of its unlock callback.
- if (getUserManager().isUserUnlocked(userId)) {
- // Clear calling identity as initialization enforces the system identity but we
- // can be coming from shell.
- long oldId = Binder.clearCallingIdentity();
- try {
- startServiceForUser(userId);
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
- }
- } else {
- try {
- //TODO(b/121198006): what if this throws an exception?
- deactivateBackupForUserLocked(userId);
- } catch (IOException e) {
- Slog.e(TAG, "Unable to persist backup service inactivity");
- }
- //TODO(b/121198006): loop through active users that have work profile and
- // stop them as well.
- onStopUser(userId);
- }
- }
- }
-
- // IBackupManager binder API
-
- /**
- * Querying activity state of backup service.
- *
- * @param userId The user in which the activity state of backup service is queried.
- * @return true if the service is active.
- */
- @Override
- public boolean isBackupServiceActive(int userId) {
- synchronized (mStateLock) {
- return !mGlobalDisable && isBackupActivatedForUser(userId);
- }
- }
-
- @Override
- public void dataChangedForUser(int userId, String packageName) throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- dataChanged(userId, packageName);
- }
- }
-
- @Override
- public void dataChanged(String packageName) throws RemoteException {
- dataChangedForUser(binderGetCallingUserId(), packageName);
- }
-
- /**
- * An app's backup agent calls this method to let the service know that there's new data to
- * backup for their app {@code packageName}. Only used for apps participating in key-value
- * backup.
- */
- public void dataChanged(@UserIdInt int userId, String packageName) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "dataChanged()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.dataChanged(packageName);
- }
- }
-
- // ---------------------------------------------
- // TRANSPORT OPERATIONS
- // ---------------------------------------------
-
- @Override
- public void initializeTransportsForUser(
- int userId, String[] transportNames, IBackupObserver observer) throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- initializeTransports(userId, transportNames, observer);
- }
- }
-
- /** Run an initialize operation for the given transports {@code transportNames}. */
- public void initializeTransports(
- @UserIdInt int userId, String[] transportNames, IBackupObserver observer) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "initializeTransports()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.initializeTransports(transportNames, observer);
- }
- }
-
- @Override
- public void clearBackupDataForUser(int userId, String transportName, String packageName)
- throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- clearBackupData(userId, transportName, packageName);
- }
- }
-
- /**
- * Clear the given package {@code packageName}'s backup data from the transport {@code
- * transportName}.
- */
- public void clearBackupData(@UserIdInt int userId, String transportName, String packageName) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "clearBackupData()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.clearBackupData(transportName, packageName);
- }
- }
-
- @Override
- public void clearBackupData(String transportName, String packageName)
- throws RemoteException {
- clearBackupDataForUser(binderGetCallingUserId(), transportName, packageName);
- }
-
- @Override
- public void agentConnectedForUser(int userId, String packageName, IBinder agent)
- throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- agentConnected(userId, packageName, agent);
- }
- }
-
- @Override
- public void agentConnected(String packageName, IBinder agent) throws RemoteException {
- agentConnectedForUser(binderGetCallingUserId(), packageName, agent);
- }
-
- /**
- * Callback: a requested backup agent has been instantiated. This should only be called from the
- * {@link ActivityManager}.
- */
- public void agentConnected(@UserIdInt int userId, String packageName, IBinder agentBinder) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "agentConnected()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.agentConnected(packageName, agentBinder);
- }
- }
-
- @Override
- public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- agentDisconnected(userId, packageName);
- }
- }
-
- @Override
- public void agentDisconnected(String packageName) throws RemoteException {
- agentDisconnectedForUser(binderGetCallingUserId(), packageName);
- }
-
- /**
- * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
- * called from the {@link ActivityManager}.
- */
- public void agentDisconnected(@UserIdInt int userId, String packageName) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.agentDisconnected(packageName);
- }
- }
-
- @Override
- public void restoreAtInstallForUser(int userId, String packageName, int token)
- throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- mService.restoreAtInstall(userId, packageName, token);
- }
- }
-
- @Override
- public void restoreAtInstall(String packageName, int token) throws RemoteException {
- restoreAtInstallForUser(binderGetCallingUserId(), packageName, token);
- }
-
- @Override
- public void setBackupEnabledForUser(@UserIdInt int userId, boolean isEnabled)
- throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- mService.setBackupEnabled(userId, isEnabled);
- }
- }
-
- @Override
- public void setBackupEnabled(boolean isEnabled) throws RemoteException {
- setBackupEnabledForUser(binderGetCallingUserId(), isEnabled);
- }
-
- @Override
- public void setAutoRestoreForUser(int userId, boolean doAutoRestore) throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- mService.setAutoRestore(userId, doAutoRestore);
- }
- }
-
- @Override
- public void setAutoRestore(boolean doAutoRestore) throws RemoteException {
- setAutoRestoreForUser(binderGetCallingUserId(), doAutoRestore);
- }
-
- @Override
- public boolean isBackupEnabledForUser(@UserIdInt int userId) throws RemoteException {
- return isUserReadyForBackup(userId) && mService.isBackupEnabled(userId);
- }
-
- @Override
- public boolean isBackupEnabled() throws RemoteException {
- return isBackupEnabledForUser(binderGetCallingUserId());
- }
-
- @Override
- public boolean setBackupPassword(String currentPw, String newPw) throws RemoteException {
- int userId = binderGetCallingUserId();
- return (isUserReadyForBackup(userId)) && mService.setBackupPassword(currentPw, newPw);
- }
-
- @Override
- public boolean hasBackupPassword() throws RemoteException {
- int userId = binderGetCallingUserId();
- return (isUserReadyForBackup(userId)) && mService.hasBackupPassword();
- }
-
- @Override
- public void backupNowForUser(@UserIdInt int userId) throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- mService.backupNow(userId);
- }
- }
-
- @Override
- public void backupNow() throws RemoteException {
- backupNowForUser(binderGetCallingUserId());
- }
-
- public void adbBackup(@UserIdInt int userId, ParcelFileDescriptor fd,
- boolean includeApks, boolean includeObbs, boolean includeShared, boolean doWidgets,
- boolean allApps, boolean allIncludesSystem, boolean doCompress, boolean doKeyValue,
- String[] packageNames) throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- mService.adbBackup(userId, fd, includeApks, includeObbs, includeShared, doWidgets,
- allApps, allIncludesSystem, doCompress, doKeyValue, packageNames);
- }
- }
-
- @Override
- public void fullTransportBackupForUser(int userId, String[] packageNames)
- throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- mService.fullTransportBackup(userId, packageNames);
- }
- }
-
- @Override
- public void adbRestore(@UserIdInt int userId, ParcelFileDescriptor fd) throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- mService.adbRestore(userId, fd);
- }
- }
-
- @Override
- public void acknowledgeFullBackupOrRestoreForUser(
- int userId,
- int token,
- boolean allow,
- String curPassword,
- String encryptionPassword,
- IFullBackupRestoreObserver observer)
- throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- mService.acknowledgeAdbBackupOrRestore(userId, token, allow,
- curPassword, encryptionPassword, observer);
- }
- }
-
- @Override
- public void acknowledgeFullBackupOrRestore(int token, boolean allow, String curPassword,
- String encryptionPassword, IFullBackupRestoreObserver observer)
- throws RemoteException {
- acknowledgeFullBackupOrRestoreForUser(
- binderGetCallingUserId(), token, allow, curPassword, encryptionPassword, observer);
- }
-
-
- @Override
- public String getCurrentTransportForUser(int userId) throws RemoteException {
- return (isUserReadyForBackup(userId)) ? getCurrentTransport(userId) : null;
- }
-
- @Override
- public String getCurrentTransport() throws RemoteException {
- return getCurrentTransportForUser(binderGetCallingUserId());
- }
-
- /** Return the name of the currently active transport. */
- @Nullable
- public String getCurrentTransport(@UserIdInt int userId) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "getCurrentTransport()");
-
- return userBackupManagerService == null
- ? null
- : userBackupManagerService.getCurrentTransport();
- }
-
- /**
- * Returns the {@link ComponentName} of the host service of the selected transport or
- * {@code null} if no transport selected or if the transport selected is not registered.
- */
- @Override
- @Nullable
- public ComponentName getCurrentTransportComponentForUser(int userId) {
- return (isUserReadyForBackup(userId)) ? getCurrentTransportComponent(userId) : null;
- }
-
- /**
- * Returns the {@link ComponentName} of the host service of the selected transport or {@code
- * null} if no transport selected or if the transport selected is not registered.
- */
- @Nullable
- public ComponentName getCurrentTransportComponent(@UserIdInt int userId) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "getCurrentTransportComponent()");
-
- return userBackupManagerService == null
- ? null
- : userBackupManagerService.getCurrentTransportComponent();
- }
-
- @Override
- public String[] listAllTransportsForUser(int userId) throws RemoteException {
- return (isUserReadyForBackup(userId)) ? listAllTransports(userId) : null;
- }
-
- /** Report all known, available backup transports by name. */
- @Nullable
- public String[] listAllTransports(@UserIdInt int userId) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "listAllTransports()");
-
- return userBackupManagerService == null
- ? null
- : userBackupManagerService.listAllTransports();
- }
-
- @Override
- public String[] listAllTransports() throws RemoteException {
- return listAllTransportsForUser(binderGetCallingUserId());
- }
-
- @Override
- public ComponentName[] listAllTransportComponentsForUser(int userId) throws RemoteException {
- return (isUserReadyForBackup(userId))
- ? listAllTransportComponents(userId) : null;
- }
-
- /** Report all known, available backup transports by {@link ComponentName}. */
- @Nullable
- public ComponentName[] listAllTransportComponents(@UserIdInt int userId) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "listAllTransportComponents()");
-
- return userBackupManagerService == null
- ? null
- : userBackupManagerService.listAllTransportComponents();
- }
-
- @Override
- public String[] getTransportWhitelist() {
- int userId = binderGetCallingUserId();
- if (!isUserReadyForBackup(userId)) {
- return null;
- }
- // No permission check, intentionally.
- String[] whitelistedTransports = new String[mTransportWhitelist.size()];
- int i = 0;
- for (ComponentName component : mTransportWhitelist) {
- whitelistedTransports[i] = component.flattenToShortString();
- i++;
- }
- return whitelistedTransports;
- }
-
- @Override
- public void updateTransportAttributesForUser(
- int userId,
- ComponentName transportComponent,
- String name,
- @Nullable Intent configurationIntent,
- String currentDestinationString,
- @Nullable Intent dataManagementIntent,
- CharSequence dataManagementLabel) {
- if (isUserReadyForBackup(userId)) {
- updateTransportAttributes(
- userId,
- transportComponent,
- name,
- configurationIntent,
- currentDestinationString,
- dataManagementIntent,
- dataManagementLabel);
- }
- }
-
- /**
- * Update the attributes of the transport identified by {@code transportComponent}. If the
- * specified transport has not been bound at least once (for registration), this call will be
- * ignored. Only the host process of the transport can change its description, otherwise a
- * {@link SecurityException} will be thrown.
- *
- * @param transportComponent The identity of the transport being described.
- * @param name A {@link String} with the new name for the transport. This is NOT for
- * identification. MUST NOT be {@code null}.
- * @param configurationIntent An {@link Intent} that can be passed to {@link
- * Context#startActivity} in order to launch the transport's configuration UI. It may be
- * {@code null} if the transport does not offer any user-facing configuration UI.
- * @param currentDestinationString A {@link String} describing the destination to which the
- * transport is currently sending data. MUST NOT be {@code null}.
- * @param dataManagementIntent An {@link Intent} that can be passed to {@link
- * Context#startActivity} in order to launch the transport's data-management UI. It may be
- * {@code null} if the transport does not offer any user-facing data management UI.
- * @param dataManagementLabel A {@link CharSequence} to be used as the label for the transport's
- * data management affordance. This MUST be {@code null} when dataManagementIntent is {@code
- * null} and MUST NOT be {@code null} when dataManagementIntent is not {@code null}.
- * @throws SecurityException If the UID of the calling process differs from the package UID of
- * {@code transportComponent} or if the caller does NOT have BACKUP permission.
- */
- public void updateTransportAttributes(
- @UserIdInt int userId,
- ComponentName transportComponent,
- String name,
- @Nullable Intent configurationIntent,
- String currentDestinationString,
- @Nullable Intent dataManagementIntent,
- CharSequence dataManagementLabel) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "updateTransportAttributes()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.updateTransportAttributes(
- transportComponent,
- name,
- configurationIntent,
- currentDestinationString,
- dataManagementIntent,
- dataManagementLabel);
- }
- }
-
- @Override
- public String selectBackupTransportForUser(int userId, String transport)
- throws RemoteException {
- return (isUserReadyForBackup(userId))
- ? selectBackupTransport(userId, transport) : null;
- }
-
- @Override
- public String selectBackupTransport(String transport) throws RemoteException {
- return selectBackupTransportForUser(binderGetCallingUserId(), transport);
- }
-
- /**
- * Selects transport {@code transportName} and returns the previously selected transport.
- *
- * @deprecated Use {@link #selectBackupTransportAsync(ComponentName,
- * ISelectBackupTransportCallback)} instead.
- */
- @Deprecated
- @Nullable
- public String selectBackupTransport(@UserIdInt int userId, String transportName) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "selectBackupTransport()");
-
- return userBackupManagerService == null
- ? null
- : userBackupManagerService.selectBackupTransport(transportName);
- }
-
- @Override
- public void selectBackupTransportAsyncForUser(int userId, ComponentName transport,
- ISelectBackupTransportCallback listener) throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- selectBackupTransportAsync(userId, transport, listener);
- } else {
- if (listener != null) {
- try {
- listener.onFailure(BackupManager.ERROR_BACKUP_NOT_ALLOWED);
- } catch (RemoteException ex) {
- // ignore
- }
- }
- }
- }
-
- /**
- * Selects transport {@code transportComponent} asynchronously and notifies {@code listener}
- * with the result upon completion.
- */
- public void selectBackupTransportAsync(
- @UserIdInt int userId,
- ComponentName transportComponent,
- ISelectBackupTransportCallback listener) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "selectBackupTransportAsync()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.selectBackupTransportAsync(transportComponent, listener);
- }
- }
-
- @Override
- public Intent getConfigurationIntentForUser(int userId, String transport)
- throws RemoteException {
- return isUserReadyForBackup(userId) ? getConfigurationIntent(userId, transport)
- : null;
- }
-
- @Override
- public Intent getConfigurationIntent(String transport)
- throws RemoteException {
- return getConfigurationIntentForUser(binderGetCallingUserId(), transport);
- }
-
- /**
- * Supply the configuration intent for the given transport. If the name is not one of the
- * available transports, or if the transport does not supply any configuration UI, the method
- * returns {@code null}.
- */
- @Nullable
- public Intent getConfigurationIntent(@UserIdInt int userId, String transportName) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "getConfigurationIntent()");
-
- return userBackupManagerService == null
- ? null
- : userBackupManagerService.getConfigurationIntent(transportName);
- }
-
- @Override
- public String getDestinationStringForUser(int userId, String transport) throws RemoteException {
- return isUserReadyForBackup(userId) ? getDestinationString(userId, transport)
- : null;
- }
-
- @Override
- public String getDestinationString(String transport) throws RemoteException {
- return getDestinationStringForUser(binderGetCallingUserId(), transport);
- }
-
- /**
- * Supply the current destination string for the given transport. If the name is not one of the
- * registered transports the method will return null.
- *
- * <p>This string is used VERBATIM as the summary text of the relevant Settings item.
- *
- * @param transportName The name of the registered transport.
- * @return The current destination string or null if the transport is not registered.
- */
- @Nullable
- public String getDestinationString(@UserIdInt int userId, String transportName) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "getDestinationString()");
-
- return userBackupManagerService == null
- ? null
- : userBackupManagerService.getDestinationString(transportName);
- }
-
- @Override
- public Intent getDataManagementIntentForUser(int userId, String transport)
- throws RemoteException {
- return isUserReadyForBackup(userId)
- ? getDataManagementIntent(userId, transport) : null;
- }
-
- @Override
- public Intent getDataManagementIntent(String transport)
- throws RemoteException {
- return getDataManagementIntentForUser(binderGetCallingUserId(), transport);
- }
-
- /** Supply the manage-data intent for the given transport. */
- @Nullable
- public Intent getDataManagementIntent(@UserIdInt int userId, String transportName) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "getDataManagementIntent()");
-
- return userBackupManagerService == null
- ? null
- : userBackupManagerService.getDataManagementIntent(transportName);
- }
-
- @Override
- public CharSequence getDataManagementLabelForUser(int userId, String transport)
- throws RemoteException {
- return isUserReadyForBackup(userId) ? getDataManagementLabel(userId, transport)
- : null;
- }
-
- /**
- * Supply the menu label for affordances that fire the manage-data intent for the given
- * transport.
- */
- @Nullable
- public CharSequence getDataManagementLabel(@UserIdInt int userId, String transportName) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "getDataManagementLabel()");
-
- return userBackupManagerService == null
- ? null
- : userBackupManagerService.getDataManagementLabel(transportName);
- }
-
- @Override
- public IRestoreSession beginRestoreSessionForUser(
- int userId, String packageName, String transportID) throws RemoteException {
- return isUserReadyForBackup(userId) ? mService.beginRestoreSession(userId, packageName,
- transportID) : null;
- }
-
- @Override
- public void opCompleteForUser(int userId, int token, long result) throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- opComplete(userId, token, result);
- }
- }
-
- @Override
- public void opComplete(int token, long result) throws RemoteException {
- opCompleteForUser(binderGetCallingUserId(), token, result);
- }
-
- /**
- * Used by a currently-active backup agent to notify the service that it has completed its given
- * outstanding asynchronous backup/restore operation.
- */
- public void opComplete(@UserIdInt int userId, int token, long result) {
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(userId, "opComplete()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.opComplete(token, result);
- }
- }
-
- @Override
- public long getAvailableRestoreTokenForUser(int userId, String packageName) {
- return isUserReadyForBackup(userId) ? mService.getAvailableRestoreToken(userId,
- packageName) : 0;
- }
-
- @Override
- public boolean isAppEligibleForBackupForUser(int userId, String packageName) {
- return isUserReadyForBackup(userId) && mService.isAppEligibleForBackup(userId,
- packageName);
- }
-
- @Override
- public String[] filterAppsEligibleForBackupForUser(int userId, String[] packages) {
- return isUserReadyForBackup(userId) ? mService.filterAppsEligibleForBackup(userId,
- packages) : null;
- }
-
- @Override
- public int requestBackupForUser(@UserIdInt int userId, String[] packages, IBackupObserver
- observer, IBackupManagerMonitor monitor, int flags) throws RemoteException {
- if (!isUserReadyForBackup(userId)) {
- return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
- }
- return mService.requestBackup(userId, packages, observer, monitor, flags);
- }
-
- @Override
- public int requestBackup(String[] packages, IBackupObserver observer,
- IBackupManagerMonitor monitor, int flags) throws RemoteException {
- return requestBackupForUser(binderGetCallingUserId(), packages,
- observer, monitor, flags);
- }
-
- @Override
- public void cancelBackupsForUser(@UserIdInt int userId) throws RemoteException {
- if (isUserReadyForBackup(userId)) {
- mService.cancelBackups(userId);
- }
- }
-
- @Override
- public void cancelBackups() throws RemoteException {
- cancelBackupsForUser(binderGetCallingUserId());
- }
-
- /**
- * Returns a {@link UserHandle} for the user that has {@code ancestralSerialNumber} as the
- * serial number of the its ancestral work profile or null if there is no {@link
- * UserBackupManagerService} associated with that user.
- *
- * <p> The ancestral work profile is set by {@link #setAncestralSerialNumber(long)}
- * and it corresponds to the profile that was used to restore to the callers profile.
- */
- @Override
- @Nullable
- public UserHandle getUserForAncestralSerialNumber(long ancestralSerialNumber) {
- if (mGlobalDisable) {
- return null;
- }
- int callingUserId = Binder.getCallingUserHandle().getIdentifier();
- long oldId = Binder.clearCallingIdentity();
- final int[] userIds;
- try {
- userIds =
- mContext
- .getSystemService(UserManager.class)
- .getProfileIds(callingUserId, false);
- } finally {
- Binder.restoreCallingIdentity(oldId);
- }
-
- for (int userId : userIds) {
- UserBackupManagerService userBackupManagerService = mUserServices.get(userId);
- if (userBackupManagerService != null) {
- if (userBackupManagerService.getAncestralSerialNumber() == ancestralSerialNumber) {
- return UserHandle.of(userId);
- }
- }
- }
-
- return null;
- }
-
- /**
- * Sets the ancestral work profile for the calling user.
- *
- * <p> The ancestral work profile corresponds to the profile that was used to restore to the
- * callers profile.
- */
- @Override
- public void setAncestralSerialNumber(long ancestralSerialNumber) {
- if (mGlobalDisable) {
- return;
- }
- UserBackupManagerService userBackupManagerService =
- getServiceForUserIfCallerHasPermission(
- Binder.getCallingUserHandle().getIdentifier(),
- "setAncestralSerialNumber()");
-
- if (userBackupManagerService != null) {
- userBackupManagerService.setAncestralSerialNumber(ancestralSerialNumber);
- }
- }
-
- @Override
- public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- int userId = binderGetCallingUserId();
- if (isUserReadyForBackup(userId)) {
- mService.dump(fd, pw, args);
- } else {
- pw.println("Inactive");
- }
- }
-
- // Full backup/restore entry points - non-Binder; called directly
- // by the full-backup scheduled job
- /* package */ boolean beginFullBackup(@UserIdInt int userId, FullBackupJob scheduledJob) {
- return (isUserReadyForBackup(userId)) && mService.beginFullBackup(userId, scheduledJob);
- }
-
- /* package */ void endFullBackup(@UserIdInt int userId) {
- if (isUserReadyForBackup(userId)) {
- mService.endFullBackup(userId);
- }
- }
-
- /**
- * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
- * If the user is not registered with the service (either the user is locked or not eligible for
- * the backup service) then return {@code null}.
- *
- * @param userId The id of the user to retrieve its instance of {@link
- * UserBackupManagerService}.
- * @param caller A {@link String} identifying the caller for logging purposes.
- * @throws SecurityException if {@code userId} is different from the calling user id and the
- * caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission.
- */
- @Nullable
- @VisibleForTesting
- UserBackupManagerService getServiceForUserIfCallerHasPermission(
- @UserIdInt int userId, String caller) {
- enforceCallingPermissionOnUserId(userId, caller);
- UserBackupManagerService userBackupManagerService = mUserServices.get(userId);
- if (userBackupManagerService == null) {
- Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
- }
- return userBackupManagerService;
- }
-
- /**
- * If {@code userId} is different from the calling user id, then the caller must hold the
- * android.permission.INTERACT_ACROSS_USERS_FULL permission.
- *
- * @param userId User id on which the backup operation is being requested.
- * @param message A message to include in the exception if it is thrown.
- */
- void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) {
- if (Binder.getCallingUserHandle().getIdentifier() != userId) {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
- }
- }
-}
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 0e81e07..77888db 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -421,13 +421,13 @@
* Creates an instance of {@link UserBackupManagerService} and initializes state for it. This
* includes setting up the directories where we keep our bookkeeping and transport management.
*
- * @see #createAndInitializeService(int, Context, Trampoline, HandlerThread, File, File,
- * TransportManager)
+ * @see #createAndInitializeService(int, Context, BackupManagerService, HandlerThread, File,
+ * File, TransportManager)
*/
static UserBackupManagerService createAndInitializeService(
@UserIdInt int userId,
Context context,
- Trampoline trampoline,
+ BackupManagerService backupManagerService,
Set<ComponentName> transportWhitelist) {
String currentTransport =
Settings.Secure.getStringForUser(
@@ -455,7 +455,7 @@
return createAndInitializeService(
userId,
context,
- trampoline,
+ backupManagerService,
userBackupThread,
baseStateDir,
dataDir,
@@ -467,7 +467,7 @@
*
* @param userId The user which this service is for.
* @param context The system server context.
- * @param trampoline A reference to the proxy to {@link BackupManagerService}.
+ * @param backupManagerService A reference to the proxy to {@link BackupManagerService}.
* @param userBackupThread The thread running backup/restore operations for the user.
* @param baseStateDir The directory we store the user's persistent bookkeeping data.
* @param dataDir The directory we store the user's temporary staging data.
@@ -478,7 +478,7 @@
public static UserBackupManagerService createAndInitializeService(
@UserIdInt int userId,
Context context,
- Trampoline trampoline,
+ BackupManagerService backupManagerService,
HandlerThread userBackupThread,
File baseStateDir,
File dataDir,
@@ -486,7 +486,7 @@
return new UserBackupManagerService(
userId,
context,
- trampoline,
+ backupManagerService,
userBackupThread,
baseStateDir,
dataDir,
@@ -509,7 +509,7 @@
private UserBackupManagerService(
@UserIdInt int userId,
Context context,
- Trampoline parent,
+ BackupManagerService parent,
HandlerThread userBackupThread,
File baseStateDir,
File dataDir,
@@ -525,8 +525,8 @@
mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
- checkNotNull(parent, "trampoline cannot be null");
- mBackupManagerBinder = Trampoline.asInterface(parent.asBinder());
+ checkNotNull(parent, "parent cannot be null");
+ mBackupManagerBinder = BackupManagerService.asInterface(parent.asBinder());
mAgentTimeoutParameters = new
BackupAgentTimeoutParameters(Handler.getMain(), mContext.getContentResolver());
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 474dbfe..c838c60 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -13,7 +13,6 @@
},
srcs: [
"java/**/*.java",
- ":platformcompat_aidl",
":dumpstate_aidl",
":idmap2_aidl",
":installd_aidl",
@@ -82,11 +81,3 @@
name: "gps_debug.conf",
src: "java/com/android/server/location/gps_debug.conf",
}
-
-filegroup {
- name: "platformcompat_aidl",
- srcs: [
- "java/com/android/server/compat/IPlatformCompat.aidl",
- ],
- path: "java",
-}
diff --git a/services/core/java/com/android/server/AnimationThread.java b/services/core/java/com/android/server/AnimationThread.java
index c86042b..c607b1e 100644
--- a/services/core/java/com/android/server/AnimationThread.java
+++ b/services/core/java/com/android/server/AnimationThread.java
@@ -21,6 +21,8 @@
import android.os.Handler;
import android.os.Trace;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Thread for handling all legacy window animations, or anything that's directly impacting
* animations like starting windows or traversals.
@@ -55,4 +57,20 @@
return sHandler;
}
}
+
+ /**
+ * Disposes current animation thread if it's initialized. Should only be used in tests to set up
+ * a new environment.
+ */
+ @VisibleForTesting
+ public static void dispose() {
+ synchronized (DisplayThread.class) {
+ if (sInstance == null) {
+ return;
+ }
+
+ getHandler().runWithScissors(() -> sInstance.quit(), 0 /* timeout */);
+ sInstance = null;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 6a9f5b6..d18b4f6 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -1235,14 +1235,21 @@
}
@Override
public void scheduleUpdate() throws RemoteException {
- traceBegin("HealthScheduleUpdate");
- try {
- IHealth service = mHealthServiceWrapper.getLastService();
- if (service == null) throw new RemoteException("no health service");
- service.update();
- } finally {
- traceEnd();
- }
+ mHealthServiceWrapper.getHandlerThread().getThreadHandler().post(() -> {
+ traceBegin("HealthScheduleUpdate");
+ try {
+ IHealth service = mHealthServiceWrapper.getLastService();
+ if (service == null) {
+ Slog.e(TAG, "no health service");
+ return;
+ }
+ service.update();
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Cannot call update on health HAL", ex);
+ } finally {
+ traceEnd();
+ }
+ });
}
}
@@ -1319,7 +1326,7 @@
Arrays.asList(INSTANCE_VENDOR, INSTANCE_HEALTHD);
private final IServiceNotification mNotification = new Notification();
- private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceRefresh");
+ private final HandlerThread mHandlerThread = new HandlerThread("HealthServiceHwbinder");
// These variables are fixed after init.
private Callback mCallback;
private IHealthSupplier mHealthSupplier;
diff --git a/services/core/java/com/android/server/BluetoothService.java b/services/core/java/com/android/server/BluetoothService.java
index 112cf08..0bcd937 100644
--- a/services/core/java/com/android/server/BluetoothService.java
+++ b/services/core/java/com/android/server/BluetoothService.java
@@ -53,8 +53,11 @@
@Override
public void onSwitchUser(int userHandle) {
- initialize();
- mBluetoothManagerService.handleOnSwitchUser(userHandle);
+ if (!mInitialized) {
+ initialize();
+ } else {
+ mBluetoothManagerService.handleOnSwitchUser(userHandle);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/DisplayThread.java b/services/core/java/com/android/server/DisplayThread.java
index 85c799c..a07ade0 100644
--- a/services/core/java/com/android/server/DisplayThread.java
+++ b/services/core/java/com/android/server/DisplayThread.java
@@ -20,6 +20,8 @@
import android.os.Process;
import android.os.Trace;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* Shared singleton foreground thread for the system. This is a thread for
* operations that affect what's on the display, which needs to have a minimum
@@ -58,4 +60,20 @@
return sHandler;
}
}
+
+ /**
+ * Disposes current display thread if it's initialized. Should only be used in tests to set up a
+ * new environment.
+ */
+ @VisibleForTesting
+ public static void dispose() {
+ synchronized (DisplayThread.class) {
+ if (sInstance == null) {
+ return;
+ }
+
+ getHandler().runWithScissors(() -> sInstance.quit(), 0 /* timeout */);
+ sInstance = null;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index e531412..18009e1 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -25,6 +25,7 @@
import android.os.Environment;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -195,7 +196,20 @@
}
@Override
- public boolean write(byte[] buf) throws RemoteException {
- return getGsiService().commitGsiChunkFromMemory(buf);
+ public boolean setAshmem(ParcelFileDescriptor ashmem, long size) {
+ try {
+ return getGsiService().setGsiAshmem(ashmem, size);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+
+ @Override
+ public boolean submitFromAshmem(long size) {
+ try {
+ return getGsiService().commitGsiChunkFromAshmem(size);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
}
}
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index ebe23f6..b085946 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -212,129 +212,67 @@
* Starts the given user.
*/
public void startUser(final @NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) {
- t.traceBegin("ssm.startUser-" + userHandle);
- Slog.i(TAG, "Calling onStartUser u" + userHandle);
- final UserInfo userInfo = getUserInfo(userHandle);
- final int serviceLen = mServices.size();
- for (int i = 0; i < serviceLen; i++) {
- final SystemService service = mServices.get(i);
- final String serviceName = service.getClass().getName();
- t.traceBegin("onStartUser-" + userHandle + " " + serviceName);
- long time = SystemClock.elapsedRealtime();
- try {
- service.onStartUser(userInfo);
- } catch (Exception ex) {
- Slog.wtf(TAG, "Failure reporting start of user " + userHandle
- + " to service " + service.getClass().getName(), ex);
- }
- warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStartUser ");
- t.traceEnd();
- }
- t.traceEnd();
+ onUser(t, "Start", userHandle, (s, u) -> s.onStartUser(u));
}
/**
* Unlocks the given user.
*/
public void unlockUser(final @UserIdInt int userHandle) {
- final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
- t.traceBegin("ssm.unlockUser-" + userHandle);
- Slog.i(TAG, "Calling onUnlockUser u" + userHandle);
- final UserInfo userInfo = getUserInfo(userHandle);
- final int serviceLen = mServices.size();
- for (int i = 0; i < serviceLen; i++) {
- final SystemService service = mServices.get(i);
- final String serviceName = service.getClass().getName();
- t.traceBegin("onUnlockUser-" + userHandle + " " + serviceName);
- long time = SystemClock.elapsedRealtime();
- try {
- service.onUnlockUser(userInfo);
- } catch (Exception ex) {
- Slog.wtf(TAG, "Failure reporting unlock of user " + userHandle
- + " to service " + serviceName, ex);
- }
- warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onUnlockUser ");
- t.traceEnd();
- }
- t.traceEnd();
+ onUser("Unlock", userHandle, (s, u) -> s.onUnlockUser(u));
}
/**
* Switches to the given user.
*/
public void switchUser(final @UserIdInt int userHandle) {
- final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
- t.traceBegin("ssm.switchUser-" + userHandle);
- Slog.i(TAG, "Calling switchUser u" + userHandle);
- final UserInfo userInfo = getUserInfo(userHandle);
- final int serviceLen = mServices.size();
- for (int i = 0; i < serviceLen; i++) {
- final SystemService service = mServices.get(i);
- final String serviceName = service.getClass().getName();
- t.traceBegin("onSwitchUser-" + userHandle + " " + serviceName);
- long time = SystemClock.elapsedRealtime();
- try {
- service.onSwitchUser(userInfo);
- } catch (Exception ex) {
- Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
- + " to service " + serviceName, ex);
- }
- warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onSwitchUser");
- t.traceEnd();
- }
- t.traceEnd();
+ onUser("Switch", userHandle, (s, u) -> s.onSwitchUser(u));
}
/**
* Stops the given user.
*/
public void stopUser(final @UserIdInt int userHandle) {
- final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
- t.traceBegin("ssm.stopUser-" + userHandle);
- Slog.i(TAG, "Calling onStopUser u" + userHandle);
- final UserInfo userInfo = getUserInfo(userHandle);
- final int serviceLen = mServices.size();
- for (int i = 0; i < serviceLen; i++) {
- final SystemService service = mServices.get(i);
- final String serviceName = service.getClass().getName();
- t.traceBegin("onStopUser-" + userHandle + " " + serviceName);
- long time = SystemClock.elapsedRealtime();
- try {
- service.onStopUser(userInfo);
- } catch (Exception ex) {
- Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
- + " to service " + serviceName, ex);
- }
- warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStopUser");
- t.traceEnd();
- }
- t.traceEnd();
+ onUser("Stop", userHandle, (s, u) -> s.onStopUser(u));
}
/**
* Cleans up the given user.
*/
public void cleanupUser(final @UserIdInt int userHandle) {
- final TimingsTraceAndSlog t = TimingsTraceAndSlog.newAsyncLog();
- t.traceBegin("ssm.cleanupUser-" + userHandle);
- Slog.i(TAG, "Calling onCleanupUser u" + userHandle);
+ onUser("Cleanup", userHandle, (s, u) -> s.onCleanupUser(u));
+ }
+
+ private interface ServiceVisitor {
+ void visit(@NonNull SystemService service, @NonNull UserInfo userInfo);
+ }
+
+ private void onUser(@NonNull String onWhat, @UserIdInt int userHandle,
+ @NonNull ServiceVisitor visitor) {
+ onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userHandle, visitor);
+ }
+
+ private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
+ @UserIdInt int userHandle, @NonNull ServiceVisitor visitor) {
+ t.traceBegin("ssm." + onWhat + "User-" + userHandle);
+ Slog.i(TAG, "Calling on" + onWhat + "User u" + userHandle);
final UserInfo userInfo = getUserInfo(userHandle);
final int serviceLen = mServices.size();
for (int i = 0; i < serviceLen; i++) {
final SystemService service = mServices.get(i);
final String serviceName = service.getClass().getName();
- t.traceBegin("onCleanupUser-" + userHandle + " " + serviceName);
+ t.traceBegin("ssm.on" + onWhat + "User-" + userHandle + " " + serviceName);
long time = SystemClock.elapsedRealtime();
try {
- service.onCleanupUser(userInfo);
+ visitor.visit(service, userInfo);
} catch (Exception ex) {
- Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
+ Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + userHandle
+ " to service " + serviceName, ex);
}
- warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onCleanupUser");
- t.traceEnd();
+ warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "on" + onWhat + "User ");
+ t.traceEnd(); // what on service
}
- t.traceEnd();
+ t.traceEnd(); // main entry
}
/** Sets the safe mode flag for services to query. */
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index e66e596..f7e825e 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1027,7 +1027,12 @@
log(str);
}
mLocalLog.log(str);
- if (validatePhoneId(phoneId)) {
+ // for service state updates, don't notify clients when subId is invalid. This prevents
+ // us from sending incorrect notifications like b/133140128
+ // In the future, we can remove this logic for every notification here and add a
+ // callback so listeners know when their PhoneStateListener's subId becomes invalid, but
+ // for now we use the simplest fix.
+ if (validatePhoneId(phoneId) && SubscriptionManager.isValidSubscriptionId(subId)) {
mServiceState[phoneId] = state;
for (Record r : mRecords) {
@@ -1059,7 +1064,8 @@
}
}
} else {
- log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId);
+ log("notifyServiceStateForSubscriber: INVALID phoneId=" + phoneId
+ + " or subId=" + subId);
}
handleRemoveListLocked();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 19c818f..9caceae 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -282,6 +282,7 @@
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.EventLog;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Pair;
import android.util.PrintWriterPrinter;
@@ -378,7 +379,7 @@
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
-import java.io.UnsupportedEncodingException;
+import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -437,6 +438,10 @@
// need not be the case.
public static final String ACTION_TRIGGER_IDLE = "com.android.server.ACTION_TRIGGER_IDLE";
+ private static final String INTENT_BUGREPORT_REQUESTED =
+ "com.android.internal.intent.action.BUGREPORT_REQUESTED";
+ private static final String SHELL_APP_PACKAGE = "com.android.shell";
+
/** Control over CPU and battery monitoring */
// write battery stats every 30 minutes.
static final long BATTERY_STATS_TIME = 30 * 60 * 1000;
@@ -555,6 +560,10 @@
OomAdjuster mOomAdjuster;
final LowMemDetector mLowMemDetector;
+ static final String EXTRA_TITLE = "android.intent.extra.TITLE";
+ static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION";
+ static final String EXTRA_BUGREPORT_TYPE = "android.intent.extra.BUGREPORT_TYPE";
+
/** All system services */
SystemServiceManager mSystemServiceManager;
@@ -1480,8 +1489,9 @@
public ActivityTaskManagerService mActivityTaskManager;
@VisibleForTesting
public ActivityTaskManagerInternal mAtmInternal;
+ UriGrantsManagerInternal mUgmInternal;
@VisibleForTesting
- public UriGrantsManagerInternal mUgmInternal;
+ public final ActivityManagerInternal mInternal;
final ActivityThread mSystemThread;
private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -2413,6 +2423,8 @@
mProcStartHandler = null;
mHiddenApiBlacklist = null;
mFactoryTest = FACTORY_TEST_OFF;
+ mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
+ mInternal = new LocalService();
}
// Note: This method is invoked on the main thread but may need to attach various
@@ -2566,6 +2578,7 @@
Slog.w(TAG, "Setting background thread cpuset failed");
}
+ mInternal = new LocalService();
}
public void setSystemServiceManager(SystemServiceManager mgr) {
@@ -2583,7 +2596,7 @@
mBatteryStatsService.publish();
mAppOpsService.publish(mContext);
Slog.d("AppOps", "AppOpsService published");
- LocalServices.addService(ActivityManagerInternal.class, new LocalService());
+ LocalServices.addService(ActivityManagerInternal.class, mInternal);
mActivityTaskManager.onActivityManagerInternalAdded();
mUgmInternal.onActivityManagerInternalAdded();
mPendingIntentController.onActivityManagerInternalAdded();
@@ -8197,6 +8210,53 @@
@Deprecated
@Override
public void requestBugReport(int bugreportType) {
+ requestBugReportWithDescription(null, null, bugreportType);
+ }
+
+ /**
+ * @deprecated This method is only used by a few internal components and it will soon be
+ * replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
+ * No new code should be calling it.
+ */
+ @Deprecated
+ public void requestBugReportWithDescription(@Nullable String shareTitle,
+ @Nullable String shareDescription, int bugreportType) {
+ if (!TextUtils.isEmpty(shareTitle)) {
+ if (shareTitle.length() > MAX_BUGREPORT_TITLE_SIZE) {
+ String errorStr = "shareTitle should be less than " +
+ MAX_BUGREPORT_TITLE_SIZE + " characters";
+ throw new IllegalArgumentException(errorStr);
+ }
+ if (!TextUtils.isEmpty(shareDescription)) {
+ int length = shareDescription.getBytes(StandardCharsets.UTF_8).length;
+ if (length > SystemProperties.PROP_VALUE_MAX) {
+ String errorStr = "shareTitle should be less than " +
+ SystemProperties.PROP_VALUE_MAX + " bytes";
+ throw new IllegalArgumentException(errorStr);
+ } else {
+ SystemProperties.set("dumpstate.options.description", shareDescription);
+ }
+ }
+ SystemProperties.set("dumpstate.options.title", shareTitle);
+ Slog.d(TAG, "Bugreport notification title " + shareTitle
+ + " description " + shareDescription);
+ }
+ final boolean useApi = FeatureFlagUtils.isEnabled(mContext,
+ FeatureFlagUtils.USE_BUGREPORT_API);
+
+ if (useApi) {
+ // Create intent to trigger Bugreport API via Shell
+ Intent triggerShellBugreport = new Intent();
+ triggerShellBugreport.setAction(INTENT_BUGREPORT_REQUESTED);
+ triggerShellBugreport.setPackage(SHELL_APP_PACKAGE);
+ triggerShellBugreport.putExtra(EXTRA_BUGREPORT_TYPE, bugreportType);
+ if (shareTitle != null) {
+ triggerShellBugreport.putExtra(EXTRA_TITLE, shareTitle);
+ }
+ if (shareDescription != null) {
+ triggerShellBugreport.putExtra(EXTRA_DESCRIPTION, shareDescription);
+ }
+ }
String extraOptions = null;
switch (bugreportType) {
case ActivityManager.BUGREPORT_OPTION_FULL:
@@ -8238,45 +8298,6 @@
* No new code should be calling it.
*/
@Deprecated
- private void requestBugReportWithDescription(String shareTitle, String shareDescription,
- int bugreportType) {
- if (!TextUtils.isEmpty(shareTitle)) {
- if (shareTitle.length() > MAX_BUGREPORT_TITLE_SIZE) {
- String errorStr = "shareTitle should be less than " +
- MAX_BUGREPORT_TITLE_SIZE + " characters";
- throw new IllegalArgumentException(errorStr);
- } else {
- if (!TextUtils.isEmpty(shareDescription)) {
- int length;
- try {
- length = shareDescription.getBytes("UTF-8").length;
- } catch (UnsupportedEncodingException e) {
- String errorStr = "shareDescription: UnsupportedEncodingException";
- throw new IllegalArgumentException(errorStr);
- }
- if (length > SystemProperties.PROP_VALUE_MAX) {
- String errorStr = "shareTitle should be less than " +
- SystemProperties.PROP_VALUE_MAX + " bytes";
- throw new IllegalArgumentException(errorStr);
- } else {
- SystemProperties.set("dumpstate.options.description", shareDescription);
- }
- }
- SystemProperties.set("dumpstate.options.title", shareTitle);
- }
- }
-
- Slog.d(TAG, "Bugreport notification title " + shareTitle
- + " description " + shareDescription);
- requestBugReport(bugreportType);
- }
-
- /**
- * @deprecated This method is only used by a few internal components and it will soon be
- * replaced by a proper bug report API (which will be restricted to a few, pre-defined apps).
- * No new code should be calling it.
- */
- @Deprecated
@Override
public void requestTelephonyBugReport(String shareTitle, String shareDescription) {
requestBugReportWithDescription(shareTitle, shareDescription,
@@ -12209,11 +12230,8 @@
proto.write(MemInfoDumpProto.MemItem.IS_PROC, mi.isProc);
proto.write(MemInfoDumpProto.MemItem.ID, mi.id);
proto.write(MemInfoDumpProto.MemItem.HAS_ACTIVITIES, mi.hasActivities);
- if (dumpPss) {
- proto.write(MemInfoDumpProto.MemItem.PSS_KB, mi.pss);
- } else {
- proto.write(MemInfoDumpProto.MemItem.RSS_KB, mi.mRss);
- }
+ proto.write(MemInfoDumpProto.MemItem.PSS_KB, mi.pss);
+ proto.write(MemInfoDumpProto.MemItem.RSS_KB, mi.mRss);
if (dumpSwapPss) {
proto.write(MemInfoDumpProto.MemItem.SWAP_PSS_KB, mi.swapPss);
}
@@ -12511,6 +12529,13 @@
if (!brief && !opts.oomOnly && (procs.size() == 1 || opts.isCheckinRequest || opts.packages)) {
opts.dumpDetails = true;
}
+ final int numProcs = procs.size();
+ final boolean collectNative = !opts.isCheckinRequest && numProcs > 1 && !opts.packages;
+ if (collectNative) {
+ // If we are showing aggregations, also look for native processes to
+ // include so that our aggregations are more accurate.
+ updateCpuStatsNow();
+ }
dumpApplicationMemoryUsageHeader(pw, uptime, realtime, opts.isCheckinRequest, opts.isCompact);
@@ -12549,7 +12574,7 @@
boolean hasSwapPss = false;
Debug.MemoryInfo mi = null;
- for (int i = procs.size() - 1 ; i >= 0 ; i--) {
+ for (int i = numProcs - 1; i >= 0; i--) {
final ProcessRecord r = procs.get(i);
final IApplicationThread thread;
final int pid;
@@ -12703,10 +12728,7 @@
long nativeProcTotalPss = 0;
- if (!opts.isCheckinRequest && procs.size() > 1 && !opts.packages) {
- // If we are showing aggregations, also look for native processes to
- // include so that our aggregations are more accurate.
- updateCpuStatsNow();
+ if (collectNative) {
mi = null;
synchronized (mProcessCpuTracker) {
final int N = mProcessCpuTracker.countStats();
@@ -13074,6 +13096,13 @@
if (!brief && !opts.oomOnly && (procs.size() == 1 || opts.isCheckinRequest || opts.packages)) {
opts.dumpDetails = true;
}
+ final int numProcs = procs.size();
+ final boolean collectNative = numProcs > 1 && !opts.packages;
+ if (collectNative) {
+ // If we are showing aggregations, also look for native processes to
+ // include so that our aggregations are more accurate.
+ updateCpuStatsNow();
+ }
ProtoOutputStream proto = new ProtoOutputStream(fd);
@@ -13115,7 +13144,7 @@
boolean hasSwapPss = false;
Debug.MemoryInfo mi = null;
- for (int i = procs.size() - 1 ; i >= 0 ; i--) {
+ for (int i = numProcs - 1; i >= 0; i--) {
final ProcessRecord r = procs.get(i);
final IApplicationThread thread;
final int pid;
@@ -13262,10 +13291,7 @@
long nativeProcTotalPss = 0;
- if (procs.size() > 1 && !opts.packages) {
- // If we are showing aggregations, also look for native processes to
- // include so that our aggregations are more accurate.
- updateCpuStatsNow();
+ if (collectNative) {
mi = null;
synchronized (mProcessCpuTracker) {
final int N = mProcessCpuTracker.countStats();
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index 65472c9..bd129f7 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -405,7 +405,7 @@
@GuardedBy("MediaFocusControl.mAudioFocusLock")
private boolean frameworkHandleFocusLoss(int focusLoss, @NonNull final FocusRequester frWinner,
boolean forceDuck) {
- if (frWinner.mCallingUid != this.mCallingUid) {
+ if (frWinner.mCallingUid == this.mCallingUid) {
// the focus change is within the same app, so let the dispatching
// happen as if the framework was not involved.
return false;
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index 4a9ccde..766e5c4 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -209,8 +209,7 @@
// will show briefly and be replaced by "device locked out" message.
if (listener != null) {
if (isBiometricPrompt()) {
- listener.onAuthenticationFailedInternal(getCookie(),
- getRequireConfirmation());
+ listener.onAuthenticationFailedInternal();
} else {
listener.onAuthenticationFailed(getHalDeviceId());
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index af2f24f..24e6a75 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -25,11 +25,9 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
-import android.app.IActivityTaskManager;
+import android.app.IActivityManager;
import android.app.KeyguardManager;
-import android.app.TaskStackListener;
import android.app.UserSwitchObserver;
import android.content.ContentResolver;
import android.content.Context;
@@ -69,6 +67,7 @@
import android.util.StatsLog;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IStatusBarService;
import com.android.server.SystemService;
@@ -88,9 +87,8 @@
private static final String TAG = "BiometricService";
private static final boolean DEBUG = true;
- private static final int MSG_ON_TASK_STACK_CHANGED = 1;
private static final int MSG_ON_AUTHENTICATION_SUCCEEDED = 2;
- private static final int MSG_ON_AUTHENTICATION_FAILED = 3;
+ private static final int MSG_ON_AUTHENTICATION_REJECTED = 3;
private static final int MSG_ON_ERROR = 4;
private static final int MSG_ON_ACQUIRED = 5;
private static final int MSG_ON_DISMISSED = 6;
@@ -101,6 +99,7 @@
private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_SUCCESS = 11;
private static final int MSG_ON_CONFIRM_DEVICE_CREDENTIAL_ERROR = 12;
private static final int MSG_REGISTER_CANCELLATION_CALLBACK = 13;
+ private static final int MSG_ON_AUTHENTICATION_TIMED_OUT = 14;
private static final int[] FEATURE_ID = {
TYPE_FINGERPRINT,
@@ -112,33 +111,41 @@
* Authentication either just called and we have not transitioned to the CALLED state, or
* authentication terminated (success or error).
*/
- private static final int STATE_AUTH_IDLE = 0;
+ static final int STATE_AUTH_IDLE = 0;
/**
* Authentication was called and we are waiting for the <Biometric>Services to return their
* cookies before starting the hardware and showing the BiometricPrompt.
*/
- private static final int STATE_AUTH_CALLED = 1;
+ static final int STATE_AUTH_CALLED = 1;
/**
* Authentication started, BiometricPrompt is showing and the hardware is authenticating.
*/
- private static final int STATE_AUTH_STARTED = 2;
+ static final int STATE_AUTH_STARTED = 2;
/**
* Authentication is paused, waiting for the user to press "try again" button. Only
* passive modalities such as Face or Iris should have this state. Note that for passive
* modalities, the HAL enters the idle state after onAuthenticated(false) which differs from
* fingerprint.
*/
- private static final int STATE_AUTH_PAUSED = 3;
+ static final int STATE_AUTH_PAUSED = 3;
/**
* Authentication is successful, but we're waiting for the user to press "confirm" button.
*/
- private static final int STATE_AUTH_PENDING_CONFIRM = 5;
+ static final int STATE_AUTH_PENDING_CONFIRM = 5;
/**
* Biometric authentication was canceled, but the device is now showing ConfirmDeviceCredential
*/
- private static final int STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC = 6;
+ static final int STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC = 6;
+ /**
+ * Biometric authenticated, waiting for SysUI to finish animation
+ */
+ static final int STATE_AUTHENTICATED_PENDING_SYSUI = 7;
+ /**
+ * Biometric error, waiting for SysUI to finish animation
+ */
+ static final int STATE_ERROR_PENDING_SYSUI = 8;
- private final class AuthSession implements IBinder.DeathRecipient {
+ final class AuthSession implements IBinder.DeathRecipient {
// Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
// <Biometric>Services before we can start authenticating. Pairs that have been returned
// are moved to mModalitiesMatched.
@@ -165,10 +172,13 @@
final boolean mRequireConfirmation;
// The current state, which can be either idle, called, or started
- private int mState = STATE_AUTH_IDLE;
+ int mState = STATE_AUTH_IDLE;
// For explicit confirmation, do not send to keystore until the user has confirmed
// the authentication.
byte[] mTokenEscrow;
+ // Waiting for SystemUI to complete animation
+ int mErrorEscrow;
+ String mErrorStringEscrow;
// Timestamp when authentication started
private long mStartTimeMs;
@@ -244,42 +254,37 @@
}
}
- private final class BiometricTaskStackListener extends TaskStackListener {
- @Override
- public void onTaskStackChanged() {
- mHandler.sendEmptyMessage(MSG_ON_TASK_STACK_CHANGED);
- }
- }
-
+ private final Injector mInjector;
+ @VisibleForTesting
+ final IBiometricService.Stub mImpl;
private final AppOpsManager mAppOps;
private final boolean mHasFeatureFingerprint;
private final boolean mHasFeatureIris;
private final boolean mHasFeatureFace;
- private final SettingObserver mSettingObserver;
+ @VisibleForTesting
+ SettingObserver mSettingObserver;
private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
- private final BiometricTaskStackListener mTaskStackListener = new BiometricTaskStackListener();
private final Random mRandom = new Random();
- private IFingerprintService mFingerprintService;
- private IFaceService mFaceService;
- private IActivityTaskManager mActivityTaskManager;
- private IStatusBarService mStatusBarService;
+ @VisibleForTesting
+ IFingerprintService mFingerprintService;
+ @VisibleForTesting
+ IFaceService mFaceService;
+ @VisibleForTesting
+ IStatusBarService mStatusBarService;
+ @VisibleForTesting
+ KeyStore mKeyStore;
// Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
// polymorphism :/
final ArrayList<Authenticator> mAuthenticators = new ArrayList<>();
- // Cache the current service that's being used. This is the service which
- // cancelAuthentication() must be forwarded to. This is just a cache, and the actual
- // check (is caller the current client) is done in the <Biometric>Service.
- // Since Settings/System (not application) is responsible for changing preference, this
- // should be safe.
- private int mCurrentModality;
-
// The current authentication session, null if idle/done. We need to track both the current
// and pending sessions since errors may be sent to either.
- private AuthSession mCurrentAuthSession;
- private AuthSession mPendingAuthSession;
+ @VisibleForTesting
+ AuthSession mCurrentAuthSession;
+ @VisibleForTesting
+ AuthSession mPendingAuthSession;
// TODO(b/123378871): Remove when moved.
// When BiometricPrompt#setAllowDeviceCredentials is set to true, we need to store the
@@ -289,15 +294,11 @@
// to this receiver.
private IBiometricServiceReceiver mConfirmDeviceCredentialReceiver;
- private final Handler mHandler = new Handler(Looper.getMainLooper()) {
+ @VisibleForTesting
+ final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
- case MSG_ON_TASK_STACK_CHANGED: {
- handleTaskStackChanged();
- break;
- }
-
case MSG_ON_AUTHENTICATION_SUCCEEDED: {
SomeArgs args = (SomeArgs) msg.obj;
handleAuthenticationSucceeded(
@@ -307,8 +308,8 @@
break;
}
- case MSG_ON_AUTHENTICATION_FAILED: {
- handleAuthenticationFailed((String) msg.obj /* failureReason */);
+ case MSG_ON_AUTHENTICATION_REJECTED: {
+ handleAuthenticationRejected((String) msg.obj /* failureReason */);
break;
}
@@ -397,6 +398,11 @@
break;
}
+ case MSG_ON_AUTHENTICATION_TIMED_OUT: {
+ handleAuthenticationTimedOut((String) msg.obj /* errorMessage */);
+ break;
+ }
+
default:
Slog.e(TAG, "Unknown message: " + msg);
break;
@@ -422,7 +428,8 @@
}
}
- private final class SettingObserver extends ContentObserver {
+ @VisibleForTesting
+ public static class SettingObserver extends ContentObserver {
private static final boolean DEFAULT_KEYGUARD_ENABLED = true;
private static final boolean DEFAULT_APP_ENABLED = true;
@@ -436,6 +443,7 @@
Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION);
private final ContentResolver mContentResolver;
+ private final List<BiometricService.EnabledOnKeyguardCallback> mCallbacks;
private Map<Integer, Boolean> mFaceEnabledOnKeyguard = new HashMap<>();
private Map<Integer, Boolean> mFaceEnabledForApps = new HashMap<>();
@@ -446,13 +454,15 @@
*
* @param handler The handler to run {@link #onChange} on, or null if none.
*/
- SettingObserver(Handler handler) {
+ public SettingObserver(Context context, Handler handler,
+ List<BiometricService.EnabledOnKeyguardCallback> callbacks) {
super(handler);
- mContentResolver = getContext().getContentResolver();
+ mContentResolver = context.getContentResolver();
+ mCallbacks = callbacks;
updateContentObserver();
}
- void updateContentObserver() {
+ public void updateContentObserver() {
mContentResolver.unregisterContentObserver(this);
mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
false /* notifyForDescendents */,
@@ -495,7 +505,7 @@
}
}
- boolean getFaceEnabledOnKeyguard() {
+ public boolean getFaceEnabledOnKeyguard() {
final int user = ActivityManager.getCurrentUser();
if (!mFaceEnabledOnKeyguard.containsKey(user)) {
onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED, user);
@@ -503,22 +513,23 @@
return mFaceEnabledOnKeyguard.get(user);
}
- boolean getFaceEnabledForApps(int userId) {
+ public boolean getFaceEnabledForApps(int userId) {
+ Slog.e(TAG, "getFaceEnabledForApps: " + userId, new Exception());
if (!mFaceEnabledForApps.containsKey(userId)) {
onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED, userId);
}
return mFaceEnabledForApps.getOrDefault(userId, DEFAULT_APP_ENABLED);
}
- boolean getFaceAlwaysRequireConfirmation(int userId) {
+ public boolean getFaceAlwaysRequireConfirmation(int userId) {
if (!mFaceAlwaysRequireConfirmation.containsKey(userId)) {
onChange(true /* selfChange */, FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, userId);
}
return mFaceAlwaysRequireConfirmation.get(userId);
}
- void notifyEnabledOnKeyguardCallbacks(int userId) {
- List<EnabledOnKeyguardCallback> callbacks = mEnabledOnKeyguardCallbacks;
+ public void notifyEnabledOnKeyguardCallbacks(int userId) {
+ List<EnabledOnKeyguardCallback> callbacks = mCallbacks;
for (int i = 0; i < callbacks.size(); i++) {
callbacks.get(i).notify(BiometricSourceType.FACE,
mFaceEnabledOnKeyguard.getOrDefault(userId, DEFAULT_KEYGUARD_ENABLED),
@@ -527,7 +538,7 @@
}
}
- private final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
+ final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
private final IBiometricEnabledOnKeyguardCallback mCallback;
@@ -559,7 +570,8 @@
}
// Wrap the client's receiver so we can do things with the BiometricDialog first
- private final IBiometricServiceReceiverInternal mInternalReceiver =
+ @VisibleForTesting
+ final IBiometricServiceReceiverInternal mInternalReceiver =
new IBiometricServiceReceiverInternal.Stub() {
@Override
public void onAuthenticationSucceeded(boolean requireConfirmation, byte[] token)
@@ -571,10 +583,11 @@
}
@Override
- public void onAuthenticationFailed(int cookie, boolean requireConfirmation)
+ public void onAuthenticationFailed()
throws RemoteException {
String failureReason = getContext().getString(R.string.biometric_not_recognized);
- mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, failureReason).sendToTarget();
+ Slog.v(TAG, "onAuthenticationFailed: " + failureReason);
+ mHandler.obtainMessage(MSG_ON_AUTHENTICATION_REJECTED, failureReason).sendToTarget();
}
@Override
@@ -583,7 +596,7 @@
// soft errors and we should allow the user to try authenticating again instead of
// dismissing BiometricPrompt.
if (error == BiometricConstants.BIOMETRIC_ERROR_TIMEOUT) {
- mHandler.obtainMessage(MSG_ON_AUTHENTICATION_FAILED, message).sendToTarget();
+ mHandler.obtainMessage(MSG_ON_AUTHENTICATION_TIMED_OUT, message).sendToTarget();
} else {
SomeArgs args = SomeArgs.obtain();
args.argi1 = cookie;
@@ -873,6 +886,44 @@
}
}
+ @VisibleForTesting
+ static class Injector {
+ IActivityManager getActivityManagerService() {
+ return ActivityManager.getService();
+ }
+
+ IStatusBarService getStatusBarService() {
+ return IStatusBarService.Stub.asInterface(
+ ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ }
+
+ IFingerprintService getFingerprintService() {
+ return IFingerprintService.Stub.asInterface(
+ ServiceManager.getService(Context.FINGERPRINT_SERVICE));
+ }
+
+ IFaceService getFaceService() {
+ return IFaceService.Stub.asInterface(ServiceManager.getService(Context.FACE_SERVICE));
+ }
+
+ SettingObserver getSettingObserver(Context context, Handler handler,
+ List<EnabledOnKeyguardCallback> callbacks) {
+ return new SettingObserver(context, handler, callbacks);
+ }
+
+ KeyStore getKeyStore() {
+ return KeyStore.getInstance();
+ }
+
+ boolean isDebugEnabled(Context context, int userId) {
+ return Utils.isDebugEnabled(context, userId);
+ }
+
+ void publishBinderService(BiometricService service, IBiometricService.Stub impl) {
+ service.publishBinderService(Context.BIOMETRIC_SERVICE, impl);
+ }
+ }
+
/**
* Initializes the system service.
* <p>
@@ -883,11 +934,19 @@
* @param context The system server context.
*/
public BiometricService(Context context) {
+ this(context, new Injector());
+ }
+
+ @VisibleForTesting
+ BiometricService(Context context, Injector injector) {
super(context);
+ mInjector = injector;
+ mImpl = new BiometricServiceWrapper();
mAppOps = context.getSystemService(AppOpsManager.class);
mEnabledOnKeyguardCallbacks = new ArrayList<>();
- mSettingObserver = new SettingObserver(mHandler);
+ mSettingObserver = mInjector.getSettingObserver(context, mHandler,
+ mEnabledOnKeyguardCallbacks);
final PackageManager pm = context.getPackageManager();
mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
@@ -895,7 +954,7 @@
mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
try {
- ActivityManager.getService().registerUserSwitchObserver(
+ injector.getActivityManagerService().registerUserSwitchObserver(
new UserSwitchObserver() {
@Override
public void onUserSwitchComplete(int newUserId) {
@@ -913,17 +972,14 @@
public void onStart() {
// TODO: maybe get these on-demand
if (mHasFeatureFingerprint) {
- mFingerprintService = IFingerprintService.Stub.asInterface(
- ServiceManager.getService(Context.FINGERPRINT_SERVICE));
+ mFingerprintService = mInjector.getFingerprintService();
}
if (mHasFeatureFace) {
- mFaceService = IFaceService.Stub.asInterface(
- ServiceManager.getService(Context.FACE_SERVICE));
+ mFaceService = mInjector.getFaceService();
}
- mActivityTaskManager = ActivityTaskManager.getService();
- mStatusBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+ mKeyStore = mInjector.getKeyStore();
+ mStatusBarService = mInjector.getStatusBarService();
// Cache the authenticators
for (int i = 0; i < FEATURE_ID.length; i++) {
@@ -934,7 +990,7 @@
}
}
- publishBinderService(Context.BIOMETRIC_SERVICE, new BiometricServiceWrapper());
+ mInjector.publishBinderService(this, mImpl);
}
/**
@@ -1068,7 +1124,7 @@
}
private void logDialogDismissed(int reason) {
- if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
+ if (reason == BiometricPrompt.DISMISSED_REASON_CONFIRMED) {
// Explicit auth, authentication confirmed.
// Latency in this case is authenticated -> confirmed. <Biometric>Service
// should have the first half (first acquired -> authenticated).
@@ -1094,7 +1150,7 @@
mCurrentAuthSession.mRequireConfirmation,
StatsLog.BIOMETRIC_AUTHENTICATED__STATE__CONFIRMED,
latency,
- Utils.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId));
+ mInjector.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId));
} else {
final long latency = System.currentTimeMillis() - mCurrentAuthSession.mStartTimeMs;
@@ -1122,7 +1178,7 @@
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT,
error,
0 /* vendorCode */,
- Utils.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId),
+ mInjector.isDebugEnabled(getContext(), mCurrentAuthSession.mUserId),
latency);
}
}
@@ -1145,51 +1201,22 @@
return modality;
}
- private void handleTaskStackChanged() {
- try {
- final List<ActivityManager.RunningTaskInfo> runningTasks =
- mActivityTaskManager.getTasks(1);
- if (!runningTasks.isEmpty()) {
- final String topPackage = runningTasks.get(0).topActivity.getPackageName();
- if (mCurrentAuthSession != null
- && !topPackage.contentEquals(mCurrentAuthSession.mOpPackageName)) {
- mStatusBarService.hideBiometricDialog();
- mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
- mCurrentAuthSession.mClientReceiver.onError(
- BiometricConstants.BIOMETRIC_ERROR_CANCELED,
- getContext().getString(
- com.android.internal.R.string.biometric_error_canceled)
- );
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
- mCurrentAuthSession = null;
- }
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to get running tasks", e);
- }
- }
-
private void handleAuthenticationSucceeded(boolean requireConfirmation, byte[] token) {
-
try {
// Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
// after user dismissed/canceled dialog).
if (mCurrentAuthSession == null) {
- Slog.e(TAG, "onAuthenticationSucceeded(): Auth session is null");
+ Slog.e(TAG, "handleAuthenticationSucceeded: Auth session is null");
return;
}
+ // Store the auth token and submit it to keystore after the dialog is confirmed /
+ // animating away.
+ mCurrentAuthSession.mTokenEscrow = token;
if (!requireConfirmation) {
- mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
- KeyStore.getInstance().addAuthToken(token);
- mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
- mCurrentAuthSession = null;
+ mCurrentAuthSession.mState = STATE_AUTHENTICATED_PENDING_SYSUI;
} else {
mCurrentAuthSession.mAuthenticatedTimeMs = System.currentTimeMillis();
- // Store the auth token and submit it to keystore after the confirmation
- // button has been pressed.
- mCurrentAuthSession.mTokenEscrow = token;
mCurrentAuthSession.mState = STATE_AUTH_PENDING_CONFIRM;
}
@@ -1201,12 +1228,13 @@
}
}
- private void handleAuthenticationFailed(String failureReason) {
+ private void handleAuthenticationRejected(String failureReason) {
+ Slog.v(TAG, "handleAuthenticationRejected: " + failureReason);
try {
// Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
// after user dismissed/canceled dialog).
if (mCurrentAuthSession == null) {
- Slog.e(TAG, "onAuthenticationFailed(): Auth session is null");
+ Slog.e(TAG, "handleAuthenticationRejected: Auth session is null");
return;
}
@@ -1225,16 +1253,31 @@
}
}
+ private void handleAuthenticationTimedOut(String message) {
+ Slog.v(TAG, "handleAuthenticationTimedOut: " + message);
+ try {
+ // Should never happen, log this to catch bad HAL behavior (e.g. auth succeeded
+ // after user dismissed/canceled dialog).
+ if (mCurrentAuthSession == null) {
+ Slog.e(TAG, "handleAuthenticationTimedOut: Auth session is null");
+ return;
+ }
+
+ mStatusBarService.onBiometricAuthenticated(false, message);
+ mCurrentAuthSession.mState = STATE_AUTH_PAUSED;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote exception", e);
+ }
+ }
+
private void handleOnConfirmDeviceCredentialSuccess() {
if (mConfirmDeviceCredentialReceiver == null) {
- Slog.w(TAG, "onCDCASuccess null!");
+ Slog.w(TAG, "handleOnConfirmDeviceCredentialSuccess null!");
return;
}
try {
- mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
mConfirmDeviceCredentialReceiver.onAuthenticationSucceeded();
if (mCurrentAuthSession != null) {
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
mCurrentAuthSession = null;
}
} catch (RemoteException e) {
@@ -1245,14 +1288,13 @@
private void handleOnConfirmDeviceCredentialError(int error, String message) {
if (mConfirmDeviceCredentialReceiver == null) {
- Slog.w(TAG, "onCDCAError null! Error: " + error + " " + message);
+ Slog.w(TAG, "handleOnConfirmDeviceCredentialError null! Error: "
+ + error + " " + message);
return;
}
try {
- mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
mConfirmDeviceCredentialReceiver.onError(error, message);
if (mCurrentAuthSession != null) {
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
mCurrentAuthSession = null;
}
} catch (RemoteException e) {
@@ -1272,7 +1314,7 @@
}
private void handleOnError(int cookie, int error, String message) {
- Slog.d(TAG, "Error: " + error + " cookie: " + cookie);
+ Slog.d(TAG, "handleOnError: " + error + " cookie: " + cookie);
// Errors can either be from the current auth session or the pending auth session.
// The pending auth session may receive errors such as ERROR_LOCKOUT before
// it becomes the current auth session. Similarly, the current auth session may
@@ -1282,6 +1324,9 @@
try {
if (mCurrentAuthSession != null && mCurrentAuthSession.containsCookie(cookie)) {
+ mCurrentAuthSession.mErrorEscrow = error;
+ mCurrentAuthSession.mErrorStringEscrow = message;
+
if (mCurrentAuthSession.isFromConfirmDeviceCredential()) {
// If we were invoked by ConfirmDeviceCredential, do not delete the current
// auth session since we still need to respond to cancel signal while
@@ -1293,39 +1338,18 @@
mCurrentAuthSession.mState = STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC;
mStatusBarService.hideBiometricDialog();
} else if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
- mStatusBarService.onBiometricError(message);
+ mCurrentAuthSession.mState = STATE_ERROR_PENDING_SYSUI;
if (error == BiometricConstants.BIOMETRIC_ERROR_CANCELED) {
- mActivityTaskManager.unregisterTaskStackListener(
- mTaskStackListener);
- mCurrentAuthSession.mClientReceiver.onError(error, message);
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
- mCurrentAuthSession = null;
mStatusBarService.hideBiometricDialog();
} else {
- // Send errors after the dialog is dismissed.
- mHandler.postDelayed(() -> {
- try {
- if (mCurrentAuthSession != null) {
- mActivityTaskManager.unregisterTaskStackListener(
- mTaskStackListener);
- mCurrentAuthSession.mClientReceiver.onError(error,
- message);
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
- mCurrentAuthSession = null;
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote exception", e);
- }
- }, BiometricPrompt.HIDE_DIALOG_DELAY);
+ mStatusBarService.onBiometricError(message);
}
} else if (mCurrentAuthSession.mState == STATE_AUTH_PAUSED) {
// In the "try again" state, we should forward canceled errors to
- // the client and and clean up.
+ // the client and and clean up. The only error we should get here is
+ // ERROR_CANCELED due to another client kicking us out.
mCurrentAuthSession.mClientReceiver.onError(error, message);
- mStatusBarService.onBiometricError(message);
- mActivityTaskManager.unregisterTaskStackListener(
- mTaskStackListener);
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
+ mStatusBarService.hideBiometricDialog();
mCurrentAuthSession = null;
} else {
Slog.e(TAG, "Impossible session error state: "
@@ -1335,7 +1359,6 @@
&& mPendingAuthSession.containsCookie(cookie)) {
if (mPendingAuthSession.mState == STATE_AUTH_CALLED) {
mPendingAuthSession.mClientReceiver.onError(error, message);
- mPendingAuthSession.mState = STATE_AUTH_IDLE;
mPendingAuthSession = null;
} else {
Slog.e(TAG, "Impossible pending session error state: "
@@ -1370,42 +1393,50 @@
private void handleOnDismissed(int reason) {
if (mCurrentAuthSession == null) {
- Slog.e(TAG, "onDialogDismissed: " + reason + ", auth session null");
+ Slog.e(TAG, "onDismissed: " + reason + ", auth session null");
return;
}
logDialogDismissed(reason);
try {
- if (reason != BiometricPrompt.DISMISSED_REASON_POSITIVE) {
- // Positive button is used by passive modalities as a "confirm" button,
- // do not send to client
- mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
- // Cancel authentication. Skip the token/package check since we are cancelling
- // from system server. The interface is permission protected so this is fine.
- cancelInternal(null /* token */, null /* package */, false /* fromClient */);
- }
- if (reason == BiometricPrompt.DISMISSED_REASON_USER_CANCEL) {
- mCurrentAuthSession.mClientReceiver.onError(
- BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
- getContext().getString(
- com.android.internal.R.string.biometric_error_user_canceled));
- } else if (reason == BiometricPrompt.DISMISSED_REASON_POSITIVE) {
- // Have the service send the token to KeyStore, and send onAuthenticated
- // to the application
- KeyStore.getInstance().addAuthToken(mCurrentAuthSession.mTokenEscrow);
- mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+ switch (reason) {
+ case BiometricPrompt.DISMISSED_REASON_CONFIRMED:
+ case BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED:
+ mKeyStore.addAuthToken(mCurrentAuthSession.mTokenEscrow);
+ mCurrentAuthSession.mClientReceiver.onAuthenticationSucceeded();
+ break;
+
+ case BiometricPrompt.DISMISSED_REASON_NEGATIVE:
+ mCurrentAuthSession.mClientReceiver.onDialogDismissed(reason);
+ // Cancel authentication. Skip the token/package check since we are cancelling
+ // from system server. The interface is permission protected so this is fine.
+ cancelInternal(null /* token */, null /* package */, false /* fromClient */);
+ break;
+
+ case BiometricPrompt.DISMISSED_REASON_USER_CANCEL:
+ mCurrentAuthSession.mClientReceiver.onError(
+ BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED,
+ getContext().getString(R.string.biometric_error_user_canceled));
+ // Cancel authentication. Skip the token/package check since we are cancelling
+ // from system server. The interface is permission protected so this is fine.
+ cancelInternal(null /* token */, null /* package */, false /* fromClient */);
+ break;
+
+ case BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED:
+ case BiometricPrompt.DISMISSED_REASON_ERROR:
+ mCurrentAuthSession.mClientReceiver.onError(mCurrentAuthSession.mErrorEscrow,
+ mCurrentAuthSession.mErrorStringEscrow);
+ break;
+
+ default:
+ Slog.w(TAG, "Unhandled reason: " + reason);
+ break;
}
- // Do not clean up yet if we are from ConfirmDeviceCredential. We should be in the
- // STATE_BIOMETRIC_AUTH_CANCELED_SHOWING_CDC. The session should only be removed when
- // ConfirmDeviceCredential is confirmed or canceled.
- // TODO(b/123378871): Remove when moved
- if (!mCurrentAuthSession.isFromConfirmDeviceCredential()) {
- mActivityTaskManager.unregisterTaskStackListener(mTaskStackListener);
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
- mCurrentAuthSession = null;
- }
+ // Dialog is gone, auth session is done.
+ mCurrentAuthSession = null;
+
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
@@ -1472,8 +1503,8 @@
if (!continuing) {
mStatusBarService.showBiometricDialog(mCurrentAuthSession.mBundle,
- mInternalReceiver, modality, requireConfirmation, userId);
- mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
+ mInternalReceiver, modality, requireConfirmation, userId,
+ mCurrentAuthSession.mOpPackageName);
}
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
@@ -1517,8 +1548,6 @@
return;
}
- mCurrentModality = modality;
-
// Start preparing for authentication. Authentication starts when
// all modalities requested have invoked onReadyForAuthentication.
authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
@@ -1610,7 +1639,6 @@
com.android.internal.R.string.biometric_error_user_canceled)
);
- mCurrentAuthSession.mState = STATE_AUTH_IDLE;
mCurrentAuthSession = null;
mStatusBarService.hideBiometricDialog();
} catch (RemoteException e) {
@@ -1637,25 +1665,31 @@
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
- mHandler.post(() -> {
- try {
- // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
- // drivers have canceled authentication.
- if ((mCurrentModality & TYPE_FINGERPRINT) != 0) {
- mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
- callingUid, callingPid, callingUserId, fromClient);
- }
- if ((mCurrentModality & TYPE_IRIS) != 0) {
- Slog.w(TAG, "Iris unsupported");
- }
- if ((mCurrentModality & TYPE_FACE) != 0) {
- mFaceService.cancelAuthenticationFromService(token, opPackageName,
- callingUid, callingPid, callingUserId, fromClient);
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to cancel authentication");
- }
- });
- }
+ try {
+ if (mCurrentAuthSession == null) {
+ Slog.w(TAG, "Skipping cancelInternal");
+ return;
+ } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
+ Slog.w(TAG, "Skipping cancelInternal, state: " + mCurrentAuthSession.mState);
+ return;
+ }
+
+ // TODO: For multiple modalities, send a single ERROR_CANCELED only when all
+ // drivers have canceled authentication.
+ if ((mCurrentAuthSession.mModality & TYPE_FINGERPRINT) != 0) {
+ mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
+ callingUid, callingPid, callingUserId, fromClient);
+ }
+ if ((mCurrentAuthSession.mModality & TYPE_IRIS) != 0) {
+ Slog.w(TAG, "Iris unsupported");
+ }
+ if ((mCurrentAuthSession.mModality & TYPE_FACE) != 0) {
+ mFaceService.cancelAuthenticationFromService(token, opPackageName,
+ callingUid, callingPid, callingUserId, fromClient);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to cancel authentication");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index f3f9754..2de18c3 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -420,7 +420,7 @@
throw new UnsupportedOperationException("Stub!");
}
- default void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
+ default void onAuthenticationFailedInternal()
throws RemoteException {
throw new UnsupportedOperationException("Stub!");
}
@@ -457,10 +457,10 @@
}
@Override
- public void onAuthenticationFailedInternal(int cookie, boolean requireConfirmation)
+ public void onAuthenticationFailedInternal()
throws RemoteException {
if (getWrapperReceiver() != null) {
- getWrapperReceiver().onAuthenticationFailed(cookie, requireConfirmation);
+ getWrapperReceiver().onAuthenticationFailed();
}
}
}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
index 53890a4..a0eafb4 100644
--- a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -35,6 +35,8 @@
import android.hardware.broadcastradio.V2_0.VendorKeyValue;
import android.hardware.radio.RadioManager;
import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.Looper;
import android.os.RemoteException;
import android.util.MutableInt;
import android.util.Slog;
@@ -45,6 +47,7 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@@ -56,6 +59,7 @@
@NonNull public final RadioManager.ModuleProperties mProperties;
private final Object mLock = new Object();
+ @NonNull private final Handler mHandler;
@GuardedBy("mLock")
private ITunerSession mHalTunerSession;
@@ -77,22 +81,24 @@
private final ITunerCallback mHalTunerCallback = new ITunerCallback.Stub() {
@Override
public void onTuneFailed(int result, ProgramSelector programSelector) {
- fanoutAidlCallback(cb -> cb.onTuneFailed(result, Convert.programSelectorFromHal(
- programSelector)));
+ lockAndFireLater(() -> {
+ android.hardware.radio.ProgramSelector csel =
+ Convert.programSelectorFromHal(programSelector);
+ fanoutAidlCallbackLocked(cb -> cb.onTuneFailed(result, csel));
+ });
}
@Override
public void onCurrentProgramInfoChanged(ProgramInfo halProgramInfo) {
- RadioManager.ProgramInfo programInfo = Convert.programInfoFromHal(halProgramInfo);
- synchronized (mLock) {
- mCurrentProgramInfo = programInfo;
- fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(programInfo));
- }
+ lockAndFireLater(() -> {
+ mCurrentProgramInfo = Convert.programInfoFromHal(halProgramInfo);
+ fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(mCurrentProgramInfo));
+ });
}
@Override
public void onProgramListUpdated(ProgramListChunk programListChunk) {
- synchronized (mLock) {
+ lockAndFireLater(() -> {
android.hardware.radio.ProgramList.Chunk chunk =
Convert.programListChunkFromHal(programListChunk);
mProgramInfoCache.filterAndApplyChunk(chunk);
@@ -100,20 +106,23 @@
for (TunerSession tunerSession : mAidlTunerSessions) {
tunerSession.onMergedProgramListUpdateFromHal(chunk);
}
- }
+ });
}
@Override
public void onAntennaStateChange(boolean connected) {
- synchronized (mLock) {
+ lockAndFireLater(() -> {
mAntennaConnected = connected;
fanoutAidlCallbackLocked(cb -> cb.onAntennaState(connected));
- }
+ });
}
@Override
public void onParametersUpdated(ArrayList<VendorKeyValue> parameters) {
- fanoutAidlCallback(cb -> cb.onParametersUpdated(Convert.vendorInfoFromHal(parameters)));
+ lockAndFireLater(() -> {
+ Map<String, String> cparam = Convert.vendorInfoFromHal(parameters);
+ fanoutAidlCallbackLocked(cb -> cb.onParametersUpdated(cparam));
+ });
}
};
@@ -126,6 +135,7 @@
@NonNull RadioManager.ModuleProperties properties) {
mProperties = Objects.requireNonNull(properties);
mService = Objects.requireNonNull(service);
+ mHandler = new Handler(Looper.getMainLooper());
}
public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName) {
@@ -310,15 +320,22 @@
}
}
+ // add to mHandler queue, but ensure the runnable holds mLock when it gets executed
+ private void lockAndFireLater(Runnable r) {
+ mHandler.post(() -> {
+ synchronized (mLock) {
+ r.run();
+ }
+ });
+ }
+
interface AidlCallbackRunnable {
void run(android.hardware.radio.ITunerCallback callback) throws RemoteException;
}
// Invokes runnable with each TunerSession currently open.
void fanoutAidlCallback(AidlCallbackRunnable runnable) {
- synchronized (mLock) {
- fanoutAidlCallbackLocked(runnable);
- }
+ lockAndFireLater(() -> fanoutAidlCallbackLocked(runnable));
}
private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 3eea194..27050fa 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -16,6 +16,7 @@
package com.android.server.compat;
+import android.compat.IPlatformCompat;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.util.Slog;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 73d160d..86d1212 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1314,11 +1314,15 @@
mOffload.excludeDownstreamInterface(who.interfaceName());
mForwardedDownstreams.remove(who);
- // If this is a Wi-Fi interface, tell WifiManager of any errors.
+ // If this is a Wi-Fi interface, tell WifiManager of any errors
+ // or the inactive serving state.
if (who.interfaceType() == TETHERING_WIFI) {
if (who.lastError() != TETHER_ERROR_NO_ERROR) {
getWifiManager().updateInterfaceIpState(
who.interfaceName(), IFACE_IP_MODE_CONFIGURATION_ERROR);
+ } else {
+ getWifiManager().updateInterfaceIpState(
+ who.interfaceName(), IFACE_IP_MODE_UNSPECIFIED);
}
}
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index 3e1817b..36e872a 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -461,7 +461,7 @@
}
synchronized (mCache) {
- final String providerPackageName = getProviderPackageName(uri);
+ final String providerPackageName = getProviderPackageName(uri, userHandle);
invalidateCacheLocked(userHandle, providerPackageName, uri);
}
} finally {
@@ -1145,9 +1145,9 @@
}
}
- private @Nullable String getProviderPackageName(Uri uri) {
- final ProviderInfo pi = mContext.getPackageManager()
- .resolveContentProvider(uri.getAuthority(), 0);
+ private @Nullable String getProviderPackageName(Uri uri, int userId) {
+ final ProviderInfo pi = mContext.getPackageManager().resolveContentProviderAsUser(
+ uri.getAuthority(), 0, userId);
return (pi != null) ? pi.packageName : null;
}
@@ -1200,7 +1200,7 @@
mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
packageName);
- final String providerPackageName = getProviderPackageName(key);
+ final String providerPackageName = getProviderPackageName(key, userId);
final Pair<String, Uri> fullKey = Pair.create(packageName, key);
synchronized (mCache) {
@@ -1222,7 +1222,7 @@
mContext.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(),
packageName);
- final String providerPackageName = getProviderPackageName(key);
+ final String providerPackageName = getProviderPackageName(key, userId);
final Pair<String, Uri> fullKey = Pair.create(packageName, key);
synchronized (mCache) {
diff --git a/services/core/java/com/android/server/display/WifiDisplayController.java b/services/core/java/com/android/server/display/WifiDisplayController.java
index fc59b5b..d9d46b8 100644
--- a/services/core/java/com/android/server/display/WifiDisplayController.java
+++ b/services/core/java/com/android/server/display/WifiDisplayController.java
@@ -94,8 +94,8 @@
private final Handler mHandler;
private final Listener mListener;
- private final WifiP2pManager mWifiP2pManager;
- private final Channel mWifiP2pChannel;
+ private WifiP2pManager mWifiP2pManager;
+ private Channel mWifiP2pChannel;
private boolean mWifiP2pEnabled;
private boolean mWfdEnabled;
@@ -164,9 +164,6 @@
mHandler = handler;
mListener = listener;
- mWifiP2pManager = (WifiP2pManager)context.getSystemService(Context.WIFI_P2P_SERVICE);
- mWifiP2pChannel = mWifiP2pManager.initialize(context, handler.getLooper(), null);
-
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
@@ -191,6 +188,18 @@
updateSettings();
}
+ /**
+ * Used to lazily retrieve WifiP2pManager service.
+ */
+ private void retrieveWifiP2pManagerAndChannel() {
+ if (mWifiP2pManager == null) {
+ mWifiP2pManager = (WifiP2pManager)mContext.getSystemService(Context.WIFI_P2P_SERVICE);
+ }
+ if (mWifiP2pChannel == null && mWifiP2pManager != null) {
+ mWifiP2pChannel = mWifiP2pManager.initialize(mContext, mHandler.getLooper(), null);
+ }
+ }
+
private void updateSettings() {
final ContentResolver resolver = mContext.getContentResolver();
mWifiDisplayOnSetting = Settings.Global.getInt(resolver,
@@ -803,6 +812,9 @@
private void handleStateChanged(boolean enabled) {
mWifiP2pEnabled = enabled;
+ if (enabled) {
+ retrieveWifiP2pManagerAndChannel();
+ }
updateWfdEnableState();
}
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 00d39e2..f38f2f9 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -106,6 +106,7 @@
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.ILockSettings;
@@ -137,8 +138,10 @@
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
@@ -169,6 +172,8 @@
private static final int PROFILE_KEY_IV_SIZE = 12;
private static final String SEPARATE_PROFILE_CHALLENGE_KEY = "lockscreen.profilechallenge";
private static final int SYNTHETIC_PASSWORD_ENABLED_BY_DEFAULT = 1;
+ private static final String PREV_SYNTHETIC_PASSWORD_HANDLE_KEY = "prev-sp-handle";
+ private static final String SYNTHETIC_PASSWORD_UPDATE_TIME_KEY = "sp-handle-ts";
// No challenge provided
private static final int CHALLENGE_NONE = 0;
@@ -1614,17 +1619,17 @@
} catch (CertificateException | UnrecoverableKeyException
| IOException | BadPaddingException | IllegalBlockSizeException | KeyStoreException
| NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
- throw new RuntimeException("Failed to encrypt key", e);
+ throw new IllegalStateException("Failed to encrypt key", e);
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
if (iv.length != PROFILE_KEY_IV_SIZE) {
- throw new RuntimeException("Invalid iv length: " + iv.length);
+ throw new IllegalArgumentException("Invalid iv length: " + iv.length);
}
outputStream.write(iv);
outputStream.write(encryptionResult);
} catch (IOException e) {
- throw new RuntimeException("Failed to concatenate byte arrays", e);
+ throw new IllegalStateException("Failed to concatenate byte arrays", e);
}
mStorage.writeChildProfileLock(userId, outputStream.toByteArray());
}
@@ -1692,7 +1697,7 @@
digest.update(credential);
return digest.digest();
} catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("NoSuchAlgorithmException for SHA-512");
+ throw new IllegalStateException("NoSuchAlgorithmException for SHA-512");
}
}
@@ -2474,7 +2479,7 @@
gateKeeperClearSecureUserId(userId);
}
fixateNewestUserKeyAuth(userId);
- setLong(SYNTHETIC_PASSWORD_HANDLE_KEY, handle, userId);
+ setSyntheticPasswordHandleLocked(handle, userId);
return auth;
}
@@ -2483,6 +2488,14 @@
SyntheticPasswordManager.DEFAULT_HANDLE, userId);
}
+ private void setSyntheticPasswordHandleLocked(long handle, int userId) {
+ final long oldHandle = getSyntheticPasswordHandleLocked(userId);
+ setLong(SYNTHETIC_PASSWORD_HANDLE_KEY, handle, userId);
+ setLong(PREV_SYNTHETIC_PASSWORD_HANDLE_KEY, oldHandle, userId);
+ setLong(SYNTHETIC_PASSWORD_UPDATE_TIME_KEY, System.currentTimeMillis(), userId);
+
+ }
+
private boolean isSyntheticPasswordBasedCredentialLocked(int userId) {
if (userId == USER_FRP) {
final int type = mStorage.readPersistentDataBlock().type;
@@ -2660,7 +2673,7 @@
unlockKeystore(auth.deriveKeyStorePassword(), userId);
setKeystorePassword(null, userId);
}
- setLong(SYNTHETIC_PASSWORD_HANDLE_KEY, newHandle, userId);
+ setSyntheticPasswordHandleLocked(newHandle, userId);
synchronizeUnifiedWorkChallengeForProfiles(userId, profilePasswords);
notifyActivePasswordMetricsAvailable(credentialType, credential, userId);
@@ -2928,29 +2941,57 @@
return true;
}
+ static String timestampToString(long timestamp) {
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(timestamp));
+ }
+
@Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args){
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ if (!DumpUtils.checkDumpPermission(mContext, TAG, printWriter)) return;
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
pw.println("Current lock settings service state:");
+
pw.println(String.format("SP Enabled = %b",
mLockPatternUtils.isSyntheticPasswordEnabled()));
+ pw.println();
+ pw.println("User State:");
+ pw.increaseIndent();
List<UserInfo> users = mUserManager.getUsers();
for (int user = 0; user < users.size(); user++) {
final int userId = users.get(user).id;
- pw.println(" User " + userId);
+ pw.println("User " + userId);
+ pw.increaseIndent();
synchronized (mSpManager) {
- pw.println(String.format(" SP Handle = %x",
+ pw.println(String.format("SP Handle: %x",
getSyntheticPasswordHandleLocked(userId)));
+ pw.println(String.format("Last changed: %s (%x)",
+ timestampToString(getLong(SYNTHETIC_PASSWORD_UPDATE_TIME_KEY, 0, userId)),
+ getLong(PREV_SYNTHETIC_PASSWORD_HANDLE_KEY, 0, userId)));
}
try {
- pw.println(String.format(" SID = %x",
+ pw.println(String.format("SID: %x",
getGateKeeperService().getSecureUserId(userId)));
} catch (RemoteException e) {
// ignore.
}
+ // It's OK to dump the password type since anyone with physical access can just
+ // observe it from the keyguard directly.
+ pw.println("PasswordType: " + getLong(LockPatternUtils.PASSWORD_TYPE_KEY, 0, userId));
+ pw.println("hasPassword: " + havePassword(userId));
+ pw.println("hasPattern: " + havePattern(userId)); // print raw credential type instead?
+ pw.println("SeparateChallenge: " + getSeparateProfileChallengeEnabled(userId));
+ pw.decreaseIndent();
}
+ pw.println();
+ pw.decreaseIndent();
+
+ pw.println("Storage:");
+ pw.increaseIndent();
+ mStorage.dump(pw);
+ pw.println();
+ pw.decreaseIndent();
}
private void disableEscrowTokenOnNonManagedDevicesIfNeeded(int userId) {
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 29b8aa2..fe12a94 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -35,6 +35,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.CredentialType;
@@ -140,7 +141,7 @@
dos.close();
return os.toByteArray();
} catch (IOException e) {
- throw new RuntimeException(e);
+ throw new IllegalStateException("Fail to serialze credential hash", e);
}
}
@@ -157,7 +158,7 @@
}
return new CredentialHash(hash, type);
} catch (IOException e) {
- throw new RuntimeException(e);
+ throw new IllegalStateException("Fail to deserialze credential hash", e);
}
}
}
@@ -666,7 +667,7 @@
dos.writeInt(qualityForUi);
dos.write(payload);
} catch (IOException e) {
- throw new RuntimeException("ByteArrayOutputStream cannot throw IOException");
+ throw new IllegalStateException("ByteArrayOutputStream cannot throw IOException");
}
return os.toByteArray();
}
@@ -676,6 +677,26 @@
void initialize(SQLiteDatabase db);
}
+ public void dump(IndentingPrintWriter pw) {
+ final UserManager um = UserManager.get(mContext);
+ for (UserInfo user : um.getUsers(false)) {
+ File userPath = getSyntheticPasswordDirectoryForUser(user.id);
+ pw.println(String.format("User %d [%s]:", user.id, userPath.getAbsolutePath()));
+ pw.increaseIndent();
+ File[] files = userPath.listFiles();
+ if (files != null) {
+ for (File file : files) {
+ pw.println(String.format("%4d %s %s", file.length(),
+ LockSettingsService.timestampToString(file.lastModified()),
+ file.getName()));
+ }
+ } else {
+ pw.println("[Not found]");
+ }
+ pw.decreaseIndent();
+ }
+ }
+
static class DatabaseHelper extends SQLiteOpenHelper {
private static final String TAG = "LockSettingsDB";
private static final String DATABASE_NAME = "locksettings.db";
diff --git a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
index 4ef63c0..17aca15 100644
--- a/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
+++ b/services/core/java/com/android/server/locksettings/PasswordSlotManager.java
@@ -109,7 +109,7 @@
public void markSlotInUse(int slot) throws RuntimeException {
ensureSlotMapLoaded();
if (mSlotMap.containsKey(slot) && !mSlotMap.get(slot).equals(getMode())) {
- throw new RuntimeException("password slot " + slot + " is not available");
+ throw new IllegalStateException("password slot " + slot + " is not available");
}
mSlotMap.put(slot, getMode());
saveSlotMap();
@@ -123,7 +123,7 @@
public void markSlotDeleted(int slot) throws RuntimeException {
ensureSlotMapLoaded();
if (mSlotMap.containsKey(slot) && !mSlotMap.get(slot).equals(getMode())) {
- throw new RuntimeException("password slot " + slot + " cannot be deleted");
+ throw new IllegalStateException("password slot " + slot + " cannot be deleted");
}
mSlotMap.remove(slot);
saveSlotMap();
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
index 388e51f..ea0fb47 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordCrypto.java
@@ -18,6 +18,7 @@
import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
+import android.util.Slog;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -43,6 +44,7 @@
import javax.crypto.spec.SecretKeySpec;
public class SyntheticPasswordCrypto {
+ private static final String TAG = "SyntheticPasswordCrypto";
private static final int PROFILE_KEY_IV_SIZE = 12;
private static final int DEFAULT_TAG_LENGTH_BITS = 128;
private static final int AES_KEY_LENGTH = 32; // 256-bit AES key
@@ -80,12 +82,12 @@
byte[] ciphertext = cipher.doFinal(blob);
byte[] iv = cipher.getIV();
if (iv.length != PROFILE_KEY_IV_SIZE) {
- throw new RuntimeException("Invalid iv length: " + iv.length);
+ throw new IllegalArgumentException("Invalid iv length: " + iv.length);
}
final GCMParameterSpec spec = cipher.getParameters().getParameterSpec(
GCMParameterSpec.class);
if (spec.getTLen() != DEFAULT_TAG_LENGTH_BITS) {
- throw new RuntimeException("Invalid tag length: " + spec.getTLen());
+ throw new IllegalArgumentException("Invalid tag length: " + spec.getTLen());
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(iv);
@@ -102,7 +104,7 @@
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException
| IllegalBlockSizeException | BadPaddingException | IOException
| InvalidParameterSpecException e) {
- e.printStackTrace();
+ Slog.e(TAG, "Failed to encrypt", e);
return null;
}
}
@@ -116,7 +118,7 @@
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException
| IllegalBlockSizeException | BadPaddingException
| InvalidAlgorithmParameterException e) {
- e.printStackTrace();
+ Slog.e(TAG, "Failed to decrypt", e);
return null;
}
}
@@ -130,8 +132,8 @@
byte[] intermediate = decrypt(applicationId, APPLICATION_ID_PERSONALIZATION, blob);
return decrypt(decryptionKey, intermediate);
} catch (Exception e) {
- e.printStackTrace();
- throw new RuntimeException("Failed to decrypt blob", e);
+ Slog.e(TAG, "Failed to decrypt V1 blob", e);
+ throw new IllegalStateException("Failed to decrypt blob", e);
}
}
@@ -148,8 +150,8 @@
| KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException
| InvalidKeyException | UnrecoverableKeyException
| InvalidAlgorithmParameterException e) {
- e.printStackTrace();
- throw new RuntimeException("Failed to decrypt blob", e);
+ Slog.e(TAG, "Failed to decrypt blob", e);
+ throw new IllegalStateException("Failed to decrypt blob", e);
}
}
@@ -180,8 +182,8 @@
| KeyStoreException | NoSuchPaddingException | NoSuchAlgorithmException
| InvalidKeyException
| InvalidParameterSpecException e) {
- e.printStackTrace();
- throw new RuntimeException("Failed to encrypt blob", e);
+ Slog.e(TAG, "Failed to create blob", e);
+ throw new IllegalStateException("Failed to encrypt blob", e);
}
}
@@ -193,7 +195,7 @@
keyStore.deleteEntry(keyAlias);
} catch (KeyStoreException | NoSuchAlgorithmException | CertificateException
| IOException e) {
- e.printStackTrace();
+ Slog.e(TAG, "Failed to destroy blob", e);
}
}
@@ -202,7 +204,7 @@
final int PADDING_LENGTH = 128;
MessageDigest digest = MessageDigest.getInstance("SHA-512");
if (personalisation.length > PADDING_LENGTH) {
- throw new RuntimeException("Personalisation too long");
+ throw new IllegalArgumentException("Personalisation too long");
}
// Personalize the hash
// Pad it to the block size of the hash function
@@ -213,7 +215,7 @@
}
return digest.digest();
} catch (NoSuchAlgorithmException e) {
- throw new RuntimeException("NoSuchAlgorithmException for SHA-512", e);
+ throw new IllegalStateException("NoSuchAlgorithmException for SHA-512", e);
}
}
}
diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
index 9bf9ca8..955a9aa 100644
--- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java
@@ -353,12 +353,12 @@
*/
private byte[] weaverEnroll(int slot, byte[] key, @Nullable byte[] value) {
if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
- throw new RuntimeException("Invalid slot for weaver");
+ throw new IllegalArgumentException("Invalid slot for weaver");
}
if (key == null) {
key = new byte[mWeaverConfig.keySize];
} else if (key.length != mWeaverConfig.keySize) {
- throw new RuntimeException("Invalid key size for weaver");
+ throw new IllegalArgumentException("Invalid key size for weaver");
}
if (value == null) {
value = secureRandom(mWeaverConfig.valueSize);
@@ -383,12 +383,12 @@
*/
private VerifyCredentialResponse weaverVerify(int slot, byte[] key) {
if (slot == INVALID_WEAVER_SLOT || slot >= mWeaverConfig.slots) {
- throw new RuntimeException("Invalid slot for weaver");
+ throw new IllegalArgumentException("Invalid slot for weaver");
}
if (key == null) {
key = new byte[mWeaverConfig.keySize];
} else if (key.length != mWeaverConfig.keySize) {
- throw new RuntimeException("Invalid key size for weaver");
+ throw new IllegalArgumentException("Invalid key size for weaver");
}
final VerifyCredentialResponse[] response = new VerifyCredentialResponse[1];
try {
@@ -620,7 +620,7 @@
return i;
}
}
- throw new RuntimeException("Run out of weaver slots.");
+ throw new IllegalStateException("Run out of weaver slots.");
}
/**
@@ -1029,10 +1029,10 @@
if (version != SYNTHETIC_PASSWORD_VERSION_V3
&& version != SYNTHETIC_PASSWORD_VERSION_V2
&& version != SYNTHETIC_PASSWORD_VERSION_V1) {
- throw new RuntimeException("Unknown blob version");
+ throw new IllegalArgumentException("Unknown blob version");
}
if (blob[1] != type) {
- throw new RuntimeException("Invalid blob type");
+ throw new IllegalArgumentException("Invalid blob type");
}
final byte[] secret;
if (version == SYNTHETIC_PASSWORD_VERSION_V1) {
@@ -1237,7 +1237,7 @@
private byte[] passwordTokenToWeaverKey(byte[] token) {
byte[] key = SyntheticPasswordCrypto.personalisedHash(PERSONALISATION_WEAVER_KEY, token);
if (key.length < mWeaverConfig.keySize) {
- throw new RuntimeException("weaver key length too small");
+ throw new IllegalArgumentException("weaver key length too small");
}
return Arrays.copyOf(key, mWeaverConfig.keySize);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index aa548f2..30b2245 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -315,7 +315,7 @@
static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps
- static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION;
+ static final int INVALID_UID = -1;
static final boolean ENABLE_BLOCKED_TOASTS = true;
@@ -621,6 +621,8 @@
mConditionProviders.readXml(
parser, mAllowedManagedServicePackages, forRestore, userId);
migratedManagedServices = true;
+ } else if (mSnoozeHelper.XML_TAG_NAME.equals(parser.getName())) {
+ mSnoozeHelper.readXml(parser);
}
if (LOCKSCREEN_ALLOW_SECURE_NOTIFICATIONS_TAG.equals(parser.getName())) {
if (forRestore && userId != UserHandle.USER_SYSTEM) {
@@ -709,6 +711,7 @@
mPreferencesHelper.writeXml(out, forBackup, userId);
mListeners.writeXml(out, forBackup, userId);
mAssistants.writeXml(out, forBackup, userId);
+ mSnoozeHelper.writeXml(out);
mConditionProviders.writeXml(out, forBackup, userId);
if (!forBackup || userId == UserHandle.USER_SYSTEM) {
writeSecureNotificationsPolicy(out);
@@ -908,8 +911,22 @@
@Override
public void onNotificationError(int callingUid, int callingPid, String pkg, String tag,
int id, int uid, int initialPid, String message, int userId) {
+ final boolean fgService;
+ synchronized (mNotificationLock) {
+ NotificationRecord r = findNotificationLocked(pkg, tag, id, userId);
+ fgService = r != null && (r.getNotification().flags & FLAG_FOREGROUND_SERVICE) != 0;
+ }
cancelNotification(callingUid, callingPid, pkg, tag, id, 0, 0, false, userId,
REASON_ERROR, null);
+ if (fgService) {
+ // Still crash for foreground services, preventing the not-crash behaviour abused
+ // by apps to give us a garbage notification and silently start a fg service.
+ Binder.withCleanCallingIdentity(
+ () -> mAm.crashApplication(uid, initialPid, pkg, -1,
+ "Bad notification(tag=" + tag + ", id=" + id + ") posted from package "
+ + pkg + ", crashing app(uid=" + uid + ", pid=" + initialPid + "): "
+ + message));
+ }
}
@Override
@@ -1753,6 +1770,7 @@
com.android.internal.R.integer.config_notificationWarnRemoteViewSizeBytes);
mStripRemoteViewsSizeBytes = getContext().getResources().getInteger(
com.android.internal.R.integer.config_notificationStripRemoteViewSizeBytes);
+
}
@Override
@@ -4695,6 +4713,12 @@
// ensure opPkg is delegate if does not match pkg
int uid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+ if (uid == INVALID_UID) {
+ Slog.w(TAG, opPkg + ":" + callingUid + " trying to cancel notification "
+ + "for nonexistent pkg " + pkg + " in user " + userId);
+ return;
+ }
+
// if opPkg is not the same as pkg, make sure the notification given was posted
// by opPkg
if (!Objects.equals(pkg, opPkg)) {
@@ -4739,6 +4763,11 @@
// as "pkg"
final int notificationUid = resolveNotificationUid(opPkg, pkg, callingUid, userId);
+ if (notificationUid == INVALID_UID) {
+ throw new SecurityException("Caller " + opPkg + ":" + callingUid
+ + " trying to post for invalid pkg " + pkg + " in user " + incomingUserId);
+ }
+
checkRestrictedCategories(notification);
// Fix the notification as best we can.
@@ -5059,8 +5088,7 @@
}
@VisibleForTesting
- int resolveNotificationUid(String callingPkg, String targetPkg,
- int callingUid, int userId) {
+ int resolveNotificationUid(String callingPkg, String targetPkg, int callingUid, int userId) {
if (userId == UserHandle.USER_ALL) {
userId = USER_SYSTEM;
}
@@ -5071,16 +5099,16 @@
return callingUid;
}
- int targetUid = -1;
+ int targetUid = INVALID_UID;
try {
targetUid = mPackageManagerClient.getPackageUidAsUser(targetPkg, userId);
} catch (NameNotFoundException e) {
- /* ignore */
+ /* ignore, handled by caller */
}
// posted from app A on behalf of app B
- if (targetUid != -1 && (isCallerAndroid(callingPkg, callingUid)
+ if (isCallerAndroid(callingPkg, callingUid)
|| mPreferencesHelper.isDelegateAllowed(
- targetPkg, targetUid, callingPkg, callingUid))) {
+ targetPkg, targetUid, callingPkg, callingUid)) {
return targetUid;
}
@@ -5284,7 +5312,7 @@
updateLightsLocked();
if (mSnoozeCriterionId != null) {
mAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId);
- mSnoozeHelper.snooze(r);
+ mSnoozeHelper.snooze(r, mSnoozeCriterionId);
} else {
mSnoozeHelper.snooze(r, mDuration);
}
@@ -5387,6 +5415,27 @@
@Override
public void run() {
synchronized (mNotificationLock) {
+ final Long snoozeAt =
+ mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+ r.getUser().getIdentifier(),
+ r.sbn.getPackageName(), r.sbn.getKey());
+ final long currentTime = System.currentTimeMillis();
+ if (snoozeAt.longValue() > currentTime) {
+ (new SnoozeNotificationRunnable(r.sbn.getKey(),
+ snoozeAt.longValue() - currentTime, null)).snoozeLocked(r);
+ return;
+ }
+
+ final String contextId =
+ mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+ r.getUser().getIdentifier(),
+ r.sbn.getPackageName(), r.sbn.getKey());
+ if (contextId != null) {
+ (new SnoozeNotificationRunnable(r.sbn.getKey(),
+ 0, contextId)).snoozeLocked(r);
+ return;
+ }
+
mEnqueuedNotifications.add(r);
scheduleTimeoutLocked(r);
@@ -6937,6 +6986,7 @@
if (DBG) {
Slog.d(TAG, String.format("unsnooze event(%s, %s)", key, listenerName));
}
+ mSnoozeHelper.cleanupPersistedContext(key);
mSnoozeHelper.repost(key);
handleSavePolicyFile();
}
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 91f497c..8125d0d 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -55,6 +55,21 @@
* NotificationManagerService helper for handling snoozed notifications.
*/
public class SnoozeHelper {
+ public static final String XML_SNOOZED_NOTIFICATION_VERSION = "1";
+
+ protected static final String XML_TAG_NAME = "snoozed-notifications";
+
+ private static final String XML_SNOOZED_NOTIFICATION = "notification";
+ private static final String XML_SNOOZED_NOTIFICATION_CONTEXT = "context";
+ private static final String XML_SNOOZED_NOTIFICATION_PKG = "pkg";
+ private static final String XML_SNOOZED_NOTIFICATION_USER_ID = "user-id";
+ private static final String XML_SNOOZED_NOTIFICATION_KEY = "key";
+ //the time the snoozed notification should be reposted
+ private static final String XML_SNOOZED_NOTIFICATION_TIME = "time";
+ private static final String XML_SNOOZED_NOTIFICATION_CONTEXT_ID = "id";
+ private static final String XML_SNOOZED_NOTIFICATION_VERSION_LABEL = "version";
+
+
private static final String TAG = "SnoozeHelper";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String INDENT = " ";
@@ -72,6 +87,17 @@
// User id : package name : notification key : record.
private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, NotificationRecord>>>
mSnoozedNotifications = new ArrayMap<>();
+ // User id : package name : notification key : time-milliseconds .
+ // This member stores persisted snoozed notification trigger times. it persists through reboots
+ // It should have the notifications that haven't expired or re-posted yet
+ private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, Long>>>
+ mPersistedSnoozedNotifications = new ArrayMap<>();
+ // User id : package name : notification key : creation ID .
+ // This member stores persisted snoozed notification trigger context for the assistant
+ // it persists through reboots.
+ // It should have the notifications that haven't expired or re-posted yet
+ private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, String>>>
+ mPersistedSnoozedNotificationsWithContext = new ArrayMap<>();
// notification key : package.
private ArrayMap<String, String> mPackages = new ArrayMap<>();
// key : userId
@@ -89,6 +115,34 @@
mUserProfiles = userProfiles;
}
+ void cleanupPersistedContext(String key){
+ int userId = mUsers.get(key);
+ String pkg = mPackages.get(key);
+ synchronized (mPersistedSnoozedNotificationsWithContext) {
+ removeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
+ }
+ }
+
+ //This function has a side effect of removing the time from the list of persisted notifications.
+ //IT IS NOT IDEMPOTENT!
+ @NonNull
+ protected Long getSnoozeTimeForUnpostedNotification(int userId, String pkg, String key) {
+ Long time;
+ synchronized (mPersistedSnoozedNotifications) {
+ time = removeRecord(pkg, key, userId, mPersistedSnoozedNotifications);
+ }
+ if (time == null) {
+ return 0L;
+ }
+ return time;
+ }
+
+ protected String getSnoozeContextForUnpostedNotification(int userId, String pkg, String key) {
+ synchronized (mPersistedSnoozedNotificationsWithContext) {
+ return removeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext);
+ }
+ }
+
protected boolean isSnoozed(int userId, String pkg, String key) {
return mSnoozedNotifications.containsKey(userId)
&& mSnoozedNotifications.get(userId).containsKey(pkg)
@@ -169,32 +223,82 @@
* Snoozes a notification and schedules an alarm to repost at that time.
*/
protected void snooze(NotificationRecord record, long duration) {
+ String pkg = record.sbn.getPackageName();
+ String key = record.getKey();
+ int userId = record.getUser().getIdentifier();
+
snooze(record);
- scheduleRepost(record.sbn.getPackageName(), record.getKey(), record.getUserId(), duration);
+ scheduleRepost(pkg, key, userId, duration);
+ Long activateAt = System.currentTimeMillis() + duration;
+ synchronized (mPersistedSnoozedNotifications) {
+ storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, activateAt);
+ }
}
/**
* Records a snoozed notification.
*/
- protected void snooze(NotificationRecord record) {
+ protected void snooze(NotificationRecord record, String contextId) {
+ int userId = record.getUser().getIdentifier();
+ if (contextId != null) {
+ synchronized (mPersistedSnoozedNotificationsWithContext) {
+ storeRecord(record.sbn.getPackageName(), record.getKey(),
+ userId, mPersistedSnoozedNotificationsWithContext, contextId);
+ }
+ }
+ snooze(record);
+ }
+
+ private void snooze(NotificationRecord record) {
int userId = record.getUser().getIdentifier();
if (DEBUG) {
Slog.d(TAG, "Snoozing " + record.getKey());
}
- ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
- mSnoozedNotifications.get(userId);
+ storeRecord(record.sbn.getPackageName(), record.getKey(),
+ userId, mSnoozedNotifications, record);
+ mPackages.put(record.getKey(), record.sbn.getPackageName());
+ mUsers.put(record.getKey(), userId);
+ }
+
+ private <T> void storeRecord(String pkg, String key, Integer userId,
+ ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, T object) {
+
+ ArrayMap<String, ArrayMap<String, T>> records =
+ targets.get(userId);
if (records == null) {
records = new ArrayMap<>();
}
- ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.sbn.getPackageName());
+ ArrayMap<String, T> pkgRecords = records.get(pkg);
if (pkgRecords == null) {
pkgRecords = new ArrayMap<>();
}
- pkgRecords.put(record.getKey(), record);
- records.put(record.sbn.getPackageName(), pkgRecords);
- mSnoozedNotifications.put(userId, records);
- mPackages.put(record.getKey(), record.sbn.getPackageName());
- mUsers.put(record.getKey(), userId);
+ pkgRecords.put(key, object);
+ records.put(pkg, pkgRecords);
+ targets.put(userId, records);
+
+ }
+
+ private <T> T removeRecord(String pkg, String key, Integer userId,
+ ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets) {
+ T object = null;
+
+ ArrayMap<String, ArrayMap<String, T>> records =
+ targets.get(userId);
+ if (records == null) {
+ return null;
+ }
+ ArrayMap<String, T> pkgRecords = records.get(pkg);
+ if (pkgRecords == null) {
+ return null;
+ }
+ object = pkgRecords.remove(key);
+ if (pkgRecords.size() == 0) {
+ records.remove(pkg);
+ }
+ if (records.size() == 0) {
+ targets.remove(userId);
+ }
+ return object;
}
protected boolean cancel(int userId, String pkg, String tag, int id) {
@@ -414,13 +518,121 @@
}
}
- protected void writeXml(XmlSerializer out, boolean forBackup) throws IOException {
-
+ protected void writeXml(XmlSerializer out) throws IOException {
+ final long currentTime = System.currentTimeMillis();
+ out.startTag(null, XML_TAG_NAME);
+ writeXml(out, mPersistedSnoozedNotifications, XML_SNOOZED_NOTIFICATION,
+ value -> {
+ if (value < currentTime) {
+ return;
+ }
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_TIME,
+ value.toString());
+ });
+ writeXml(out, mPersistedSnoozedNotificationsWithContext, XML_SNOOZED_NOTIFICATION_CONTEXT,
+ value -> {
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID,
+ value);
+ });
+ out.endTag(null, XML_TAG_NAME);
}
- public void readXml(XmlPullParser parser, boolean forRestore)
- throws XmlPullParserException, IOException {
+ private interface Inserter<T> {
+ void insert(T t) throws IOException;
+ }
+ private <T> void writeXml(XmlSerializer out,
+ ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, String tag,
+ Inserter<T> attributeInserter)
+ throws IOException {
+ synchronized (targets) {
+ final int M = targets.size();
+ for (int i = 0; i < M; i++) {
+ final ArrayMap<String, ArrayMap<String, T>> packages =
+ targets.valueAt(i);
+ if (packages == null) {
+ continue;
+ }
+ final int N = packages.size();
+ for (int j = 0; j < N; j++) {
+ final ArrayMap<String, T> keyToValue = packages.valueAt(j);
+ if (keyToValue == null) {
+ continue;
+ }
+ final int O = keyToValue.size();
+ for (int k = 0; k < O; k++) {
+ T value = keyToValue.valueAt(k);
+ out.startTag(null, tag);
+
+ attributeInserter.insert(value);
+
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
+ XML_SNOOZED_NOTIFICATION_VERSION);
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, keyToValue.keyAt(k));
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, packages.keyAt(j));
+ out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID,
+ targets.keyAt(i).toString());
+
+ out.endTag(null, tag);
+
+ }
+ }
+ }
+ }
+ }
+
+ protected void readXml(XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ String tag = parser.getName();
+ if (type == XmlPullParser.END_TAG
+ && XML_TAG_NAME.equals(tag)) {
+ break;
+ }
+ if (type == XmlPullParser.START_TAG
+ && (XML_SNOOZED_NOTIFICATION.equals(tag)
+ || tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT))
+ && parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL)
+ .equals(XML_SNOOZED_NOTIFICATION_VERSION)) {
+ try {
+ final String key = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_KEY);
+ final String pkg = parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_PKG);
+ final int userId = Integer.parseInt(
+ parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_USER_ID));
+ if (tag.equals(XML_SNOOZED_NOTIFICATION)) {
+ final Long time = Long.parseLong(
+ parser.getAttributeValue(null, XML_SNOOZED_NOTIFICATION_TIME));
+ if (time > System.currentTimeMillis()) { //only read new stuff
+ synchronized (mPersistedSnoozedNotifications) {
+ storeRecord(pkg, key, userId, mPersistedSnoozedNotifications, time);
+ }
+ scheduleRepost(pkg, key, userId, time - System.currentTimeMillis());
+ }
+ continue;
+ }
+ if (tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) {
+ final String creationId = parser.getAttributeValue(
+ null, XML_SNOOZED_NOTIFICATION_CONTEXT_ID);
+ synchronized (mPersistedSnoozedNotificationsWithContext) {
+ storeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext,
+ creationId);
+ }
+ continue;
+ }
+
+
+ } catch (Exception e) {
+ //we dont cre if it is a number format exception or a null pointer exception.
+ //we just want to debug it and continue with our lives
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Exception in reading snooze data from policy xml: "
+ + e.getMessage());
+ }
+ }
+ }
+ }
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index 91824c3..20a4c75 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -129,11 +129,11 @@
}
}
- static void startIdmapService() {
+ private static void startIdmapService() {
SystemProperties.set("ctl.start", IDMAP_DAEMON);
}
- static void stopIdmapService() {
+ private static void stopIdmapService() {
SystemProperties.set("ctl.stop", IDMAP_DAEMON);
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 8171a8d..965ddc9 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -262,7 +262,6 @@
initIfNeeded();
onSwitchUser(UserHandle.USER_SYSTEM);
- IdmapDaemon.stopIdmapService();
publishBinderService(Context.OVERLAY_SERVICE, mService);
publishLocalService(OverlayManagerService.class, this);
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 4431d86..7eb7438 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -16,10 +16,10 @@
package com.android.server.pm;
+import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;
+
import android.Manifest;
import android.annotation.Nullable;
-import android.app.AppOpsManager;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
@@ -28,11 +28,14 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.permission.IPermissionManager;
+import android.provider.DeviceConfig;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.R;
+import com.android.server.FgThread;
+import com.android.server.compat.PlatformCompat;
import java.io.PrintWriter;
import java.util.Collections;
@@ -41,7 +44,6 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* The entity responsible for filtering visibility between apps based on declarations in their
@@ -98,14 +100,11 @@
private final IPermissionManager mPermissionManager;
- private final AppOpsManager mAppOpsManager;
- private final ConfigProvider mConfigProvider;
+ private final FeatureConfig mFeatureConfig;
- AppsFilter(ConfigProvider configProvider, IPermissionManager permissionManager,
- AppOpsManager appOpsManager, String[] forceQueryableWhitelist,
- boolean systemAppsQueryable) {
- mConfigProvider = configProvider;
- mAppOpsManager = appOpsManager;
+ AppsFilter(FeatureConfig featureConfig, IPermissionManager permissionManager,
+ String[] forceQueryableWhitelist, boolean systemAppsQueryable) {
+ mFeatureConfig = featureConfig;
final HashSet<String> forceQueryableByDeviceSet = new HashSet<>();
Collections.addAll(forceQueryableByDeviceSet, forceQueryableWhitelist);
this.mForceQueryableByDevice = Collections.unmodifiableSet(forceQueryableByDeviceSet);
@@ -114,27 +113,83 @@
mSystemAppsQueryable = systemAppsQueryable;
}
- public static AppsFilter create(Context context) {
- // tracks whether the feature is enabled where -1 is unknown, 0 is false and 1 is true;
- final AtomicInteger featureEnabled = new AtomicInteger(-1);
+ public interface FeatureConfig {
+ /** Called when the system is ready and components can be queried. */
+ void onSystemReady();
+ /** @return true if we should filter apps at all. */
+ boolean isGloballyEnabled();
+
+ /** @return true if the feature is enabled for the given package. */
+ boolean packageIsEnabled(PackageParser.Package pkg);
+ }
+
+ private static class FeatureConfigImpl implements FeatureConfig {
+ private static final String FILTERING_ENABLED_NAME = "package_query_filtering_enabled";
+
+ // STOPSHIP(patb): set this to true if we plan to launch this in R
+ private static final boolean DEFAULT_ENABLED_STATE = false;
+ private final PackageManagerService.Injector mInjector;
+ private volatile boolean mFeatureEnabled = DEFAULT_ENABLED_STATE;
+
+ private FeatureConfigImpl(PackageManagerService.Injector injector) {
+ mInjector = injector;
+ }
+
+ @Override
+ public void onSystemReady() {
+ mFeatureEnabled = DeviceConfig.getBoolean(
+ NAMESPACE_PACKAGE_MANAGER_SERVICE, FILTERING_ENABLED_NAME,
+ DEFAULT_ENABLED_STATE);
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(),
+ properties -> {
+ synchronized (FeatureConfigImpl.this) {
+ mFeatureEnabled = properties.getBoolean(
+ FILTERING_ENABLED_NAME, DEFAULT_ENABLED_STATE);
+ }
+ });
+ }
+
+ @Override
+ public boolean isGloballyEnabled() {
+ return mFeatureEnabled;
+ }
+
+ @Override
+ public boolean packageIsEnabled(PackageParser.Package pkg) {
+ final PlatformCompat compatibility = mInjector.getCompatibility();
+ if (compatibility == null) {
+ Slog.wtf(TAG, "PlatformCompat is null");
+ return mFeatureEnabled;
+ }
+ return compatibility.isChangeEnabled(
+ PackageManager.FILTER_APPLICATION_QUERY, pkg.applicationInfo);
+ }
+ }
+
+
+ public static AppsFilter create(PackageManagerService.Injector injector) {
final boolean forceSystemAppsQueryable =
- context.getResources().getBoolean(R.bool.config_forceSystemPackagesQueryable);
+ injector.getContext().getResources()
+ .getBoolean(R.bool.config_forceSystemPackagesQueryable);
+ final FeatureConfig featureConfig = new FeatureConfigImpl(injector);
final String[] forcedQueryablePackageNames;
if (forceSystemAppsQueryable) {
// all system apps already queryable, no need to read and parse individual exceptions
forcedQueryablePackageNames = new String[]{};
} else {
forcedQueryablePackageNames =
- context.getResources().getStringArray(R.array.config_forceQueryablePackages);
+ injector.getContext().getResources()
+ .getStringArray(R.array.config_forceQueryablePackages);
for (int i = 0; i < forcedQueryablePackageNames.length; i++) {
forcedQueryablePackageNames[i] = forcedQueryablePackageNames[i].intern();
}
}
IPermissionManager permissionmgr =
(IPermissionManager) ServiceManager.getService("permissionmgr");
- return new AppsFilter(() -> false, permissionmgr,
- context.getSystemService(AppOpsManager.class), forcedQueryablePackageNames,
+
+ return new AppsFilter(featureConfig, permissionmgr, forcedQueryablePackageNames,
forceSystemAppsQueryable);
}
@@ -186,6 +241,10 @@
currentUser.get(targetPackage.pkg.packageName).add(initiatingPackage.pkg.packageName);
}
+ public void onSystemReady() {
+ mFeatureConfig.onSystemReady();
+ }
+
/**
* Adds a package that should be considered when filtering visibility between apps.
*
@@ -267,11 +326,11 @@
*/
public boolean shouldFilterApplication(int callingUid, @Nullable SettingBase callingSetting,
PackageSetting targetPkgSetting, int userId) {
- if (callingUid < Process.FIRST_APPLICATION_UID) {
+ final boolean featureEnabled = mFeatureConfig.isGloballyEnabled();
+ if (!featureEnabled && !DEBUG_RUN_WHEN_DISABLED) {
return false;
}
- final boolean featureEnabled = mConfigProvider.isEnabled();
- if (!featureEnabled && !DEBUG_RUN_WHEN_DISABLED) {
+ if (callingUid < Process.FIRST_APPLICATION_UID) {
return false;
}
if (callingSetting == null) {
@@ -312,31 +371,21 @@
return true;
}
}
-
if (!featureEnabled) {
return false;
}
- final int mode = mAppOpsManager
- .checkOpNoThrow(AppOpsManager.OP_QUERY_ALL_PACKAGES, callingUid,
- callingPkgSetting.pkg.packageName);
- switch (mode) {
- case AppOpsManager.MODE_DEFAULT:
- if (DEBUG_LOGGING) {
- Slog.d(TAG, "filtered interaction: " + callingPkgSetting.name + " -> "
- + targetPkgSetting.name + (DEBUG_ALLOW_ALL ? " ALLOWED" : ""));
- }
- return !DEBUG_ALLOW_ALL;
- case AppOpsManager.MODE_ALLOWED:
- // explicitly allowed to see all packages, don't filter
- return false;
- case AppOpsManager.MODE_ERRORED:
- // deny / error: let's log so developer can audit usages
- Slog.i(TAG, callingPkgSetting.pkg.packageName
- + " blocked from accessing " + targetPkgSetting.pkg.packageName);
- case AppOpsManager.MODE_IGNORED:
- // fall through
- default:
- return true;
+ if (mFeatureConfig.packageIsEnabled(callingPkgSetting.pkg)) {
+ if (DEBUG_LOGGING) {
+ Slog.d(TAG, "interaction: " + callingPkgSetting.name + " -> "
+ + targetPkgSetting.name + (DEBUG_ALLOW_ALL ? " ALLOWED" : "BLOCKED"));
+ }
+ return !DEBUG_ALLOW_ALL;
+ } else {
+ if (DEBUG_LOGGING) {
+ Slog.d(TAG, "interaction: " + callingPkgSetting.name + " -> "
+ + targetPkgSetting.name + " DISABLED");
+ }
+ return false;
}
}
@@ -377,6 +426,13 @@
&& mImplicitlyQueryable.get(userId).get(callingName).contains(targetName)) {
return false;
}
+ if (callingPkgSetting.pkg.instrumentation.size() > 0) {
+ for (int i = 0, max = callingPkgSetting.pkg.instrumentation.size(); i < max; i++) {
+ if (callingPkgSetting.pkg.instrumentation.get(i).info.targetPackage == targetName) {
+ return false;
+ }
+ }
+ }
try {
if (mPermissionManager.checkPermission(
Manifest.permission.QUERY_ALL_PACKAGES, callingName, userId)
@@ -400,7 +456,6 @@
pw.println();
pw.println("Queries:");
dumpState.onTitlePrinted();
- pw.println(" enabled: " + mConfigProvider.isEnabled());
pw.println(" system apps queryable: " + mSystemAppsQueryable);
dumpPackageSet(pw, filteringPackageName, mForceQueryableByDevice, "System whitelist", " ");
dumpPackageSet(pw, filteringPackageName, mForceQueryable, "forceQueryable", " ");
@@ -442,9 +497,4 @@
}
}
}
-
- public interface ConfigProvider {
- boolean isEnabled();
- }
-
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 0032e9a..3aeb2b1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1184,9 +1184,26 @@
synchronized (mSessions) {
pw.println("Active install sessions:");
pw.increaseIndent();
+
+ List<PackageInstallerSession> finalizedSessions = new ArrayList<>();
int N = mSessions.size();
for (int i = 0; i < N; i++) {
final PackageInstallerSession session = mSessions.valueAt(i);
+ if (session.isStagedAndInTerminalState()) {
+ finalizedSessions.add(session);
+ continue;
+ }
+ session.dump(pw);
+ pw.println();
+ }
+ pw.println();
+ pw.decreaseIndent();
+
+ pw.println("Finalized install sessions:");
+ pw.increaseIndent();
+ N = finalizedSessions.size();
+ for (int i = 0; i < N; i++) {
+ final PackageInstallerSession session = finalizedSessions.get(i);
session.dump(pw);
pw.println();
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4eddb930..b720290 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -2337,6 +2337,7 @@
pw.printPair("mInstallerPackageName", mInstallerPackageName);
pw.printPair("mInstallerUid", mInstallerUid);
pw.printPair("createdMillis", createdMillis);
+ pw.printPair("updatedMillis", updatedMillis);
pw.printPair("stageDir", stageDir);
pw.printPair("stageCid", stageCid);
pw.println();
@@ -2356,6 +2357,13 @@
pw.printPair("mFinalMessage", mFinalMessage);
pw.printPair("params.isMultiPackage", params.isMultiPackage);
pw.printPair("params.isStaged", params.isStaged);
+ pw.printPair("mParentSessionId", mParentSessionId);
+ pw.printPair("mChildSessionIds", mChildSessionIds);
+ pw.printPair("mStagedSessionApplied", mStagedSessionApplied);
+ pw.printPair("mStagedSessionFailed", mStagedSessionFailed);
+ pw.printPair("mStagedSessionReady", mStagedSessionReady);
+ pw.printPair("mStagedSessionErrorCode", mStagedSessionErrorCode);
+ pw.printPair("mStagedSessionErrorMessage", mStagedSessionErrorMessage);
pw.println();
pw.decreaseIndent();
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4f0e4c8..15a3a72 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -300,6 +300,7 @@
import com.android.server.SystemConfig;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.Watchdog;
+import com.android.server.compat.PlatformCompat;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.Settings.DatabaseVersion;
@@ -824,6 +825,8 @@
private final Singleton<DisplayManager> mDisplayManagerProducer;
private final Singleton<StorageManager> mStorageManagerProducer;
private final Singleton<AppOpsManager> mAppOpsManagerProducer;
+ private final Singleton<AppsFilter> mAppsFilterProducer;
+ private final Singleton<PlatformCompat> mPlatformCompatProducer;
Injector(Context context, Object lock, Installer installer,
Object installLock, PackageAbiHelper abiHelper,
@@ -839,7 +842,9 @@
Producer<DeviceStorageMonitorInternal> deviceStorageMonitorProducer,
Producer<DisplayManager> displayManagerProducer,
Producer<StorageManager> storageManagerProducer,
- Producer<AppOpsManager> appOpsManagerProducer) {
+ Producer<AppOpsManager> appOpsManagerProducer,
+ Producer<AppsFilter> appsFilterProducer,
+ Producer<PlatformCompat> platformCompatProducer) {
mContext = context;
mLock = lock;
mInstaller = installer;
@@ -858,6 +863,8 @@
mDisplayManagerProducer = new Singleton<>(displayManagerProducer);
mStorageManagerProducer = new Singleton<>(storageManagerProducer);
mAppOpsManagerProducer = new Singleton<>(appOpsManagerProducer);
+ mAppsFilterProducer = new Singleton<>(appsFilterProducer);
+ mPlatformCompatProducer = new Singleton<>(platformCompatProducer);
}
/**
@@ -943,6 +950,14 @@
public AppOpsManager getAppOpsManager() {
return mAppOpsManagerProducer.get(this, mPackageManager);
}
+
+ public AppsFilter getAppsFilter() {
+ return mAppsFilterProducer.get(this, mPackageManager);
+ }
+
+ public PlatformCompat getCompatibility() {
+ return mPlatformCompatProducer.get(this, mPackageManager);
+ }
}
private final AppsFilter mAppsFilter;
@@ -2273,9 +2288,9 @@
* @param packageVolume The storage volume of the package.
* @param packageIsExternal true if the package is currently installed on
* external/removable/unprotected storage.
- * @return {@link StorageEnum#TYPE_UNKNOWN} if the package is not stored externally or the
- * corresponding {@link StorageEnum} storage type value if it is.
- * corresponding {@link StorageEnum} storage type value if it is.
+ * @return {@link StorageEnums#UNKNOWN} if the package is not stored externally or the
+ * corresponding {@link StorageEnums} storage type value if it is.
+ * corresponding {@link StorageEnums} storage type value if it is.
*/
private static int getPackageExternalStorageType(VolumeInfo packageVolume,
boolean packageIsExternal) {
@@ -2432,7 +2447,9 @@
new Injector.LocalServicesProducer<>(DeviceStorageMonitorInternal.class),
new Injector.SystemServiceProducer<>(DisplayManager.class),
new Injector.SystemServiceProducer<>(StorageManager.class),
- new Injector.SystemServiceProducer<>(AppOpsManager.class));
+ new Injector.SystemServiceProducer<>(AppOpsManager.class),
+ (i, pm) -> AppsFilter.create(i),
+ (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"));
PackageManagerService m = new PackageManagerService(injector, factoryTest, onlyCore);
t.traceEnd(); // "create package manager"
@@ -2617,7 +2634,7 @@
mProtectedPackages = new ProtectedPackages(mContext);
mApexManager = ApexManager.create(mContext);
- mAppsFilter = AppsFilter.create(mContext);
+ mAppsFilter = mInjector.getAppsFilter();
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
@@ -2725,14 +2742,10 @@
mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q;
- int preUpgradeSdkVersion = ver.sdkVersion;
-
// save off the names of pre-existing system packages prior to scanning; we don't
// want to automatically grant runtime permissions for new system apps
if (mPromoteSystemApps) {
- Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();
- while (pkgSettingIter.hasNext()) {
- PackageSetting ps = pkgSettingIter.next();
+ for (PackageSetting ps : mSettings.mPackages.values()) {
if (isSystemApp(ps)) {
mExistingSystemPackages.add(ps.name);
}
@@ -20445,6 +20458,8 @@
.getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, UserHandle.USER_ALL);
co.onChange(true);
+ mAppsFilter.onSystemReady();
+
// Disable any carrier apps. We do this very early in boot to prevent the apps from being
// disabled after already being started.
CarrierAppUtils.disableCarrierAppsUntilPrivileged(mContext.getOpPackageName(), this,
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
index af94e44..c0d71ac 100644
--- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -62,5 +62,10 @@
}
]
}
+ ],
+ "imports": [
+ {
+ "path": "vendor/xts/gts-tests/tests/permission"
+ }
]
}
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index 563ccbe..cae09ea3 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -98,14 +98,14 @@
final IntArray pendingBackups = packageRollbackInfo.getPendingBackups();
final List<RestoreInfo> pendingRestores = packageRollbackInfo.getPendingRestores();
- boolean changedRollbackData = false;
+ boolean changedRollback = false;
// If we still have a userdata backup pending for this user, it implies that the user
// hasn't unlocked their device between the point of backup and the point of restore,
// so the data cannot have changed. We simply skip restoring CE data in this case.
if (pendingBackups != null && pendingBackups.indexOf(userId) != -1) {
pendingBackups.remove(pendingBackups.indexOf(userId));
- changedRollbackData = true;
+ changedRollback = true;
} else {
// There's no pending CE backup for this user, which means that we successfully
// managed to backup data for the user, which means we seek to restore it
@@ -113,7 +113,7 @@
// We've encountered a user that hasn't unlocked on a FBE device, so we can't
// copy across app user data until the user unlocks their device.
pendingRestores.add(new RestoreInfo(userId, appId, seInfo));
- changedRollbackData = true;
+ changedRollback = true;
} else {
// This user has unlocked, we can proceed to restore both CE and DE data.
storageFlags = storageFlags | Installer.FLAG_STORAGE_CE;
@@ -128,7 +128,7 @@
+ packageRollbackInfo.getPackageName(), ie);
}
- return changedRollbackData;
+ return changedRollback;
}
/**
@@ -160,29 +160,29 @@
* Packages pending backup for the given user are added to {@code pendingBackupPackages} along
* with their corresponding {@code PackageRollbackInfo}.
*
- * @return the list of {@code RollbackData} that has pending backups. Note that some of the
+ * @return the list of rollbacks that have pending backups. Note that some of the
* backups won't be performed, because they might be counteracted by pending restores.
*/
- private static List<RollbackData> computePendingBackups(int userId,
+ private static List<Rollback> computePendingBackups(int userId,
Map<String, PackageRollbackInfo> pendingBackupPackages,
- List<RollbackData> rollbacks) {
- List<RollbackData> rd = new ArrayList<>();
+ List<Rollback> rollbacks) {
+ List<Rollback> rollbacksWithPendingBackups = new ArrayList<>();
- for (RollbackData data : rollbacks) {
- for (PackageRollbackInfo info : data.info.getPackages()) {
+ for (Rollback rollback : rollbacks) {
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
final IntArray pendingBackupUsers = info.getPendingBackups();
if (pendingBackupUsers != null) {
final int idx = pendingBackupUsers.indexOf(userId);
if (idx != -1) {
pendingBackupPackages.put(info.getPackageName(), info);
- if (rd.indexOf(data) == -1) {
- rd.add(data);
+ if (rollbacksWithPendingBackups.indexOf(rollback) == -1) {
+ rollbacksWithPendingBackups.add(rollback);
}
}
}
}
}
- return rd;
+ return rollbacksWithPendingBackups;
}
/**
@@ -190,45 +190,45 @@
* Packages pending restore are added to {@code pendingRestores} along with their corresponding
* {@code PackageRollbackInfo}.
*
- * @return the list of {@code RollbackData} that has pending restores. Note that some of the
+ * @return the list of rollbacks that have pending restores. Note that some of the
* restores won't be performed, because they might be counteracted by pending backups.
*/
- private static List<RollbackData> computePendingRestores(int userId,
+ private static List<Rollback> computePendingRestores(int userId,
Map<String, PackageRollbackInfo> pendingRestorePackages,
- List<RollbackData> rollbacks) {
- List<RollbackData> rd = new ArrayList<>();
+ List<Rollback> rollbacks) {
+ List<Rollback> rollbacksWithPendingRestores = new ArrayList<>();
- for (RollbackData data : rollbacks) {
- for (PackageRollbackInfo info : data.info.getPackages()) {
+ for (Rollback rollback : rollbacks) {
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
final RestoreInfo ri = info.getRestoreInfo(userId);
if (ri != null) {
pendingRestorePackages.put(info.getPackageName(), info);
- if (rd.indexOf(data) == -1) {
- rd.add(data);
+ if (rollbacksWithPendingRestores.indexOf(rollback) == -1) {
+ rollbacksWithPendingRestores.add(rollback);
}
}
}
}
- return rd;
+ return rollbacksWithPendingRestores;
}
/**
- * Commits the list of pending backups and restores for a given {@code userId}. For the pending
- * backups updates corresponding {@code changedRollbackData} with a mapping from {@code userId}
- * to a inode of theirs CE user data snapshot.
+ * Commits the list of pending backups and restores for a given {@code userId}. For rollbacks
+ * with pending backups, updates the {@code Rollback} instance with a mapping from
+ * {@code userId} to inode of the CE user data snapshot.
*
- * @return the set of {@code RollbackData} that have been changed and should be stored on disk.
+ * @return the set of rollbacks with changes that should be stored on disk.
*/
- public Set<RollbackData> commitPendingBackupAndRestoreForUser(int userId,
- List<RollbackData> rollbacks) {
+ public Set<Rollback> commitPendingBackupAndRestoreForUser(int userId,
+ List<Rollback> rollbacks) {
final Map<String, PackageRollbackInfo> pendingBackupPackages = new HashMap<>();
- final List<RollbackData> pendingBackups = computePendingBackups(userId,
+ final List<Rollback> pendingBackups = computePendingBackups(userId,
pendingBackupPackages, rollbacks);
final Map<String, PackageRollbackInfo> pendingRestorePackages = new HashMap<>();
- final List<RollbackData> pendingRestores = computePendingRestores(userId,
+ final List<Rollback> pendingRestores = computePendingRestores(userId,
pendingRestorePackages, rollbacks);
// First remove unnecessary backups, i.e. when user did not unlock their phone between the
@@ -248,14 +248,15 @@
}
if (!pendingBackupPackages.isEmpty()) {
- for (RollbackData data : pendingBackups) {
- for (PackageRollbackInfo info : data.info.getPackages()) {
+ for (Rollback rollback : pendingBackups) {
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
final IntArray pendingBackupUsers = info.getPendingBackups();
final int idx = pendingBackupUsers.indexOf(userId);
if (idx != -1) {
try {
long ceSnapshotInode = mInstaller.snapshotAppData(info.getPackageName(),
- userId, data.info.getRollbackId(), Installer.FLAG_STORAGE_CE);
+ userId, rollback.info.getRollbackId(),
+ Installer.FLAG_STORAGE_CE);
info.putCeSnapshotInode(userId, ceSnapshotInode);
pendingBackupUsers.remove(idx);
} catch (InstallerException ie) {
@@ -269,13 +270,13 @@
}
if (!pendingRestorePackages.isEmpty()) {
- for (RollbackData data : pendingRestores) {
- for (PackageRollbackInfo info : data.info.getPackages()) {
+ for (Rollback rollback : pendingRestores) {
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
final RestoreInfo ri = info.getRestoreInfo(userId);
if (ri != null) {
try {
mInstaller.restoreAppDataSnapshot(info.getPackageName(), ri.appId,
- ri.seInfo, userId, data.info.getRollbackId(),
+ ri.seInfo, userId, rollback.info.getRollbackId(),
Installer.FLAG_STORAGE_CE);
info.removeRestoreInfo(ri);
} catch (InstallerException ie) {
@@ -287,7 +288,7 @@
}
}
- final Set<RollbackData> changed = new HashSet<>(pendingBackups);
+ final Set<Rollback> changed = new HashSet<>(pendingBackups);
changed.addAll(pendingRestores);
return changed;
}
diff --git a/services/core/java/com/android/server/rollback/RollbackData.java b/services/core/java/com/android/server/rollback/Rollback.java
similarity index 85%
rename from services/core/java/com/android/server/rollback/RollbackData.java
rename to services/core/java/com/android/server/rollback/Rollback.java
index b37e268..0d5746b 100644
--- a/services/core/java/com/android/server/rollback/RollbackData.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -32,7 +32,7 @@
* Information about a rollback available for a set of atomically installed
* packages.
*/
-class RollbackData {
+class Rollback {
@IntDef(flag = true, prefix = { "ROLLBACK_STATE_" }, value = {
ROLLBACK_STATE_ENABLING,
ROLLBACK_STATE_AVAILABLE,
@@ -102,13 +102,13 @@
public boolean restoreUserDataInProgress = false;
/**
- * Constructs a new, empty RollbackData instance.
+ * Constructs a new, empty Rollback instance.
*
* @param rollbackId the id of the rollback.
* @param backupDir the directory where the rollback data is stored.
* @param stagedSessionId the session id if this is a staged rollback, -1 otherwise.
*/
- RollbackData(int rollbackId, File backupDir, int stagedSessionId) {
+ Rollback(int rollbackId, File backupDir, int stagedSessionId) {
this.info = new RollbackInfo(rollbackId,
/* packages */ new ArrayList<>(),
/* isStaged */ stagedSessionId != -1,
@@ -121,9 +121,9 @@
}
/**
- * Constructs a RollbackData instance with full rollback data information.
+ * Constructs a pre-populated Rollback instance.
*/
- RollbackData(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
+ Rollback(RollbackInfo info, File backupDir, Instant timestamp, int stagedSessionId,
@RollbackState int state, int apkSessionId, boolean restoreUserDataInProgress) {
this.info = info;
this.backupDir = backupDir;
@@ -143,9 +143,9 @@
static String rollbackStateToString(@RollbackState int state) {
switch (state) {
- case RollbackData.ROLLBACK_STATE_ENABLING: return "enabling";
- case RollbackData.ROLLBACK_STATE_AVAILABLE: return "available";
- case RollbackData.ROLLBACK_STATE_COMMITTED: return "committed";
+ case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
+ case Rollback.ROLLBACK_STATE_AVAILABLE: return "available";
+ case Rollback.ROLLBACK_STATE_COMMITTED: return "committed";
}
throw new AssertionError("Invalid rollback state: " + state);
}
@@ -153,9 +153,9 @@
static @RollbackState int rollbackStateFromString(String state)
throws ParseException {
switch (state) {
- case "enabling": return RollbackData.ROLLBACK_STATE_ENABLING;
- case "available": return RollbackData.ROLLBACK_STATE_AVAILABLE;
- case "committed": return RollbackData.ROLLBACK_STATE_COMMITTED;
+ case "enabling": return Rollback.ROLLBACK_STATE_ENABLING;
+ case "available": return Rollback.ROLLBACK_STATE_AVAILABLE;
+ case "committed": return Rollback.ROLLBACK_STATE_COMMITTED;
}
throw new ParseException("Invalid rollback state: " + state, 0);
}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 1b6af40..bfd280c 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -110,13 +110,13 @@
@GuardedBy("mLock")
private final SparseBooleanArray mAllocatedRollbackIds = new SparseBooleanArray();
- // Package rollback data for rollbacks we are in the process of enabling.
+ // Rollbacks we are in the process of enabling.
@GuardedBy("mLock")
private final Set<NewRollback> mNewRollbacks = new ArraySet<>();
// The list of all rollbacks, including available and committed rollbacks.
@GuardedBy("mLock")
- private final List<RollbackData> mRollbacks;
+ private final List<Rollback> mRollbacks;
private final RollbackStore mRollbackStore;
@@ -127,7 +127,7 @@
private final AppDataRollbackHelper mAppDataRollbackHelper;
// This field stores the difference in Millis between the uptime (millis since device
- // has booted) and current time (device wall clock) - it's used to update rollback data
+ // has booted) and current time (device wall clock) - it's used to update rollback
// timestamps when the time is changed, by the user or by change of timezone.
// No need for guarding with lock because value is only accessed in handler thread.
private long mRelativeBootTime = calculateRelativeBootTime();
@@ -146,9 +146,9 @@
// Load rollback data from device storage.
synchronized (mLock) {
- mRollbacks = mRollbackStore.loadAllRollbackData();
- for (RollbackData data : mRollbacks) {
- mAllocatedRollbackIds.put(data.info.getRollbackId(), true);
+ mRollbacks = mRollbackStore.loadRollbacks();
+ for (Rollback rollback : mRollbacks) {
+ mAllocatedRollbackIds.put(rollback.info.getRollbackId(), true);
}
}
@@ -271,39 +271,24 @@
}, filter, null, getHandler());
}
- /**
- * This method posts a blocking call to the handler thread, so it should not be called from
- * that same thread.
- * @throws {@link IllegalStateException} if called from {@link #mHandlerThread}
- */
@Override
public ParceledListSlice getAvailableRollbacks() {
enforceManageRollbacks("getAvailableRollbacks");
- if (Thread.currentThread().equals(mHandlerThread)) {
- Slog.wtf(TAG, "Calling getAvailableRollbacks from mHandlerThread "
- + "causes a deadlock");
- throw new IllegalStateException("Cannot call RollbackManager#getAvailableRollbacks "
- + "from the handler thread!");
- }
-
- // Wait for the handler thread to get the list of available rollbacks
- // to get the most up-to-date results. This is intended to reduce test
- // flakiness when checking available rollbacks immediately after
- // installing a package with rollback enabled.
- CountDownLatch latch = new CountDownLatch(1);
- getHandler().post(() -> latch.countDown());
- try {
- latch.await();
- } catch (InterruptedException ie) {
- throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
- }
-
synchronized (mLock) {
List<RollbackInfo> rollbacks = new ArrayList<>();
for (int i = 0; i < mRollbacks.size(); ++i) {
- RollbackData data = mRollbacks.get(i);
- if (data.state == RollbackData.ROLLBACK_STATE_AVAILABLE) {
- rollbacks.add(data.info);
+ Rollback rollback = mRollbacks.get(i);
+ if (rollback.state == Rollback.ROLLBACK_STATE_AVAILABLE) {
+ rollbacks.add(rollback.info);
+ }
+ }
+
+ // Also return new rollbacks for which the PackageRollbackInfo is complete.
+ for (NewRollback newRollback : mNewRollbacks) {
+ if (newRollback.rollback.info.getPackages().size()
+ == newRollback.packageSessionIds.length
+ && !newRollback.isCancelled) {
+ rollbacks.add(newRollback.rollback.info);
}
}
return new ParceledListSlice<>(rollbacks);
@@ -317,9 +302,9 @@
synchronized (mLock) {
List<RollbackInfo> rollbacks = new ArrayList<>();
for (int i = 0; i < mRollbacks.size(); ++i) {
- RollbackData data = mRollbacks.get(i);
- if (data.state == RollbackData.ROLLBACK_STATE_COMMITTED) {
- rollbacks.add(data.info);
+ Rollback rollback = mRollbacks.get(i);
+ if (rollback.state == Rollback.ROLLBACK_STATE_COMMITTED) {
+ rollbacks.add(rollback.info);
}
}
return new ParceledListSlice<>(rollbacks);
@@ -349,11 +334,11 @@
final long timeDifference = mRelativeBootTime - oldRelativeBootTime;
synchronized (mLock) {
- Iterator<RollbackData> iter = mRollbacks.iterator();
+ Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
- RollbackData data = iter.next();
- data.timestamp = data.timestamp.plusMillis(timeDifference);
- saveRollbackData(data);
+ Rollback rollback = iter.next();
+ rollback.timestamp = rollback.timestamp.plusMillis(timeDifference);
+ saveRollback(rollback);
}
}
}
@@ -377,8 +362,8 @@
String callerPackageName, IntentSender statusReceiver) {
Slog.i(TAG, "Initiating rollback");
- RollbackData data = getRollbackForId(rollbackId);
- if (data == null || data.state != RollbackData.ROLLBACK_STATE_AVAILABLE) {
+ Rollback rollback = getRollbackForId(rollbackId);
+ if (rollback == null || rollback.state != Rollback.ROLLBACK_STATE_AVAILABLE) {
sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
"Rollback unavailable");
return;
@@ -402,14 +387,14 @@
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
parentParams.setRequestDowngrade(true);
parentParams.setMultiPackage();
- if (data.isStaged()) {
+ if (rollback.isStaged()) {
parentParams.setStaged();
}
int parentSessionId = packageInstaller.createSession(parentParams);
PackageInstaller.Session parentSession = packageInstaller.openSession(parentSessionId);
- for (PackageRollbackInfo info : data.info.getPackages()) {
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
// TODO: We can't get the installerPackageName for apex
@@ -424,7 +409,7 @@
params.setRequestDowngrade(true);
params.setRequiredInstalledVersionCode(
info.getVersionRolledBackFrom().getLongVersionCode());
- if (data.isStaged()) {
+ if (rollback.isStaged()) {
params.setStaged();
}
if (info.isApex()) {
@@ -433,7 +418,7 @@
int sessionId = packageInstaller.createSession(params);
PackageInstaller.Session session = packageInstaller.openSession(sessionId);
File[] packageCodePaths = RollbackStore.getPackageCodePaths(
- data, info.getPackageName());
+ rollback, info.getPackageName());
if (packageCodePaths == null) {
sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
"Backup copy of package inaccessible");
@@ -474,8 +459,8 @@
// TODO: Could this cause a rollback to be
// resurrected if it should otherwise have
// expired by now?
- data.state = RollbackData.ROLLBACK_STATE_AVAILABLE;
- data.restoreUserDataInProgress = false;
+ rollback.state = Rollback.ROLLBACK_STATE_AVAILABLE;
+ rollback.restoreUserDataInProgress = false;
}
sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_INSTALL,
"Rollback downgrade install failed: "
@@ -485,17 +470,17 @@
}
synchronized (mLock) {
- if (!data.isStaged()) {
+ if (!rollback.isStaged()) {
// All calls to restoreUserData should have
// completed by now for a non-staged install.
- data.restoreUserDataInProgress = false;
+ rollback.restoreUserDataInProgress = false;
}
- data.info.setCommittedSessionId(parentSessionId);
- data.info.getCausePackages().addAll(causePackages);
+ rollback.info.setCommittedSessionId(parentSessionId);
+ rollback.info.getCausePackages().addAll(causePackages);
}
- mRollbackStore.deletePackageCodePaths(data);
- saveRollbackData(data);
+ mRollbackStore.deletePackageCodePaths(rollback);
+ saveRollback(rollback);
sendSuccess(statusReceiver);
@@ -510,8 +495,8 @@
);
synchronized (mLock) {
- data.state = RollbackData.ROLLBACK_STATE_COMMITTED;
- data.restoreUserDataInProgress = true;
+ rollback.state = Rollback.ROLLBACK_STATE_COMMITTED;
+ rollback.restoreUserDataInProgress = true;
}
parentSession.commit(receiver.getIntentSender());
} catch (IOException e) {
@@ -533,7 +518,7 @@
updateRollbackLifetimeDurationInMillis();
synchronized (mLock) {
mRollbacks.clear();
- mRollbacks.addAll(mRollbackStore.loadAllRollbackData());
+ mRollbacks.addAll(mRollbackStore.loadRollbacks());
}
latch.countDown();
});
@@ -551,13 +536,21 @@
Manifest.permission.TEST_MANAGE_ROLLBACKS,
"expireRollbackForPackage");
synchronized (mLock) {
- Iterator<RollbackData> iter = mRollbacks.iterator();
+ Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
- RollbackData data = iter.next();
- for (PackageRollbackInfo info : data.info.getPackages()) {
+ Rollback rollback = iter.next();
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
if (info.getPackageName().equals(packageName)) {
iter.remove();
- deleteRollback(data);
+ deleteRollback(rollback);
+ break;
+ }
+ }
+ }
+ for (NewRollback newRollback : mNewRollbacks) {
+ for (PackageRollbackInfo info : newRollback.rollback.info.getPackages()) {
+ if (info.getPackageName().equals(packageName)) {
+ newRollback.isCancelled = true;
break;
}
}
@@ -581,16 +574,16 @@
void onUnlockUser(int userId) {
getHandler().post(() -> {
- final List<RollbackData> rollbacks;
+ final List<Rollback> rollbacks;
synchronized (mLock) {
rollbacks = new ArrayList<>(mRollbacks);
}
- final Set<RollbackData> changed =
+ final Set<Rollback> changed =
mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(userId, rollbacks);
- for (RollbackData rd : changed) {
- saveRollbackData(rd);
+ for (Rollback rollback : changed) {
+ saveRollback(rollback);
}
});
}
@@ -613,19 +606,19 @@
getHandler().post(() -> {
// Check to see if any rollback-enabled staged sessions or staged
// rollback sessions been applied.
- List<RollbackData> enabling = new ArrayList<>();
- List<RollbackData> restoreInProgress = new ArrayList<>();
+ List<Rollback> enabling = new ArrayList<>();
+ List<Rollback> restoreInProgress = new ArrayList<>();
Set<String> apexPackageNames = new HashSet<>();
synchronized (mLock) {
- for (RollbackData data : mRollbacks) {
- if (data.isStaged()) {
- if (data.state == RollbackData.ROLLBACK_STATE_ENABLING) {
- enabling.add(data);
- } else if (data.restoreUserDataInProgress) {
- restoreInProgress.add(data);
+ for (Rollback rollback : mRollbacks) {
+ if (rollback.isStaged()) {
+ if (rollback.state == Rollback.ROLLBACK_STATE_ENABLING) {
+ enabling.add(rollback);
+ } else if (rollback.restoreUserDataInProgress) {
+ restoreInProgress.add(rollback);
}
- for (PackageRollbackInfo info : data.info.getPackages()) {
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
if (info.isApex()) {
apexPackageNames.add(info.getPackageName());
}
@@ -634,32 +627,32 @@
}
}
- for (RollbackData data : enabling) {
+ for (Rollback rollback : enabling) {
PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo session = installer.getSessionInfo(
- data.stagedSessionId);
+ rollback.stagedSessionId);
if (session == null || session.isStagedSessionFailed()) {
// TODO: Do we need to remove this from
// mRollbacks, or is it okay to leave as
// unavailable until the next reboot when it will go
// away on its own?
- deleteRollback(data);
+ deleteRollback(rollback);
} else if (session.isStagedSessionApplied()) {
- makeRollbackAvailable(data);
+ makeRollbackAvailable(rollback);
}
}
- for (RollbackData data : restoreInProgress) {
+ for (Rollback rollback : restoreInProgress) {
PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo session = installer.getSessionInfo(
- data.stagedSessionId);
+ rollback.stagedSessionId);
// TODO: What if session is null?
if (session != null) {
if (session.isStagedSessionApplied() || session.isStagedSessionFailed()) {
synchronized (mLock) {
- data.restoreUserDataInProgress = false;
+ rollback.restoreUserDataInProgress = false;
}
- saveRollbackData(data);
+ saveRollback(rollback);
}
}
}
@@ -686,19 +679,19 @@
VersionedPackage installedVersion = getInstalledPackageVersion(packageName);
synchronized (mLock) {
- Iterator<RollbackData> iter = mRollbacks.iterator();
+ Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
- RollbackData data = iter.next();
+ Rollback rollback = iter.next();
// TODO: Should we remove rollbacks in the ENABLING state here?
- if (data.state == RollbackData.ROLLBACK_STATE_AVAILABLE
- || data.state == RollbackData.ROLLBACK_STATE_ENABLING) {
- for (PackageRollbackInfo info : data.info.getPackages()) {
+ if (rollback.state == Rollback.ROLLBACK_STATE_AVAILABLE
+ || rollback.state == Rollback.ROLLBACK_STATE_ENABLING) {
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
if (info.getPackageName().equals(packageName)
&& !packageVersionsEqual(
info.getVersionRolledBackFrom(),
installedVersion)) {
iter.remove();
- deleteRollback(data);
+ deleteRollback(rollback);
break;
}
}
@@ -754,17 +747,18 @@
Instant now = Instant.now();
Instant oldest = null;
synchronized (mLock) {
- Iterator<RollbackData> iter = mRollbacks.iterator();
+ Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
- RollbackData data = iter.next();
- if (data.state != RollbackData.ROLLBACK_STATE_AVAILABLE) {
+ Rollback rollback = iter.next();
+ if (rollback.state != Rollback.ROLLBACK_STATE_AVAILABLE) {
continue;
}
- if (!now.isBefore(data.timestamp.plusMillis(mRollbackLifetimeDurationInMillis))) {
+ if (!now.isBefore(
+ rollback.timestamp.plusMillis(mRollbackLifetimeDurationInMillis))) {
iter.remove();
- deleteRollback(data);
- } else if (oldest == null || oldest.isAfter(data.timestamp)) {
- oldest = data.timestamp;
+ deleteRollback(rollback);
+ } else if (oldest == null || oldest.isAfter(rollback.timestamp)) {
+ oldest = rollback.timestamp;
}
}
}
@@ -871,8 +865,8 @@
// rollback enabled.
synchronized (mLock) {
for (int i = 0; i < mRollbacks.size(); ++i) {
- RollbackData data = mRollbacks.get(i);
- if (data.apkSessionId == parentSession.getSessionId()) {
+ Rollback rollback = mRollbacks.get(i);
+ if (rollback.apkSessionId == parentSession.getSessionId()) {
// This is the apk session for a staged session with rollback enabled. We do not
// need to create a new rollback for this session.
return true;
@@ -893,7 +887,7 @@
}
newRollback.addToken(token);
- return enableRollbackForPackageSession(newRollback.data, packageSession);
+ return enableRollbackForPackageSession(newRollback.rollback, packageSession);
}
/**
@@ -903,7 +897,7 @@
*
* @return true on success, false on failure.
*/
- private boolean enableRollbackForPackageSession(RollbackData data,
+ private boolean enableRollbackForPackageSession(Rollback rollback,
PackageInstaller.SessionInfo session) {
// TODO: Don't attempt to enable rollback for split installs.
final int installFlags = session.installFlags;
@@ -966,10 +960,10 @@
try {
ApplicationInfo appInfo = pkgInfo.applicationInfo;
- RollbackStore.backupPackageCodePath(data, packageName, appInfo.sourceDir);
+ RollbackStore.backupPackageCodePath(rollback, packageName, appInfo.sourceDir);
if (!ArrayUtils.isEmpty(appInfo.splitSourceDirs)) {
for (String sourceDir : appInfo.splitSourceDirs) {
- RollbackStore.backupPackageCodePath(data, packageName, sourceDir);
+ RollbackStore.backupPackageCodePath(rollback, packageName, sourceDir);
}
}
} catch (IOException e) {
@@ -978,7 +972,7 @@
}
synchronized (mLock) {
- data.info.getPackages().add(packageRollbackInfo);
+ rollback.info.getPackages().add(packageRollbackInfo);
}
return true;
}
@@ -1004,16 +998,16 @@
synchronized (mLock) {
// staged installs
for (int i = 0; i < mRollbacks.size(); i++) {
- RollbackData data = mRollbacks.get(i);
- if (data.state != RollbackData.ROLLBACK_STATE_ENABLING) {
+ Rollback rollback = mRollbacks.get(i);
+ if (rollback.state != Rollback.ROLLBACK_STATE_ENABLING) {
continue;
}
- for (PackageRollbackInfo info : data.info.getPackages()) {
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
if (info.getPackageName().equals(packageName)) {
mAppDataRollbackHelper.snapshotAppData(
- data.info.getRollbackId(), info, userIds);
- saveRollbackData(data);
+ rollback.info.getRollbackId(), info, userIds);
+ saveRollback(rollback);
break;
}
}
@@ -1021,11 +1015,11 @@
// non-staged installs
PackageRollbackInfo info;
for (NewRollback rollback : mNewRollbacks) {
- info = getPackageRollbackInfo(rollback.data, packageName);
+ info = getPackageRollbackInfo(rollback.rollback, packageName);
if (info != null) {
mAppDataRollbackHelper.snapshotAppData(
- rollback.data.info.getRollbackId(), info, userIds);
- saveRollbackData(rollback.data);
+ rollback.rollback.info.getRollbackId(), info, userIds);
+ saveRollback(rollback.rollback);
}
}
}
@@ -1034,31 +1028,31 @@
private void restoreUserDataInternal(String packageName, int[] userIds, int appId,
long ceDataInode, String seInfo, int token) {
PackageRollbackInfo info = null;
- RollbackData rollbackData = null;
+ Rollback rollback = null;
synchronized (mLock) {
for (int i = 0; i < mRollbacks.size(); ++i) {
- RollbackData data = mRollbacks.get(i);
- if (data.restoreUserDataInProgress) {
- info = getPackageRollbackInfo(data, packageName);
+ Rollback candidate = mRollbacks.get(i);
+ if (candidate.restoreUserDataInProgress) {
+ info = getPackageRollbackInfo(candidate, packageName);
if (info != null) {
- rollbackData = data;
+ rollback = candidate;
break;
}
}
}
}
- if (rollbackData == null) {
+ if (rollback == null) {
return;
}
for (int userId : userIds) {
- final boolean changedRollbackData = mAppDataRollbackHelper.restoreAppData(
- rollbackData.info.getRollbackId(), info, userId, appId, seInfo);
+ final boolean changedRollback = mAppDataRollbackHelper.restoreAppData(
+ rollback.info.getRollbackId(), info, userId, appId, seInfo);
// We've updated metadata about this rollback, so save it to flash.
- if (changedRollbackData) {
- saveRollbackData(rollbackData);
+ if (changedRollback) {
+ saveRollback(rollback);
}
}
}
@@ -1088,7 +1082,7 @@
}
if (!session.isMultiPackage()) {
- if (!enableRollbackForPackageSession(newRollback.data, session)) {
+ if (!enableRollbackForPackageSession(newRollback.rollback, session)) {
Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
result.offer(false);
return;
@@ -1102,7 +1096,7 @@
result.offer(false);
return;
}
- if (!enableRollbackForPackageSession(newRollback.data, childSession)) {
+ if (!enableRollbackForPackageSession(newRollback.rollback, childSession)) {
Slog.e(TAG, "Unable to enable rollback for session: " + sessionId);
result.offer(false);
return;
@@ -1127,20 +1121,20 @@
throw new SecurityException("notifyStagedApkSession may only be called by the system.");
}
getHandler().post(() -> {
- RollbackData rd = null;
+ Rollback rollback = null;
synchronized (mLock) {
for (int i = 0; i < mRollbacks.size(); ++i) {
- RollbackData data = mRollbacks.get(i);
- if (data.stagedSessionId == originalSessionId) {
- data.apkSessionId = apkSessionId;
- rd = data;
+ Rollback candidate = mRollbacks.get(i);
+ if (candidate.stagedSessionId == originalSessionId) {
+ candidate.apkSessionId = apkSessionId;
+ rollback = candidate;
break;
}
}
}
- if (rd != null) {
- saveRollbackData(rd);
+ if (rollback != null) {
+ saveRollback(rollback);
}
});
}
@@ -1250,7 +1244,7 @@
}
if (newRollback != null) {
- RollbackData rollback = completeEnableRollback(newRollback, success);
+ Rollback rollback = completeEnableRollback(newRollback, success);
if (rollback != null && !rollback.isStaged()) {
makeRollbackAvailable(rollback);
}
@@ -1263,32 +1257,32 @@
* This should be called after rollback has been enabled for all packages
* in the rollback. It does not make the rollback available yet.
*
- * @return the rollback data for a successfully enable-completed rollback,
+ * @return the Rollback instance for a successfully enable-completed rollback,
* or null on error.
*/
- private RollbackData completeEnableRollback(NewRollback newRollback, boolean success) {
- RollbackData data = newRollback.data;
+ private Rollback completeEnableRollback(NewRollback newRollback, boolean success) {
+ Rollback rollback = newRollback.rollback;
if (!success) {
// The install session was aborted, clean up the pending install.
- deleteRollback(data);
+ deleteRollback(rollback);
return null;
}
if (newRollback.isCancelled) {
Slog.e(TAG, "Rollback has been cancelled by PackageManager");
- deleteRollback(data);
+ deleteRollback(rollback);
return null;
}
- // It's safe to access data.info outside a synchronized block because
+ // It's safe to access rollback.info outside a synchronized block because
// this is running on the handler thread and all changes to the
- // data.info occur on the handler thread.
- if (data.info.getPackages().size() != newRollback.packageSessionIds.length) {
+ // rollback.info occur on the handler thread.
+ if (rollback.info.getPackages().size() != newRollback.packageSessionIds.length) {
Slog.e(TAG, "Failed to enable rollback for all packages in session.");
- deleteRollback(data);
+ deleteRollback(rollback);
return null;
}
- saveRollbackData(data);
+ saveRollback(rollback);
synchronized (mLock) {
// Note: There is a small window of time between when
// the session has been committed by the package
@@ -1296,25 +1290,25 @@
// here. Presumably the window is small enough that
// nobody will want to roll back the newly installed
// package before we make the rollback available.
- // TODO: We'll lose the rollback data if the
+ // TODO: We'll lose the rollback if the
// device reboots between when the session is
// committed and this point. Revisit this after
// adding support for rollback of staged installs.
- mRollbacks.add(data);
+ mRollbacks.add(rollback);
}
- return data;
+ return rollback;
}
- private void makeRollbackAvailable(RollbackData data) {
+ private void makeRollbackAvailable(Rollback rollback) {
// TODO: What if the rollback has since been expired, for example due
// to a new package being installed. Won't this revive an expired
// rollback? Consider adding a ROLLBACK_STATE_EXPIRED to address this.
synchronized (mLock) {
- data.state = RollbackData.ROLLBACK_STATE_AVAILABLE;
- data.timestamp = Instant.now();
+ rollback.state = Rollback.ROLLBACK_STATE_AVAILABLE;
+ rollback.timestamp = Instant.now();
}
- saveRollbackData(data);
+ saveRollback(rollback);
// TODO(zezeozue): Provide API to explicitly start observing instead
// of doing this for all rollbacks. If we do this for all rollbacks,
@@ -1322,8 +1316,8 @@
// After enabling and commiting any rollback, observe packages and
// prepare to rollback if packages crashes too frequently.
List<String> packages = new ArrayList<>();
- for (int i = 0; i < data.info.getPackages().size(); i++) {
- packages.add(data.info.getPackages().get(i).getPackageName());
+ for (int i = 0; i < rollback.info.getPackages().size(); i++) {
+ packages.add(rollback.info.getPackages().get(i).getPackageName());
}
mPackageHealthObserver.startObservingHealth(packages,
mRollbackLifetimeDurationInMillis);
@@ -1331,15 +1325,14 @@
}
/*
- * Returns the RollbackData, if any, for a rollback with the given
- * rollbackId.
+ * Returns the rollback with the given rollbackId, if any.
*/
- private RollbackData getRollbackForId(int rollbackId) {
+ private Rollback getRollbackForId(int rollbackId) {
synchronized (mLock) {
for (int i = 0; i < mRollbacks.size(); ++i) {
- RollbackData data = mRollbacks.get(i);
- if (data.info.getRollbackId() == rollbackId) {
- return data;
+ Rollback rollback = mRollbacks.get(i);
+ if (rollback.info.getRollbackId() == rollbackId) {
+ return rollback;
}
}
}
@@ -1349,11 +1342,11 @@
/**
* Returns the {@code PackageRollbackInfo} associated with {@code packageName} from
- * a specified {@code RollbackData}.
+ * a specified {@code Rollback}.
*/
- private static PackageRollbackInfo getPackageRollbackInfo(RollbackData data,
+ private static PackageRollbackInfo getPackageRollbackInfo(Rollback rollback,
String packageName) {
- for (PackageRollbackInfo info : data.info.getPackages()) {
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
if (info.getPackageName().equals(packageName)) {
return info;
}
@@ -1377,30 +1370,30 @@
throw new IllegalStateException("Failed to allocate rollback ID");
}
- private void deleteRollback(RollbackData rollbackData) {
- for (PackageRollbackInfo info : rollbackData.info.getPackages()) {
+ private void deleteRollback(Rollback rollback) {
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
IntArray snapshottedUsers = info.getSnapshottedUsers();
for (int i = 0; i < snapshottedUsers.size(); i++) {
int userId = snapshottedUsers.get(i);
- mAppDataRollbackHelper.destroyAppDataSnapshot(rollbackData.info.getRollbackId(),
+ mAppDataRollbackHelper.destroyAppDataSnapshot(rollback.info.getRollbackId(),
info, userId);
}
}
- mRollbackStore.deleteRollbackData(rollbackData);
+ mRollbackStore.deleteRollback(rollback);
}
/**
- * Saves rollback data, swallowing any IOExceptions.
+ * Saves a rollback, swallowing any IOExceptions.
* For those times when it's not obvious what to do about the IOException.
* TODO: Double check we can't do a better job handling the IOException in
* a cases where this method is called.
*/
- private void saveRollbackData(RollbackData rollbackData) {
+ private void saveRollback(Rollback rollback) {
try {
- mRollbackStore.saveRollbackData(rollbackData);
+ mRollbackStore.saveRollback(rollback);
} catch (IOException ioe) {
- Slog.e(TAG, "Unable to save rollback info for: "
- + rollbackData.info.getRollbackId(), ioe);
+ Slog.e(TAG, "Unable to save rollback for: "
+ + rollback.info.getRollbackId(), ioe);
}
}
@@ -1408,14 +1401,14 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
synchronized (mLock) {
- for (RollbackData data : mRollbacks) {
- RollbackInfo info = data.info;
+ for (Rollback rollback : mRollbacks) {
+ RollbackInfo info = rollback.info;
ipw.println(info.getRollbackId() + ":");
ipw.increaseIndent();
- ipw.println("-state: " + data.getStateAsString());
- ipw.println("-timestamp: " + data.timestamp);
- if (data.stagedSessionId != -1) {
- ipw.println("-stagedSessionId: " + data.stagedSessionId);
+ ipw.println("-state: " + rollback.getStateAsString());
+ ipw.println("-timestamp: " + rollback.timestamp);
+ if (rollback.stagedSessionId != -1) {
+ ipw.println("-stagedSessionId: " + rollback.stagedSessionId);
}
ipw.println("-packages:");
ipw.increaseIndent();
@@ -1425,7 +1418,7 @@
+ " -> " + pkg.getVersionRolledBackTo().getLongVersionCode());
}
ipw.decreaseIndent();
- if (data.state == RollbackData.ROLLBACK_STATE_COMMITTED) {
+ if (rollback.state == Rollback.ROLLBACK_STATE_COMMITTED) {
ipw.println("-causePackages:");
ipw.increaseIndent();
for (VersionedPackage cPkg : info.getCausePackages()) {
@@ -1451,7 +1444,7 @@
}
private static class NewRollback {
- public final RollbackData data;
+ public final Rollback rollback;
/**
* This array holds all of the rollback tokens associated with package sessions included
@@ -1469,9 +1462,9 @@
public final int[] packageSessionIds;
/**
- * Flag to determine whether the RollbackData has been cancelled.
+ * Flag to determine whether the rollback has been cancelled.
*
- * <p>RollbackData could be invalidated and cancelled if RollbackManager receives
+ * <p>Rollback could be invalidated and cancelled if RollbackManager receives
* {@link Intent#ACTION_CANCEL_ENABLE_ROLLBACK} from {@link PackageManager}.
*
* <p>The main underlying assumption here is that if enabling the rollback times out, then
@@ -1481,8 +1474,8 @@
*/
public boolean isCancelled = false;
- NewRollback(RollbackData data, int[] packageSessionIds) {
- this.data = data;
+ NewRollback(Rollback rollback, int[] packageSessionIds) {
+ this.rollback = rollback;
this.packageSessionIds = packageSessionIds;
}
@@ -1497,13 +1490,13 @@
NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
int rollbackId = allocateRollbackIdLocked();
- final RollbackData data;
+ final Rollback rollback;
int parentSessionId = parentSession.getSessionId();
if (parentSession.isStaged()) {
- data = mRollbackStore.createStagedRollback(rollbackId, parentSessionId);
+ rollback = mRollbackStore.createStagedRollback(rollbackId, parentSessionId);
} else {
- data = mRollbackStore.createNonStagedRollback(rollbackId);
+ rollback = mRollbackStore.createNonStagedRollback(rollbackId);
}
int[] packageSessionIds;
@@ -1513,7 +1506,7 @@
packageSessionIds = new int[]{parentSessionId};
}
- return new NewRollback(data, packageSessionIds);
+ return new NewRollback(rollback, packageSessionIds);
}
/**
@@ -1524,10 +1517,10 @@
NewRollback getNewRollbackForPackageSessionLocked(int packageSessionId) {
// We expect mNewRollbacks to be a very small list; linear search
// should be plenty fast.
- for (NewRollback newRollbackData : mNewRollbacks) {
- for (int id : newRollbackData.packageSessionIds) {
+ for (NewRollback newRollback: mNewRollbacks) {
+ for (int id : newRollback.packageSessionIds) {
if (id == packageSessionId) {
- return newRollbackData;
+ return newRollback;
}
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index d106825..b2448f6 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -16,8 +16,8 @@
package com.android.server.rollback;
-import static com.android.server.rollback.RollbackData.rollbackStateFromString;
-import static com.android.server.rollback.RollbackData.rollbackStateToString;
+import static com.android.server.rollback.Rollback.rollbackStateFromString;
+import static com.android.server.rollback.Rollback.rollbackStateToString;
import android.annotation.NonNull;
import android.content.pm.VersionedPackage;
@@ -73,17 +73,17 @@
}
/**
- * Reads the rollback data from persistent storage.
+ * Reads the rollbacks from persistent storage.
*/
- List<RollbackData> loadAllRollbackData() {
- List<RollbackData> rollbacks = new ArrayList<>();
+ List<Rollback> loadRollbacks() {
+ List<Rollback> rollbacks = new ArrayList<>();
mRollbackDataDir.mkdirs();
for (File rollbackDir : mRollbackDataDir.listFiles()) {
if (rollbackDir.isDirectory()) {
try {
- rollbacks.add(loadRollbackData(rollbackDir));
+ rollbacks.add(loadRollback(rollbackDir));
} catch (IOException e) {
- Slog.e(TAG, "Unable to read rollback data at " + rollbackDir, e);
+ Slog.e(TAG, "Unable to read rollback at " + rollbackDir, e);
removeFile(rollbackDir);
}
}
@@ -191,21 +191,21 @@
}
/**
- * Creates a new RollbackData instance for a non-staged rollback with
+ * Creates a new Rollback instance for a non-staged rollback with
* backupDir assigned.
*/
- RollbackData createNonStagedRollback(int rollbackId) {
+ Rollback createNonStagedRollback(int rollbackId) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
- return new RollbackData(rollbackId, backupDir, -1);
+ return new Rollback(rollbackId, backupDir, -1);
}
/**
- * Creates a new RollbackData instance for a staged rollback with
+ * Creates a new Rollback instance for a staged rollback with
* backupDir assigned.
*/
- RollbackData createStagedRollback(int rollbackId, int stagedSessionId) {
+ Rollback createStagedRollback(int rollbackId, int stagedSessionId) {
File backupDir = new File(mRollbackDataDir, Integer.toString(rollbackId));
- return new RollbackData(rollbackId, backupDir, stagedSessionId);
+ return new Rollback(rollbackId, backupDir, stagedSessionId);
}
/**
@@ -213,10 +213,10 @@
* For packages containing splits, this method should be called for each
* of the package's split apks in addition to the base apk.
*/
- static void backupPackageCodePath(RollbackData data, String packageName, String codePath)
+ static void backupPackageCodePath(Rollback rollback, String packageName, String codePath)
throws IOException {
File sourceFile = new File(codePath);
- File targetDir = new File(data.backupDir, packageName);
+ File targetDir = new File(rollback.backupDir, packageName);
targetDir.mkdirs();
File targetFile = new File(targetDir, sourceFile.getName());
@@ -228,8 +228,8 @@
* Returns the apk or apex files backed up for the given package.
* Includes the base apk and any splits. Returns null if none found.
*/
- static File[] getPackageCodePaths(RollbackData data, String packageName) {
- File targetDir = new File(data.backupDir, packageName);
+ static File[] getPackageCodePaths(Rollback rollback, String packageName) {
+ File targetDir = new File(rollback.backupDir, packageName);
File[] files = targetDir.listFiles();
if (files == null || files.length == 0) {
return null;
@@ -241,27 +241,27 @@
* Deletes all backed up apks and apex files associated with the given
* rollback.
*/
- static void deletePackageCodePaths(RollbackData data) {
- for (PackageRollbackInfo info : data.info.getPackages()) {
- File targetDir = new File(data.backupDir, info.getPackageName());
+ static void deletePackageCodePaths(Rollback rollback) {
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
+ File targetDir = new File(rollback.backupDir, info.getPackageName());
removeFile(targetDir);
}
}
/**
- * Saves the rollback data to persistent storage.
+ * Saves the given rollback to persistent storage.
*/
- void saveRollbackData(RollbackData data) throws IOException {
+ void saveRollback(Rollback rollback) throws IOException {
try {
JSONObject dataJson = new JSONObject();
- dataJson.put("info", rollbackInfoToJson(data.info));
- dataJson.put("timestamp", data.timestamp.toString());
- dataJson.put("stagedSessionId", data.stagedSessionId);
- dataJson.put("state", rollbackStateToString(data.state));
- dataJson.put("apkSessionId", data.apkSessionId);
- dataJson.put("restoreUserDataInProgress", data.restoreUserDataInProgress);
+ dataJson.put("info", rollbackInfoToJson(rollback.info));
+ dataJson.put("timestamp", rollback.timestamp.toString());
+ dataJson.put("stagedSessionId", rollback.stagedSessionId);
+ dataJson.put("state", rollbackStateToString(rollback.state));
+ dataJson.put("apkSessionId", rollback.apkSessionId);
+ dataJson.put("restoreUserDataInProgress", rollback.restoreUserDataInProgress);
- PrintWriter pw = new PrintWriter(new File(data.backupDir, "rollback.json"));
+ PrintWriter pw = new PrintWriter(new File(rollback.backupDir, "rollback.json"));
pw.println(dataJson.toString());
pw.close();
} catch (JSONException e) {
@@ -270,23 +270,23 @@
}
/**
- * Removes all persistant storage associated with the given rollback data.
+ * Removes all persistent storage associated with the given rollback.
*/
- void deleteRollbackData(RollbackData data) {
- removeFile(data.backupDir);
+ void deleteRollback(Rollback rollback) {
+ removeFile(rollback.backupDir);
}
/**
* Reads the metadata for a rollback from the given directory.
* @throws IOException in case of error reading the data.
*/
- private static RollbackData loadRollbackData(File backupDir) throws IOException {
+ private static Rollback loadRollback(File backupDir) throws IOException {
try {
File rollbackJsonFile = new File(backupDir, "rollback.json");
JSONObject dataJson = new JSONObject(
IoUtils.readFileAsString(rollbackJsonFile.getAbsolutePath()));
- return new RollbackData(
+ return new Rollback(
rollbackInfoFromJson(dataJson.getJSONObject("info")),
backupDir,
Instant.parse(dataJson.getString("timestamp")),
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index d67048f..8897eca 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -610,11 +610,12 @@
@Override
public void showBiometricDialog(Bundle bundle, IBiometricServiceReceiverInternal receiver,
- int type, boolean requireConfirmation, int userId) {
+ int type, boolean requireConfirmation, int userId, String opPackageName) {
enforceBiometricDialog();
if (mBar != null) {
try {
- mBar.showBiometricDialog(bundle, receiver, type, requireConfirmation, userId);
+ mBar.showBiometricDialog(bundle, receiver, type, requireConfirmation, userId,
+ opPackageName);
} catch (RemoteException ex) {
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index a2134be..8e66b14 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -842,6 +842,10 @@
private void setStateLocked(String inputId, int state, int userId) {
UserState userState = getOrCreateUserStateLocked(userId);
TvInputState inputState = userState.inputMap.get(inputId);
+ if (inputState == null) {
+ Slog.e(TAG, "failed to setStateLocked - unknown input id " + inputId);
+ return;
+ }
ServiceState serviceState = userState.serviceStateMap.get(inputState.info.getComponent());
int oldState = inputState.state;
inputState.state = state;
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index f1cd721..e76078f 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -41,6 +41,7 @@
import static com.android.server.am.ActivityDisplayProto.SINGLE_TASK_INSTANCE;
import static com.android.server.am.ActivityDisplayProto.STACKS;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
import static com.android.server.wm.ActivityStackSupervisor.TAG_TASKS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
@@ -173,18 +174,10 @@
mService = root.mService;
mDisplayId = display.getDisplayId();
mDisplay = display;
- mDisplayContent = createDisplayContent();
+ mDisplayContent = mService.mWindowManager.mRoot.createDisplayContent(mDisplay, this);
mDisplayContent.reconfigureDisplayLocked();
- updateBounds();
- }
-
- protected DisplayContent createDisplayContent() {
- return mService.mWindowManager.mRoot.createDisplayContent(mDisplay, this);
- }
-
- private void updateBounds() {
- mDisplay.getRealSize(mTmpDisplaySize);
- setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
+ onRequestedOverrideConfigurationChanged(
+ mDisplayContent.getRequestedOverrideConfiguration());
}
void onDisplayChanged() {
@@ -200,7 +193,8 @@
}
}
- updateBounds();
+ mDisplay.getRealSize(mTmpDisplaySize);
+ setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
if (mDisplayContent != null) {
mDisplayContent.updateDisplayInfo();
mService.mWindowManager.requestTraversal();
@@ -368,7 +362,7 @@
return null;
}
- private boolean alwaysCreateStack(int windowingMode, int activityType) {
+ boolean alwaysCreateStack(int windowingMode, int activityType) {
// Always create a stack for fullscreen, freeform, and split-screen-secondary windowing
// modes so that we can manage visual ordering and return types correctly.
return activityType == ACTIVITY_TYPE_STANDARD
@@ -496,10 +490,6 @@
return null;
}
- ActivityStack getNextFocusableStack() {
- return getNextFocusableStack(null /* currentFocus */, false /* ignoreCurrent */);
- }
-
ActivityStack getNextFocusableStack(ActivityStack currentFocus, boolean ignoreCurrent) {
final int currentWindowingMode = currentFocus != null
? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
@@ -1541,6 +1531,17 @@
return mSingleTaskInstance;
}
+ @VisibleForTesting
+ void removeAllTasks() {
+ for (int i = getChildCount() - 1; i >= 0; --i) {
+ final ActivityStack stack = getChildAt(i);
+ final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ for (int j = tasks.size() - 1; j >= 0; --j) {
+ stack.removeTask(tasks.get(j), "removeAllTasks", REMOVE_TASK_MODE_DESTROYING);
+ }
+ }
+ }
+
public void dump(PrintWriter pw, String prefix) {
pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + mStacks.size()
+ (mSingleTaskInstance ? " mSingleTaskInstance" : ""));
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 31e8bbdab..292c400 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -121,7 +121,7 @@
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
import static com.android.server.wm.ActivityStack.STOP_TIMEOUT_MSG;
import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONFIGURATION;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_CONTAINERS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_FOCUS;
@@ -1372,16 +1372,17 @@
return stack != null ? stack.getDisplay() : null;
}
- boolean changeWindowTranslucency(boolean toOpaque) {
- if (fullscreen == toOpaque) {
- return false;
+ boolean setOccludesParent(boolean occludesParent) {
+ final boolean changed = mAppWindowToken.setOccludesParent(occludesParent);
+ if (changed) {
+ if (!occludesParent) {
+ getActivityStack().convertActivityToTranslucent(this);
+ }
+ // Keep track of the number of fullscreen activities in this task.
+ task.numFullscreen += occludesParent ? +1 : -1;
+ mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
}
-
- // Keep track of the number of fullscreen activities in this task.
- task.numFullscreen += toOpaque ? +1 : -1;
-
- fullscreen = toOpaque;
- return true;
+ return changed;
}
void takeFromHistory() {
@@ -1590,8 +1591,7 @@
if (!Objects.equals(cur.taskAffinity, taskAffinity)) {
break;
}
- cur.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
- "request-affinity", true /* oomAdj */);
+ cur.finishIfPossible("request-affinity", true /* oomAdj */);
}
}
@@ -1648,14 +1648,17 @@
@interface FinishRequest {}
/**
- * See {@link #finishActivityLocked(int, Intent, String, boolean, boolean)}
+ * See {@link #finishIfPossible(int, Intent, String, boolean, boolean)}
*/
- @FinishRequest int finishActivityLocked(int resultCode, Intent resultData, String reason,
- boolean oomAdj) {
- return finishActivityLocked(resultCode, resultData, reason, oomAdj, !PAUSE_IMMEDIATELY);
+ @FinishRequest int finishIfPossible(String reason, boolean oomAdj) {
+ return finishIfPossible(Activity.RESULT_CANCELED, null /* resultData */, reason,
+ oomAdj, !PAUSE_IMMEDIATELY);
}
/**
+ * Finish activity if possible. If activity was resumed - we must first pause it to make the
+ * activity below resumed. Otherwise we will try to complete the request immediately by calling
+ * {@link #completeFinishing(String)}.
* @return One of {@link FinishRequest} values:
* {@link #FINISH_RESULT_REMOVED} if this activity has been removed from the history list.
* {@link #FINISH_RESULT_REQUESTED} if removal process was started, but it is still in the list
@@ -1663,7 +1666,7 @@
* {@link #FINISH_RESULT_CANCELLED} if activity is already finishing or in invalid state and the
* request to finish it was not ignored.
*/
- @FinishRequest int finishActivityLocked(int resultCode, Intent resultData, String reason,
+ @FinishRequest int finishIfPossible(int resultCode, Intent resultData, String reason,
boolean oomAdj, boolean pauseImmediately) {
if (DEBUG_RESULTS || DEBUG_STATES) {
Slog.v(TAG_STATES, "Finishing activity r=" + this + ", result=" + resultCode
@@ -1702,20 +1705,24 @@
pauseKeyDispatchingLocked();
final ActivityStack stack = getActivityStack();
- stack.adjustFocusedActivityStack(this, "finishActivity");
+ stack.adjustFocusedActivityStack(this, "finishIfPossible");
finishActivityResults(resultCode, resultData);
final boolean endTask = index <= 0 && !task.isClearingToReuseTask();
final int transit = endTask ? TRANSIT_TASK_CLOSE : TRANSIT_ACTIVITY_CLOSE;
- if (stack.getResumedActivity() == this) {
- if (DEBUG_VISIBILITY || DEBUG_TRANSITION) {
- Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
- }
+ if (isState(RESUMED)) {
if (endTask) {
mAtmService.getTaskChangeNotificationController().notifyTaskRemovalStarted(
task.getTaskInfo());
}
+ // Prepare app close transition, but don't execute just yet. It is possible that
+ // an activity that will be made resumed in place of this one will immediately
+ // launch another new activity. In this case current closing transition will be
+ // combined with open transition for the new activity.
+ if (DEBUG_VISIBILITY || DEBUG_TRANSITION) {
+ Slog.v(TAG_TRANSITION, "Prepare close transition: finishing " + this);
+ }
getDisplay().mDisplayContent.prepareAppTransition(transit, false);
// When finishing the activity preemptively take the snapshot before the app window
@@ -1742,17 +1749,17 @@
mAtmService.getLockTaskController().clearLockedTask(task);
}
} else if (!isState(PAUSING)) {
- // If the activity is PAUSING, we will complete the finish once
- // it is done pausing; else we can just directly finish it here.
- if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish not pausing: " + this);
if (visible) {
+ // Prepare and execute close transition.
prepareActivityHideTransitionAnimation(transit);
}
- final int finishMode = (visible || nowVisible) ? FINISH_AFTER_VISIBLE
- : FINISH_AFTER_PAUSE;
- final boolean removedActivity = finishCurrentActivityLocked(finishMode, oomAdj,
- "finishActivityLocked") == null;
+ final boolean removedActivity = completeFinishing("finishIfPossible") == null;
+ // Performance optimization - only invoke OOM adjustment if the state changed to
+ // 'STOPPING'. Otherwise it will not change the OOM scores.
+ if (oomAdj && isState(STOPPING)) {
+ mAtmService.updateOomAdj();
+ }
// The following code is an optimization. When the last non-task overlay activity
// is removed from the task, we remove the entire task from the stack. However,
@@ -1769,6 +1776,7 @@
taskOverlay.prepareActivityHideTransitionAnimation(transit);
}
}
+
return removedActivity ? FINISH_RESULT_REMOVED : FINISH_RESULT_REQUESTED;
} else {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Finish waiting for pause of: " + this);
@@ -1787,101 +1795,123 @@
dc.executeAppTransition();
}
- static final int FINISH_IMMEDIATELY = 0;
- private static final int FINISH_AFTER_PAUSE = 1;
- static final int FINISH_AFTER_VISIBLE = 2;
+ /**
+ * Complete activity finish request that was initiated earlier. If the activity is still
+ * pausing we will wait for it to complete its transition. If the activity that should appear in
+ * place of this one is not visible yet - we'll wait for it first. Otherwise - activity can be
+ * destroyed right away.
+ * @param reason Reason for finishing the activity.
+ * @return Flag indicating whether the activity was removed from history.
+ */
+ ActivityRecord completeFinishing(String reason) {
+ if (!finishing || isState(RESUMED)) {
+ throw new IllegalArgumentException(
+ "Activity must be finishing and not resumed to complete, r=" + this
+ + ", finishing=" + finishing + ", state=" + mState);
+ }
- ActivityRecord finishCurrentActivityLocked(int mode, boolean oomAdj, String reason) {
- // First things first: if this activity is currently visible,
- // and the resumed activity is not yet visible, then hold off on
- // finishing until the resumed one becomes visible.
+ if (isState(PAUSING)) {
+ // Activity is marked as finishing and will be processed once it completes.
+ return this;
+ }
+ boolean activityRemoved = false;
+
+ // If this activity is currently visible, and the resumed activity is not yet visible, then
+ // hold off on finishing until the resumed one becomes visible.
// The activity that we are finishing may be over the lock screen. In this case, we do not
// want to consider activities that cannot be shown on the lock screen as running and should
// proceed with finishing the activity if there is no valid next top running activity.
// Note that if this finishing activity is floating task, we don't need to wait the
// next activity resume and can destroy it directly.
+ // TODO(b/137329632): find the next activity directly underneath this one, not just anywhere
+ final ActivityRecord next = getDisplay().topRunningActivity(
+ true /* considerKeyguardState */);
+ final boolean isVisible = visible || nowVisible;
final ActivityStack stack = getActivityStack();
- final ActivityDisplay display = getDisplay();
- final ActivityRecord next = display.topRunningActivity(true /* considerKeyguardState */);
- final boolean isFloating = getConfiguration().windowConfiguration.tasksAreFloating();
-
- if (mode == FINISH_AFTER_VISIBLE && (visible || nowVisible)
- && next != null && !next.nowVisible && !isFloating) {
+ final boolean notFocusedStack = stack != mRootActivityContainer.getTopDisplayFocusedStack();
+ if (isVisible && next != null && !next.nowVisible) {
if (!mStackSupervisor.mStoppingActivities.contains(this)) {
- stack.addToStopping(this, false /* scheduleIdle */, false /* idleDelayed */,
- "finishCurrentActivityLocked");
+ getActivityStack().addToStopping(this, false /* scheduleIdle */,
+ false /* idleDelayed */, "finishCurrentActivityLocked");
}
if (DEBUG_STATES) {
Slog.v(TAG_STATES, "Moving to STOPPING: " + this + " (finish requested)");
}
setState(STOPPING, "finishCurrentActivityLocked");
- if (oomAdj) {
- mAtmService.updateOomAdj();
- }
- return this;
- }
-
- // make sure the record is cleaned out of other places.
- mStackSupervisor.mStoppingActivities.remove(this);
- mStackSupervisor.mGoingToSleepActivities.remove(this);
- final ActivityState prevState = getState();
- if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to FINISHING: " + this);
-
- setState(FINISHING, "finishCurrentActivityLocked");
-
- // Don't destroy activity immediately if the display contains home stack, although there is
- // no next activity at the moment but another home activity should be started later. Keep
- // this activity alive until next home activity is resumed then user won't see a temporary
- // black screen.
- final boolean noRunningStack = next == null && display.topRunningActivity() == null
- && display.getHomeStack() == null;
- final boolean noFocusedStack = stack != display.getFocusedStack();
- final boolean finishingInNonFocusedStackOrNoRunning = mode == FINISH_AFTER_VISIBLE
- && prevState == PAUSED && (noFocusedStack || noRunningStack);
-
- if (mode == FINISH_IMMEDIATELY
- || (prevState == PAUSED && (mode == FINISH_AFTER_PAUSE || inPinnedWindowingMode()))
- || finishingInNonFocusedStackOrNoRunning
- || prevState == STARTED
- || prevState == STOPPING
- || prevState == STOPPED
- || prevState == ActivityState.INITIALIZING) {
- makeFinishingLocked();
- boolean activityRemoved = stack.destroyActivityLocked(this, true /* removeFromApp */,
- "finish-imm:" + reason);
-
- if (finishingInNonFocusedStackOrNoRunning) {
- // Finishing activity that was in paused state and it was in not currently focused
- // stack, need to make something visible in its place. Also if the display does not
- // have running activity, the configuration may need to be updated for restoring
- // original orientation of the display.
+ if (notFocusedStack) {
mRootActivityContainer.ensureVisibilityAndConfig(next, getDisplayId(),
false /* markFrozenIfConfigChanged */, true /* deferResume */);
}
- if (activityRemoved) {
- mRootActivityContainer.resumeFocusedStacksTopActivities();
- }
- if (DEBUG_CONTAINERS) {
- Slog.d(TAG_CONTAINERS, "destroyActivityLocked: finishCurrentActivityLocked r="
- + this + " destroy returned removed=" + activityRemoved);
- }
- return activityRemoved ? null : this;
+ } else if (isVisible && isState(PAUSED) && getActivityStack().isFocusedStackOnDisplay()
+ && !inPinnedWindowingMode()) {
+ // TODO(b/137329632): Currently non-focused stack is handled differently.
+ addToFinishingAndWaitForIdle();
+ } else {
+ // Not waiting for the next one to become visible - finish right away.
+ activityRemoved = destroyIfPossible(reason);
}
- // Need to go through the full pause cycle to get this
- // activity into the stopped state and then finish it.
- if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + this);
+ return activityRemoved ? null : this;
+ }
+
+ /**
+ * Destroy and cleanup the activity both on client and server if possible. If activity is the
+ * last one left on display with home stack and there is no other running activity - delay
+ * destroying it until the next one starts.
+ */
+ boolean destroyIfPossible(String reason) {
+ setState(FINISHING, "destroyIfPossible");
+
+ // Make sure the record is cleaned out of other places.
+ mStackSupervisor.mStoppingActivities.remove(this);
+ mStackSupervisor.mGoingToSleepActivities.remove(this);
+
+ final ActivityStack stack = getActivityStack();
+ final ActivityDisplay display = getDisplay();
+ // TODO(b/137329632): Exclude current activity when looking for the next one with
+ // ActivityDisplay#topRunningActivity().
+ final ActivityRecord next = display.topRunningActivity();
+ final boolean isLastStackOverEmptyHome =
+ next == null && stack.isFocusedStackOnDisplay() && display.getHomeStack() != null;
+ if (isLastStackOverEmptyHome) {
+ // Don't destroy activity immediately if this is the last activity on the display and
+ // the display contains home stack. Although there is no next activity at the moment,
+ // another home activity should be started later. Keep this activity alive until next
+ // home activity is resumed. This way the user won't see a temporary black screen.
+ addToFinishingAndWaitForIdle();
+ return false;
+ }
+ makeFinishingLocked();
+
+ boolean activityRemoved = getActivityStack().destroyActivityLocked(this,
+ true /* removeFromApp */, "finish-imm:" + reason);
+
+ // If the display does not have running activity, the configuration may need to be
+ // updated for restoring original orientation of the display.
+ if (next == null) {
+ mRootActivityContainer.ensureVisibilityAndConfig(next, getDisplayId(),
+ false /* markFrozenIfConfigChanged */, true /* deferResume */);
+ }
+ if (activityRemoved) {
+ mRootActivityContainer.resumeFocusedStacksTopActivities();
+ }
+
+ if (DEBUG_CONTAINERS) {
+ Slog.d(TAG_CONTAINERS, "destroyIfPossible: r=" + this + " destroy returned removed="
+ + activityRemoved);
+ }
+
+ return activityRemoved;
+ }
+
+ @VisibleForTesting
+ void addToFinishingAndWaitForIdle() {
+ if (DEBUG_STATES) Slog.v(TAG, "Enqueueing pending finish: " + this);
+ setState(FINISHING, "addToFinishingAndWaitForIdle");
mStackSupervisor.mFinishingActivities.add(this);
resumeKeyDispatchingLocked();
mRootActivityContainer.resumeFocusedStacksTopActivities();
- // If activity was not paused at this point - explicitly pause it to start finishing
- // process. Finishing will be completed once it reports pause back.
- if (isState(RESUMED) && stack.mPausingActivity != null) {
- stack.startPausingLocked(false /* userLeaving */, false /* uiSleeping */,
- next /* resuming */, false /* dontWait */);
- }
- return this;
}
void makeFinishingLocked() {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 12eab50..186f838 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -56,8 +56,6 @@
import static com.android.server.am.ActivityStackProto.TASKS;
import static com.android.server.wm.ActivityDisplay.POSITION_BOTTOM;
import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
-import static com.android.server.wm.ActivityRecord.FINISH_AFTER_VISIBLE;
-import static com.android.server.wm.ActivityRecord.FINISH_IMMEDIATELY;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
import static com.android.server.wm.ActivityStack.ActivityState.DESTROYED;
@@ -108,6 +106,7 @@
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
import static com.android.server.wm.RootActivityContainer.FindTaskResult;
+import static com.android.server.wm.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
import static java.lang.Integer.MAX_VALUE;
@@ -1771,8 +1770,7 @@
if (r.finishing) {
if (DEBUG_PAUSE) Slog.v(TAG,
"Executing finish of failed to pause activity: " + r);
- r.finishCurrentActivityLocked(FINISH_AFTER_VISIBLE, false,
- "activityPausedLocked");
+ r.completeFinishing("activityPausedLocked");
}
}
}
@@ -1790,8 +1788,7 @@
prev.setState(PAUSED, "completePausedLocked");
if (prev.finishing) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
- prev = prev.finishCurrentActivityLocked(FINISH_AFTER_VISIBLE, false /* oomAdj */,
- "completePausedLocked");
+ prev = prev.completeFinishing("completePausedLocked");
} else if (prev.hasProcess()) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueue pending stop if needed: " + prev
+ " wasStopping=" + wasStopping + " visible=" + prev.visible);
@@ -2492,7 +2489,7 @@
mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
if (waitingActivity != null) {
- mWindowManager.setWindowOpaque(waitingActivity.appToken, false);
+ mWindowManager.setWindowOpaqueLocked(waitingActivity.appToken, false);
if (waitingActivity.attachedToProcess()) {
try {
waitingActivity.app.getThread().scheduleTranslucentConversionComplete(
@@ -2782,8 +2779,7 @@
!mLastNoHistoryActivity.finishing) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
"no-history finish of " + mLastNoHistoryActivity + " on new resume");
- mLastNoHistoryActivity.finishActivityLocked(Activity.RESULT_CANCELED,
- null /* resultData */, "resume-no-history", false /* oomAdj */);
+ mLastNoHistoryActivity.finishIfPossible("resume-no-history", false /* oomAdj */);
mLastNoHistoryActivity = null;
}
@@ -2813,7 +2809,7 @@
// Launching this app's activity, make sure the app is no longer
// considered stopped.
try {
- AppGlobals.getPackageManager().setPackageStoppedState(
+ mService.getPackageManager().setPackageStoppedState(
next.packageName, false, next.mUserId); /* TODO: Verify if correct userid */
} catch (RemoteException e1) {
} catch (IllegalArgumentException e) {
@@ -3018,8 +3014,7 @@
// If any exception gets thrown, toss away this
// activity and try the next one.
Slog.w(TAG, "Exception thrown during resume of " + next, e);
- next.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
- "resume-exception", true /* oomAdj */);
+ next.finishIfPossible("resume-exception", true /* oomAdj */);
return true;
}
} else {
@@ -3322,6 +3317,7 @@
// to activities in the same chain will depend on what the end activity of the chain needs.
int replyChainEnd = -1;
boolean canMoveOptions = true;
+ int numTasksCreated = 0;
// We only do this for activities that are not the root of the task (since if we finish
// the root, we may no longer have the task!).
@@ -3386,6 +3382,7 @@
target.info, null /* intent */, null /* voiceSession */,
null /* voiceInteractor */, false /* toTop */);
targetTask.affinityIntent = target.intent;
+ numTasksCreated++;
if (DEBUG_TASKS) Slog.v(TAG_TASKS, "Start pushing activity " + target
+ " out to new task " + targetTask);
}
@@ -3414,6 +3411,7 @@
}
positionChildWindowContainerAtBottom(targetTask);
+ mStackSupervisor.mRecentTasks.add(targetTask);
replyChainEnd = -1;
} else if (forceReset || finishOnTaskLaunch || clearWhenTaskReset) {
// If the activity should just be removed -- either
@@ -3446,8 +3444,8 @@
}
if (DEBUG_TASKS) Slog.w(TAG_TASKS,
"resetTaskIntendedTask: calling finishActivity on " + p);
- if (p.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
- "reset-task", false /* oomAdj */) == FINISH_RESULT_REMOVED) {
+ if (p.finishIfPossible("reset-task", false /* oomAdj */)
+ == FINISH_RESULT_REMOVED) {
end--;
srcPos--;
}
@@ -3461,6 +3459,27 @@
}
}
+ // Create target stack for the newly created tasks if necessary
+ if (numTasksCreated > 0) {
+ ActivityDisplay display = getDisplay();
+ final boolean singleTaskInstanceDisplay = display.isSingleTaskInstance();
+ if (singleTaskInstanceDisplay) {
+ display = mRootActivityContainer.getDefaultDisplay();
+ }
+
+ if (singleTaskInstanceDisplay || display.alwaysCreateStack(getWindowingMode(),
+ getActivityType())) {
+ for (int index = numTasksCreated - 1; index >= 0; index--) {
+ final TaskRecord targetTask = mTaskHistory.get(index);
+ final ActivityStack targetStack = display.getOrCreateStack(getWindowingMode(),
+ getActivityType(), false /* onTop */);
+ targetTask.reparent(targetStack, false /* toTop */,
+ REPARENT_LEAVE_STACK_IN_PLACE, false /* animate */,
+ true /* deferResume */, "resetTargetTask");
+ }
+ }
+ }
+
return topOptions;
}
@@ -3531,8 +3550,7 @@
if (p.finishing) {
continue;
}
- p.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
- "move-affinity", false /* oomAdj */);
+ p.finishIfPossible("move-affinity", false /* oomAdj */);
}
} else {
if (taskInsertionPoint < 0) {
@@ -3566,8 +3584,7 @@
if (targetNdx > 0) {
final ActivityRecord p = taskActivities.get(targetNdx - 1);
if (p.intent.getComponent().equals(target.intent.getComponent())) {
- p.finishActivityLocked(Activity.RESULT_CANCELED,
- null /* resultData */, "replace", false /* oomAdj */);
+ p.finishIfPossible("replace", false /* oomAdj */);
}
}
}
@@ -3755,8 +3772,8 @@
if (!r.finishing) {
if (!shouldSleepActivities()) {
if (DEBUG_STATES) Slog.d(TAG_STATES, "no-history finish of " + r);
- if (r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
- "stop-no-history", false /* oomAdj */) != FINISH_RESULT_CANCELLED) {
+ if (r.finishIfPossible("stop-no-history", false /* oomAdj */)
+ != FINISH_RESULT_CANCELLED) {
// {@link adjustFocusedActivityStack} must have been already called.
r.resumeKeyDispatchingLocked();
return;
@@ -3815,8 +3832,7 @@
if (r.resultTo == self && r.requestCode == requestCode) {
if ((r.resultWho == null && resultWho == null) ||
(r.resultWho != null && r.resultWho.equals(resultWho))) {
- r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
- "request-sub", false /* oomAdj */);
+ r.finishIfPossible("request-sub", false /* oomAdj */);
}
}
}
@@ -3846,8 +3862,7 @@
int activityNdx = task.mActivities.indexOf(r);
getDisplay().mDisplayContent.prepareAppTransition(
TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
- r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, reason,
- false /* oomAdj */);
+ r.finishIfPossible(reason, false /* oomAdj */);
finishedTask = task;
// Also terminate any activities below it that aren't yet
// stopped, to avoid a situation where one will get
@@ -3868,8 +3883,7 @@
if (!r.isActivityTypeHome() || mService.mHomeProcess != r.app) {
Slog.w(TAG, " Force finishing activity "
+ r.intent.getComponent().flattenToShortString());
- r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */, reason,
- false /* oomAdj */);
+ r.finishIfPossible(reason, false /* oomAdj */);
}
}
}
@@ -3885,8 +3899,7 @@
for (int activityNdx = tr.mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
ActivityRecord r = tr.mActivities.get(activityNdx);
if (!r.finishing) {
- r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
- "finish-voice", false /* oomAdj */);
+ r.finishIfPossible("finish-voice", false /* oomAdj */);
didOne = true;
}
}
@@ -3924,8 +3937,7 @@
final ActivityRecord r = activities.get(activityNdx);
noActivitiesInStack = false;
Slog.d(TAG, "finishAllActivitiesImmediatelyLocked: finishing " + r);
- r.finishCurrentActivityLocked(FINISH_IMMEDIATELY, false /* oomAdj */,
- "finishAllActivitiesImmediatelyLocked");
+ r.destroyIfPossible("finishAllActivitiesImmediatelyLocked");
}
}
if (noActivitiesInStack) {
@@ -4029,7 +4041,8 @@
final long origId = Binder.clearCallingIdentity();
for (int i = start; i > finishTo; i--) {
final ActivityRecord r = activities.get(i);
- r.finishActivityLocked(resultCode, resultData, "navigate-up", true /* oomAdj */);
+ r.finishIfPossible(resultCode, resultData, "navigate-up", true /* oomAdj */,
+ !PAUSE_IMMEDIATELY);
// Only return the supplied result for the first activity finished
resultCode = Activity.RESULT_CANCELED;
resultData = null;
@@ -4066,8 +4079,8 @@
} catch (RemoteException e) {
foundParentInTask = false;
}
- parent.finishActivityLocked(resultCode, resultData, "navigate-top",
- true /* oomAdj */);
+ parent.finishIfPossible(resultCode, resultData, "navigate-top",
+ true /* oomAdj */, !PAUSE_IMMEDIATELY);
}
}
Binder.restoreCallingIdentity(origId);
@@ -4312,10 +4325,13 @@
}
/**
- * Destroy the current CLIENT SIDE instance of an activity. This may be
- * called both when actually finishing an activity, or when performing
- * a configuration switch where we destroy the current client-side object
- * but then create a new client-side object for this same HistoryRecord.
+ * Destroy the current CLIENT SIDE instance of an activity. This may be * called both when
+ * actually finishing an activity, or when performing a configuration switch where we destroy
+ * the current client-side object but then create a new client-side object for this same
+ * HistoryRecord.
+ * Normally the server-side record will be removed when the client reports back after
+ * destruction. If, however, at this point there is no client process attached, the record will
+ * removed immediately.
*/
final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(TAG_SWITCH,
@@ -4890,7 +4906,7 @@
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
if ((r.info.flags&ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS) != 0) {
- r.finishActivityLocked(Activity.RESULT_CANCELED, null, "close-sys", true);
+ r.finishIfPossible("close-sys", true /* oomAdj */);
}
}
}
@@ -4934,7 +4950,7 @@
didSomething = true;
Slog.i(TAG, " Force finishing activity " + r);
lastTask = r.getTaskRecord();
- r.finishActivityLocked(Activity.RESULT_CANCELED, null, "force-stop", true);
+ r.finishIfPossible("force-stop", true);
}
}
}
@@ -4988,8 +5004,7 @@
final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities;
int activityTop = activities.size() - 1;
if (activityTop >= 0) {
- activities.get(activityTop).finishActivityLocked(Activity.RESULT_CANCELED, null,
- "unhandled-back", true);
+ activities.get(activityTop).finishIfPossible("unhandled-back", true /* oomAdj */);
}
}
}
@@ -5025,8 +5040,7 @@
r.app = null;
getDisplay().mDisplayContent.prepareAppTransition(
TRANSIT_CRASHING_ACTIVITY_CLOSE, false /* alwaysKeepCurrent */);
- r.finishCurrentActivityLocked(FINISH_IMMEDIATELY, false /* oomAdj */,
- "handleAppCrashedLocked");
+ r.destroyIfPossible("handleAppCrashedLocked");
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 1c56a10..d0c70de 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -250,7 +250,7 @@
RecentTasks mRecentTasks;
/** Helper class to abstract out logic for fetching the set of currently running tasks */
- RunningTasks mRunningTasks;
+ private RunningTasks mRunningTasks;
final ActivityStackSupervisorHandler mHandler;
final Looper mLooper;
@@ -444,7 +444,7 @@
}
mInitialized = true;
- mRunningTasks = createRunningTasks();
+ setRunningTasks(new RunningTasks());
mActivityMetricsLogger = new ActivityMetricsLogger(this, mService.mContext,
mHandler.getLooper());
@@ -485,13 +485,20 @@
}
void setRecentTasks(RecentTasks recentTasks) {
+ if (mRecentTasks != null) {
+ mRecentTasks.unregisterCallback(this);
+ }
mRecentTasks = recentTasks;
mRecentTasks.registerCallback(this);
}
@VisibleForTesting
- RunningTasks createRunningTasks() {
- return new RunningTasks();
+ void setRunningTasks(RunningTasks runningTasks) {
+ mRunningTasks = runningTasks;
+ }
+
+ RunningTasks getRunningTasks() {
+ return mRunningTasks;
}
/**
@@ -890,8 +897,7 @@
Slog.e(TAG, "Second failure launching "
+ r.intent.getComponent().flattenToShortString() + ", giving up", e);
proc.appDied();
- r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
- "2nd-crash", false /* oomAdj */);
+ r.finishIfPossible("2nd-crash", false /* oomAdj */);
return false;
}
@@ -1340,8 +1346,8 @@
final ActivityStack stack = r.getActivityStack();
if (stack != null) {
if (r.finishing) {
- r.finishCurrentActivityLocked(ActivityRecord.FINISH_IMMEDIATELY,
- false /* oomAdj */, "activityIdleInternalLocked");
+ // TODO(b/137329632): Wait for idle of the right activity, not just any.
+ r.destroyIfPossible("activityIdleInternalLocked");
} else {
stack.stopActivityLocked(r);
}
@@ -2735,7 +2741,7 @@
mWindowManager.deferSurfaceLayout();
try {
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- mWindowManager.setDockedStackCreateState(
+ mWindowManager.setDockedStackCreateStateLocked(
activityOptions.getSplitScreenCreateMode(), null /* initialBounds */);
// Defer updating the stack in which recents is until the app transition is done, to
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 9ed93c4..d6250f6 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1426,8 +1426,7 @@
// performing operations without a window container.
final ActivityStack stack = mStartActivity.getActivityStack();
if (stack != null) {
- mStartActivity.finishActivityLocked(RESULT_CANCELED,
- null /* intentResultData */, "startActivity", true /* oomAdj */);
+ mStartActivity.finishIfPossible("startActivity", true /* oomAdj */);
}
// Stack should also be detached from display and be removed if it's empty.
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index a3ab27e..2da2ebc 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -43,7 +43,6 @@
import static android.content.pm.ConfigurationInfo.GL_ES_VERSION_UNDEFINED;
import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
import static android.content.pm.PackageManager.FEATURE_FREEFORM_WINDOW_MANAGEMENT;
-import static android.content.pm.PackageManager.FEATURE_PC;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.res.Configuration.UI_MODE_TYPE_TELEVISION;
@@ -87,6 +86,7 @@
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.wm.ActivityStackSupervisor.DEFER_RESUME;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
+import static com.android.server.wm.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
import static com.android.server.wm.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
@@ -211,7 +211,7 @@
import android.sysprop.DisplayProperties;
import android.telecom.TelecomManager;
import android.text.TextUtils;
-import android.text.format.Time;
+import android.text.format.TimeMigrationUtils;
import android.util.ArrayMap;
import android.util.EventLog;
import android.util.Log;
@@ -361,7 +361,7 @@
/* Global service lock used by the package the owns this service. */
final WindowManagerGlobalLock mGlobalLock = new WindowManagerGlobalLock();
/**
- * It is the same instance as {@link mGlobalLock}, just declared as a type that the
+ * It is the same instance as {@link #mGlobalLock}, just declared as a type that the
* locked-region-code-injection does't recognize it. It is used to skip wrapping priority
* booster for places that are already in the scope of another booster (e.g. computing oom-adj).
*
@@ -730,7 +730,6 @@
final boolean forceRtl = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_RTL, 0) != 0;
final boolean forceResizable = Settings.Global.getInt(
resolver, DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
- final boolean isPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
// Transfer any global setting for forcing RTL layout, into a System Property
DisplayProperties.debug_force_rtl(forceRtl);
@@ -761,10 +760,6 @@
mSupportsPictureInPicture = false;
mSupportsMultiDisplay = false;
}
- mWindowManager.setForceResizableTasks(mForceResizableActivities);
- mWindowManager.setSupportsPictureInPicture(mSupportsPictureInPicture);
- mWindowManager.setSupportsFreeformWindowManagement(mSupportsFreeformWindowManagement);
- mWindowManager.setIsPc(isPc);
mWindowManager.mRoot.onSettingsRetrieved();
// This happens before any activities are started, so we can change global configuration
// in-place.
@@ -821,8 +816,7 @@
new TaskChangeNotificationController(mGlobalLock, mStackSupervisor, mH);
mLockTaskController = new LockTaskController(mContext, mStackSupervisor, mH);
mActivityStartController = new ActivityStartController(this);
- mRecentTasks = createRecentTasks();
- mStackSupervisor.setRecentTasks(mRecentTasks);
+ setRecentTasks(new RecentTasks(this, mStackSupervisor));
mVrController = new VrController(mGlobalLock);
mKeyguardController = mStackSupervisor.getKeyguardController();
}
@@ -890,8 +884,10 @@
return mode == AppOpsManager.MODE_ALLOWED;
}
- protected RecentTasks createRecentTasks() {
- return new RecentTasks(this, mStackSupervisor);
+ @VisibleForTesting
+ protected void setRecentTasks(RecentTasks recentTasks) {
+ mRecentTasks = recentTasks;
+ mStackSupervisor.setRecentTasks(recentTasks);
}
RecentTasks getRecentTasks() {
@@ -1619,8 +1615,8 @@
// Explicitly dismissing the activity so reset its relaunch flag.
r.mRelaunchReason = RELAUNCH_REASON_NONE;
} else {
- r.finishActivityLocked(resultCode, resultData, "app-request",
- true /* oomAdj */);
+ r.finishIfPossible(resultCode, resultData, "app-request",
+ true /* oomAdj */, !PAUSE_IMMEDIATELY);
res = r.finishing;
if (!res) {
Slog.i(TAG, "Failed to finish by app-request");
@@ -1954,12 +1950,7 @@
if (r == null) {
return false;
}
- final boolean translucentChanged = r.changeWindowTranslucency(true);
- if (translucentChanged) {
- mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- }
- mWindowManager.setAppFullscreen(token, true);
- return translucentChanged;
+ return r.setOccludesParent(true);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -1982,13 +1973,7 @@
ActivityRecord under = task.mActivities.get(index - 1);
under.returningOptions = safeOptions != null ? safeOptions.getOptions(r) : null;
}
- final boolean translucentChanged = r.changeWindowTranslucency(false);
- if (translucentChanged) {
- r.getActivityStack().convertActivityToTranslucent(r);
- }
- mRootActivityContainer.ensureActivitiesVisible(null, 0, !PRESERVE_WINDOWS);
- mWindowManager.setAppFullscreen(token, false);
- return translucentChanged;
+ return r.setOccludesParent(false);
}
} finally {
Binder.restoreCallingIdentity(origId);
@@ -2581,7 +2566,7 @@
+ taskId + " to stack " + stackId);
}
if (stack.inSplitScreenPrimaryWindowingMode()) {
- mWindowManager.setDockedStackCreateState(
+ mWindowManager.setDockedStackCreateStateLocked(
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
}
task.reparent(stack, toTop, REPARENT_KEEP_STACK_AT_FRONT, ANIMATE, !DEFER_RESUME,
@@ -2700,7 +2685,7 @@
+ " non-standard task " + taskId + " to split-screen windowing mode");
}
- mWindowManager.setDockedStackCreateState(createMode, initialBounds);
+ mWindowManager.setDockedStackCreateStateLocked(createMode, initialBounds);
final int windowingMode = task.getWindowingMode();
final ActivityStack stack = task.getStack();
if (toTop) {
@@ -2802,7 +2787,7 @@
try {
synchronized (mGlobalLock) {
// Cancel the recents animation synchronously (do not hold the WM lock)
- mWindowManager.cancelRecentsAnimationSynchronously(restoreHomeStackPosition
+ mWindowManager.cancelRecentsAnimation(restoreHomeStackPosition
? REORDER_MOVE_TO_ORIGINAL_POSITION
: REORDER_KEEP_IN_PLACE, "cancelRecentsAnimation/uid=" + callingUid);
}
@@ -5878,9 +5863,9 @@
tracesFile = File.createTempFile("app_slow", null, tracesDir);
StringBuilder sb = new StringBuilder();
- Time tobj = new Time();
- tobj.set(System.currentTimeMillis());
- sb.append(tobj.format("%Y-%m-%d %H:%M:%S"));
+ String timeString =
+ TimeMigrationUtils.formatMillisWithFixedFormat(System.currentTimeMillis());
+ sb.append(timeString);
sb.append(": ");
TimeUtils.formatDuration(SystemClock.uptimeMillis()-startTime, sb);
sb.append(" since ");
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 7fde6de..7e0d9a0 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -92,6 +92,7 @@
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.GraphicBuffer;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.Binder;
@@ -153,8 +154,11 @@
final ComponentName mActivityComponent;
final boolean mVoiceInteraction;
- /** @see WindowContainer#fillsParent() */
- private boolean mFillsParent;
+ /**
+ * The activity is opaque and fills the entire space of this task.
+ * @see WindowContainer#fillsParent()
+ */
+ private boolean mOccludesParent;
boolean mShowForAllUsers;
int mTargetSdk;
@@ -373,7 +377,7 @@
appToken = token;
mActivityComponent = activityComponent;
mVoiceInteraction = voiceInteraction;
- mFillsParent = fillsParent;
+ mOccludesParent = fillsParent;
mInputApplicationHandle = new InputApplicationHandle(appToken.asBinder());
}
@@ -2354,11 +2358,29 @@
@Override
boolean fillsParent() {
- return mFillsParent;
+ return occludesParent();
}
- void setFillsParent(boolean fillsParent) {
- mFillsParent = fillsParent;
+ /** Returns true if this activity is opaque and fills the entire space of this task. */
+ boolean occludesParent() {
+ return mOccludesParent;
+ }
+
+ boolean setOccludesParent(boolean occludesParent) {
+ final boolean changed = occludesParent != mOccludesParent;
+ mOccludesParent = occludesParent;
+ setMainWindowOpaque(occludesParent);
+ mWmService.mWindowPlacerLocked.requestTraversal();
+ return changed;
+ }
+
+ void setMainWindowOpaque(boolean isOpaque) {
+ final WindowState win = findMainWindow();
+ if (win == null) {
+ return;
+ }
+ isOpaque = isOpaque & !PixelFormat.formatHasAlpha(win.getAttrs().format);
+ win.mWinAnimator.setOpaqueLocked(isOpaque);
}
boolean containsDismissKeyguardWindow() {
@@ -3035,7 +3057,7 @@
}
pw.println(prefix + "component=" + mActivityComponent.flattenToShortString());
pw.print(prefix); pw.print("task="); pw.println(getTask());
- pw.print(prefix); pw.print(" mFillsParent="); pw.print(mFillsParent);
+ pw.print(prefix); pw.print(" mOccludesParent="); pw.print(mOccludesParent);
pw.print(" mOrientation="); pw.println(mOrientation);
pw.println(prefix + "hiddenRequested=" + hiddenRequested + " mClientHidden=" + mClientHidden
+ ((mDeferHidingClient) ? " mDeferHidingClient=" + mDeferHidingClient : "")
@@ -3152,7 +3174,7 @@
if (mThumbnail != null){
mThumbnail.writeToProto(proto, THUMBNAIL);
}
- proto.write(FILLS_PARENT, mFillsParent);
+ proto.write(FILLS_PARENT, mOccludesParent);
proto.write(APP_STOPPED, mAppStopped);
proto.write(HIDDEN_REQUESTED, hiddenRequested);
proto.write(CLIENT_HIDDEN, mClientHidden);
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index 282ed42..410cc94 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -295,7 +295,8 @@
false /* forceRelayout */);
}
- private void setUserRotation(int userRotationMode, int userRotation) {
+ @VisibleForTesting
+ void setUserRotation(int userRotationMode, int userRotation) {
if (isDefaultDisplay) {
// We'll be notified via settings listener, so we don't need to update internal values.
final ContentResolver res = mContext.getContentResolver();
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 207e8ef..8507918 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -275,14 +275,14 @@
// This display used to be in freeform, but we don't support freeform anymore, so fall
// back to fullscreen.
if (windowingMode == WindowConfiguration.WINDOWING_MODE_FREEFORM
- && !mService.mSupportsFreeformWindowManagement) {
+ && !mService.mAtmService.mSupportsFreeformWindowManagement) {
return WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
}
// No record is present so use default windowing mode policy.
if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
final boolean forceDesktopMode = mService.mForceDesktopModeOnExternalDisplays
&& displayId != Display.DEFAULT_DISPLAY;
- windowingMode = mService.mSupportsFreeformWindowManagement
+ windowingMode = mService.mAtmService.mSupportsFreeformWindowManagement
&& (mService.mIsPc || forceDesktopMode)
? WindowConfiguration.WINDOWING_MODE_FREEFORM
: WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index b1bc2197..120ce3e 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -355,7 +355,9 @@
void getTouchRegion(Rect outRegion) {
outRegion.set(mTouchRegion);
- outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
+ if (mWindow != null) {
+ outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
+ }
}
private void resetDragResizingChangeReported() {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 4f0332c..caa8363 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -241,7 +241,7 @@
// Fetch all the surface controls and pass them to the client to get the animation
// started. Cancel any existing recents animation running synchronously (do not hold the
// WM lock)
- mWindowManager.cancelRecentsAnimationSynchronously(REORDER_MOVE_TO_ORIGINAL_POSITION,
+ mWindowManager.cancelRecentsAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION,
"startRecentsActivity");
mWindowManager.initializeRecentsAnimation(mTargetActivityType, recentsAnimationRunner,
this, mDefaultDisplay.mDisplayId,
@@ -396,12 +396,8 @@
@Override
public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode,
- boolean runSychronously, boolean sendUserLeaveHint) {
- if (runSychronously) {
- finishAnimation(reorderMode, sendUserLeaveHint);
- } else {
- mService.mH.post(() -> finishAnimation(reorderMode, sendUserLeaveHint));
- }
+ boolean sendUserLeaveHint) {
+ finishAnimation(reorderMode, sendUserLeaveHint);
}
@Override
@@ -435,8 +431,7 @@
} else {
// Just cancel directly to unleash from launcher when the next launching task is the
// current top task.
- mWindowManager.cancelRecentsAnimationSynchronously(REORDER_KEEP_IN_PLACE,
- "stackOrderChanged");
+ mWindowManager.cancelRecentsAnimation(REORDER_KEEP_IN_PLACE, "stackOrderChanged");
}
}
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 724a72e..8752f37 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -168,8 +168,7 @@
public interface RecentsAnimationCallbacks {
/** Callback when recents animation is finished. */
- void onAnimationFinished(@ReorderMode int reorderMode, boolean runSychronously,
- boolean sendUserLeaveHint);
+ void onAnimationFinished(@ReorderMode int reorderMode, boolean sendUserLeaveHint);
}
private final IRecentsAnimationController mController =
@@ -221,8 +220,7 @@
// prior to calling the callback
mCallbacks.onAnimationFinished(moveHomeToTop
? REORDER_MOVE_TO_TOP
- : REORDER_MOVE_TO_ORIGINAL_POSITION,
- true /* runSynchronously */, sendUserLeaveHint);
+ : REORDER_MOVE_TO_ORIGINAL_POSITION, sendUserLeaveHint);
mDisplayContent.mBoundsAnimationController.setAnimationType(FADE_IN);
} finally {
Binder.restoreCallingIdentity(token);
@@ -498,21 +496,15 @@
}
void cancelAnimation(@ReorderMode int reorderMode, String reason) {
- cancelAnimation(reorderMode, false /* runSynchronously */, false /*screenshot */, reason);
- }
-
- void cancelAnimationSynchronously(@ReorderMode int reorderMode, String reason) {
- cancelAnimation(reorderMode, true /* runSynchronously */, false /* screenshot */, reason);
+ cancelAnimation(reorderMode, false /*screenshot */, reason);
}
void cancelAnimationWithScreenshot(boolean screenshot) {
- cancelAnimation(REORDER_KEEP_IN_PLACE, true /* sync */, screenshot, "stackOrderChanged");
+ cancelAnimation(REORDER_KEEP_IN_PLACE, screenshot, "stackOrderChanged");
}
- private void cancelAnimation(@ReorderMode int reorderMode, boolean runSynchronously,
- boolean screenshot, String reason) {
- if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason
- + " runSynchronously=" + runSynchronously);
+ private void cancelAnimation(@ReorderMode int reorderMode, boolean screenshot, String reason) {
+ if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason);
synchronized (mService.getWindowManagerLock()) {
if (mCanceled) {
// We've already canceled the animation
@@ -525,16 +517,14 @@
// Screen shot previous task when next task starts transition and notify the runner.
// We will actually finish the animation once the runner calls cleanUpScreenshot().
final Task task = mPendingAnimations.get(0).mTask;
- final TaskSnapshot taskSnapshot = screenshotRecentTask(task, reorderMode,
- runSynchronously);
+ final TaskSnapshot taskSnapshot = screenshotRecentTask(task, reorderMode);
try {
mRunner.onAnimationCanceled(taskSnapshot);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation", e);
}
if (taskSnapshot == null) {
- mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
- false /* sendUserLeaveHint */);
+ mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
}
} else {
// Otherwise, notify the runner and clean up the animation immediately
@@ -545,8 +535,7 @@
} catch (RemoteException e) {
Slog.e(TAG, "Failed to cancel recents animation", e);
}
- mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
- false /* sendUserLeaveHint */);
+ mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
}
}
}
@@ -592,8 +581,7 @@
return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot;
}
- TaskSnapshot screenshotRecentTask(Task task, @ReorderMode int reorderMode,
- boolean runSynchronously) {
+ TaskSnapshot screenshotRecentTask(Task task, @ReorderMode int reorderMode) {
final TaskSnapshotController snapshotController = mService.mTaskSnapshotController;
final ArraySet<Task> tasks = Sets.newArraySet(task);
snapshotController.snapshotTasks(tasks);
@@ -613,8 +601,7 @@
if (DEBUG_RECENTS_ANIMATIONS) {
Slog.d(TAG, "mRecentScreenshotAnimator finish");
}
- mCallbacks.onAnimationFinished(reorderMode, runSynchronously,
- false /* sendUserLeaveHint */);
+ mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
}, mService);
mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator);
return taskSnapshot;
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 66d42db..3401de6 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -227,15 +227,10 @@
mStackSupervisor.mRootActivityContainer = this;
}
- @VisibleForTesting
- void setWindowContainer(RootWindowContainer container) {
- mRootWindowContainer = container;
- mRootWindowContainer.setRootActivityContainer(this);
- }
-
void setWindowManager(WindowManagerService wm) {
mWindowManager = wm;
- setWindowContainer(mWindowManager.mRoot);
+ mRootWindowContainer = mWindowManager.mRoot;
+ mRootWindowContainer.setRootActivityContainer(this);
mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -2266,7 +2261,7 @@
@WindowConfiguration.ActivityType int ignoreActivityType,
@WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid,
boolean allowed) {
- mStackSupervisor.mRunningTasks.getTasks(maxNum, list, ignoreActivityType,
+ mStackSupervisor.getRunningTasks().getTasks(maxNum, list, ignoreActivityType,
ignoreWindowingMode, mActivityDisplays, callingUid, allowed);
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index fd86faa..3a2eb57 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -380,7 +380,7 @@
boolean isResizeable() {
return ActivityInfo.isResizeableMode(mResizeMode) || mSupportsPictureInPicture
- || mWmService.mForceResizableTasks;
+ || mWmService.mAtmService.mForceResizableActivities;
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index 882f411..ede2f56 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -1419,8 +1419,9 @@
mActivities.remove(activityNdx);
--activityNdx;
--numActivities;
- } else if (r.finishActivityLocked(Activity.RESULT_CANCELED, null,
- reason, false /* oomAdj */, pauseImmediately) == FINISH_RESULT_REMOVED) {
+ } else if (r.finishIfPossible(Activity.RESULT_CANCELED,
+ null /* resultData */, reason, false /* oomAdj */, pauseImmediately)
+ == FINISH_RESULT_REMOVED) {
--activityNdx;
--numActivities;
}
@@ -1474,8 +1475,8 @@
if (opts != null) {
ret.updateOptionsLocked(opts);
}
- if (r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
- "clear-task-stack", false /* oomAdj */) == FINISH_RESULT_REMOVED) {
+ if (r.finishIfPossible("clear-task-stack", false /* oomAdj */)
+ == FINISH_RESULT_REMOVED) {
--activityNdx;
--numActivities;
}
@@ -1488,8 +1489,7 @@
&& (launchFlags & Intent.FLAG_ACTIVITY_SINGLE_TOP) == 0
&& !ActivityStarter.isDocumentLaunchesIntoExisting(launchFlags)) {
if (!ret.finishing) {
- ret.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
- "clear-task-top", false /* oomAdj */);
+ ret.finishIfPossible("clear-task-top", false /* oomAdj */);
return null;
}
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 79367a0..cc2112e 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -1666,7 +1666,7 @@
* default bounds.
*/
Rect getPictureInPictureBounds(float aspectRatio, Rect stackBounds) {
- if (!mWmService.mSupportsPictureInPicture) {
+ if (!mWmService.mAtmService.mSupportsPictureInPicture) {
return null;
}
@@ -1762,7 +1762,7 @@
* Sets the current picture-in-picture aspect ratio.
*/
void setPictureInPictureAspectRatio(float aspectRatio) {
- if (!mWmService.mSupportsPictureInPicture) {
+ if (!mWmService.mAtmService.mSupportsPictureInPicture) {
return;
}
@@ -1792,7 +1792,7 @@
* Sets the current picture-in-picture actions.
*/
void setPictureInPictureActions(List<RemoteAction> actions) {
- if (!mWmService.mSupportsPictureInPicture) {
+ if (!mWmService.mAtmService.mSupportsPictureInPicture) {
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index bbef261..29d232f 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -767,11 +767,11 @@
*/
void setOrientation(int orientation, @Nullable IBinder freezeDisplayToken,
@Nullable ConfigurationContainer requestingContainer) {
- final boolean changed = mOrientation != orientation;
- mOrientation = orientation;
- if (!changed) {
+ if (mOrientation == orientation) {
return;
}
+
+ mOrientation = orientation;
final WindowContainer parent = getParent();
if (parent != null) {
onDescendantOrientationChanged(freezeDisplayToken, requestingContainer);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 86faad0..3874fdc 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -28,6 +28,7 @@
import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
import static android.app.StatusBarManager.DISABLE_MASK;
import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
+import static android.content.pm.PackageManager.FEATURE_PC;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
@@ -591,9 +592,6 @@
int mDockedStackCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
Rect mDockedStackCreateBounds;
- boolean mForceResizableTasks;
- boolean mSupportsPictureInPicture;
- boolean mSupportsFreeformWindowManagement;
boolean mIsPc;
/**
* Flag that indicates that desktop mode is forced for public secondary screens.
@@ -819,7 +817,7 @@
int mTransactionSequence;
final WindowAnimator mAnimator;
- final SurfaceAnimationRunner mSurfaceAnimationRunner;
+ SurfaceAnimationRunner mSurfaceAnimationRunner;
/**
* Keeps track of which animations got transferred to which animators. Entries will get cleaned
@@ -957,6 +955,9 @@
final ArrayList<AppFreezeListener> mAppFreezeListeners = new ArrayList<>();
+ @VisibleForTesting
+ final DeviceConfig.OnPropertiesChangedListener mPropertiesChangedListener;
+
interface AppFreezeListener {
void onAppFreezeTimeout();
}
@@ -1010,6 +1011,7 @@
mGlobalLock = atm.getGlobalLock();
mAtmService = atm;
mContext = context;
+ mIsPc = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
mAllowBootMessages = showBootMsgs;
mOnlyCore = onlyCore;
mLimitedAlphaCompositing = context.getResources().getBoolean(
@@ -1159,26 +1161,28 @@
mSystemGestureExcludedByPreQStickyImmersive =
DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
- DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- new HandlerExecutor(mH), properties -> {
- synchronized (mGlobalLock) {
- final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
- properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
- final boolean excludedByPreQSticky = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
- if (mSystemGestureExcludedByPreQStickyImmersive != excludedByPreQSticky
- || mSystemGestureExclusionLimitDp != exclusionLimitDp) {
- mSystemGestureExclusionLimitDp = exclusionLimitDp;
- mSystemGestureExcludedByPreQStickyImmersive = excludedByPreQSticky;
- mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
- }
- mSystemGestureExclusionLogDebounceTimeoutMillis =
- DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS, 0);
- }
- });
+ mPropertiesChangedListener = properties -> {
+ synchronized (mGlobalLock) {
+ final int exclusionLimitDp = Math.max(MIN_GESTURE_EXCLUSION_LIMIT_DP,
+ properties.getInt(KEY_SYSTEM_GESTURE_EXCLUSION_LIMIT_DP, 0));
+ final boolean excludedByPreQSticky = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_SYSTEM_GESTURES_EXCLUDED_BY_PRE_Q_STICKY_IMMERSIVE, false);
+ if (mSystemGestureExcludedByPreQStickyImmersive != excludedByPreQSticky
+ || mSystemGestureExclusionLimitDp != exclusionLimitDp) {
+ mSystemGestureExclusionLimitDp = exclusionLimitDp;
+ mSystemGestureExcludedByPreQStickyImmersive = excludedByPreQSticky;
+ mRoot.forAllDisplays(DisplayContent::updateSystemGestureExclusionLimit);
+ }
+
+ mSystemGestureExclusionLogDebounceTimeoutMillis =
+ DeviceConfig.getInt(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ KEY_SYSTEM_GESTURE_EXCLUSION_LOG_DEBOUNCE_MILLIS, 0);
+ }
+ };
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
+ new HandlerExecutor(mH), mPropertiesChangedListener);
LocalServices.addService(WindowManagerInternal.class, new LocalService());
}
@@ -2595,7 +2599,7 @@
mRecentsAnimationController = controller;
}
- public RecentsAnimationController getRecentsAnimationController() {
+ RecentsAnimationController getRecentsAnimationController() {
return mRecentsAnimationController;
}
@@ -2603,74 +2607,37 @@
* @return Whether the next recents animation can continue to start. Called from
* {@link RecentsAnimation#startRecentsActivity}.
*/
- public boolean canStartRecentsAnimation() {
- synchronized (mGlobalLock) {
- // TODO(multi-display): currently only default display support recent activity
- if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) {
- return false;
- }
- return true;
+ boolean canStartRecentsAnimation() {
+ // TODO(multi-display): currently only default display support recent activity
+ if (getDefaultDisplayContentLocked().mAppTransition.isTransitionSet()) {
+ return false;
}
+ return true;
}
- /**
- * Cancels any running recents animation. The caller should NOT hold the WM lock while calling
- * this method, as it will call back into AM and may cause a deadlock. Any locking will be done
- * in the animation controller itself.
- */
- public void cancelRecentsAnimationSynchronously(
+ void cancelRecentsAnimation(
@RecentsAnimationController.ReorderMode int reorderMode, String reason) {
if (mRecentsAnimationController != null) {
// This call will call through to cleanupAnimation() below after the animation is
// canceled
- mRecentsAnimationController.cancelAnimationSynchronously(reorderMode, reason);
+ mRecentsAnimationController.cancelAnimation(reorderMode, reason);
}
}
- public void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
- synchronized (mGlobalLock) {
- if (mRecentsAnimationController != null) {
- final RecentsAnimationController controller = mRecentsAnimationController;
- mRecentsAnimationController = null;
- controller.cleanupAnimation(reorderMode);
- // TODO(mult-display): currently only default display support recents animation.
- getDefaultDisplayContentLocked().mAppTransition.updateBooster();
- }
+ void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) {
+ if (mRecentsAnimationController != null) {
+ final RecentsAnimationController controller = mRecentsAnimationController;
+ mRecentsAnimationController = null;
+ controller.cleanupAnimation(reorderMode);
+ // TODO(mult-display): currently only default display support recents animation.
+ getDefaultDisplayContentLocked().mAppTransition.updateBooster();
}
}
- public void setAppFullscreen(IBinder token, boolean toOpaque) {
- synchronized (mGlobalLock) {
- final AppWindowToken atoken = mRoot.getAppWindowToken(token);
- if (atoken != null) {
- atoken.setFillsParent(toOpaque);
- setWindowOpaqueLocked(token, toOpaque);
- mWindowPlacerLocked.requestTraversal();
- }
- }
- }
-
- public void setWindowOpaque(IBinder token, boolean isOpaque) {
- synchronized (mGlobalLock) {
- setWindowOpaqueLocked(token, isOpaque);
- }
- }
-
- private void setWindowOpaqueLocked(IBinder token, boolean isOpaque) {
+ void setWindowOpaqueLocked(IBinder token, boolean isOpaque) {
final AppWindowToken wtoken = mRoot.getAppWindowToken(token);
if (wtoken != null) {
- final WindowState win = wtoken.findMainWindow();
- if (win == null) {
- return;
- }
- isOpaque = isOpaque & !PixelFormat.formatHasAlpha(win.getAttrs().format);
- win.mWinAnimator.setOpaqueLocked(isOpaque);
- }
- }
-
- public void setDockedStackCreateState(int mode, Rect bounds) {
- synchronized (mGlobalLock) {
- setDockedStackCreateStateLocked(mode, bounds);
+ wtoken.setMainWindowOpaque(isOpaque);
}
}
@@ -2679,14 +2646,12 @@
mDockedStackCreateBounds = bounds;
}
- public void checkSplitScreenMinimizedChanged(boolean animate) {
- synchronized (mGlobalLock) {
- final DisplayContent displayContent = getDefaultDisplayContentLocked();
- displayContent.getDockedDividerController().checkMinimizeChanged(animate);
- }
+ void checkSplitScreenMinimizedChanged(boolean animate) {
+ final DisplayContent displayContent = getDefaultDisplayContentLocked();
+ displayContent.getDockedDividerController().checkMinimizeChanged(animate);
}
- public boolean isValidPictureInPictureAspectRatio(int displayId, float aspectRatio) {
+ boolean isValidPictureInPictureAspectRatio(int displayId, float aspectRatio) {
final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
return displayContent.getPinnedStackController().isValidPictureInPictureAspectRatio(
aspectRatio);
@@ -4847,8 +4812,10 @@
case UPDATE_DOCKED_STACK_DIVIDER: {
synchronized (mGlobalLock) {
final DisplayContent displayContent = getDefaultDisplayContentLocked();
- displayContent.getDockedDividerController().reevaluateVisibility(false);
- displayContent.adjustForImeIfNeeded();
+ if (displayContent != null) {
+ displayContent.getDockedDividerController().reevaluateVisibility(false);
+ displayContent.adjustForImeIfNeeded();
+ }
}
break;
}
@@ -6495,31 +6462,14 @@
}
}
- public void setForceResizableTasks(boolean forceResizableTasks) {
- synchronized (mGlobalLock) {
- mForceResizableTasks = forceResizableTasks;
- }
- }
-
- public void setSupportsPictureInPicture(boolean supportsPictureInPicture) {
- synchronized (mGlobalLock) {
- mSupportsPictureInPicture = supportsPictureInPicture;
- }
- }
-
- public void setSupportsFreeformWindowManagement(boolean supportsFreeformWindowManagement) {
- synchronized (mGlobalLock) {
- mSupportsFreeformWindowManagement = supportsFreeformWindowManagement;
- }
- }
-
void setForceDesktopModeOnExternalDisplays(boolean forceDesktopModeOnExternalDisplays) {
synchronized (mGlobalLock) {
mForceDesktopModeOnExternalDisplays = forceDesktopModeOnExternalDisplays;
}
}
- public void setIsPc(boolean isPc) {
+ @VisibleForTesting
+ void setIsPc(boolean isPc) {
synchronized (mGlobalLock) {
mIsPc = isPc;
}
@@ -6546,7 +6496,7 @@
"registerPinnedStackListener()")) {
return;
}
- if (!mSupportsPictureInPicture) {
+ if (!mAtmService.mSupportsPictureInPicture) {
return;
}
synchronized (mGlobalLock) {
@@ -7767,8 +7717,7 @@
clientChannel.transferTo(outInputChannel);
clientChannel.dispose();
- IBinder token = new Binder();
- mInputManager.registerInputChannel(inputChannel, token);
+ mInputManager.registerInputChannel(inputChannel, null /* generate new token */);
// Prevent the java finalizer from breaking the input channel. But we won't
// do any further management so we just release the java ref and let the
@@ -7776,7 +7725,7 @@
inputChannel.release();
InputWindowHandle h = new InputWindowHandle(null, null, displayId);
- h.token = token;
+ h.token = inputChannel.getToken();
h.name = name;
h.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
h.layoutParamsType = 0;
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index cf8e1e8..242b116 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -41,7 +41,6 @@
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
import android.annotation.NonNull;
-import android.app.Activity;
import android.app.ActivityThread;
import android.app.IApplicationThread;
import android.app.ProfilerInfo;
@@ -644,8 +643,7 @@
for (int i = 0; i < activities.size(); i++) {
final ActivityRecord r = activities.get(i);
if (!r.finishing && r.isInStackLocked()) {
- r.finishActivityLocked(Activity.RESULT_CANCELED, null /* resultData */,
- "finish-heavy", true /* oomAdj */);
+ r.finishIfPossible("finish-heavy", true /* oomAdj */);
}
}
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 466ca93..03f4755 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -165,6 +165,7 @@
outPointerIcon->bitmap.readPixels(bitmapCopy->info(), bitmapCopy->getPixels(),
bitmapCopy->rowBytes(), 0, 0);
}
+ outSpriteIcon->style = outPointerIcon->style;
outSpriteIcon->hotSpotX = outPointerIcon->hotSpotX;
outSpriteIcon->hotSpotY = outPointerIcon->hotSpotY;
}
@@ -1252,7 +1253,8 @@
status_t status = android_view_PointerIcon_load(env, pointerIconObj.get(),
displayContext.get(), &pointerIcon);
if (!status && !pointerIcon.isNullIcon()) {
- *icon = SpriteIcon(pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
+ *icon = SpriteIcon(
+ pointerIcon.bitmap, pointerIcon.style, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
} else {
*icon = SpriteIcon();
}
@@ -1293,10 +1295,12 @@
milliseconds_to_nanoseconds(pointerIcon.durationPerFrame);
animationData.animationFrames.reserve(numFrames);
animationData.animationFrames.push_back(SpriteIcon(
- pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+ pointerIcon.bitmap, pointerIcon.style,
+ pointerIcon.hotSpotX, pointerIcon.hotSpotY));
for (size_t i = 0; i < numFrames - 1; ++i) {
animationData.animationFrames.push_back(SpriteIcon(
- pointerIcon.bitmapFrames[i], pointerIcon.hotSpotX, pointerIcon.hotSpotY));
+ pointerIcon.bitmapFrames[i], pointerIcon.style,
+ pointerIcon.hotSpotX, pointerIcon.hotSpotY));
}
}
}
@@ -1711,6 +1715,7 @@
pointerIcon.bitmap.readPixels(spriteInfo, spriteIcon.bitmap.getPixels(),
spriteIcon.bitmap.rowBytes(), 0, 0);
}
+ spriteIcon.style = pointerIcon.style;
spriteIcon.hotSpotX = pointerIcon.hotSpotX;
spriteIcon.hotSpotY = pointerIcon.hotSpotY;
im->setCustomPointerIcon(spriteIcon);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index ed900b1..67ae407 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -635,6 +635,13 @@
SystemServerInitThreadPool.get().submit(SystemConfig::getInstance, TAG_SYSTEM_CONFIG);
t.traceEnd();
+ // Platform compat service is used by ActivityManagerService, PackageManagerService, and
+ // possibly others in the future. b/135010838.
+ t.traceBegin("PlatformCompat");
+ ServiceManager.addService(Context.PLATFORM_COMPAT_SERVICE,
+ new PlatformCompat(mSystemContext));
+ t.traceEnd();
+
// Wait for installd to finish starting up so that it has a chance to
// create critical directories such as /data/user with the appropriate
// permissions. We need this to complete before we initialize other services.
@@ -1102,10 +1109,6 @@
SignedConfigService.registerUpdateReceiver(mSystemContext);
t.traceEnd();
- t.traceBegin("PlatformCompat");
- ServiceManager.addService("platform_compat", new PlatformCompat(context));
- t.traceEnd();
-
} catch (RuntimeException e) {
Slog.e("System", "******************************************");
Slog.e("System", "************ Failure starting core service", e);
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
new file mode 100644
index 0000000..a1bfcdf
--- /dev/null
+++ b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceRoboTest.java
@@ -0,0 +1,1652 @@
+/*
+ * 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.backup;
+
+import static android.Manifest.permission.BACKUP;
+import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.PACKAGE_USAGE_STATS;
+
+import static com.android.server.backup.testing.TransportData.backupTransport;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.robolectric.Shadows.shadowOf;
+import static org.testng.Assert.expectThrows;
+
+import android.annotation.UserIdInt;
+import android.app.Application;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
+
+import com.android.server.backup.testing.TransportData;
+import com.android.server.testing.shadows.ShadowApplicationPackageManager;
+import com.android.server.testing.shadows.ShadowBinder;
+import com.android.server.testing.shadows.ShadowEnvironment;
+import com.android.server.testing.shadows.ShadowSystemServiceRegistry;
+import com.android.server.testing.shadows.ShadowUserManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
+import org.robolectric.shadows.ShadowContextWrapper;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/** Tests for {@link BackupManagerService}. */
+@RunWith(RobolectricTestRunner.class)
+@Config(
+ shadows = {
+ ShadowApplicationPackageManager.class,
+ ShadowBinder.class,
+ ShadowUserManager.class,
+ ShadowEnvironment.class,
+ ShadowSystemServiceRegistry.class
+ })
+@Presubmit
+public class BackupManagerServiceRoboTest {
+ private static final String TEST_PACKAGE = "package";
+ private static final String TEST_TRANSPORT = "transport";
+ private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE};
+
+ private Context mContext;
+ private ShadowContextWrapper mShadowContext;
+ private ShadowUserManager mShadowUserManager;
+ @UserIdInt private int mUserOneId;
+ @UserIdInt private int mUserTwoId;
+ @Mock private UserBackupManagerService mUserSystemService;
+ @Mock private UserBackupManagerService mUserOneService;
+ @Mock private UserBackupManagerService mUserTwoService;
+
+ /** Setup */
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ Application application = RuntimeEnvironment.application;
+ mContext = application;
+ mShadowContext = shadowOf(application);
+ mShadowUserManager = Shadow.extract(UserManager.get(application));
+
+ mUserOneId = UserHandle.USER_SYSTEM + 1;
+ mUserTwoId = mUserOneId + 1;
+ mShadowUserManager.addUser(mUserOneId, "mUserOneId", 0);
+ mShadowUserManager.addUser(mUserTwoId, "mUserTwoId", 0);
+
+ mShadowContext.grantPermissions(BACKUP);
+ mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
+
+ ShadowBinder.setCallingUid(Process.SYSTEM_UID);
+ }
+
+ /** Test that the service registers users. */
+ @Test
+ public void testStartServiceForUser_registersUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ backupManagerService.setBackupServiceActive(mUserOneId, true);
+
+ backupManagerService.startServiceForUser(mUserOneId);
+
+ SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
+ assertThat(serviceUsers.size()).isEqualTo(1);
+ assertThat(serviceUsers.get(mUserOneId)).isNotNull();
+ }
+
+ /** Test that the service registers users. */
+ @Test
+ public void testStartServiceForUser_withServiceInstance_registersUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ backupManagerService.setBackupServiceActive(mUserOneId, true);
+
+ backupManagerService.startServiceForUser(mUserOneId, mUserOneService);
+
+ SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
+ assertThat(serviceUsers.size()).isEqualTo(1);
+ assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService);
+ }
+
+ /** Test that the service unregisters users when stopped. */
+ @Test
+ public void testStopServiceForUser_forRegisteredUser_unregistersCorrectUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+ ShadowBinder.setCallingUid(Process.SYSTEM_UID);
+
+ backupManagerService.stopServiceForUser(mUserOneId);
+
+ SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
+ assertThat(serviceUsers.size()).isEqualTo(1);
+ assertThat(serviceUsers.get(mUserOneId)).isNull();
+ assertThat(serviceUsers.get(mUserTwoId)).isEqualTo(mUserTwoService);
+ }
+
+ /** Test that the service unregisters users when stopped. */
+ @Test
+ public void testStopServiceForUser_forRegisteredUser_tearsDownCorrectUser() throws Exception {
+ BackupManagerService backupManagerService =
+ createServiceAndRegisterUser(mUserOneId, mUserOneService);
+ backupManagerService.setBackupServiceActive(mUserTwoId, true);
+ backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
+
+ backupManagerService.stopServiceForUser(mUserOneId);
+
+ verify(mUserOneService).tearDownService();
+ verify(mUserTwoService, never()).tearDownService();
+ }
+
+ /** Test that the service unregisters users when stopped. */
+ @Test
+ public void testStopServiceForUser_forUnknownUser_doesNothing() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ backupManagerService.setBackupServiceActive(mUserOneId, true);
+ ShadowBinder.setCallingUid(Process.SYSTEM_UID);
+
+ backupManagerService.stopServiceForUser(mUserOneId);
+
+ SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
+ assertThat(serviceUsers.size()).isEqualTo(0);
+ }
+
+ // ---------------------------------------------
+ // Backup agent tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.dataChanged(mUserOneId, TEST_PACKAGE);
+
+ verify(mUserOneService).dataChanged(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.dataChanged(mUserTwoId, TEST_PACKAGE);
+
+ verify(mUserOneService, never()).dataChanged(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ IBinder agentBinder = mock(IBinder.class);
+
+ backupManagerService.agentConnected(mUserOneId, TEST_PACKAGE, agentBinder);
+
+ verify(mUserOneService).agentConnected(TEST_PACKAGE, agentBinder);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+ IBinder agentBinder = mock(IBinder.class);
+
+ backupManagerService.agentConnected(mUserTwoId, TEST_PACKAGE, agentBinder);
+
+ verify(mUserOneService, never()).agentConnected(TEST_PACKAGE, agentBinder);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.opComplete(mUserOneId, /* token */ 0, /* result */ 0L);
+
+ verify(mUserOneService).opComplete(/* token */ 0, /* result */ 0L);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.opComplete(mUserTwoId, /* token */ 0, /* result */ 0L);
+
+ verify(mUserOneService, never()).opComplete(/* token */ 0, /* result */ 0L);
+ }
+
+ // ---------------------------------------------
+ // Transport tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ String[] transports = {TEST_TRANSPORT};
+
+ backupManagerService.initializeTransports(mUserOneId, transports, /* observer */ null);
+
+ verify(mUserOneService).initializeTransports(transports, /* observer */ null);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testInitializeTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+ String[] transports = {TEST_TRANSPORT};
+
+ backupManagerService.initializeTransports(mUserTwoId, transports, /* observer */ null);
+
+ verify(mUserOneService, never()).initializeTransports(transports, /* observer */ null);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.clearBackupData(mUserOneId, TEST_TRANSPORT, TEST_PACKAGE);
+
+ verify(mUserOneService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testClearBackupData_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.clearBackupData(mUserTwoId, TEST_TRANSPORT, TEST_PACKAGE);
+
+ verify(mUserOneService, never()).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetCurrentTransport_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getCurrentTransport(mUserOneId);
+
+ verify(mUserOneService).getCurrentTransport();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetCurrentTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getCurrentTransport(mUserTwoId);
+
+ verify(mUserOneService, never()).getCurrentTransport();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetCurrentTransportComponent_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getCurrentTransportComponent(mUserOneId);
+
+ verify(mUserOneService).getCurrentTransportComponent();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getCurrentTransportComponent(mUserTwoId);
+
+ verify(mUserOneService, never()).getCurrentTransportComponent();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.listAllTransports(mUserOneId);
+
+ verify(mUserOneService).listAllTransports();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testListAllTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.listAllTransports(mUserTwoId);
+
+ verify(mUserOneService, never()).listAllTransports();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testListAllTransportComponents_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.listAllTransportComponents(mUserOneId);
+
+ verify(mUserOneService).listAllTransportComponents();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testListAllTransportComponents_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.listAllTransportComponents(mUserTwoId);
+
+ verify(mUserOneService, never()).listAllTransportComponents();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.selectBackupTransport(mUserOneId, TEST_TRANSPORT);
+
+ verify(mUserOneService).selectBackupTransport(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSelectBackupTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.selectBackupTransport(mUserTwoId, TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).selectBackupTransport(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ TransportData transport = backupTransport();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(
+ mUserOneId, transport.getTransportComponent(), callback);
+
+ verify(mUserOneService)
+ .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+ TransportData transport = backupTransport();
+ ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
+
+ backupManagerService.selectBackupTransportAsync(
+ mUserTwoId, transport.getTransportComponent(), callback);
+
+ verify(mUserOneService, never())
+ .selectBackupTransportAsync(transport.getTransportComponent(), callback);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getConfigurationIntent(mUserOneId, TEST_TRANSPORT);
+
+ verify(mUserOneService).getConfigurationIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetConfigurationIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getConfigurationIntent(mUserTwoId, TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getConfigurationIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getDestinationString(mUserOneId, TEST_TRANSPORT);
+
+ verify(mUserOneService).getDestinationString(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetDestinationString_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getDestinationString(mUserTwoId, TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getDestinationString(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getDataManagementIntent(mUserOneId, TEST_TRANSPORT);
+
+ verify(mUserOneService).getDataManagementIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetDataManagementIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getDataManagementIntent(mUserTwoId, TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getDataManagementIntent(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getDataManagementLabel(mUserOneId, TEST_TRANSPORT);
+
+ verify(mUserOneService).getDataManagementLabel(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetDataManagementLabel_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getDataManagementLabel(mUserTwoId, TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).getDataManagementLabel(TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testUpdateTransportAttributes_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ TransportData transport = backupTransport();
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+
+ backupManagerService.updateTransportAttributes(
+ mUserOneId,
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mUserOneService)
+ .updateTransportAttributes(
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+ TransportData transport = backupTransport();
+ Intent configurationIntent = new Intent();
+ Intent dataManagementIntent = new Intent();
+
+ backupManagerService.updateTransportAttributes(
+ mUserTwoId,
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+
+ verify(mUserOneService, never())
+ .updateTransportAttributes(
+ transport.getTransportComponent(),
+ transport.transportName,
+ configurationIntent,
+ "currentDestinationString",
+ dataManagementIntent,
+ "dataManagementLabel");
+ }
+
+ // ---------------------------------------------
+ // Settings tests
+ // ---------------------------------------------
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testSetBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class,
+ () -> backupManagerService.setBackupEnabled(mUserTwoId, true));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testSetBackupEnabled_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ registerUser(backupManagerService, mUserTwoId, mUserTwoService);
+
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.setBackupEnabled(mUserTwoId, true);
+
+ verify(mUserTwoService).setBackupEnabled(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.setBackupEnabled(mUserOneId, true);
+
+ verify(mUserOneService).setBackupEnabled(true);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSetBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.setBackupEnabled(mUserTwoId, true);
+
+ verify(mUserOneService, never()).setBackupEnabled(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.setAutoRestore(mUserOneId, true);
+
+ verify(mUserOneService).setAutoRestore(true);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSetAutoRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.setAutoRestore(mUserTwoId, true);
+
+ verify(mUserOneService, never()).setAutoRestore(true);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testIsBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.isBackupEnabled(mUserOneId);
+
+ verify(mUserOneService).isBackupEnabled();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testIsBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.isBackupEnabled(mUserTwoId);
+
+ verify(mUserOneService, never()).isBackupEnabled();
+ }
+
+ // ---------------------------------------------
+ // Backup tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.isAppEligibleForBackup(mUserOneId, TEST_PACKAGE);
+
+ verify(mUserOneService).isAppEligibleForBackup(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testIsAppEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.isAppEligibleForBackup(mUserTwoId, TEST_PACKAGE);
+
+ verify(mUserOneService, never()).isAppEligibleForBackup(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testFilterAppsEligibleForBackup_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ String[] packages = {TEST_PACKAGE};
+
+ backupManagerService.filterAppsEligibleForBackup(mUserOneId, packages);
+
+ verify(mUserOneService).filterAppsEligibleForBackup(packages);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testFilterAppsEligibleForBackup_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+ String[] packages = {TEST_PACKAGE};
+
+ backupManagerService.filterAppsEligibleForBackup(mUserTwoId, packages);
+
+ verify(mUserOneService, never()).filterAppsEligibleForBackup(packages);
+ }
+
+ /**
+ * Test verifying that {@link BackupManagerService#backupNow(int)} throws a {@link
+ * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void testBackupNow_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(SecurityException.class, () -> backupManagerService.backupNow(mUserTwoId));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testBackupNow_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ registerUser(backupManagerService, mUserTwoId, mUserTwoService);
+
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.backupNow(mUserTwoId);
+
+ verify(mUserTwoService).backupNow();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testBackupNow_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.backupNow(mUserOneId);
+
+ verify(mUserOneService).backupNow();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testBackupNow_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.backupNow(mUserTwoId);
+
+ verify(mUserOneService, never()).backupNow();
+ }
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testRequestBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.requestBackup(
+ mUserTwoId, packages, observer, monitor, 0));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testRequestBackup_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ registerUser(backupManagerService, mUserTwoId, mUserTwoService);
+
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+
+ verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRequestBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0);
+
+ verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRequestBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ String[] packages = {TEST_PACKAGE};
+ IBackupObserver observer = mock(IBackupObserver.class);
+ IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
+
+ verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0);
+ }
+
+ /**
+ * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a {@link
+ * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
+ */
+ @Test
+ public void testCancelBackups_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(SecurityException.class, () -> backupManagerService.cancelBackups(mUserTwoId));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testCancelBackups_withPermission_propagatesForNonCallingUser() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ registerUser(backupManagerService, mUserTwoId, mUserTwoService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.cancelBackups(mUserTwoId);
+
+ verify(mUserTwoService).cancelBackups();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testCancelBackups_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.cancelBackups(mUserOneId);
+
+ verify(mUserOneService).cancelBackups();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testCancelBackups_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.cancelBackups(mUserTwoId);
+
+ verify(mUserOneService, never()).cancelBackups();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testBeginFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService);
+ FullBackupJob job = new FullBackupJob();
+
+ backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
+
+ verify(mUserOneService).beginFullBackup(job);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testBeginFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ FullBackupJob job = new FullBackupJob();
+
+ backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
+
+ verify(mUserOneService, never()).beginFullBackup(job);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testEndFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService);
+
+ backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
+
+ verify(mUserOneService).endFullBackup();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testEndFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
+
+ verify(mUserOneService, never()).endFullBackup();
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testFullTransportBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ String[] packages = {TEST_PACKAGE};
+
+ backupManagerService.fullTransportBackup(mUserOneId, packages);
+
+ verify(mUserOneService).fullTransportBackup(packages);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testFullTransportBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+ String[] packages = {TEST_PACKAGE};
+
+ backupManagerService.fullTransportBackup(mUserTwoId, packages);
+
+ verify(mUserOneService, never()).fullTransportBackup(packages);
+ }
+
+ // ---------------------------------------------
+ // Restore tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.restoreAtInstall(mUserOneId, TEST_PACKAGE, /* token */ 0);
+
+ verify(mUserOneService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testRestoreAtInstall_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.restoreAtInstall(mUserTwoId, TEST_PACKAGE, /* token */ 0);
+
+ verify(mUserOneService, never()).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testBeginRestoreSession_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT);
+
+ verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testBeginRestoreSession_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT);
+
+ verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testGetAvailableRestoreToken_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getAvailableRestoreToken(mUserOneId, TEST_PACKAGE);
+
+ verify(mUserOneService).getAvailableRestoreToken(TEST_PACKAGE);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testGetAvailableRestoreToken_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.getAvailableRestoreToken(mUserTwoId, TEST_PACKAGE);
+
+ verify(mUserOneService, never()).getAvailableRestoreToken(TEST_PACKAGE);
+ }
+
+ // ---------------------------------------------
+ // Adb backup/restore tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService);
+ ShadowBinder.setCallingUserHandle(UserHandle.of(UserHandle.USER_SYSTEM));
+
+ backupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+ verify(mUserOneService).setBackupPassword("currentPassword", "newPassword");
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testSetBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.setBackupPassword("currentPassword", "newPassword");
+
+ verify(mUserOneService, never()).setBackupPassword("currentPassword", "newPassword");
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testHasBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserOneService);
+ ShadowBinder.setCallingUserHandle(UserHandle.of(UserHandle.USER_SYSTEM));
+
+ backupManagerService.hasBackupPassword();
+
+ verify(mUserOneService).hasBackupPassword();
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testHasBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ backupManagerService.hasBackupPassword();
+
+ verify(mUserOneService, never()).hasBackupPassword();
+ }
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService = createSystemRegisteredService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ registerUser(backupManagerService, mUserTwoId, mUserTwoService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.adbBackup(
+ mUserTwoId,
+ /* parcelFileDescriptor*/ null,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ null));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbBackup_withPermission_propagatesForNonCallingUser() throws Exception {
+ BackupManagerService backupManagerService = createSystemRegisteredService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ registerUser(backupManagerService, mUserTwoId, mUserTwoService);
+
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.adbBackup(
+ mUserTwoId,
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+
+ verify(mUserTwoService)
+ .adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAdbBackup_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createSystemRegisteredService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbBackup(
+ mUserOneId,
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+
+ verify(mUserOneService)
+ .adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAdbBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createSystemRegisteredService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbBackup(
+ mUserTwoId,
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+
+ verify(mUserOneService, never())
+ .adbBackup(
+ parcelFileDescriptor,
+ /* includeApks */ true,
+ /* includeObbs */ true,
+ /* includeShared */ true,
+ /* doWidgets */ true,
+ /* doAllApps */ true,
+ /* includeSystem */ true,
+ /* doCompress */ true,
+ /* doKeyValue */ true,
+ ADB_TEST_PACKAGES);
+ }
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbRestore_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService = createSystemRegisteredService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ registerUser(backupManagerService, mUserTwoId, mUserTwoService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class, () -> backupManagerService.adbRestore(mUserTwoId, null));
+ }
+
+ /**
+ * Test that the backup service does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testAdbRestore_withPermission_propagatesForNonCallingUser() throws Exception {
+ BackupManagerService backupManagerService = createSystemRegisteredService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ registerUser(backupManagerService, mUserTwoId, mUserTwoService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
+
+ backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
+
+ verify(mUserTwoService).adbRestore(parcelFileDescriptor);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAdbRestore_onRegisteredUser_callsMethodForUser() throws Exception {
+ BackupManagerService backupManagerService = createSystemRegisteredService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbRestore(mUserOneId, parcelFileDescriptor);
+
+ verify(mUserOneService).adbRestore(parcelFileDescriptor);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAdbRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
+ BackupManagerService backupManagerService = createSystemRegisteredService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
+
+ verify(mUserOneService, never()).adbRestore(parcelFileDescriptor);
+ }
+
+ private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception {
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
+ }
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testAcknowledgeAdbBackupOrRestore_onRegisteredUser_callsMethodForUser()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+ IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
+
+ backupManagerService.acknowledgeAdbBackupOrRestore(
+ mUserOneId,
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
+
+ verify(mUserOneService)
+ .acknowledgeAdbBackupOrRestore(
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testAcknowledgeAdbBackupOrRestore_onUnknownUser_doesNotPropagateCall()
+ throws Exception {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+ IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
+
+ backupManagerService.acknowledgeAdbBackupOrRestore(
+ mUserTwoId,
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
+
+ verify(mUserOneService, never())
+ .acknowledgeAdbBackupOrRestore(
+ /* token */ 0,
+ /* allow */ true,
+ "currentPassword",
+ "encryptionPassword",
+ observer);
+ }
+
+ // ---------------------------------------------
+ // Service tests
+ // ---------------------------------------------
+
+ /** Test that the backup service routes methods correctly to the user that requests it. */
+ @Test
+ public void testDump_onRegisteredUser_callsMethodForUser() throws Exception {
+ grantDumpPermissions();
+ BackupManagerService backupManagerService = createSystemRegisteredService();
+ File testFile = createTestFile();
+ FileDescriptor fileDescriptor = new FileDescriptor();
+ PrintWriter printWriter = new PrintWriter(testFile);
+ String[] args = {"1", "2"};
+ ShadowBinder.setCallingUserHandle(UserHandle.of(UserHandle.USER_SYSTEM));
+
+ backupManagerService.dump(fileDescriptor, printWriter, args);
+
+ verify(mUserSystemService).dump(fileDescriptor, printWriter, args);
+ }
+
+ /** Test that the backup service does not route methods for non-registered users. */
+ @Test
+ public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception {
+ grantDumpPermissions();
+ BackupManagerService backupManagerService = createService();
+ File testFile = createTestFile();
+ FileDescriptor fileDescriptor = new FileDescriptor();
+ PrintWriter printWriter = new PrintWriter(testFile);
+ String[] args = {"1", "2"};
+
+ backupManagerService.dump(fileDescriptor, printWriter, args);
+
+ verify(mUserOneService, never()).dump(fileDescriptor, printWriter, args);
+ }
+
+ /** Test that 'dumpsys backup users' dumps the list of users registered in backup service*/
+ @Test
+ public void testDump_users_dumpsListOfRegisteredUsers() {
+ grantDumpPermissions();
+ BackupManagerService backupManagerService = createSystemRegisteredService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ StringWriter out = new StringWriter();
+ PrintWriter writer = new PrintWriter(out);
+ String[] args = {"users"};
+
+ backupManagerService.dump(null, writer, args);
+
+ writer.flush();
+ assertEquals(
+ String.format("%s %d %d\n", BackupManagerService.DUMP_RUNNING_USERS_MESSAGE,
+ UserHandle.USER_SYSTEM, mUserOneId),
+ out.toString());
+ }
+
+ private File createTestFile() throws IOException {
+ File testFile = new File(mContext.getFilesDir(), "test");
+ testFile.createNewFile();
+ return testFile;
+ }
+
+ private void grantDumpPermissions() {
+ mShadowContext.grantPermissions(DUMP);
+ mShadowContext.grantPermissions(PACKAGE_USAGE_STATS);
+ }
+
+ /**
+ * Test that the backup services throws a {@link SecurityException} if the caller does not have
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testGetServiceForUser_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
+
+ expectThrows(
+ SecurityException.class,
+ () ->
+ backupManagerService.getServiceForUserIfCallerHasPermission(
+ mUserOneId, "test"));
+ }
+
+ /**
+ * Test that the backup services does not throw a {@link SecurityException} if the caller has
+ * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
+ */
+ @Test
+ public void testGetServiceForUserIfCallerHasPermission_withPermission_worksForNonCallingUser() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ true);
+
+ assertEquals(
+ mUserOneService,
+ backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
+ }
+
+ /**
+ * Test that the backup services does not throw a {@link SecurityException} if the caller does
+ * not have INTERACT_ACROSS_USERS_FULL permission and passes in the calling user id.
+ */
+ @Test
+ public void testGetServiceForUserIfCallerHasPermission_withoutPermission_worksForCallingUser() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, mUserOneId, mUserOneService);
+ setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
+
+ assertEquals(
+ mUserOneService,
+ backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
+ }
+
+ /**
+ * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
+ * specifically to prevent overloading the logs in production.
+ */
+ @Test
+ public void testMoreDebug_isFalse() throws Exception {
+ boolean moreDebug = BackupManagerService.MORE_DEBUG;
+
+ assertThat(moreDebug).isFalse();
+ }
+
+ /** Test that the constructor handles {@code null} parameters. */
+ @Test
+ public void testConstructor_withNullContext_throws() throws Exception {
+ expectThrows(
+ NullPointerException.class,
+ () ->
+ new BackupManagerService(
+ /* context */ null,
+ new SparseArray<>()));
+ }
+
+ /** Test that the constructor does not create {@link UserBackupManagerService} instances. */
+ @Test
+ public void testConstructor_doesNotRegisterUsers() throws Exception {
+ BackupManagerService backupManagerService = createService();
+
+ assertThat(backupManagerService.getUserServices().size()).isEqualTo(0);
+ }
+
+ // ---------------------------------------------
+ // Lifecycle tests
+ // ---------------------------------------------
+
+ /** testOnStart_publishesService */
+ @Test
+ public void testOnStart_publishesService() {
+ BackupManagerService backupManagerService = mock(BackupManagerService.class);
+ BackupManagerService.Lifecycle lifecycle =
+ spy(new BackupManagerService.Lifecycle(mContext, backupManagerService));
+ doNothing().when(lifecycle).publishService(anyString(), any());
+
+ lifecycle.onStart();
+
+ verify(lifecycle).publishService(Context.BACKUP_SERVICE, backupManagerService);
+ }
+
+ /** testOnUnlockUser_forwards */
+ @Test
+ public void testOnUnlockUser_forwards() {
+ BackupManagerService backupManagerService = mock(BackupManagerService.class);
+ BackupManagerService.Lifecycle lifecycle =
+ new BackupManagerService.Lifecycle(mContext, backupManagerService);
+
+ lifecycle.onUnlockUser(UserHandle.USER_SYSTEM);
+
+ verify(backupManagerService).onUnlockUser(UserHandle.USER_SYSTEM);
+ }
+
+ /** testOnStopUser_forwards */
+ @Test
+ public void testOnStopUser_forwards() {
+ BackupManagerService backupManagerService = mock(BackupManagerService.class);
+ BackupManagerService.Lifecycle lifecycle =
+ new BackupManagerService.Lifecycle(mContext, backupManagerService);
+
+ lifecycle.onStopUser(UserHandle.USER_SYSTEM);
+
+ verify(backupManagerService).onStopUser(UserHandle.USER_SYSTEM);
+ }
+
+ private BackupManagerService createService() {
+ return new BackupManagerService(mContext);
+ }
+
+ private BackupManagerService createSystemRegisteredService() {
+ BackupManagerService backupManagerService = createService();
+ registerUser(backupManagerService, UserHandle.USER_SYSTEM, mUserSystemService);
+ return backupManagerService;
+ }
+
+ private void registerUser(
+ BackupManagerService backupManagerService,
+ int userId,
+ UserBackupManagerService userBackupManagerService) {
+ backupManagerService.setBackupServiceActive(userId, true);
+ backupManagerService.startServiceForUser(userId, userBackupManagerService);
+ }
+
+ private BackupManagerService createServiceAndRegisterUser(
+ int userId, UserBackupManagerService userBackupManagerService) {
+ BackupManagerService backupManagerService = createService();
+ backupManagerService.setBackupServiceActive(userBackupManagerService.getUserId(), true);
+ backupManagerService.startServiceForUser(userId, userBackupManagerService);
+ return backupManagerService;
+ }
+
+ /**
+ * Sets the calling user to {@code userId} and grants the permission INTERACT_ACROSS_USERS_FULL
+ * to the caller if {@code shouldGrantPermission} is {@code true}, else it denies the
+ * permission.
+ */
+ private void setCallerAndGrantInteractUserPermission(
+ @UserIdInt int userId, boolean shouldGrantPermission) {
+ ShadowBinder.setCallingUserHandle(UserHandle.of(userId));
+ if (shouldGrantPermission) {
+ mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
+ } else {
+ mShadowContext.denyPermissions(INTERACT_ACROSS_USERS_FULL);
+ }
+ }
+}
diff --git a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
deleted file mode 100644
index ca3c6ce..0000000
--- a/services/robotests/backup/src/com/android/server/backup/BackupManagerServiceTest.java
+++ /dev/null
@@ -1,1137 +0,0 @@
-/*
- * Copyright (C) 2018 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.backup;
-
-import static android.Manifest.permission.BACKUP;
-import static android.Manifest.permission.DUMP;
-import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-import static android.Manifest.permission.PACKAGE_USAGE_STATS;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assert.assertEquals;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
-import static org.testng.Assert.expectThrows;
-
-import android.annotation.UserIdInt;
-import android.app.Application;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-import android.app.backup.IFullBackupRestoreObserver;
-import android.content.Context;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.platform.test.annotations.Presubmit;
-import android.util.SparseArray;
-
-import com.android.server.testing.shadows.ShadowApplicationPackageManager;
-import com.android.server.testing.shadows.ShadowBinder;
-import com.android.server.testing.shadows.ShadowEnvironment;
-import com.android.server.testing.shadows.ShadowSystemServiceRegistry;
-import com.android.server.testing.shadows.ShadowUserManager;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowContextWrapper;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-
-/** Tests for the user-aware backup/restore system service {@link BackupManagerService}. */
-@RunWith(RobolectricTestRunner.class)
-@Config(
- shadows = {
- ShadowApplicationPackageManager.class,
- ShadowBinder.class,
- ShadowEnvironment.class,
- ShadowSystemServiceRegistry.class,
- ShadowUserManager.class,
- })
-@Presubmit
-public class BackupManagerServiceTest {
- private static final String TEST_PACKAGE = "package";
- private static final String TEST_TRANSPORT = "transport";
- private static final String[] ADB_TEST_PACKAGES = {TEST_PACKAGE};
-
- private ShadowContextWrapper mShadowContext;
- private ShadowUserManager mShadowUserManager;
- private Context mContext;
- private Trampoline mTrampoline;
- @UserIdInt private int mUserOneId;
- @UserIdInt private int mUserTwoId;
- @Mock private UserBackupManagerService mUserOneService;
- @Mock private UserBackupManagerService mUserTwoService;
-
- /** Initialize {@link BackupManagerService}. */
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- Application application = RuntimeEnvironment.application;
- mContext = application;
- mShadowContext = shadowOf(application);
- mShadowUserManager = Shadow.extract(UserManager.get(application));
-
- mUserOneId = UserHandle.USER_SYSTEM + 1;
- mUserTwoId = mUserOneId + 1;
- mShadowUserManager.addUser(mUserOneId, "mUserOneId", 0);
- mShadowUserManager.addUser(mUserTwoId, "mUserTwoId", 0);
-
- mShadowContext.grantPermissions(BACKUP);
- mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
-
- mTrampoline = new Trampoline(mContext);
- ShadowBinder.setCallingUid(Process.SYSTEM_UID);
- }
-
- /**
- * Clean up and reset state that was created for testing {@link BackupManagerService}
- * operations.
- */
- @After
- public void tearDown() throws Exception {
- ShadowBinder.reset();
- }
-
- /**
- * Test verifying that {@link BackupManagerService#MORE_DEBUG} is set to {@code false}. This is
- * specifically to prevent overloading the logs in production.
- */
- @Test
- public void testMoreDebug_isFalse() throws Exception {
- boolean moreDebug = BackupManagerService.MORE_DEBUG;
-
- assertThat(moreDebug).isFalse();
- }
-
- /** Test that the constructor does not create {@link UserBackupManagerService} instances. */
- @Test
- public void testConstructor_doesNotRegisterUsers() throws Exception {
- BackupManagerService backupManagerService = createService();
-
- assertThat(mTrampoline.getUserServices().size()).isEqualTo(0);
- }
-
- /** Test that the constructor handles {@code null} parameters. */
- @Test
- public void testConstructor_withNullContext_throws() throws Exception {
- expectThrows(
- NullPointerException.class,
- () ->
- new BackupManagerService(
- /* context */ null,
- new Trampoline(mContext),
- new SparseArray<>()));
- }
-
- /** Test that the constructor handles {@code null} parameters. */
- @Test
- public void testConstructor_withNullTrampoline_throws() throws Exception {
- expectThrows(
- NullPointerException.class,
- () ->
- new BackupManagerService(
- mContext, /* trampoline */ null, new SparseArray<>()));
- }
-
- /**
- * Test that the backup services throws a {@link SecurityException} if the caller does not have
- * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
- */
- @Test
- public void testGetServiceForUser_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- expectThrows(
- SecurityException.class,
- () ->
- backupManagerService.getServiceForUserIfCallerHasPermission(
- mUserOneId, "test"));
- }
-
- /**
- * Test that the backup services does not throw a {@link SecurityException} if the caller has
- * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
- */
- @Test
- public void testGetServiceForUserIfCallerHasPermission_withPermission_worksForNonCallingUser() {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ true);
-
- assertEquals(
- mUserOneService,
- backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
- }
-
- /**
- * Test that the backup services does not throw a {@link SecurityException} if the caller does
- * not have INTERACT_ACROSS_USERS_FULL permission and passes in the calling user id.
- */
- @Test
- public void testGetServiceForUserIfCallerHasPermission_withoutPermission_worksForCallingUser() {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- assertEquals(
- mUserOneService,
- backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
- }
-
- // ---------------------------------------------
- // Settings tests
- // ---------------------------------------------
- /**
- * Test that the backup services throws a {@link SecurityException} if the caller does not have
- * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
- */
- @Test
- public void testSetBackupEnabled_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- expectThrows(
- SecurityException.class,
- () -> backupManagerService.setBackupEnabled(mUserTwoId, true));
- }
-
- /**
- * Test that the backup service does not throw a {@link SecurityException} if the caller has
- * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
- */
- @Test
- public void testSetBackupEnabled_withPermission_propagatesForNonCallingUser() {
- registerUser(mUserOneId, mUserOneService);
- registerUser(mUserTwoId, mUserTwoService);
- BackupManagerService backupManagerService = createService();
-
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
-
- backupManagerService.setBackupEnabled(mUserTwoId, true);
-
- verify(mUserTwoService).setBackupEnabled(true);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testSetBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.setBackupEnabled(mUserOneId, true);
-
- verify(mUserOneService).setBackupEnabled(true);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testSetBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.setBackupEnabled(mUserTwoId, true);
-
- verify(mUserOneService, never()).setBackupEnabled(true);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testSetAutoRestore_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.setAutoRestore(mUserOneId, true);
-
- verify(mUserOneService).setAutoRestore(true);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testSetAutoRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.setAutoRestore(mUserTwoId, true);
-
- verify(mUserOneService, never()).setAutoRestore(true);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testIsBackupEnabled_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.isBackupEnabled(mUserOneId);
-
- verify(mUserOneService).isBackupEnabled();
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testIsBackupEnabled_onUnknownUser_doesNotPropagateCall() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.isBackupEnabled(mUserTwoId);
-
- verify(mUserOneService, never()).isBackupEnabled();
- }
-
- // ---------------------------------------------
- // Backup tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testIsAppEligibleForBackup_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.isAppEligibleForBackup(mUserOneId, TEST_PACKAGE);
-
- verify(mUserOneService).isAppEligibleForBackup(TEST_PACKAGE);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testIsAppEligibleForBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.isAppEligibleForBackup(mUserTwoId, TEST_PACKAGE);
-
- verify(mUserOneService, never()).isAppEligibleForBackup(TEST_PACKAGE);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testFilterAppsEligibleForBackup_onRegisteredUser_callsMethodForUser()
- throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- String[] packages = {TEST_PACKAGE};
-
- backupManagerService.filterAppsEligibleForBackup(mUserOneId, packages);
-
- verify(mUserOneService).filterAppsEligibleForBackup(packages);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testFilterAppsEligibleForBackup_onUnknownUser_doesNotPropagateCall()
- throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- String[] packages = {TEST_PACKAGE};
-
- backupManagerService.filterAppsEligibleForBackup(mUserTwoId, packages);
-
- verify(mUserOneService, never()).filterAppsEligibleForBackup(packages);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#backupNow(int)} throws a {@link
- * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
- */
- @Test
- public void testBackupNow_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- expectThrows(SecurityException.class, () -> backupManagerService.backupNow(mUserTwoId));
- }
-
- /**
- * Test that the backup service does not throw a {@link SecurityException} if the caller has
- * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
- */
- @Test
- public void testBackupNow_withPermission_propagatesForNonCallingUser() {
- registerUser(mUserOneId, mUserOneService);
- registerUser(mUserTwoId, mUserTwoService);
- BackupManagerService backupManagerService = createService();
-
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
-
- backupManagerService.backupNow(mUserTwoId);
-
- verify(mUserTwoService).backupNow();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testBackupNow_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.backupNow(mUserOneId);
-
- verify(mUserOneService).backupNow();
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testBackupNow_onUnknownUser_doesNotPropagateCall() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.backupNow(mUserTwoId);
-
- verify(mUserOneService, never()).backupNow();
- }
-
- /**
- * Test that the backup services throws a {@link SecurityException} if the caller does not have
- * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
- */
- @Test
- public void testRequestBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- String[] packages = {TEST_PACKAGE};
- IBackupObserver observer = mock(IBackupObserver.class);
- IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
-
- expectThrows(
- SecurityException.class,
- () ->
- backupManagerService.requestBackup(
- mUserTwoId, packages, observer, monitor, 0));
- }
-
- /**
- * Test that the backup service does not throw a {@link SecurityException} if the caller has
- * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
- */
- @Test
- public void testRequestBackup_withPermission_propagatesForNonCallingUser() {
- registerUser(mUserOneId, mUserOneService);
- registerUser(mUserTwoId, mUserTwoService);
- BackupManagerService backupManagerService = createService();
-
- String[] packages = {TEST_PACKAGE};
- IBackupObserver observer = mock(IBackupObserver.class);
- IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
-
- backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
-
- verify(mUserTwoService).requestBackup(packages, observer, monitor, /* flags */ 0);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testRequestBackup_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- String[] packages = {TEST_PACKAGE};
- IBackupObserver observer = mock(IBackupObserver.class);
- IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.requestBackup(mUserOneId, packages, observer, monitor, /* flags */ 0);
-
- verify(mUserOneService).requestBackup(packages, observer, monitor, /* flags */ 0);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testRequestBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- String[] packages = {TEST_PACKAGE};
- IBackupObserver observer = mock(IBackupObserver.class);
- IBackupManagerMonitor monitor = mock(IBackupManagerMonitor.class);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.requestBackup(mUserTwoId, packages, observer, monitor, /* flags */ 0);
-
- verify(mUserOneService, never()).requestBackup(packages, observer, monitor, /* flags */ 0);
- }
-
- /**
- * Test verifying that {@link BackupManagerService#cancelBackups(int)} throws a {@link
- * SecurityException} if the caller does not have INTERACT_ACROSS_USERS_FULL permission.
- */
- @Test
- public void testCancelBackups_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- expectThrows(SecurityException.class, () -> backupManagerService.cancelBackups(mUserTwoId));
- }
-
- /**
- * Test that the backup service does not throw a {@link SecurityException} if the caller has
- * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
- */
- @Test
- public void testCancelBackups_withPermission_propagatesForNonCallingUser() {
- registerUser(mUserOneId, mUserOneService);
- registerUser(mUserTwoId, mUserTwoService);
- BackupManagerService backupManagerService = createService();
-
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
-
- backupManagerService.cancelBackups(mUserTwoId);
-
- verify(mUserTwoService).cancelBackups();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testCancelBackups_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.cancelBackups(mUserOneId);
-
- verify(mUserOneService).cancelBackups();
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testCancelBackups_onUnknownUser_doesNotPropagateCall() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.cancelBackups(mUserTwoId);
-
- verify(mUserOneService, never()).cancelBackups();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testBeginFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(UserHandle.USER_SYSTEM, mUserOneService);
- BackupManagerService backupManagerService = createService();
- FullBackupJob job = new FullBackupJob();
-
- backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
-
- verify(mUserOneService).beginFullBackup(job);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testBeginFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
- FullBackupJob job = new FullBackupJob();
-
- backupManagerService.beginFullBackup(UserHandle.USER_SYSTEM, job);
-
- verify(mUserOneService, never()).beginFullBackup(job);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testEndFullBackup_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(UserHandle.USER_SYSTEM, mUserOneService);
- BackupManagerService backupManagerService = createService();
-
- backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
-
- verify(mUserOneService).endFullBackup();
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testEndFullBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
-
- backupManagerService.endFullBackup(UserHandle.USER_SYSTEM);
-
- verify(mUserOneService, never()).endFullBackup();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testFullTransportBackup_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- String[] packages = {TEST_PACKAGE};
-
- backupManagerService.fullTransportBackup(mUserOneId, packages);
-
- verify(mUserOneService).fullTransportBackup(packages);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testFullTransportBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- String[] packages = {TEST_PACKAGE};
-
- backupManagerService.fullTransportBackup(mUserTwoId, packages);
-
- verify(mUserOneService, never()).fullTransportBackup(packages);
- }
-
- // ---------------------------------------------
- // Restore tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testRestoreAtInstall_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.restoreAtInstall(mUserOneId, TEST_PACKAGE, /* token */ 0);
-
- verify(mUserOneService).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testRestoreAtInstall_onUnknownUser_doesNotPropagateCall() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.restoreAtInstall(mUserTwoId, TEST_PACKAGE, /* token */ 0);
-
- verify(mUserOneService, never()).restoreAtInstall(TEST_PACKAGE, /* token */ 0);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testBeginRestoreSession_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.beginRestoreSession(mUserOneId, TEST_PACKAGE, TEST_TRANSPORT);
-
- verify(mUserOneService).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testBeginRestoreSession_onUnknownUser_doesNotPropagateCall() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.beginRestoreSession(mUserTwoId, TEST_PACKAGE, TEST_TRANSPORT);
-
- verify(mUserOneService, never()).beginRestoreSession(TEST_PACKAGE, TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetAvailableRestoreToken_onRegisteredUser_callsMethodForUser()
- throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.getAvailableRestoreToken(mUserOneId, TEST_PACKAGE);
-
- verify(mUserOneService).getAvailableRestoreToken(TEST_PACKAGE);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testGetAvailableRestoreToken_onUnknownUser_doesNotPropagateCall() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.getAvailableRestoreToken(mUserTwoId, TEST_PACKAGE);
-
- verify(mUserOneService, never()).getAvailableRestoreToken(TEST_PACKAGE);
- }
-
- // ---------------------------------------------
- // Adb backup/restore tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testSetBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(UserHandle.USER_SYSTEM, mUserOneService);
- BackupManagerService backupManagerService = createService();
-
- backupManagerService.setBackupPassword("currentPassword", "newPassword");
-
- verify(mUserOneService).setBackupPassword("currentPassword", "newPassword");
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testSetBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
-
- backupManagerService.setBackupPassword("currentPassword", "newPassword");
-
- verify(mUserOneService, never()).setBackupPassword("currentPassword", "newPassword");
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testHasBackupPassword_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(UserHandle.USER_SYSTEM, mUserOneService);
- BackupManagerService backupManagerService = createService();
-
- backupManagerService.hasBackupPassword();
-
- verify(mUserOneService).hasBackupPassword();
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testHasBackupPassword_onUnknownUser_doesNotPropagateCall() throws Exception {
- BackupManagerService backupManagerService = createService();
-
- backupManagerService.hasBackupPassword();
-
- verify(mUserOneService, never()).hasBackupPassword();
- }
-
- /**
- * Test that the backup services throws a {@link SecurityException} if the caller does not have
- * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
- */
- @Test
- public void testAdbBackup_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- expectThrows(
- SecurityException.class,
- () ->
- backupManagerService.adbBackup(
- mUserTwoId,
- /* parcelFileDescriptor*/ null,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- null));
- }
-
- /**
- * Test that the backup service does not throw a {@link SecurityException} if the caller has
- * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
- */
- @Test
- public void testAdbBackup_withPermission_propagatesForNonCallingUser() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- registerUser(mUserTwoId, mUserTwoService);
- BackupManagerService backupManagerService = createService();
-
- ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
-
- backupManagerService.adbBackup(
- mUserTwoId,
- parcelFileDescriptor,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- ADB_TEST_PACKAGES);
-
- verify(mUserTwoService)
- .adbBackup(
- parcelFileDescriptor,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- ADB_TEST_PACKAGES);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testAdbBackup_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.adbBackup(
- mUserOneId,
- parcelFileDescriptor,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- ADB_TEST_PACKAGES);
-
- verify(mUserOneService)
- .adbBackup(
- parcelFileDescriptor,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- ADB_TEST_PACKAGES);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testAdbBackup_onUnknownUser_doesNotPropagateCall() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.adbBackup(
- mUserTwoId,
- parcelFileDescriptor,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- ADB_TEST_PACKAGES);
-
- verify(mUserOneService, never())
- .adbBackup(
- parcelFileDescriptor,
- /* includeApks */ true,
- /* includeObbs */ true,
- /* includeShared */ true,
- /* doWidgets */ true,
- /* doAllApps */ true,
- /* includeSystem */ true,
- /* doCompress */ true,
- /* doKeyValue */ true,
- ADB_TEST_PACKAGES);
- }
-
- /**
- * Test that the backup services throws a {@link SecurityException} if the caller does not have
- * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
- */
- @Test
- public void testAdbRestore_withoutPermission_throwsSecurityExceptionForNonCallingUser() {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- expectThrows(
- SecurityException.class, () -> backupManagerService.adbRestore(mUserTwoId, null));
- }
-
- /**
- * Test that the backup service does not throw a {@link SecurityException} if the caller has
- * INTERACT_ACROSS_USERS_FULL permission and passes a different user id.
- */
- @Test
- public void testAdbRestore_withPermission_propagatesForNonCallingUser() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- registerUser(mUserTwoId, mUserTwoService);
- BackupManagerService backupManagerService = createService();
- ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ true);
-
- backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
-
- verify(mUserTwoService).adbRestore(parcelFileDescriptor);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testAdbRestore_onRegisteredUser_callsMethodForUser() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.adbRestore(mUserOneId, parcelFileDescriptor);
-
- verify(mUserOneService).adbRestore(parcelFileDescriptor);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testAdbRestore_onUnknownUser_doesNotPropagateCall() throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- ParcelFileDescriptor parcelFileDescriptor = getFileDescriptorForAdbTest();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.adbRestore(mUserTwoId, parcelFileDescriptor);
-
- verify(mUserOneService, never()).adbRestore(parcelFileDescriptor);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testAcknowledgeAdbBackupOrRestore_onRegisteredUser_callsMethodForUser()
- throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
-
- backupManagerService.acknowledgeAdbBackupOrRestore(
- mUserOneId,
- /* token */ 0,
- /* allow */ true,
- "currentPassword",
- "encryptionPassword",
- observer);
-
- verify(mUserOneService)
- .acknowledgeAdbBackupOrRestore(
- /* token */ 0,
- /* allow */ true,
- "currentPassword",
- "encryptionPassword",
- observer);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testAcknowledgeAdbBackupOrRestore_onUnknownUser_doesNotPropagateCall()
- throws Exception {
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- IFullBackupRestoreObserver observer = mock(IFullBackupRestoreObserver.class);
-
- backupManagerService.acknowledgeAdbBackupOrRestore(
- mUserTwoId,
- /* token */ 0,
- /* allow */ true,
- "currentPassword",
- "encryptionPassword",
- observer);
-
- verify(mUserOneService, never())
- .acknowledgeAdbBackupOrRestore(
- /* token */ 0,
- /* allow */ true,
- "currentPassword",
- "encryptionPassword",
- observer);
- }
-
- // ---------------------------------------------
- // Lifecycle tests
- // ---------------------------------------------
-
- /** testOnStart_publishesService */
- @Test
- public void testOnStart_publishesService() {
- Trampoline trampoline = mock(Trampoline.class);
- BackupManagerService.Lifecycle lifecycle =
- spy(new BackupManagerService.Lifecycle(mContext, trampoline));
- doNothing().when(lifecycle).publishService(anyString(), any());
-
- lifecycle.onStart();
-
- verify(lifecycle).publishService(Context.BACKUP_SERVICE, trampoline);
- }
-
- /** testOnUnlockUser_forwards */
- @Test
- public void testOnUnlockUser_forwards() {
- Trampoline trampoline = mock(Trampoline.class);
- BackupManagerService.Lifecycle lifecycle =
- new BackupManagerService.Lifecycle(mContext, trampoline);
-
- lifecycle.onUnlockUser(UserHandle.USER_SYSTEM);
-
- verify(trampoline).onUnlockUser(UserHandle.USER_SYSTEM);
- }
-
- /** testOnStopUser_forwards */
- @Test
- public void testOnStopUser_forwards() {
- Trampoline trampoline = mock(Trampoline.class);
- BackupManagerService.Lifecycle lifecycle =
- new BackupManagerService.Lifecycle(mContext, trampoline);
-
- lifecycle.onStopUser(UserHandle.USER_SYSTEM);
-
- verify(trampoline).onStopUser(UserHandle.USER_SYSTEM);
- }
-
- // ---------------------------------------------
- // Service tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testDump_onRegisteredUser_callsMethodForUser() throws Exception {
- grantDumpPermissions();
-
- registerUser(UserHandle.USER_SYSTEM, mUserOneService);
- BackupManagerService backupManagerService = createService();
- File testFile = createTestFile();
- FileDescriptor fileDescriptor = new FileDescriptor();
- PrintWriter printWriter = new PrintWriter(testFile);
- String[] args = {"1", "2"};
-
- backupManagerService.dump(fileDescriptor, printWriter, args);
-
- verify(mUserOneService).dump(fileDescriptor, printWriter, args);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testDump_onUnknownUser_doesNotPropagateCall() throws Exception {
- grantDumpPermissions();
-
- BackupManagerService backupManagerService = createService();
- File testFile = createTestFile();
- FileDescriptor fileDescriptor = new FileDescriptor();
- PrintWriter printWriter = new PrintWriter(testFile);
- String[] args = {"1", "2"};
-
- backupManagerService.dump(fileDescriptor, printWriter, args);
-
- verify(mUserOneService, never()).dump(fileDescriptor, printWriter, args);
- }
-
- /** Test that 'dumpsys backup users' dumps the list of users registered in backup service*/
- @Test
- public void testDump_users_dumpsListOfRegisteredUsers() {
- grantDumpPermissions();
-
- registerUser(mUserOneId, mUserOneService);
- BackupManagerService backupManagerService = createService();
- StringWriter out = new StringWriter();
- PrintWriter writer = new PrintWriter(out);
- String[] args = {"users"};
-
- backupManagerService.dump(null, writer, args);
-
- writer.flush();
- assertEquals(
- String.format("%s %d\n", BackupManagerService.DUMP_RUNNING_USERS_MESSAGE,
- mUserOneId),
- out.toString());
- }
-
- private void grantDumpPermissions() {
- mShadowContext.grantPermissions(DUMP);
- mShadowContext.grantPermissions(PACKAGE_USAGE_STATS);
- }
-
- private File createTestFile() throws IOException {
- File testFile = new File(mContext.getFilesDir(), "test");
- testFile.createNewFile();
- return testFile;
- }
-
- private BackupManagerService createService() {
- mShadowContext.grantPermissions(BACKUP);
- return new BackupManagerService(mContext, mTrampoline, mTrampoline.getUserServices());
- }
-
- private void registerUser(int userId, UserBackupManagerService userBackupManagerService) {
- mTrampoline.setBackupServiceActive(userId, true);
- mTrampoline.startServiceForUser(userId, userBackupManagerService);
- }
-
- /**
- * Sets the calling user to {@code userId} and grants the permission INTERACT_ACROSS_USERS_FULL
- * to the caller if {@code shouldGrantPermission} is {@code true}, else it denies the
- * permission.
- */
- private void setCallerAndGrantInteractUserPermission(
- @UserIdInt int userId, boolean shouldGrantPermission) {
- ShadowBinder.setCallingUserHandle(UserHandle.of(userId));
- if (shouldGrantPermission) {
- mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
- } else {
- mShadowContext.denyPermissions(INTERACT_ACROSS_USERS_FULL);
- }
- }
-
- private ParcelFileDescriptor getFileDescriptorForAdbTest() throws Exception {
- File testFile = new File(mContext.getFilesDir(), "test");
- testFile.createNewFile();
- return ParcelFileDescriptor.open(testFile, ParcelFileDescriptor.MODE_READ_WRITE);
- }
-}
diff --git a/services/robotests/backup/src/com/android/server/backup/TrampolineRoboTest.java b/services/robotests/backup/src/com/android/server/backup/TrampolineRoboTest.java
deleted file mode 100644
index d1fb10a..0000000
--- a/services/robotests/backup/src/com/android/server/backup/TrampolineRoboTest.java
+++ /dev/null
@@ -1,651 +0,0 @@
-/*
- * 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.backup;
-
-import static android.Manifest.permission.BACKUP;
-import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
-
-import static com.android.server.backup.testing.TransportData.backupTransport;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.robolectric.Shadows.shadowOf;
-
-import android.annotation.UserIdInt;
-import android.app.Application;
-import android.app.backup.ISelectBackupTransportCallback;
-import android.content.Context;
-import android.content.Intent;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.platform.test.annotations.Presubmit;
-import android.util.SparseArray;
-
-import com.android.server.backup.testing.TransportData;
-import com.android.server.testing.shadows.ShadowApplicationPackageManager;
-import com.android.server.testing.shadows.ShadowBinder;
-import com.android.server.testing.shadows.ShadowEnvironment;
-import com.android.server.testing.shadows.ShadowSystemServiceRegistry;
-import com.android.server.testing.shadows.ShadowUserManager;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
-import org.robolectric.annotation.Config;
-import org.robolectric.shadow.api.Shadow;
-import org.robolectric.shadows.ShadowContextWrapper;
-
-/** Tests for {@link com.android.server.backup.Trampoline}. */
-@RunWith(RobolectricTestRunner.class)
-@Config(
- shadows = {
- ShadowApplicationPackageManager.class,
- ShadowBinder.class,
- ShadowUserManager.class,
- ShadowEnvironment.class,
- ShadowSystemServiceRegistry.class
- })
-@Presubmit
-public class TrampolineRoboTest {
- private static final String TEST_PACKAGE = "package";
- private static final String TEST_TRANSPORT = "transport";
-
- private Context mContext;
- private ShadowContextWrapper mShadowContext;
- private ShadowUserManager mShadowUserManager;
- @UserIdInt private int mUserOneId;
- @UserIdInt private int mUserTwoId;
- @Mock private UserBackupManagerService mUserOneService;
- @Mock private UserBackupManagerService mUserTwoService;
-
- /** Setup */
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
-
- Application application = RuntimeEnvironment.application;
- mContext = application;
- mShadowContext = shadowOf(application);
- mShadowUserManager = Shadow.extract(UserManager.get(application));
-
- mUserOneId = UserHandle.USER_SYSTEM + 1;
- mUserTwoId = mUserOneId + 1;
- mShadowUserManager.addUser(mUserOneId, "mUserOneId", 0);
- mShadowUserManager.addUser(mUserTwoId, "mUserTwoId", 0);
-
- mShadowContext.grantPermissions(BACKUP);
- mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
-
- ShadowBinder.setCallingUid(Process.SYSTEM_UID);
- }
-
- /** Test that the service registers users. */
- @Test
- public void testStartServiceForUser_registersUser() throws Exception {
- Trampoline backupManagerService = createService();
- backupManagerService.setBackupServiceActive(mUserOneId, true);
-
- backupManagerService.startServiceForUser(mUserOneId);
-
- SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
- assertThat(serviceUsers.size()).isEqualTo(1);
- assertThat(serviceUsers.get(mUserOneId)).isNotNull();
- }
-
- /** Test that the service registers users. */
- @Test
- public void testStartServiceForUser_withServiceInstance_registersUser() throws Exception {
- Trampoline backupManagerService = createService();
- backupManagerService.setBackupServiceActive(mUserOneId, true);
-
- backupManagerService.startServiceForUser(mUserOneId, mUserOneService);
-
- SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
- assertThat(serviceUsers.size()).isEqualTo(1);
- assertThat(serviceUsers.get(mUserOneId)).isEqualTo(mUserOneService);
- }
-
- /** Test that the service unregisters users when stopped. */
- @Test
- public void testStopServiceForUser_forRegisteredUser_unregistersCorrectUser() throws Exception {
- Trampoline backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
- backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
- ShadowBinder.setCallingUid(Process.SYSTEM_UID);
-
- backupManagerService.stopServiceForUser(mUserOneId);
-
- SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
- assertThat(serviceUsers.size()).isEqualTo(1);
- assertThat(serviceUsers.get(mUserOneId)).isNull();
- assertThat(serviceUsers.get(mUserTwoId)).isEqualTo(mUserTwoService);
- }
-
- /** Test that the service unregisters users when stopped. */
- @Test
- public void testStopServiceForUser_forRegisteredUser_tearsDownCorrectUser() throws Exception {
- Trampoline backupManagerService =
- createServiceAndRegisterUser(mUserOneId, mUserOneService);
- backupManagerService.setBackupServiceActive(mUserTwoId, true);
- backupManagerService.startServiceForUser(mUserTwoId, mUserTwoService);
-
- backupManagerService.stopServiceForUser(mUserOneId);
-
- verify(mUserOneService).tearDownService();
- verify(mUserTwoService, never()).tearDownService();
- }
-
- /** Test that the service unregisters users when stopped. */
- @Test
- public void testStopServiceForUser_forUnknownUser_doesNothing() throws Exception {
- Trampoline backupManagerService = createService();
- backupManagerService.setBackupServiceActive(mUserOneId, true);
- ShadowBinder.setCallingUid(Process.SYSTEM_UID);
-
- backupManagerService.stopServiceForUser(mUserOneId);
-
- SparseArray<UserBackupManagerService> serviceUsers = backupManagerService.getUserServices();
- assertThat(serviceUsers.size()).isEqualTo(0);
- }
-
- // ---------------------------------------------
- // Backup agent tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.dataChanged(mUserOneId, TEST_PACKAGE);
-
- verify(mUserOneService).dataChanged(TEST_PACKAGE);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.dataChanged(mUserTwoId, TEST_PACKAGE);
-
- verify(mUserOneService, never()).dataChanged(TEST_PACKAGE);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- IBinder agentBinder = mock(IBinder.class);
-
- backupManagerService.agentConnected(mUserOneId, TEST_PACKAGE, agentBinder);
-
- verify(mUserOneService).agentConnected(TEST_PACKAGE, agentBinder);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- IBinder agentBinder = mock(IBinder.class);
-
- backupManagerService.agentConnected(mUserTwoId, TEST_PACKAGE, agentBinder);
-
- verify(mUserOneService, never()).agentConnected(TEST_PACKAGE, agentBinder);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.opComplete(mUserOneId, /* token */ 0, /* result */ 0L);
-
- verify(mUserOneService).opComplete(/* token */ 0, /* result */ 0L);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.opComplete(mUserTwoId, /* token */ 0, /* result */ 0L);
-
- verify(mUserOneService, never()).opComplete(/* token */ 0, /* result */ 0L);
- }
-
- // ---------------------------------------------
- // Transport tests
- // ---------------------------------------------
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testInitializeTransports_onRegisteredUser_callsMethodForUser() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- String[] transports = {TEST_TRANSPORT};
-
- backupManagerService.initializeTransports(mUserOneId, transports, /* observer */ null);
-
- verify(mUserOneService).initializeTransports(transports, /* observer */ null);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testInitializeTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- String[] transports = {TEST_TRANSPORT};
-
- backupManagerService.initializeTransports(mUserTwoId, transports, /* observer */ null);
-
- verify(mUserOneService, never()).initializeTransports(transports, /* observer */ null);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testClearBackupData_onRegisteredUser_callsMethodForUser() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.clearBackupData(mUserOneId, TEST_TRANSPORT, TEST_PACKAGE);
-
- verify(mUserOneService).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testClearBackupData_onUnknownUser_doesNotPropagateCall() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.clearBackupData(mUserTwoId, TEST_TRANSPORT, TEST_PACKAGE);
-
- verify(mUserOneService, never()).clearBackupData(TEST_TRANSPORT, TEST_PACKAGE);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetCurrentTransport_onRegisteredUser_callsMethodForUser() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.getCurrentTransport(mUserOneId);
-
- verify(mUserOneService).getCurrentTransport();
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testGetCurrentTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.getCurrentTransport(mUserTwoId);
-
- verify(mUserOneService, never()).getCurrentTransport();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetCurrentTransportComponent_onRegisteredUser_callsMethodForUser()
- throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.getCurrentTransportComponent(mUserOneId);
-
- verify(mUserOneService).getCurrentTransportComponent();
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testGetCurrentTransportComponent_onUnknownUser_doesNotPropagateCall()
- throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.getCurrentTransportComponent(mUserTwoId);
-
- verify(mUserOneService, never()).getCurrentTransportComponent();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testListAllTransports_onRegisteredUser_callsMethodForUser() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.listAllTransports(mUserOneId);
-
- verify(mUserOneService).listAllTransports();
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testListAllTransports_onUnknownUser_doesNotPropagateCall() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.listAllTransports(mUserTwoId);
-
- verify(mUserOneService, never()).listAllTransports();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testListAllTransportComponents_onRegisteredUser_callsMethodForUser()
- throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.listAllTransportComponents(mUserOneId);
-
- verify(mUserOneService).listAllTransportComponents();
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testListAllTransportComponents_onUnknownUser_doesNotPropagateCall()
- throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.listAllTransportComponents(mUserTwoId);
-
- verify(mUserOneService, never()).listAllTransportComponents();
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testSelectBackupTransport_onRegisteredUser_callsMethodForUser() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.selectBackupTransport(mUserOneId, TEST_TRANSPORT);
-
- verify(mUserOneService).selectBackupTransport(TEST_TRANSPORT);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testSelectBackupTransport_onUnknownUser_doesNotPropagateCall() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.selectBackupTransport(mUserTwoId, TEST_TRANSPORT);
-
- verify(mUserOneService, never()).selectBackupTransport(TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testSelectTransportAsync_onRegisteredUser_callsMethodForUser() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- TransportData transport = backupTransport();
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
- backupManagerService.selectBackupTransportAsync(
- mUserOneId, transport.getTransportComponent(), callback);
-
- verify(mUserOneService)
- .selectBackupTransportAsync(transport.getTransportComponent(), callback);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testSelectBackupTransportAsync_onUnknownUser_doesNotPropagateCall()
- throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- TransportData transport = backupTransport();
- ISelectBackupTransportCallback callback = mock(ISelectBackupTransportCallback.class);
-
- backupManagerService.selectBackupTransportAsync(
- mUserTwoId, transport.getTransportComponent(), callback);
-
- verify(mUserOneService, never())
- .selectBackupTransportAsync(transport.getTransportComponent(), callback);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetConfigurationIntent_onRegisteredUser_callsMethodForUser() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.getConfigurationIntent(mUserOneId, TEST_TRANSPORT);
-
- verify(mUserOneService).getConfigurationIntent(TEST_TRANSPORT);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testGetConfigurationIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.getConfigurationIntent(mUserTwoId, TEST_TRANSPORT);
-
- verify(mUserOneService, never()).getConfigurationIntent(TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetDestinationString_onRegisteredUser_callsMethodForUser() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.getDestinationString(mUserOneId, TEST_TRANSPORT);
-
- verify(mUserOneService).getDestinationString(TEST_TRANSPORT);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testGetDestinationString_onUnknownUser_doesNotPropagateCall() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.getDestinationString(mUserTwoId, TEST_TRANSPORT);
-
- verify(mUserOneService, never()).getDestinationString(TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetDataManagementIntent_onRegisteredUser_callsMethodForUser() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.getDataManagementIntent(mUserOneId, TEST_TRANSPORT);
-
- verify(mUserOneService).getDataManagementIntent(TEST_TRANSPORT);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testGetDataManagementIntent_onUnknownUser_doesNotPropagateCall() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.getDataManagementIntent(mUserTwoId, TEST_TRANSPORT);
-
- verify(mUserOneService, never()).getDataManagementIntent(TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testGetDataManagementLabel_onRegisteredUser_callsMethodForUser() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
-
- backupManagerService.getDataManagementLabel(mUserOneId, TEST_TRANSPORT);
-
- verify(mUserOneService).getDataManagementLabel(TEST_TRANSPORT);
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testGetDataManagementLabel_onUnknownUser_doesNotPropagateCall() throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
-
- backupManagerService.getDataManagementLabel(mUserTwoId, TEST_TRANSPORT);
-
- verify(mUserOneService, never()).getDataManagementLabel(TEST_TRANSPORT);
- }
-
- /** Test that the backup service routes methods correctly to the user that requests it. */
- @Test
- public void testUpdateTransportAttributes_onRegisteredUser_callsMethodForUser()
- throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
- TransportData transport = backupTransport();
- Intent configurationIntent = new Intent();
- Intent dataManagementIntent = new Intent();
-
- backupManagerService.updateTransportAttributes(
- mUserOneId,
- transport.getTransportComponent(),
- transport.transportName,
- configurationIntent,
- "currentDestinationString",
- dataManagementIntent,
- "dataManagementLabel");
-
- verify(mUserOneService)
- .updateTransportAttributes(
- transport.getTransportComponent(),
- transport.transportName,
- configurationIntent,
- "currentDestinationString",
- dataManagementIntent,
- "dataManagementLabel");
- }
-
- /** Test that the backup service does not route methods for non-registered users. */
- @Test
- public void testUpdateTransportAttributes_onUnknownUser_doesNotPropagateCall()
- throws Exception {
- Trampoline backupManagerService = createService();
- registerUser(backupManagerService, mUserOneId, mUserOneService);
- setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
- TransportData transport = backupTransport();
- Intent configurationIntent = new Intent();
- Intent dataManagementIntent = new Intent();
-
- backupManagerService.updateTransportAttributes(
- mUserTwoId,
- transport.getTransportComponent(),
- transport.transportName,
- configurationIntent,
- "currentDestinationString",
- dataManagementIntent,
- "dataManagementLabel");
-
- verify(mUserOneService, never())
- .updateTransportAttributes(
- transport.getTransportComponent(),
- transport.transportName,
- configurationIntent,
- "currentDestinationString",
- dataManagementIntent,
- "dataManagementLabel");
- }
-
- private Trampoline createService() {
- return new Trampoline(mContext);
- }
-
- private void registerUser(
- Trampoline trampoline, int userId, UserBackupManagerService userBackupManagerService) {
- trampoline.setBackupServiceActive(userId, true);
- trampoline.startServiceForUser(userId, userBackupManagerService);
- }
-
- private Trampoline createServiceAndRegisterUser(
- int userId, UserBackupManagerService userBackupManagerService) {
- Trampoline backupManagerService = createService();
- backupManagerService.setBackupServiceActive(userBackupManagerService.getUserId(), true);
- backupManagerService.startServiceForUser(userId, userBackupManagerService);
- return backupManagerService;
- }
-
- /**
- * Sets the calling user to {@code userId} and grants the permission INTERACT_ACROSS_USERS_FULL
- * to the caller if {@code shouldGrantPermission} is {@code true}, else it denies the
- * permission.
- */
- private void setCallerAndGrantInteractUserPermission(
- @UserIdInt int userId, boolean shouldGrantPermission) {
- ShadowBinder.setCallingUserHandle(UserHandle.of(userId));
- if (shouldGrantPermission) {
- mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
- } else {
- mShadowContext.denyPermissions(INTERACT_ACROSS_USERS_FULL);
- }
- }
-}
diff --git a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
index 84e810d..8632ca4 100644
--- a/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/UserBackupManagerServiceTest.java
@@ -1005,7 +1005,7 @@
UserBackupManagerService.createAndInitializeService(
USER_ID,
mContext,
- new Trampoline(mContext),
+ new BackupManagerService(mContext),
mBackupThread,
mBaseStateDir,
mDataDir,
@@ -1026,7 +1026,7 @@
UserBackupManagerService.createAndInitializeService(
USER_ID,
mContext,
- new Trampoline(mContext),
+ new BackupManagerService(mContext),
mBackupThread,
mBaseStateDir,
mDataDir,
@@ -1045,7 +1045,7 @@
UserBackupManagerService.createAndInitializeService(
USER_ID,
/* context */ null,
- new Trampoline(mContext),
+ new BackupManagerService(mContext),
mBackupThread,
mBaseStateDir,
mDataDir,
@@ -1077,7 +1077,7 @@
UserBackupManagerService.createAndInitializeService(
USER_ID,
mContext,
- new Trampoline(mContext),
+ new BackupManagerService(mContext),
/* backupThread */ null,
mBaseStateDir,
mDataDir,
@@ -1093,7 +1093,7 @@
UserBackupManagerService.createAndInitializeService(
USER_ID,
mContext,
- new Trampoline(mContext),
+ new BackupManagerService(mContext),
mBackupThread,
/* baseStateDir */ null,
mDataDir,
@@ -1102,8 +1102,8 @@
/**
* Test checking non-null argument on {@link
- * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
- * File, File, TransportManager)}.
+ * UserBackupManagerService#createAndInitializeService(int, Context, BackupManagerService,
+ * HandlerThread, File, File, TransportManager)}.
*/
@Test
public void testCreateAndInitializeService_withNullDataDir_throws() {
@@ -1113,7 +1113,7 @@
UserBackupManagerService.createAndInitializeService(
USER_ID,
mContext,
- new Trampoline(mContext),
+ new BackupManagerService(mContext),
mBackupThread,
mBaseStateDir,
/* dataDir */ null,
@@ -1122,8 +1122,8 @@
/**
* Test checking non-null argument on {@link
- * UserBackupManagerService#createAndInitializeService(int, Context, Trampoline, HandlerThread,
- * File, File, TransportManager)}.
+ * UserBackupManagerService#createAndInitializeService(int, Context, BackupManagerService,
+ * HandlerThread, File, File, TransportManager)}.
*/
@Test
public void testCreateAndInitializeService_withNullTransportManager_throws() {
@@ -1133,7 +1133,7 @@
UserBackupManagerService.createAndInitializeService(
USER_ID,
mContext,
- new Trampoline(mContext),
+ new BackupManagerService(mContext),
mBackupThread,
mBaseStateDir,
mDataDir,
@@ -1151,7 +1151,7 @@
UserBackupManagerService service = UserBackupManagerService.createAndInitializeService(
USER_ID,
contextSpy,
- new Trampoline(mContext),
+ new BackupManagerService(mContext),
mBackupThread,
mBaseStateDir,
mDataDir,
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
index 392d182..84421ef 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
+++ b/services/robotests/backup/src/com/android/server/backup/testing/BackupManagerServiceTestUtils.java
@@ -37,7 +37,7 @@
import android.util.Log;
import com.android.server.backup.BackupAgentTimeoutParameters;
-import com.android.server.backup.Trampoline;
+import com.android.server.backup.BackupManagerService;
import com.android.server.backup.TransportManager;
import com.android.server.backup.UserBackupManagerService;
@@ -89,7 +89,7 @@
UserBackupManagerService.createAndInitializeService(
userId,
context,
- new Trampoline(context),
+ new BackupManagerService(context),
backupThread,
baseStateDir,
dataDir,
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
index 3614763..5d041b7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/TimeControllerTest.java
@@ -71,6 +71,7 @@
private static final String SOURCE_PACKAGE = "com.android.frameworks.mockingservicestests";
private static final int SOURCE_USER_ID = 0;
+ private TimeController.TcConstants mConstants;
private TimeController mTimeController;
private MockitoSession mMockingSession;
@@ -110,6 +111,7 @@
// Initialize real objects.
mTimeController = new TimeController(mJobSchedulerService);
+ mConstants = mTimeController.getTcConstants();
spyOn(mTimeController);
}
@@ -528,6 +530,46 @@
}
@Test
+ public void testJobDelayWakeupAlarmToggling() {
+ final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+
+ JobStatus job = createJobStatus(
+ "testMaybeStartTrackingJobLocked_DeadlineReverseOrder",
+ createJob().setMinimumLatency(HOUR_IN_MILLIS));
+
+ doReturn(true).when(mTimeController)
+ .wouldBeReadyWithConstraintLocked(eq(job), anyInt());
+
+ // Starting off with using a wakeup alarm.
+ mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = false;
+ InOrder inOrder = inOrder(mAlarmManager);
+
+ mTimeController.maybeStartTrackingJobLocked(job, null);
+ inOrder.verify(mAlarmManager, times(1))
+ .set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(now + HOUR_IN_MILLIS), anyLong(),
+ anyLong(),
+ eq(TAG_DELAY), any(), any(), any());
+
+ // Use a non wakeup alarm.
+ mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = true;
+
+ mTimeController.maybeStartTrackingJobLocked(job, null);
+ inOrder.verify(mAlarmManager, times(1))
+ .set(eq(AlarmManager.ELAPSED_REALTIME), eq(now + HOUR_IN_MILLIS), anyLong(),
+ anyLong(), eq(TAG_DELAY),
+ any(), any(), any());
+
+ // Back off, use a wakeup alarm.
+ mConstants.USE_NON_WAKEUP_ALARM_FOR_DELAY = false;
+
+ mTimeController.maybeStartTrackingJobLocked(job, null);
+ inOrder.verify(mAlarmManager, times(1))
+ .set(eq(AlarmManager.ELAPSED_REALTIME_WAKEUP), eq(now + HOUR_IN_MILLIS), anyLong(),
+ anyLong(),
+ eq(TAG_DELAY), any(), any(), any());
+ }
+
+ @Test
public void testCheckExpiredDeadlinesAndResetAlarm_AllReady() {
doReturn(true).when(mTimeController).wouldBeReadyWithConstraintLocked(any(), anyInt());
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
index 1084d62..939aafa 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java
@@ -752,7 +752,7 @@
}
@Override
- protected boolean isCalledForCurrentUserLocked() {
+ protected boolean hasRightsToCurrentUserLocked() {
return mResolvedUserId == mSystemSupport.getCurrentUserIdLocked();
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index 7887d5b..93c16fe 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -77,6 +77,8 @@
private static final boolean FORCE_SEND = true;
private static final boolean SEND_ON_WINDOW_CHANGES = false;
private static final int USER_SYSTEM_ID = UserHandle.USER_SYSTEM;
+ private static final int USER_PROFILE = 11;
+ private static final int USER_PROFILE_PARENT = 1;
// TO-DO [Multi-Display] : change the display count to 2
private static final int DISPLAY_COUNT = 1;
private static final int NUM_GLOBAL_WINDOWS = 4;
@@ -110,6 +112,8 @@
MockitoAnnotations.initMocks(this);
when(mMockA11yUserManager.getCurrentUserIdLocked()).thenReturn(USER_SYSTEM_ID);
when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
+ USER_PROFILE)).thenReturn(USER_PROFILE_PARENT);
+ when(mMockA11ySecurityPolicy.resolveCallingUserIdEnforcingPermissionsLocked(
USER_SYSTEM_ID)).thenReturn(USER_SYSTEM_ID);
when(mMockA11ySecurityPolicy.resolveValidReportedPackageLocked(
anyString(), anyInt(), anyInt())).thenReturn(PACKAGE_NAME);
@@ -247,8 +251,8 @@
throws RemoteException {
final AccessibilityWindowInfo oldWindow =
mA11yWindowManager.getWindowListLocked().get(0);
- final IWindow token =
- addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY, true);
+ final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ true, USER_SYSTEM_ID);
final WindowInfo windowInfo = WindowInfo.obtain();
windowInfo.type = AccessibilityWindowInfo.TYPE_APPLICATION;
windowInfo.token = token.asBinder();
@@ -605,6 +609,16 @@
verify(mockRemoteConnection).notifyOutsideTouch();
}
+ @Test
+ public void addAccessibilityInteractionConnection_profileUser_findInParentUser()
+ throws RemoteException {
+ final IWindow token = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
+ false, USER_PROFILE);
+ final int windowId = mA11yWindowManager.findWindowIdLocked(
+ USER_PROFILE_PARENT, token.asBinder());
+ assertTrue(windowId >= 0);
+ }
+
private void startTrackingPerDisplay(int displayId) throws RemoteException {
ArrayList<WindowInfo> windowInfosForDisplay = new ArrayList<>();
// Adds RemoteAccessibilityConnection into AccessibilityWindowManager, and copy
@@ -612,12 +626,14 @@
// for the test.
int layer = 0;
for (int i = 0; i < NUM_GLOBAL_WINDOWS; i++) {
- final IWindow token = addAccessibilityInteractionConnection(displayId, true);
+ final IWindow token = addAccessibilityInteractionConnection(displayId,
+ true, USER_SYSTEM_ID);
addWindowInfo(windowInfosForDisplay, token, layer++);
}
for (int i = 0; i < NUM_APP_WINDOWS; i++) {
- final IWindow token = addAccessibilityInteractionConnection(displayId, false);
+ final IWindow token = addAccessibilityInteractionConnection(displayId,
+ false, USER_SYSTEM_ID);
addWindowInfo(windowInfosForDisplay, token, layer++);
}
// Setups default focus.
@@ -647,8 +663,8 @@
return windowsForAccessibilityCallbacksCaptor.getValue();
}
- private IWindow addAccessibilityInteractionConnection(int displayId, boolean bGlobal)
- throws RemoteException {
+ private IWindow addAccessibilityInteractionConnection(int displayId, boolean bGlobal,
+ int userId) throws RemoteException {
final IWindow mockWindowToken = Mockito.mock(IWindow.class);
final IAccessibilityInteractionConnection mockA11yConnection = Mockito.mock(
IAccessibilityInteractionConnection.class);
@@ -656,13 +672,13 @@
final IBinder mockWindowBinder = Mockito.mock(IBinder.class);
when(mockA11yConnection.asBinder()).thenReturn(mockConnectionBinder);
when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
- when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(USER_SYSTEM_ID))
+ when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
.thenReturn(bGlobal);
when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder()))
.thenReturn(displayId);
int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
- mockWindowToken, mockA11yConnection, PACKAGE_NAME, USER_SYSTEM_ID);
+ mockWindowToken, mockA11yConnection, PACKAGE_NAME, userId);
mA11yWindowTokens.put(windowId, mockWindowToken);
return mockWindowToken;
}
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
new file mode 100644
index 0000000..68b413f
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -0,0 +1,621 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.annotation.UserIdInt;
+import android.app.backup.BackupManager;
+import android.app.backup.IBackupManagerMonitor;
+import android.app.backup.IBackupObserver;
+import android.app.backup.IFullBackupRestoreObserver;
+import android.app.backup.ISelectBackupTransportCallback;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.ConditionVariable;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.platform.test.annotations.Presubmit;
+import android.util.SparseArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.backup.utils.RandomAccessFileUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class BackupManagerServiceTest {
+ private static final String PACKAGE_NAME = "some.package.name";
+ private static final String TRANSPORT_NAME = "some.transport.name";
+ private static final String CURRENT_PASSWORD = "current_password";
+ private static final String NEW_PASSWORD = "new_password";
+ private static final String ENCRYPTION_PASSWORD = "encryption_password";
+ private static final CharSequence DATA_MANAGEMENT_LABEL = "data_management_label";
+ private static final String DESTINATION_STRING = "destination_string";
+ private static final String[] PACKAGE_NAMES =
+ new String[]{"some.package.name._1", "some.package.name._2"};
+ private static final String[] TRANSPORTS =
+ new String[]{"some.transport.name._1", "some.transport.name._2"};
+ private static final ComponentName TRANSPORT_COMPONENT_NAME = new ComponentName("package",
+ "class");
+ private static final ComponentName[] TRANSPORT_COMPONENTS = new ComponentName[]{
+ new ComponentName("package1", "class1"),
+ new ComponentName("package2", "class2")
+ };
+ private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
+ private static final int UNSTARTED_NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 2;
+
+ @UserIdInt
+ private int mUserId;
+ @Mock
+ private UserBackupManagerService mUserBackupManagerService;
+ @Mock
+ private Context mContextMock;
+ @Mock
+ private IBinder mAgentMock;
+ @Mock
+ private ParcelFileDescriptor mParcelFileDescriptorMock;
+ @Mock
+ private IFullBackupRestoreObserver mFullBackupRestoreObserverMock;
+ @Mock
+ private IBackupObserver mBackupObserverMock;
+ @Mock
+ private IBackupManagerMonitor mBackupManagerMonitorMock;
+ @Mock
+ private PrintWriter mPrintWriterMock;
+ @Mock
+ private UserManager mUserManagerMock;
+ @Mock
+ private UserInfo mUserInfoMock;
+
+ private FileDescriptor mFileDescriptorStub = new FileDescriptor();
+
+ private BackupManagerServiceTestable mService;
+ private File mTestDir;
+ private File mSuppressFile;
+ private SparseArray<UserBackupManagerService> mUserServices;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mUserId = UserHandle.USER_SYSTEM;
+
+ mUserServices = new SparseArray<>();
+ mUserServices.append(UserHandle.USER_SYSTEM, mUserBackupManagerService);
+ mUserServices.append(NON_USER_SYSTEM, mUserBackupManagerService);
+
+ when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock);
+ when(mUserManagerMock.getUserInfo(NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
+ when(mUserManagerMock.getUserInfo(UNSTARTED_NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
+
+ BackupManagerServiceTestable.sCallingUserId = UserHandle.USER_SYSTEM;
+ BackupManagerServiceTestable.sCallingUid = Process.SYSTEM_UID;
+ BackupManagerServiceTestable.sBackupDisabled = false;
+ BackupManagerServiceTestable.sUserManagerMock = mUserManagerMock;
+
+ mTestDir = InstrumentationRegistry.getContext().getFilesDir();
+ mTestDir.mkdirs();
+
+ mSuppressFile = new File(mTestDir, "suppress");
+ BackupManagerServiceTestable.sSuppressFile = mSuppressFile;
+
+ setUpStateFilesForNonSystemUser(NON_USER_SYSTEM);
+ setUpStateFilesForNonSystemUser(UNSTARTED_NON_USER_SYSTEM);
+
+ when(mContextMock.getSystemService(Context.JOB_SCHEDULER_SERVICE))
+ .thenReturn(mock(JobScheduler.class));
+ mService = new BackupManagerServiceTestable(mContextMock, mUserServices);
+ }
+
+ private void setUpStateFilesForNonSystemUser(int userId) {
+ File activatedFile = new File(mTestDir, "activate-" + userId);
+ BackupManagerServiceTestable.sActivatedFiles.append(userId, activatedFile);
+ File rememberActivatedFile = new File(mTestDir, "rem-activate-" + userId);
+ BackupManagerServiceTestable.sRememberActivatedFiles.append(userId, rememberActivatedFile);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mSuppressFile.delete();
+ deleteFiles(BackupManagerServiceTestable.sActivatedFiles);
+ deleteFiles(BackupManagerServiceTestable.sRememberActivatedFiles);
+ }
+
+ private void deleteFiles(SparseArray<File> files) {
+ int numFiles = files.size();
+ for (int i = 0; i < numFiles; i++) {
+ files.valueAt(i).delete();
+ }
+ }
+
+ @Test
+ public void testIsBackupServiceActive_whenBackupsNotDisabledAndSuppressFileDoesNotExist() {
+ assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void testOnUnlockUser_forNonSystemUserWhenBackupsDisabled_doesNotStartUser() {
+ BackupManagerServiceTestable.sBackupDisabled = true;
+ BackupManagerServiceTestable service =
+ new BackupManagerServiceTestable(mContextMock, new SparseArray<>());
+ ConditionVariable unlocked = new ConditionVariable(false);
+
+ service.onUnlockUser(NON_USER_SYSTEM);
+
+ service.getBackupHandler().post(unlocked::open);
+ unlocked.block();
+ assertNull(service.getUserService(NON_USER_SYSTEM));
+ }
+
+ @Test
+ public void testOnUnlockUser_forSystemUserWhenBackupsDisabled_doesNotStartUser() {
+ BackupManagerServiceTestable.sBackupDisabled = true;
+ BackupManagerServiceTestable service =
+ new BackupManagerServiceTestable(mContextMock, new SparseArray<>());
+ ConditionVariable unlocked = new ConditionVariable(false);
+
+ service.onUnlockUser(UserHandle.USER_SYSTEM);
+
+ service.getBackupHandler().post(unlocked::open);
+ unlocked.block();
+ assertNull(service.getUserService(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void testOnUnlockUser_whenBackupNotActivated_doesNotStartUser() {
+ BackupManagerServiceTestable.sBackupDisabled = false;
+ BackupManagerServiceTestable service =
+ new BackupManagerServiceTestable(mContextMock, new SparseArray<>());
+ service.setBackupServiceActive(NON_USER_SYSTEM, false);
+ ConditionVariable unlocked = new ConditionVariable(false);
+
+ service.onUnlockUser(NON_USER_SYSTEM);
+
+ service.getBackupHandler().post(unlocked::open);
+ unlocked.block();
+ assertNull(service.getUserService(NON_USER_SYSTEM));
+ }
+
+ @Test
+ public void testIsBackupServiceActive_forSystemUserWhenBackupDisabled_returnsTrue()
+ throws Exception {
+ BackupManagerServiceTestable.sBackupDisabled = true;
+ BackupManagerService backupManagerService =
+ new BackupManagerServiceTestable(mContextMock, mUserServices);
+ backupManagerService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+ assertFalse(backupManagerService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void testIsBackupServiceActive_forNonSystemUserWhenBackupDisabled_returnsTrue()
+ throws Exception {
+ BackupManagerServiceTestable.sBackupDisabled = true;
+ BackupManagerService backupManagerService =
+ new BackupManagerServiceTestable(mContextMock, mUserServices);
+ backupManagerService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertFalse(backupManagerService.isBackupServiceActive(NON_USER_SYSTEM));
+ }
+
+ @Test
+ public void isBackupServiceActive_forSystemUser_returnsTrueWhenActivated() throws Exception {
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+ assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void isBackupServiceActive_forSystemUser_returnsFalseWhenDeactivated() throws Exception {
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+
+ assertFalse(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenSystemUserDeactivated()
+ throws Exception {
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+ mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertFalse(mService.isBackupServiceActive(NON_USER_SYSTEM));
+ }
+
+ @Test
+ public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenNonSystemUserDeactivated()
+ throws Exception {
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ // Don't activate non-system user.
+
+ assertFalse(mService.isBackupServiceActive(NON_USER_SYSTEM));
+ }
+
+ @Test
+ public void
+ isBackupServiceActive_forNonSystemUser_returnsTrueWhenSystemAndNonSystemUserActivated()
+ throws Exception {
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM));
+ }
+
+ @Test
+ public void
+ isBackupServiceActive_forUnstartedNonSystemUser_returnsTrueWhenSystemAndUserActivated()
+ throws Exception {
+ mService.setBackupServiceActive(UNSTARTED_NON_USER_SYSTEM, true);
+
+ assertTrue(mService.isBackupServiceActive(UNSTARTED_NON_USER_SYSTEM));
+ }
+
+ @Test
+ public void setBackupServiceActive_forSystemUserAndCallerSystemUid_serviceCreated() {
+ BackupManagerServiceTestable.sCallingUid = Process.SYSTEM_UID;
+
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+ assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void setBackupServiceActive_forSystemUserAndCallerRootUid_serviceCreated() {
+ BackupManagerServiceTestable.sCallingUid = Process.ROOT_UID;
+
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+ assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void setBackupServiceActive_forSystemUserAndCallerNonRootNonSystem_throws() {
+ BackupManagerServiceTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
+
+ try {
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ fail();
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void setBackupServiceActive_forManagedProfileAndCallerSystemUid_serviceCreated() {
+ when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+ BackupManagerServiceTestable.sCallingUid = Process.SYSTEM_UID;
+
+ mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM));
+ }
+
+ @Test
+ public void setBackupServiceActive_forManagedProfileAndCallerRootUid_serviceCreated() {
+ when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+ BackupManagerServiceTestable.sCallingUid = Process.ROOT_UID;
+
+ mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM));
+ }
+
+ @Test
+ public void setBackupServiceActive_forManagedProfileAndCallerNonRootNonSystem_throws() {
+ when(mUserInfoMock.isManagedProfile()).thenReturn(true);
+ BackupManagerServiceTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
+
+ try {
+ mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+ fail();
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void setBackupServiceActive_forNonSystemUserAndCallerWithoutBackupPermission_throws() {
+ doThrow(new SecurityException())
+ .when(mContextMock)
+ .enforceCallingOrSelfPermission(eq(Manifest.permission.BACKUP), anyString());
+
+ try {
+ mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+ fail();
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void setBackupServiceActive_forNonSystemUserAndCallerWithoutUserPermission_throws() {
+ doThrow(new SecurityException())
+ .when(mContextMock)
+ .enforceCallingOrSelfPermission(
+ eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL), anyString());
+
+ try {
+ mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+ fail();
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void setBackupServiceActive_backupDisabled_ignored() {
+ BackupManagerServiceTestable.sBackupDisabled = true;
+ BackupManagerServiceTestable service =
+ new BackupManagerServiceTestable(mContextMock, mUserServices);
+
+ service.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+ assertFalse(service.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void setBackupServiceActive_alreadyActive_ignored() {
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+ assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void setBackupServiceActive_makeNonActive_alreadyNonActive_ignored() {
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+
+ assertFalse(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void setBackupServiceActive_makeActive_serviceCreatedAndSuppressFileDeleted() {
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+ assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated()
+ throws IOException {
+ assertTrue(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+
+ assertFalse(mService.isBackupServiceActive(UserHandle.USER_SYSTEM));
+ }
+
+ @Test
+ public void setBackupActive_nonSystemUser_disabledForSystemUser_ignored() {
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
+ mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertFalse(mService.isBackupServiceActive(NON_USER_SYSTEM));
+ }
+
+ @Test
+ public void setBackupServiceActive_forOneNonSystemUser_doesNotActivateForAllNonSystemUsers() {
+ int otherUser = NON_USER_SYSTEM + 1;
+ File activateFile = new File(mTestDir, "activate-" + otherUser);
+ BackupManagerServiceTestable.sActivatedFiles.append(otherUser, activateFile);
+ mService.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
+
+ mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertTrue(mService.isBackupServiceActive(NON_USER_SYSTEM));
+ assertFalse(mService.isBackupServiceActive(otherUser));
+ activateFile.delete();
+ }
+
+ @Test
+ public void setBackupServiceActive_forNonSystemUser_remembersActivated() {
+
+ mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+
+ assertTrue(RandomAccessFileUtils.readBoolean(
+ BackupManagerServiceTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), false));
+ }
+
+ @Test
+ public void setBackupServiceActiveFalse_forNonSystemUser_remembersActivated() {
+
+ mService.setBackupServiceActive(NON_USER_SYSTEM, false);
+
+ assertFalse(RandomAccessFileUtils.readBoolean(
+ BackupManagerServiceTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true));
+ }
+
+ @Test
+ public void setBackupServiceActiveTwice_forNonSystemUser_remembersLastActivated() {
+ mService.setBackupServiceActive(NON_USER_SYSTEM, true);
+ mService.setBackupServiceActive(NON_USER_SYSTEM, false);
+
+ assertFalse(RandomAccessFileUtils.readBoolean(
+ BackupManagerServiceTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true));
+ }
+
+ @Test
+ public void selectBackupTransportAsyncForUser_beforeUserUnlocked_notifiesBackupNotAllowed()
+ throws Exception {
+ mUserServices.clear();
+ CompletableFuture<Integer> future = new CompletableFuture<>();
+ ISelectBackupTransportCallback listener =
+ new ISelectBackupTransportCallback.Stub() {
+ @Override
+ public void onSuccess(String transportName) {
+ future.completeExceptionally(new AssertionError());
+ }
+ @Override
+ public void onFailure(int reason) {
+ future.complete(reason);
+ }
+ };
+
+ mService.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, listener);
+
+ assertEquals(BackupManager.ERROR_BACKUP_NOT_ALLOWED, (int) future.get(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void selectBackupTransportAsyncForUser_beforeUserUnlockedWithNullListener_doesNotThrow()
+ throws Exception {
+ mService.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
+
+ // No crash.
+ }
+
+ @Test
+ public void
+ selectBackupTransportAsyncForUser_beforeUserUnlockedListenerThrowing_doesNotThrow()
+ throws Exception {
+ ISelectBackupTransportCallback.Stub listener =
+ new ISelectBackupTransportCallback.Stub() {
+ @Override
+ public void onSuccess(String transportName) {}
+ @Override
+ public void onFailure(int reason) throws RemoteException {
+ throw new RemoteException();
+ }
+ };
+
+ mService.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, listener);
+
+ // No crash.
+ }
+
+ @Test
+ public void dump_callerDoesNotHavePermission_ignored() {
+ when(mContextMock.checkCallingOrSelfPermission(
+ android.Manifest.permission.DUMP)).thenReturn(
+ PackageManager.PERMISSION_DENIED);
+
+ mService.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);
+
+ verifyNoMoreInteractions(mUserBackupManagerService);
+ }
+
+ public void testGetUserForAncestralSerialNumber() {
+ BackupManagerServiceTestable.sBackupDisabled = false;
+ BackupManagerService backupManagerService =
+ new BackupManagerServiceTestable(mContextMock, mUserServices);
+ when(mUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L);
+
+ UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L);
+
+ assertThat(user).isEqualTo(UserHandle.of(1));
+ }
+
+ public void testGetUserForAncestralSerialNumber_whenDisabled() {
+ BackupManagerServiceTestable.sBackupDisabled = true;
+ BackupManagerService backupManagerService =
+ new BackupManagerServiceTestable(mContextMock, mUserServices);
+ when(mUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L);
+
+ UserHandle user = backupManagerService.getUserForAncestralSerialNumber(11L);
+
+ assertThat(user).isNull();
+ }
+
+ private static class BackupManagerServiceTestable extends BackupManagerService {
+ static boolean sBackupDisabled = false;
+ static int sCallingUserId = -1;
+ static int sCallingUid = -1;
+ static File sSuppressFile = null;
+ static SparseArray<File> sActivatedFiles = new SparseArray<>();
+ static SparseArray<File> sRememberActivatedFiles = new SparseArray<>();
+ static UserManager sUserManagerMock = null;
+
+ BackupManagerServiceTestable(
+ Context context, SparseArray<UserBackupManagerService> userServices) {
+ super(context, userServices);
+ }
+
+ @Override
+ protected UserManager getUserManager() {
+ return sUserManagerMock;
+ }
+
+ @Override
+ protected boolean isBackupDisabled() {
+ return sBackupDisabled;
+ }
+
+ @Override
+ protected File getSuppressFileForSystemUser() {
+ return sSuppressFile;
+ }
+
+ @Override
+ protected File getRememberActivatedFileForNonSystemUser(int userId) {
+ return sRememberActivatedFiles.get(userId);
+ }
+
+ @Override
+ protected File getActivatedFileForNonSystemUser(int userId) {
+ return sActivatedFiles.get(userId);
+ }
+
+ protected int binderGetCallingUserId() {
+ return sCallingUserId;
+ }
+
+ @Override
+ protected int binderGetCallingUid() {
+ return sCallingUid;
+ }
+
+ @Override
+ protected void postToHandler(Runnable runnable) {
+ runnable.run();
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java b/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
deleted file mode 100644
index e95c578..0000000
--- a/services/tests/servicestests/src/com/android/server/backup/TrampolineTest.java
+++ /dev/null
@@ -1,860 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.backup;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static junit.framework.Assert.fail;
-
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
-import static org.mockito.Mockito.when;
-
-import android.Manifest;
-import android.annotation.UserIdInt;
-import android.app.backup.BackupManager;
-import android.app.backup.IBackupManagerMonitor;
-import android.app.backup.IBackupObserver;
-import android.app.backup.IFullBackupRestoreObserver;
-import android.app.backup.ISelectBackupTransportCallback;
-import android.app.job.JobScheduler;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.os.ConditionVariable;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.platform.test.annotations.Presubmit;
-import android.util.SparseArray;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.server.backup.utils.RandomAccessFileUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
-
-@SmallTest
-@Presubmit
-@RunWith(AndroidJUnit4.class)
-public class TrampolineTest {
- private static final String PACKAGE_NAME = "some.package.name";
- private static final String TRANSPORT_NAME = "some.transport.name";
- private static final String CURRENT_PASSWORD = "current_password";
- private static final String NEW_PASSWORD = "new_password";
- private static final String ENCRYPTION_PASSWORD = "encryption_password";
- private static final CharSequence DATA_MANAGEMENT_LABEL = "data_management_label";
- private static final String DESTINATION_STRING = "destination_string";
- private static final String[] PACKAGE_NAMES =
- new String[]{"some.package.name._1", "some.package.name._2"};
- private static final String[] TRANSPORTS =
- new String[]{"some.transport.name._1", "some.transport.name._2"};
- private static final ComponentName TRANSPORT_COMPONENT_NAME = new ComponentName("package",
- "class");
- private static final ComponentName[] TRANSPORT_COMPONENTS = new ComponentName[]{
- new ComponentName("package1", "class1"),
- new ComponentName("package2", "class2")
- };
- private static final int NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 1;
- private static final int UNSTARTED_NON_USER_SYSTEM = UserHandle.USER_SYSTEM + 2;
-
- @UserIdInt
- private int mUserId;
- @Mock
- private BackupManagerService mBackupManagerServiceMock;
- @Mock
- private UserBackupManagerService mUserBackupManagerService;
- @Mock
- private Context mContextMock;
- @Mock
- private IBinder mAgentMock;
- @Mock
- private ParcelFileDescriptor mParcelFileDescriptorMock;
- @Mock
- private IFullBackupRestoreObserver mFullBackupRestoreObserverMock;
- @Mock
- private IBackupObserver mBackupObserverMock;
- @Mock
- private IBackupManagerMonitor mBackupManagerMonitorMock;
- @Mock
- private PrintWriter mPrintWriterMock;
- @Mock
- private UserManager mUserManagerMock;
- @Mock
- private UserInfo mUserInfoMock;
-
- private FileDescriptor mFileDescriptorStub = new FileDescriptor();
-
- private TrampolineTestable mTrampoline;
- private File mTestDir;
- private File mSuppressFile;
- private SparseArray<UserBackupManagerService> mUserServices;
-
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- mUserId = UserHandle.USER_SYSTEM;
-
- mUserServices = new SparseArray<>();
- mUserServices.append(UserHandle.USER_SYSTEM, mUserBackupManagerService);
- mUserServices.append(NON_USER_SYSTEM, mUserBackupManagerService);
-
- when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock);
- when(mUserManagerMock.getUserInfo(NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
- when(mUserManagerMock.getUserInfo(UNSTARTED_NON_USER_SYSTEM)).thenReturn(mUserInfoMock);
-
- TrampolineTestable.sBackupManagerServiceMock = mBackupManagerServiceMock;
- TrampolineTestable.sCallingUserId = UserHandle.USER_SYSTEM;
- TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
- TrampolineTestable.sBackupDisabled = false;
- TrampolineTestable.sUserManagerMock = mUserManagerMock;
-
- mTestDir = InstrumentationRegistry.getContext().getFilesDir();
- mTestDir.mkdirs();
-
- mSuppressFile = new File(mTestDir, "suppress");
- TrampolineTestable.sSuppressFile = mSuppressFile;
-
- setUpStateFilesForNonSystemUser(NON_USER_SYSTEM);
- setUpStateFilesForNonSystemUser(UNSTARTED_NON_USER_SYSTEM);
-
- when(mContextMock.getSystemService(Context.JOB_SCHEDULER_SERVICE))
- .thenReturn(mock(JobScheduler.class));
- mTrampoline = new TrampolineTestable(mContextMock, mUserServices);
- }
-
- private void setUpStateFilesForNonSystemUser(int userId) {
- File activatedFile = new File(mTestDir, "activate-" + userId);
- TrampolineTestable.sActivatedFiles.append(userId, activatedFile);
- File rememberActivatedFile = new File(mTestDir, "rem-activate-" + userId);
- TrampolineTestable.sRememberActivatedFiles.append(userId, rememberActivatedFile);
- }
-
- @After
- public void tearDown() throws Exception {
- mSuppressFile.delete();
- deleteFiles(TrampolineTestable.sActivatedFiles);
- deleteFiles(TrampolineTestable.sRememberActivatedFiles);
- }
-
- private void deleteFiles(SparseArray<File> files) {
- int numFiles = files.size();
- for (int i = 0; i < numFiles; i++) {
- files.valueAt(i).delete();
- }
- }
-
- @Test
- public void testIsBackupServiceActive_whenBackupsNotDisabledAndSuppressFileDoesNotExist() {
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
- }
-
- @Test
- public void testOnUnlockUser_forNonSystemUserWhenBackupsDisabled_doesNotStartUser() {
- TrampolineTestable.sBackupDisabled = true;
- TrampolineTestable trampoline = new TrampolineTestable(mContextMock, new SparseArray<>());
- ConditionVariable unlocked = new ConditionVariable(false);
-
- trampoline.onUnlockUser(NON_USER_SYSTEM);
-
- trampoline.getBackupHandler().post(unlocked::open);
- unlocked.block();
- assertNull(trampoline.getUserService(NON_USER_SYSTEM));
- }
-
- @Test
- public void testOnUnlockUser_forSystemUserWhenBackupsDisabled_doesNotStartUser() {
- TrampolineTestable.sBackupDisabled = true;
- TrampolineTestable trampoline = new TrampolineTestable(mContextMock, new SparseArray<>());
- ConditionVariable unlocked = new ConditionVariable(false);
-
- trampoline.onUnlockUser(UserHandle.USER_SYSTEM);
-
- trampoline.getBackupHandler().post(unlocked::open);
- unlocked.block();
- assertNull(trampoline.getUserService(UserHandle.USER_SYSTEM));
- }
-
- @Test
- public void testOnUnlockUser_whenBackupNotActivated_doesNotStartUser() {
- TrampolineTestable.sBackupDisabled = false;
- TrampolineTestable trampoline = new TrampolineTestable(mContextMock, new SparseArray<>());
- trampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
- ConditionVariable unlocked = new ConditionVariable(false);
-
- trampoline.onUnlockUser(NON_USER_SYSTEM);
-
- trampoline.getBackupHandler().post(unlocked::open);
- unlocked.block();
- assertNull(trampoline.getUserService(NON_USER_SYSTEM));
- }
-
- @Test
- public void testIsBackupServiceActive_forSystemUserWhenBackupDisabled_returnsTrue()
- throws Exception {
- TrampolineTestable.sBackupDisabled = true;
- Trampoline trampoline = new TrampolineTestable(mContextMock, mUserServices);
- trampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
- assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
- }
-
- @Test
- public void testIsBackupServiceActive_forNonSystemUserWhenBackupDisabled_returnsTrue()
- throws Exception {
- TrampolineTestable.sBackupDisabled = true;
- Trampoline trampoline = new TrampolineTestable(mContextMock, mUserServices);
- trampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
- assertFalse(trampoline.isBackupServiceActive(NON_USER_SYSTEM));
- }
-
- @Test
- public void isBackupServiceActive_forSystemUser_returnsTrueWhenActivated() throws Exception {
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
- }
-
- @Test
- public void isBackupServiceActive_forSystemUser_returnsFalseWhenDeactivated() throws Exception {
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
-
- assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
- }
-
- @Test
- public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenSystemUserDeactivated()
- throws Exception {
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
- assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
- }
-
- @Test
- public void isBackupServiceActive_forNonSystemUser_returnsFalseWhenNonSystemUserDeactivated()
- throws Exception {
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
- // Don't activate non-system user.
-
- assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
- }
-
- @Test
- public void
- isBackupServiceActive_forNonSystemUser_returnsTrueWhenSystemAndNonSystemUserActivated()
- throws Exception {
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
- assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
- }
-
- @Test
- public void
- isBackupServiceActive_forUnstartedNonSystemUser_returnsTrueWhenSystemAndUserActivated()
- throws Exception {
- mTrampoline.setBackupServiceActive(UNSTARTED_NON_USER_SYSTEM, true);
-
- assertTrue(mTrampoline.isBackupServiceActive(UNSTARTED_NON_USER_SYSTEM));
- }
-
- @Test
- public void setBackupServiceActive_forSystemUserAndCallerSystemUid_serviceCreated() {
- TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
-
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
- }
-
- @Test
- public void setBackupServiceActive_forSystemUserAndCallerRootUid_serviceCreated() {
- TrampolineTestable.sCallingUid = Process.ROOT_UID;
-
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
- }
-
- @Test
- public void setBackupServiceActive_forSystemUserAndCallerNonRootNonSystem_throws() {
- TrampolineTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
-
- try {
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
- fail();
- } catch (SecurityException expected) {
- }
- }
-
- @Test
- public void setBackupServiceActive_forManagedProfileAndCallerSystemUid_serviceCreated() {
- when(mUserInfoMock.isManagedProfile()).thenReturn(true);
- TrampolineTestable.sCallingUid = Process.SYSTEM_UID;
-
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
- assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
- }
-
- @Test
- public void setBackupServiceActive_forManagedProfileAndCallerRootUid_serviceCreated() {
- when(mUserInfoMock.isManagedProfile()).thenReturn(true);
- TrampolineTestable.sCallingUid = Process.ROOT_UID;
-
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
- assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
- }
-
- @Test
- public void setBackupServiceActive_forManagedProfileAndCallerNonRootNonSystem_throws() {
- when(mUserInfoMock.isManagedProfile()).thenReturn(true);
- TrampolineTestable.sCallingUid = Process.FIRST_APPLICATION_UID;
-
- try {
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
- fail();
- } catch (SecurityException expected) {
- }
- }
-
- @Test
- public void setBackupServiceActive_forNonSystemUserAndCallerWithoutBackupPermission_throws() {
- doThrow(new SecurityException())
- .when(mContextMock)
- .enforceCallingOrSelfPermission(eq(Manifest.permission.BACKUP), anyString());
-
- try {
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
- fail();
- } catch (SecurityException expected) {
- }
- }
-
- @Test
- public void setBackupServiceActive_forNonSystemUserAndCallerWithoutUserPermission_throws() {
- doThrow(new SecurityException())
- .when(mContextMock)
- .enforceCallingOrSelfPermission(
- eq(Manifest.permission.INTERACT_ACROSS_USERS_FULL), anyString());
-
- try {
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
- fail();
- } catch (SecurityException expected) {
- }
- }
-
- @Test
- public void setBackupServiceActive_backupDisabled_ignored() {
- TrampolineTestable.sBackupDisabled = true;
- TrampolineTestable trampoline = new TrampolineTestable(mContextMock, mUserServices);
-
- trampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
- assertFalse(trampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
- }
-
- @Test
- public void setBackupServiceActive_alreadyActive_ignored() {
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
- }
-
- @Test
- public void setBackupServiceActive_makeNonActive_alreadyNonActive_ignored() {
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
-
- assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
- }
-
- @Test
- public void setBackupServiceActive_makeActive_serviceCreatedAndSuppressFileDeleted() {
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
- }
-
- @Test
- public void setBackupServiceActive_makeNonActive_serviceDeletedAndSuppressFileCreated()
- throws IOException {
- assertTrue(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
-
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
-
- assertFalse(mTrampoline.isBackupServiceActive(UserHandle.USER_SYSTEM));
- }
-
- @Test
- public void setBackupActive_nonSystemUser_disabledForSystemUser_ignored() {
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, false);
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
- assertFalse(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
- }
-
- @Test
- public void setBackupServiceActive_forOneNonSystemUser_doesNotActivateForAllNonSystemUsers() {
- int otherUser = NON_USER_SYSTEM + 1;
- File activateFile = new File(mTestDir, "activate-" + otherUser);
- TrampolineTestable.sActivatedFiles.append(otherUser, activateFile);
- mTrampoline.setBackupServiceActive(UserHandle.USER_SYSTEM, true);
-
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
- assertTrue(mTrampoline.isBackupServiceActive(NON_USER_SYSTEM));
- assertFalse(mTrampoline.isBackupServiceActive(otherUser));
- activateFile.delete();
- }
-
- @Test
- public void setBackupServiceActive_forNonSystemUser_remembersActivated() {
-
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
-
- assertTrue(RandomAccessFileUtils.readBoolean(
- TrampolineTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), false));
- }
-
- @Test
- public void setBackupServiceActiveFalse_forNonSystemUser_remembersActivated() {
-
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
-
- assertFalse(RandomAccessFileUtils.readBoolean(
- TrampolineTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true));
- }
-
- @Test
- public void setBackupServiceActiveTwice_forNonSystemUser_remembersLastActivated() {
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, true);
- mTrampoline.setBackupServiceActive(NON_USER_SYSTEM, false);
-
- assertFalse(RandomAccessFileUtils.readBoolean(
- TrampolineTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true));
- }
-
- @Test
- public void restoreAtInstallForUser_forwarded() throws Exception {
-
- mTrampoline.restoreAtInstallForUser(mUserId, PACKAGE_NAME, 123);
-
- verify(mBackupManagerServiceMock).restoreAtInstall(mUserId, PACKAGE_NAME, 123);
- }
-
- @Test
- public void restoreAtInstall_forwarded() throws Exception {
- TrampolineTestable.sCallingUserId = mUserId;
-
- mTrampoline.restoreAtInstall(PACKAGE_NAME, 123);
-
- verify(mBackupManagerServiceMock).restoreAtInstall(mUserId, PACKAGE_NAME, 123);
- }
-
- @Test
- public void setBackupEnabledForUser_forwarded() throws Exception {
-
- mTrampoline.setBackupEnabledForUser(mUserId, true);
-
- verify(mBackupManagerServiceMock).setBackupEnabled(mUserId, true);
- }
-
- @Test
- public void setBackupEnabled_forwardedToCallingUserId() throws Exception {
- TrampolineTestable.sCallingUserId = mUserId;
-
- mTrampoline.setBackupEnabled(true);
-
- verify(mBackupManagerServiceMock).setBackupEnabled(mUserId, true);
- }
-
- @Test
- public void setAutoRestoreForUser_forwarded() throws Exception {
-
- mTrampoline.setAutoRestoreForUser(mUserId, true);
-
- verify(mBackupManagerServiceMock).setAutoRestore(mUserId, true);
- }
-
- @Test
- public void setAutoRestore_forwarded() throws Exception {
- TrampolineTestable.sCallingUserId = mUserId;
-
- mTrampoline.setAutoRestore(true);
-
- verify(mBackupManagerServiceMock).setAutoRestore(mUserId, true);
- }
-
- @Test
- public void isBackupEnabledForUser_forwarded() throws Exception {
-
- mTrampoline.isBackupEnabledForUser(mUserId);
-
- verify(mBackupManagerServiceMock).isBackupEnabled(mUserId);
- }
-
- @Test
- public void isBackupEnabled_forwardedToCallingUserId() throws Exception {
- TrampolineTestable.sCallingUserId = mUserId;
-
- mTrampoline.isBackupEnabled();
-
- verify(mBackupManagerServiceMock).isBackupEnabled(mUserId);
- }
-
- @Test
- public void setBackupPassword_forwarded() throws Exception {
- mTrampoline.setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD);
- verify(mBackupManagerServiceMock).setBackupPassword(CURRENT_PASSWORD, NEW_PASSWORD);
- }
-
- @Test
- public void hasBackupPassword_forwarded() throws Exception {
- mTrampoline.hasBackupPassword();
- verify(mBackupManagerServiceMock).hasBackupPassword();
- }
-
- @Test
- public void backupNowForUser_forwarded() throws Exception {
-
- mTrampoline.backupNowForUser(mUserId);
-
- verify(mBackupManagerServiceMock).backupNow(mUserId);
- }
-
- @Test
- public void backupNow_forwardedToCallingUserId() throws Exception {
- TrampolineTestable.sCallingUserId = mUserId;
-
- mTrampoline.backupNow();
-
- verify(mBackupManagerServiceMock).backupNow(mUserId);
- }
-
- @Test
- public void adbBackup_forwarded() throws Exception {
- mTrampoline.adbBackup(mUserId, mParcelFileDescriptorMock, true, true,
- true, true, true, true, true, true,
- PACKAGE_NAMES);
- verify(mBackupManagerServiceMock).adbBackup(mUserId, mParcelFileDescriptorMock, true,
- true, true, true, true, true, true, true, PACKAGE_NAMES);
- }
-
- @Test
- public void fullTransportBackupForUser_forwarded() throws Exception {
-
- mTrampoline.fullTransportBackupForUser(mUserId, PACKAGE_NAMES);
-
- verify(mBackupManagerServiceMock).fullTransportBackup(mUserId, PACKAGE_NAMES);
- }
-
- @Test
- public void adbRestore_forwarded() throws Exception {
- mTrampoline.adbRestore(mUserId, mParcelFileDescriptorMock);
- verify(mBackupManagerServiceMock).adbRestore(mUserId, mParcelFileDescriptorMock);
- }
-
- @Test
- public void acknowledgeFullBackupOrRestoreForUser_forwarded() throws Exception {
-
- mTrampoline.acknowledgeFullBackupOrRestoreForUser(
- mUserId,
- 123,
- true,
- CURRENT_PASSWORD,
- ENCRYPTION_PASSWORD,
- mFullBackupRestoreObserverMock);
-
- verify(mBackupManagerServiceMock)
- .acknowledgeAdbBackupOrRestore(
- mUserId,
- 123,
- true,
- CURRENT_PASSWORD,
- ENCRYPTION_PASSWORD,
- mFullBackupRestoreObserverMock);
- }
-
- @Test
- public void acknowledgeFullBackupOrRestore_forwarded() throws Exception {
- TrampolineTestable.sCallingUserId = mUserId;
-
- mTrampoline.acknowledgeFullBackupOrRestore(123, true, CURRENT_PASSWORD, ENCRYPTION_PASSWORD,
- mFullBackupRestoreObserverMock);
-
- verify(mBackupManagerServiceMock)
- .acknowledgeAdbBackupOrRestore(
- mUserId,
- 123,
- true,
- CURRENT_PASSWORD,
- ENCRYPTION_PASSWORD,
- mFullBackupRestoreObserverMock);
- }
-
- @Test
- public void selectBackupTransportAsyncForUser_beforeUserUnlocked_notifiesBackupNotAllowed()
- throws Exception {
- mUserServices.clear();
- CompletableFuture<Integer> future = new CompletableFuture<>();
- ISelectBackupTransportCallback listener =
- new ISelectBackupTransportCallback.Stub() {
- @Override
- public void onSuccess(String transportName) {
- future.completeExceptionally(new AssertionError());
- }
- @Override
- public void onFailure(int reason) {
- future.complete(reason);
- }
- };
-
- mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, listener);
-
- assertEquals(BackupManager.ERROR_BACKUP_NOT_ALLOWED, (int) future.get(5, TimeUnit.SECONDS));
- }
-
- @Test
- public void selectBackupTransportAsyncForUser_beforeUserUnlockedWithNullListener_doesNotThrow()
- throws Exception {
- mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, null);
-
- // No crash.
- }
-
- @Test
- public void
- selectBackupTransportAsyncForUser_beforeUserUnlockedWithThrowingListener_doesNotThrow()
- throws Exception {
- ISelectBackupTransportCallback.Stub listener =
- new ISelectBackupTransportCallback.Stub() {
- @Override
- public void onSuccess(String transportName) {}
- @Override
- public void onFailure(int reason) throws RemoteException {
- throw new RemoteException();
- }
- };
-
- mTrampoline.selectBackupTransportAsyncForUser(mUserId, TRANSPORT_COMPONENT_NAME, listener);
-
- // No crash.
- }
-
- @Test
- public void getAvailableRestoreTokenForUser_forwarded() {
- when(mBackupManagerServiceMock.getAvailableRestoreToken(mUserId, PACKAGE_NAME))
- .thenReturn(123L);
-
- assertEquals(123, mTrampoline.getAvailableRestoreTokenForUser(mUserId, PACKAGE_NAME));
- verify(mBackupManagerServiceMock).getAvailableRestoreToken(mUserId, PACKAGE_NAME);
- }
-
- @Test
- public void isAppEligibleForBackupForUser_forwarded() {
- when(mBackupManagerServiceMock.isAppEligibleForBackup(mUserId, PACKAGE_NAME))
- .thenReturn(true);
-
- assertTrue(mTrampoline.isAppEligibleForBackupForUser(mUserId, PACKAGE_NAME));
- verify(mBackupManagerServiceMock).isAppEligibleForBackup(mUserId, PACKAGE_NAME);
- }
-
- @Test
- public void requestBackupForUser_forwarded() throws Exception {
- when(mBackupManagerServiceMock.requestBackup(mUserId, PACKAGE_NAMES,
- mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
-
- assertEquals(456, mTrampoline.requestBackupForUser(mUserId, PACKAGE_NAMES,
- mBackupObserverMock, mBackupManagerMonitorMock, 123));
- verify(mBackupManagerServiceMock).requestBackup(mUserId, PACKAGE_NAMES,
- mBackupObserverMock, mBackupManagerMonitorMock, 123);
- }
-
- @Test
- public void requestBackup_forwardedToCallingUserId() throws Exception {
- TrampolineTestable.sCallingUserId = mUserId;
- when(mBackupManagerServiceMock.requestBackup(mUserId, PACKAGE_NAMES,
- mBackupObserverMock, mBackupManagerMonitorMock, 123)).thenReturn(456);
-
- assertEquals(456, mTrampoline.requestBackup(PACKAGE_NAMES,
- mBackupObserverMock, mBackupManagerMonitorMock, 123));
- verify(mBackupManagerServiceMock).requestBackup(mUserId, PACKAGE_NAMES,
- mBackupObserverMock, mBackupManagerMonitorMock, 123);
- }
-
- @Test
- public void cancelBackupsForUser_forwarded() throws Exception {
-
- mTrampoline.cancelBackupsForUser(mUserId);
-
- verify(mBackupManagerServiceMock).cancelBackups(mUserId);
- }
-
- @Test
- public void cancelBackups_forwardedToCallingUserId() throws Exception {
- TrampolineTestable.sCallingUserId = mUserId;
-
- mTrampoline.cancelBackups();
-
- verify(mBackupManagerServiceMock).cancelBackups(mUserId);
- }
-
- @Test
- public void beginFullBackup_forwarded() throws Exception {
- FullBackupJob fullBackupJob = new FullBackupJob();
- when(mBackupManagerServiceMock.beginFullBackup(mUserId, fullBackupJob)).thenReturn(true);
-
- assertTrue(mTrampoline.beginFullBackup(mUserId, fullBackupJob));
- verify(mBackupManagerServiceMock).beginFullBackup(mUserId, fullBackupJob);
- }
-
- @Test
- public void endFullBackup_forwarded() {
- mTrampoline.endFullBackup(mUserId);
- verify(mBackupManagerServiceMock).endFullBackup(mUserId);
- }
-
- @Test
- public void dump_callerDoesNotHavePermission_ignored() {
- when(mContextMock.checkCallingOrSelfPermission(
- android.Manifest.permission.DUMP)).thenReturn(
- PackageManager.PERMISSION_DENIED);
-
- mTrampoline.dump(mFileDescriptorStub, mPrintWriterMock, new String[0]);
-
- verifyNoMoreInteractions(mBackupManagerServiceMock);
- }
-
- @Test
- public void dump_callerHasPermission_forwarded() {
- when(mContextMock.checkCallingOrSelfPermission(
- android.Manifest.permission.DUMP)).thenReturn(
- PackageManager.PERMISSION_GRANTED);
-
- mTrampoline.dump(mFileDescriptorStub, mPrintWriterMock, null);
-
- verify(mBackupManagerServiceMock).dump(mFileDescriptorStub, mPrintWriterMock, null);
- }
-
- public void testGetUserForAncestralSerialNumber() {
- TrampolineTestable.sBackupDisabled = false;
- Trampoline trampoline = new TrampolineTestable(mContextMock, mUserServices);
- when(mUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L);
-
- UserHandle user = trampoline.getUserForAncestralSerialNumber(11L);
-
- assertThat(user).isEqualTo(UserHandle.of(1));
- }
-
- public void testGetUserForAncestralSerialNumber_whenDisabled() {
- TrampolineTestable.sBackupDisabled = true;
- Trampoline trampoline = new TrampolineTestable(mContextMock, mUserServices);
- when(mUserBackupManagerService.getAncestralSerialNumber()).thenReturn(11L);
-
- UserHandle user = trampoline.getUserForAncestralSerialNumber(11L);
-
- assertThat(user).isNull();
- }
-
- private static class TrampolineTestable extends Trampoline {
- static boolean sBackupDisabled = false;
- static int sCallingUserId = -1;
- static int sCallingUid = -1;
- static BackupManagerService sBackupManagerServiceMock = null;
- static File sSuppressFile = null;
- static SparseArray<File> sActivatedFiles = new SparseArray<>();
- static SparseArray<File> sRememberActivatedFiles = new SparseArray<>();
- static UserManager sUserManagerMock = null;
-
- TrampolineTestable(Context context, SparseArray<UserBackupManagerService> userServices) {
- super(context, userServices);
- mService = sBackupManagerServiceMock;
- }
-
- @Override
- protected UserManager getUserManager() {
- return sUserManagerMock;
- }
-
- @Override
- protected boolean isBackupDisabled() {
- return sBackupDisabled;
- }
-
- @Override
- protected File getSuppressFileForSystemUser() {
- return sSuppressFile;
- }
-
- @Override
- protected File getRememberActivatedFileForNonSystemUser(int userId) {
- return sRememberActivatedFiles.get(userId);
- }
-
- @Override
- protected File getActivatedFileForNonSystemUser(int userId) {
- return sActivatedFiles.get(userId);
- }
-
- protected int binderGetCallingUserId() {
- return sCallingUserId;
- }
-
- @Override
- protected int binderGetCallingUid() {
- return sCallingUid;
- }
-
- @Override
- protected void postToHandler(Runnable runnable) {
- runnable.run();
- }
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
new file mode 100644
index 0000000..ccf3a90
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java
@@ -0,0 +1,748 @@
+/*
+ * 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.biometrics;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.AppOpsManager;
+import android.app.IActivityManager;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricPrompt;
+import android.hardware.biometrics.IBiometricService;
+import android.hardware.biometrics.IBiometricServiceReceiver;
+import android.hardware.biometrics.IBiometricServiceReceiverInternal;
+import android.hardware.face.FaceManager;
+import android.hardware.face.IFaceService;
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.IFingerprintService;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.security.KeyStore;
+
+import com.android.internal.R;
+import com.android.internal.statusbar.IStatusBarService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+@SmallTest
+public class BiometricServiceTest {
+
+ private static final String TAG = "BiometricServiceTest";
+
+ private static final String TEST_PACKAGE_NAME = "test_package";
+
+ private static final String ERROR_HW_UNAVAILABLE = "hw_unavailable";
+ private static final String ERROR_NOT_RECOGNIZED = "not_recognized";
+ private static final String ERROR_TIMEOUT = "error_timeout";
+ private static final String ERROR_CANCELED = "error_canceled";
+ private static final String ERROR_UNABLE_TO_PROCESS = "error_unable_to_process";
+ private static final String ERROR_USER_CANCELED = "error_user_canceled";
+
+ private static final String FINGERPRINT_ACQUIRED_SENSOR_DIRTY = "sensor_dirty";
+
+ private BiometricService mBiometricService;
+
+ @Mock
+ private Context mContext;
+ @Mock
+ private ContentResolver mContentResolver;
+ @Mock
+ private Resources mResources;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private AppOpsManager mAppOpsManager;
+ @Mock
+ IBiometricServiceReceiver mReceiver1;
+ @Mock
+ IBiometricServiceReceiver mReceiver2;
+ @Mock
+ FingerprintManager mFingerprintManager;
+ @Mock
+ FaceManager mFaceManager;
+
+ private static class MockInjector extends BiometricService.Injector {
+ @Override
+ IActivityManager getActivityManagerService() {
+ return mock(IActivityManager.class);
+ }
+
+ @Override
+ IStatusBarService getStatusBarService() {
+ return mock(IStatusBarService.class);
+ }
+
+ @Override
+ IFingerprintService getFingerprintService() {
+ return mock(IFingerprintService.class);
+ }
+
+ @Override
+ IFaceService getFaceService() {
+ return mock(IFaceService.class);
+ }
+
+ @Override
+ BiometricService.SettingObserver getSettingObserver(Context context, Handler handler,
+ List<BiometricService.EnabledOnKeyguardCallback> callbacks) {
+ return mock(BiometricService.SettingObserver.class);
+ }
+
+ @Override
+ KeyStore getKeyStore() {
+ return mock(KeyStore.class);
+ }
+
+ @Override
+ boolean isDebugEnabled(Context context, int userId) {
+ return false;
+ }
+
+ @Override
+ void publishBinderService(BiometricService service, IBiometricService.Stub impl) {
+ // no-op for test
+ }
+ }
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mContext.getPackageManager()).thenReturn(mPackageManager);
+ when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
+ when(mContext.getSystemService(Context.FINGERPRINT_SERVICE))
+ .thenReturn(mFingerprintManager);
+ when(mContext.getSystemService(Context.FACE_SERVICE)).thenReturn(mFaceManager);
+ when(mContext.getContentResolver()).thenReturn(mContentResolver);
+ when(mContext.getResources()).thenReturn(mResources);
+
+ when(mResources.getString(R.string.biometric_error_hw_unavailable))
+ .thenReturn(ERROR_HW_UNAVAILABLE);
+ when(mResources.getString(R.string.biometric_not_recognized))
+ .thenReturn(ERROR_NOT_RECOGNIZED);
+ when(mResources.getString(R.string.biometric_error_user_canceled))
+ .thenReturn(ERROR_USER_CANCELED);
+ }
+
+ @Test
+ public void testAuthenticate_withoutHardware_returnsErrorHardwareNotPresent() throws Exception {
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
+ .thenReturn(false);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_IRIS)).thenReturn(false);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false);
+
+ mBiometricService = new BiometricService(mContext, new MockInjector());
+ mBiometricService.onStart();
+
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ waitForIdle();
+ verify(mReceiver1).onError(
+ eq(BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT), eq(ERROR_HW_UNAVAILABLE));
+ }
+
+ @Test
+ public void testAuthenticate_withoutEnrolled_returnsErrorNoBiometrics() throws Exception {
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+
+ mBiometricService = new BiometricService(mContext, new MockInjector());
+ mBiometricService.onStart();
+
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ waitForIdle();
+ verify(mReceiver1).onError(
+ eq(BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS), any());
+ }
+
+ @Test
+ public void testAuthenticate_whenHalIsDead_returnsErrorHardwareUnavailable() throws Exception {
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
+ when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(false);
+
+ mBiometricService = new BiometricService(mContext, new MockInjector());
+ mBiometricService.onStart();
+
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ waitForIdle();
+ verify(mReceiver1).onError(
+ eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(ERROR_HW_UNAVAILABLE));
+ }
+
+ @Test
+ public void testAuthenticateFace_respectsUserSetting()
+ throws Exception {
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+
+ mBiometricService = new BiometricService(mContext, new MockInjector());
+ mBiometricService.onStart();
+
+ // Disabled in user settings receives onError
+ when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(false);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ waitForIdle();
+ verify(mReceiver1).onError(
+ eq(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE), eq(ERROR_HW_UNAVAILABLE));
+
+ // Enrolled, not disabled in settings, user requires confirmation in settings
+ resetReceiver();
+ when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
+ when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
+ .thenReturn(true);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ waitForIdle();
+ verify(mReceiver1, never()).onError(anyInt(), any(String.class));
+ verify(mBiometricService.mFaceService).prepareForAuthentication(
+ eq(true) /* requireConfirmation */,
+ any(IBinder.class),
+ anyLong() /* sessionId */,
+ anyInt() /* userId */,
+ any(IBiometricServiceReceiverInternal.class),
+ anyString() /* opPackageName */,
+ anyInt() /* cookie */,
+ anyInt() /* callingUid */,
+ anyInt() /* callingPid */,
+ anyInt() /* callingUserId */);
+
+ // Enrolled, not disabled in settings, user doesn't require confirmation in settings
+ resetReceiver();
+ when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
+ .thenReturn(false);
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ waitForIdle();
+ verify(mBiometricService.mFaceService).prepareForAuthentication(
+ eq(false) /* requireConfirmation */,
+ any(IBinder.class),
+ anyLong() /* sessionId */,
+ anyInt() /* userId */,
+ any(IBiometricServiceReceiverInternal.class),
+ anyString() /* opPackageName */,
+ anyInt() /* cookie */,
+ anyInt() /* callingUid */,
+ anyInt() /* callingPid */,
+ anyInt() /* callingUserId */);
+ }
+
+ @Test
+ public void testAuthenticate_happyPathWithoutConfirmation() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ mBiometricService = new BiometricService(mContext, new MockInjector());
+ mBiometricService.onStart();
+
+ // Start testing the happy path
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */);
+ waitForIdle();
+
+ // Creates a pending auth session with the correct initial states
+ assertEquals(mBiometricService.mPendingAuthSession.mState,
+ BiometricService.STATE_AUTH_CALLED);
+
+ // Invokes <Modality>Service#prepareForAuthentication
+ ArgumentCaptor<Integer> cookieCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(mReceiver1, never()).onError(anyInt(), any(String.class));
+ verify(mBiometricService.mFingerprintService).prepareForAuthentication(
+ any(IBinder.class),
+ anyLong() /* sessionId */,
+ anyInt() /* userId */,
+ any(IBiometricServiceReceiverInternal.class),
+ anyString() /* opPackageName */,
+ cookieCaptor.capture() /* cookie */,
+ anyInt() /* callingUid */,
+ anyInt() /* callingPid */,
+ anyInt() /* callingUserId */);
+
+ // onReadyForAuthentication, mCurrentAuthSession state OK
+ mBiometricService.mImpl.onReadyForAuthentication(cookieCaptor.getValue(),
+ anyBoolean() /* requireConfirmation */, anyInt() /* userId */);
+ waitForIdle();
+ assertNull(mBiometricService.mPendingAuthSession);
+ assertEquals(mBiometricService.mCurrentAuthSession.mState,
+ BiometricService.STATE_AUTH_STARTED);
+
+ // startPreparedClient invoked
+ verify(mBiometricService.mFingerprintService)
+ .startPreparedClient(cookieCaptor.getValue());
+
+ // StatusBar showBiometricDialog invoked
+ verify(mBiometricService.mStatusBarService).showBiometricDialog(
+ eq(mBiometricService.mCurrentAuthSession.mBundle),
+ any(IBiometricServiceReceiverInternal.class),
+ eq(BiometricAuthenticator.TYPE_FINGERPRINT),
+ anyBoolean() /* requireConfirmation */,
+ anyInt() /* userId */,
+ eq(TEST_PACKAGE_NAME));
+
+ // Hardware authenticated
+ mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
+ false /* requireConfirmation */,
+ new byte[69] /* HAT */);
+ waitForIdle();
+ // Waiting for SystemUI to send dismissed callback
+ assertEquals(mBiometricService.mCurrentAuthSession.mState,
+ BiometricService.STATE_AUTHENTICATED_PENDING_SYSUI);
+ // Notify SystemUI hardware authenticated
+ verify(mBiometricService.mStatusBarService).onBiometricAuthenticated(
+ eq(true) /* authenticated */, eq(null) /* failureReason */);
+
+ // SystemUI sends callback with dismissed reason
+ mBiometricService.mInternalReceiver.onDialogDismissed(
+ BiometricPrompt.DISMISSED_REASON_CONFIRM_NOT_REQUIRED);
+ waitForIdle();
+ // HAT sent to keystore
+ verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
+ // Send onAuthenticated to client
+ verify(mReceiver1).onAuthenticationSucceeded();
+ // Current session becomes null
+ assertNull(mBiometricService.mCurrentAuthSession);
+ }
+
+ @Test
+ public void testAuthenticate_happyPathWithConfirmation() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ true /* requireConfirmation */);
+
+ // Test authentication succeeded goes to PENDING_CONFIRMATION and that the HAT is not
+ // sent to KeyStore yet
+ mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
+ true /* requireConfirmation */,
+ new byte[69] /* HAT */);
+ waitForIdle();
+ // Waiting for SystemUI to send confirmation callback
+ assertEquals(mBiometricService.mCurrentAuthSession.mState,
+ BiometricService.STATE_AUTH_PENDING_CONFIRM);
+ verify(mBiometricService.mKeyStore, never()).addAuthToken(any(byte[].class));
+
+ // SystemUI sends confirm, HAT is sent to keystore and client is notified.
+ mBiometricService.mInternalReceiver.onDialogDismissed(
+ BiometricPrompt.DISMISSED_REASON_CONFIRMED);
+ waitForIdle();
+ verify(mBiometricService.mKeyStore).addAuthToken(any(byte[].class));
+ verify(mReceiver1).onAuthenticationSucceeded();
+ }
+
+ @Test
+ public void testRejectFace_whenAuthenticating_notifiesSystemUIAndClient_thenPaused()
+ throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */);
+
+ mBiometricService.mInternalReceiver.onAuthenticationFailed();
+ waitForIdle();
+
+ verify(mBiometricService.mStatusBarService)
+ .onBiometricAuthenticated(eq(false), eq(ERROR_NOT_RECOGNIZED));
+ verify(mReceiver1).onAuthenticationFailed();
+ assertEquals(mBiometricService.mCurrentAuthSession.mState,
+ BiometricService.STATE_AUTH_PAUSED);
+ }
+
+ @Test
+ public void testRejectFingerprint_whenAuthenticating_notifiesAndKeepsAuthenticating()
+ throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */);
+
+ mBiometricService.mInternalReceiver.onAuthenticationFailed();
+ waitForIdle();
+
+ verify(mBiometricService.mStatusBarService)
+ .onBiometricAuthenticated(eq(false), eq(ERROR_NOT_RECOGNIZED));
+ verify(mReceiver1).onAuthenticationFailed();
+ assertEquals(mBiometricService.mCurrentAuthSession.mState,
+ BiometricService.STATE_AUTH_STARTED);
+ }
+
+ @Test
+ public void testErrorCanceled_whenAuthenticating_notifiesSystemUIAndClient() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */);
+
+ // Create a new pending auth session but don't start it yet. HAL contract is that previous
+ // one must get ERROR_CANCELED. Simulate that here by creating the pending auth session,
+ // sending ERROR_CANCELED to the current auth session, and then having the second one
+ // onReadyForAuthentication.
+ invokeAuthenticate(mBiometricService.mImpl, mReceiver2, false /* requireConfirmation */);
+ waitForIdle();
+
+ assertEquals(mBiometricService.mCurrentAuthSession.mState,
+ BiometricService.STATE_AUTH_STARTED);
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricConstants.BIOMETRIC_ERROR_CANCELED, ERROR_CANCELED);
+ waitForIdle();
+
+ // Auth session doesn't become null until SystemUI responds that the animation is completed
+ assertNotNull(mBiometricService.mCurrentAuthSession);
+ // ERROR_CANCELED is not sent until SystemUI responded that animation is completed
+ verify(mReceiver1, never()).onError(
+ anyInt(), anyString());
+ verify(mReceiver2, never()).onError(anyInt(), any(String.class));
+
+ // SystemUI dialog closed
+ verify(mBiometricService.mStatusBarService).hideBiometricDialog();
+
+ // After SystemUI notifies that the animation has completed
+ mBiometricService.mInternalReceiver
+ .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED);
+ waitForIdle();
+ verify(mReceiver1).onError(
+ eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
+ eq(ERROR_CANCELED));
+ assertNull(mBiometricService.mCurrentAuthSession);
+ }
+
+ @Test
+ public void testErrorHalTimeout_whenAuthenticating_entersPausedState() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */);
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+ ERROR_TIMEOUT);
+ waitForIdle();
+
+ assertEquals(mBiometricService.mCurrentAuthSession.mState,
+ BiometricService.STATE_AUTH_PAUSED);
+ verify(mBiometricService.mStatusBarService)
+ .onBiometricAuthenticated(eq(false), eq(ERROR_TIMEOUT));
+ // Timeout does not count as fail as per BiometricPrompt documentation.
+ verify(mReceiver1, never()).onAuthenticationFailed();
+
+ // No pending auth session. Pressing try again will create one.
+ assertNull(mBiometricService.mPendingAuthSession);
+
+ // Pressing "Try again" on SystemUI starts a new auth session.
+ mBiometricService.mInternalReceiver.onTryAgainPressed();
+ waitForIdle();
+
+ // The last one is still paused, and a new one has been created.
+ assertEquals(mBiometricService.mCurrentAuthSession.mState,
+ BiometricService.STATE_AUTH_PAUSED);
+ assertEquals(mBiometricService.mPendingAuthSession.mState,
+ BiometricService.STATE_AUTH_CALLED);
+
+ // Test resuming when hardware becomes ready. SystemUI should not be requested to
+ // show another dialog since it's already showing.
+ resetStatusBar();
+ startPendingAuthSession(mBiometricService);
+ waitForIdle();
+ verify(mBiometricService.mStatusBarService, never()).showBiometricDialog(
+ any(Bundle.class),
+ any(IBiometricServiceReceiverInternal.class),
+ anyInt(),
+ anyBoolean() /* requireConfirmation */,
+ anyInt() /* userId */,
+ anyString());
+ }
+
+ @Test
+ public void testErrorFromHal_whenPaused_notifiesSystemUIAndClient() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireCOnfirmation */);
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+ ERROR_TIMEOUT);
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricConstants.BIOMETRIC_ERROR_CANCELED,
+ ERROR_CANCELED);
+ waitForIdle();
+
+ // Client receives error immediately
+ verify(mReceiver1).onError(
+ eq(BiometricConstants.BIOMETRIC_ERROR_CANCELED),
+ eq(ERROR_CANCELED));
+ // Dialog is hidden immediately
+ verify(mBiometricService.mStatusBarService).hideBiometricDialog();
+ // Auth session is over
+ assertNull(mBiometricService.mCurrentAuthSession);
+ }
+
+ @Test
+ public void testErrorFromHal_whileAuthenticating_waitsForSysUIBeforeNotifyingClient()
+ throws Exception {
+ // For errors that show in SystemUI, BiometricService stays in STATE_ERROR_PENDING_SYSUI
+ // until SystemUI notifies us that the dialog is dismissed at which point the current
+ // session is done.
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */);
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS,
+ ERROR_UNABLE_TO_PROCESS);
+ waitForIdle();
+
+ // Sends error to SystemUI and does not notify client yet
+ assertEquals(mBiometricService.mCurrentAuthSession.mState,
+ BiometricService.STATE_ERROR_PENDING_SYSUI);
+ verify(mBiometricService.mStatusBarService)
+ .onBiometricError(eq(ERROR_UNABLE_TO_PROCESS));
+ verify(mBiometricService.mStatusBarService, never()).hideBiometricDialog();
+ verify(mReceiver1, never()).onError(anyInt(), anyString());
+
+ // SystemUI animation completed, client is notified, auth session is over
+ mBiometricService.mInternalReceiver
+ .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_ERROR);
+ waitForIdle();
+ verify(mReceiver1).onError(
+ eq(BiometricConstants.BIOMETRIC_ERROR_UNABLE_TO_PROCESS),
+ eq(ERROR_UNABLE_TO_PROCESS));
+ assertNull(mBiometricService.mCurrentAuthSession);
+ }
+
+ @Test
+ public void testDismissedReasonUserCancel_whileAuthenticating_cancelsHalAuthentication()
+ throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */);
+
+ mBiometricService.mInternalReceiver
+ .onDialogDismissed(BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ waitForIdle();
+ verify(mReceiver1).onError(
+ eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
+ eq(ERROR_USER_CANCELED));
+ verify(mBiometricService.mFingerprintService).cancelAuthenticationFromService(
+ any(),
+ any(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ eq(false) /* fromClient */);
+ assertNull(mBiometricService.mCurrentAuthSession);
+ }
+
+ @Test
+ public void testDismissedReasonNegative_whilePaused_doesntInvokeHalCancel() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */);
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+ ERROR_TIMEOUT);
+ mBiometricService.mInternalReceiver.onDialogDismissed(
+ BiometricPrompt.DISMISSED_REASON_NEGATIVE);
+ waitForIdle();
+
+ verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService(
+ any(),
+ any(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyBoolean());
+ }
+
+ @Test
+ public void testDismissedReasonUserCancel_whilePaused_doesntInvokeHalCancel() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */);
+
+ mBiometricService.mInternalReceiver.onError(
+ getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
+ BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
+ ERROR_TIMEOUT);
+ mBiometricService.mInternalReceiver.onDialogDismissed(
+ BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ waitForIdle();
+
+ verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService(
+ any(),
+ any(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyBoolean());
+ }
+
+ @Test
+ public void testDismissedReasonUserCancel_whenPendingConfirmation() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FACE);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ true /* requireConfirmation */);
+
+ mBiometricService.mInternalReceiver.onAuthenticationSucceeded(
+ true /* requireConfirmation */,
+ new byte[69] /* HAT */);
+ mBiometricService.mInternalReceiver.onDialogDismissed(
+ BiometricPrompt.DISMISSED_REASON_USER_CANCEL);
+ waitForIdle();
+
+ // doesn't send cancel to HAL
+ verify(mBiometricService.mFaceService, never()).cancelAuthenticationFromService(
+ any(),
+ any(),
+ anyInt(),
+ anyInt(),
+ anyInt(),
+ anyBoolean());
+ verify(mReceiver1).onError(
+ eq(BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED),
+ eq(ERROR_USER_CANCELED));
+ assertNull(mBiometricService.mCurrentAuthSession);
+ }
+
+ @Test
+ public void testAcquire_whenAuthenticating_sentToSystemUI() throws Exception {
+ setupAuthForOnly(BiometricAuthenticator.TYPE_FINGERPRINT);
+ invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
+ false /* requireConfirmation */);
+
+ mBiometricService.mInternalReceiver.onAcquired(
+ FingerprintManager.FINGERPRINT_ACQUIRED_IMAGER_DIRTY,
+ FINGERPRINT_ACQUIRED_SENSOR_DIRTY);
+ waitForIdle();
+
+ // Sends to SysUI and stays in authenticating state
+ verify(mBiometricService.mStatusBarService)
+ .onBiometricHelp(eq(FINGERPRINT_ACQUIRED_SENSOR_DIRTY));
+ assertEquals(mBiometricService.mCurrentAuthSession.mState,
+ BiometricService.STATE_AUTH_STARTED);
+ }
+
+ // Helper methods
+
+ private void setupAuthForOnly(int modality) {
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
+ .thenReturn(false);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false);
+
+ if (modality == BiometricAuthenticator.TYPE_FINGERPRINT) {
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
+ .thenReturn(true);
+ when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
+ } else if (modality == BiometricAuthenticator.TYPE_FACE) {
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
+ when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
+ when(mFaceManager.isHardwareDetected()).thenReturn(true);
+ } else {
+ fail("Unknown modality: " + modality);
+ }
+
+ mBiometricService = new BiometricService(mContext, new MockInjector());
+ mBiometricService.onStart();
+
+ when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
+ }
+
+ private void resetReceiver() {
+ mReceiver1 = mock(IBiometricServiceReceiver.class);
+ mReceiver2 = mock(IBiometricServiceReceiver.class);
+ }
+
+ private void resetStatusBar() {
+ mBiometricService.mStatusBarService = mock(IStatusBarService.class);
+ }
+
+ private void invokeAuthenticateAndStart(IBiometricService.Stub service,
+ IBiometricServiceReceiver receiver, boolean requireConfirmation) throws Exception {
+ // Request auth, creates a pending session
+ invokeAuthenticate(service, receiver, requireConfirmation);
+ waitForIdle();
+
+ startPendingAuthSession(mBiometricService);
+ waitForIdle();
+ }
+
+ private static void startPendingAuthSession(BiometricService service) throws Exception {
+ // Get the cookie so we can pretend the hardware is ready to authenticate
+ // Currently we only support single modality per auth
+ assertEquals(service.mPendingAuthSession.mModalitiesWaiting.values().size(), 1);
+ final int cookie = service.mPendingAuthSession.mModalitiesWaiting.values()
+ .iterator().next();
+ assertNotEquals(cookie, 0);
+
+ service.mImpl.onReadyForAuthentication(cookie,
+ anyBoolean() /* requireConfirmation */, anyInt() /* userId */);
+ }
+
+ private static void invokeAuthenticate(IBiometricService.Stub service,
+ IBiometricServiceReceiver receiver, boolean requireConfirmation) throws Exception {
+ service.authenticate(
+ new Binder() /* token */,
+ 0 /* sessionId */,
+ 0 /* userId */,
+ receiver,
+ TEST_PACKAGE_NAME /* packageName */,
+ createTestBiometricPromptBundle(requireConfirmation),
+ null /* IBiometricConfirmDeviceCredentialCallback */);
+ }
+
+ private static Bundle createTestBiometricPromptBundle(boolean requireConfirmation) {
+ final Bundle bundle = new Bundle();
+ bundle.putBoolean(BiometricPrompt.KEY_REQUIRE_CONFIRMATION, requireConfirmation);
+ return bundle;
+ }
+
+ private static int getCookieForCurrentSession(BiometricService.AuthSession session) {
+ assertEquals(session.mModalitiesMatched.values().size(), 1);
+ return session.mModalitiesMatched.values().iterator().next();
+ }
+
+ private static void waitForIdle() {
+ InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 75e5847..819091c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -19,12 +19,12 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.AppOpsManager;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
@@ -54,10 +54,7 @@
IPermissionManager mPermissionManagerMock;
@Mock
- AppsFilter.ConfigProvider mConfigProviderMock;
-
- @Mock
- AppOpsManager mAppOpsManager;
+ AppsFilter.FeatureConfig mFeatureConfigMock;
private Map<String, PackageParser.Package> mExisting = new ArrayMap<>();
@@ -108,16 +105,23 @@
when(mPermissionManagerMock
.checkPermission(anyString(), anyString(), anyInt()))
.thenReturn(PackageManager.PERMISSION_DENIED);
- when(mConfigProviderMock.isEnabled()).thenReturn(true);
- when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq(
- DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_DEFAULT);
+ when(mFeatureConfigMock.isGloballyEnabled()).thenReturn(true);
+ when(mFeatureConfigMock.packageIsEnabled(any(PackageParser.Package.class)))
+ .thenReturn(true);
+ }
+
+ @Test
+ public void testSystemReadyPropogates() throws Exception {
+ final AppsFilter appsFilter =
+ new AppsFilter(mFeatureConfigMock, mPermissionManagerMock, new String[]{}, false);
+ appsFilter.onSystemReady();
+ verify(mFeatureConfigMock).onSystemReady();
}
@Test
public void testQueriesAction_FilterMatches() {
final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
- new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, mPermissionManagerMock, new String[]{}, false);
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package", new IntentFilter("TEST_ACTION"))).build();
@@ -130,8 +134,7 @@
@Test
public void testQueriesAction_NoMatchingAction_Filters() {
final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
- new String[]{}, false);
+ new AppsFilter(mFeatureConfigMock, mPermissionManagerMock, new String[]{}, false);
PackageSetting target = simulateAddPackage(appsFilter,
pkg("com.some.package")).build();
@@ -144,7 +147,7 @@
@Test
public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+ new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
new String[]{}, false);
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
@@ -158,7 +161,7 @@
@Test
public void testNoQueries_Filters() {
final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+ new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
new String[]{}, false);
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
@@ -171,7 +174,7 @@
@Test
public void testForceQueryable_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+ new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
new String[]{}, false);
PackageSetting target =
@@ -186,7 +189,7 @@
@Test
public void testForceQueryableByDevice_SystemCaller_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+ new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
new String[]{"com.some.package"}, false);
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"))
@@ -201,7 +204,7 @@
@Test
public void testForceQueryableByDevice_NonSystemCaller_Filters() {
final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+ new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
new String[]{"com.some.package"}, false);
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
@@ -215,7 +218,7 @@
@Test
public void testSystemQueryable_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+ new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
new String[]{}, true /* system force queryable */);
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"))
@@ -230,7 +233,7 @@
@Test
public void testQueriesPackage_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+ new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
new String[]{}, false);
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
@@ -241,55 +244,11 @@
}
@Test
- public void testNoQueries_AppOpModeDeny_Filters() {
- when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq(
- DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_ERRORED);
- final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
- new String[]{}, false);
-
- PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
- PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package")).build();
-
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
- }
-
- @Test
- public void testNoQueries_AppOpModeAllow_DoesntFilter() {
- when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq(
- DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_ALLOWED);
- final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
- new String[]{}, false);
-
- PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
- PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package")).build();
-
- assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
- }
-
- @Test
- public void testNoQueries_AppOpModeIgnore_Filters() {
- when(mAppOpsManager.checkOpNoThrow(eq(AppOpsManager.OP_QUERY_ALL_PACKAGES), eq(
- DUMMY_CALLING_UID), anyString())).thenReturn(AppOpsManager.MODE_IGNORED);
- final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
- new String[]{}, false);
-
- PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
- PackageSetting calling = simulateAddPackage(appsFilter,
- pkg("com.some.other.package")).build();
-
- assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_UID, calling, target, 0));
- }
-
- @Test
public void testNoQueries_FeatureOff_DoesntFilter() {
- when(mConfigProviderMock.isEnabled()).thenReturn(false);
+ when(mFeatureConfigMock.packageIsEnabled(any(PackageParser.Package.class)))
+ .thenReturn(false);
final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+ new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
new String[]{}, false);
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
@@ -302,7 +261,7 @@
@Test
public void testSystemUid_DoesntFilter() {
final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+ new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
new String[]{}, false);
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
@@ -315,7 +274,7 @@
@Test
public void testNonSystemUid_NoCallingSetting_Filters() {
final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+ new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
new String[]{}, false);
PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package")).build();
@@ -326,7 +285,7 @@
@Test
public void testNoTargetPackage_filters() {
final AppsFilter appsFilter =
- new AppsFilter(mConfigProviderMock, mPermissionManagerMock, mAppOpsManager,
+ new AppsFilter(mFeatureConfigMock, mPermissionManagerMock,
new String[]{}, false);
PackageSetting target = new PackageSettingBuilder()
diff --git a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
index 1f0ef6e..8cb5197 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -235,22 +235,22 @@
wasRecentlyRestored.getPendingRestores().add(
new RestoreInfo(73 /* userId */, 239 /* appId*/, "seInfo"));
- RollbackData dataWithPendingBackup = new RollbackData(101, new File("/does/not/exist"), -1);
+ Rollback dataWithPendingBackup = new Rollback(101, new File("/does/not/exist"), -1);
dataWithPendingBackup.info.getPackages().add(pendingBackup);
- RollbackData dataWithRecentRestore = new RollbackData(17239, new File("/does/not/exist"),
+ Rollback dataWithRecentRestore = new Rollback(17239, new File("/does/not/exist"),
-1);
dataWithRecentRestore.info.getPackages().add(wasRecentlyRestored);
- RollbackData dataForDifferentUser = new RollbackData(17239, new File("/does/not/exist"),
+ Rollback dataForDifferentUser = new Rollback(17239, new File("/does/not/exist"),
-1);
dataForDifferentUser.info.getPackages().add(ignoredInfo);
- RollbackData dataForRestore = new RollbackData(17239, new File("/does/not/exist"), -1);
+ Rollback dataForRestore = new Rollback(17239, new File("/does/not/exist"), -1);
dataForRestore.info.getPackages().add(pendingRestore);
dataForRestore.info.getPackages().add(wasRecentlyRestored);
- Set<RollbackData> changed = helper.commitPendingBackupAndRestoreForUser(37,
+ Set<Rollback> changed = helper.commitPendingBackupAndRestoreForUser(37,
Arrays.asList(dataWithPendingBackup, dataWithRecentRestore, dataForDifferentUser,
dataForRestore));
InOrder inOrder = Mockito.inOrder(installer);
@@ -265,7 +265,7 @@
assertEquals(-1, pendingBackup.getPendingBackups().indexOf(37));
assertEquals(53, pendingBackup.getCeSnapshotInodes().get(37));
- // Check that changed returns correct RollbackData.
+ // Check that changed returns correct Rollback.
assertEquals(3, changed.size());
assertTrue(changed.contains(dataWithPendingBackup));
assertTrue(changed.contains(dataWithRecentRestore));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index c1c0a30..3ac7a79 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -181,12 +181,6 @@
@RunWithLooper
public class NotificationManagerServiceTest extends UiServiceTestCase {
private static final String TEST_CHANNEL_ID = "NotificationManagerServiceTestChannelId";
- private static final String CLEAR_DEVICE_CONFIG_KEY_CMD =
- "device_config delete " + DeviceConfig.NAMESPACE_SYSTEMUI + " "
- + SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE;
- private static final String SET_DEFAULT_ASSISTANT_DEVICE_CONFIG_CMD =
- "device_config put " + DeviceConfig.NAMESPACE_SYSTEMUI + " "
- + SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE;
private final int mUid = Binder.getCallingUid();
private TestableNotificationManagerService mService;
@@ -2766,6 +2760,18 @@
}
@Test
+ public void testReadPolicyXml_readSnoozedNotificationsFromXml() throws Exception {
+ final String upgradeXml = "<notification-policy version=\"1\">"
+ + "<snoozed-notifications>></snoozed-notifications>"
+ + "</notification-policy>";
+ mService.readPolicyXml(
+ new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())),
+ false,
+ UserHandle.USER_ALL);
+ verify(mSnoozeHelper, times(1)).readXml(any(XmlPullParser.class));
+ }
+
+ @Test
public void testReadPolicyXml_readApprovedServicesFromSettings() throws Exception {
final String preupgradeXml = "<notification-policy version=\"1\">"
+ "<ranking></ranking>"
@@ -4028,6 +4034,41 @@
}
@Test
+ public void testPostFromAndroidForNonExistentPackage() throws Exception {
+ final String notReal = "NOT REAL";
+ when(mPackageManagerClient.getPackageUidAsUser(anyString(), anyInt())).thenThrow(
+ PackageManager.NameNotFoundException.class);
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.uid = -1;
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(ai);
+
+ final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+ try {
+ mInternalService.enqueueNotification(notReal, "android", 0, 0, "tag",
+ sbn.getId(), sbn.getNotification(), sbn.getUserId());
+ fail("can't post notifications for nonexistent packages, even if you exist");
+ } catch (SecurityException e) {
+ // yay
+ }
+ }
+
+ @Test
+ public void testCancelFromAndroidForNonExistentPackage() throws Exception {
+ final String notReal = "NOT REAL";
+ when(mPackageManagerClient.getPackageUidAsUser(eq(notReal), anyInt())).thenThrow(
+ PackageManager.NameNotFoundException.class);
+ ApplicationInfo ai = new ApplicationInfo();
+ ai.uid = -1;
+ when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(ai);
+
+ // unlike the post case, ignore instead of throwing
+ final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+
+ mInternalService.cancelNotification(notReal, "android", 0, 0, "tag",
+ sbn.getId(), sbn.getUserId());
+ }
+
+ @Test
public void testResolveNotificationUid_delegateNotAllowed() throws Exception {
when(mPackageManagerClient.getPackageUidAsUser("target", 0)).thenReturn(123);
// no delegate
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 2e7277f..36175a9 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -18,6 +18,7 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.assertNull;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
@@ -37,9 +38,11 @@
import android.service.notification.StatusBarNotification;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.IntArray;
+import android.util.Xml;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.util.FastXmlSerializer;
import com.android.server.UiServiceTestCase;
import org.junit.Before;
@@ -48,6 +51,15 @@
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -69,6 +81,117 @@
}
@Test
+ public void testWriteXMLformattedCorrectly_testReadingCorrectTime()
+ throws XmlPullParserException, IOException {
+ final String max_time_str = Long.toString(Long.MAX_VALUE);
+ final String xml_string = "<snoozed-notifications>"
+ + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
+ + "pkg=\"pkg\" key=\"key\" time=\"" + max_time_str + "\"/>"
+ + "<notification version=\"1\" user-id=\"0\" notification=\"notification\" "
+ + "pkg=\"pkg\" key=\"key2\" time=\"" + max_time_str + "\"/>"
+ + "</snoozed-notifications>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml_string.getBytes())), null);
+ mSnoozeHelper.readXml(parser);
+ assertTrue("Should read the notification time from xml and it should be more than zero",
+ 0 < mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+ 0, "pkg", "key").doubleValue());
+ }
+
+ @Test
+ public void testWriteXMLformattedCorrectly_testCorrectContextURI()
+ throws XmlPullParserException, IOException {
+ final String max_time_str = Long.toString(Long.MAX_VALUE);
+ final String xml_string = "<snoozed-notifications>"
+ + "<context version=\"1\" user-id=\"0\" notification=\"notification\" "
+ + "pkg=\"pkg\" key=\"key\" id=\"uri\"/>"
+ + "<context version=\"1\" user-id=\"0\" notification=\"notification\" "
+ + "pkg=\"pkg\" key=\"key2\" id=\"uri\"/>"
+ + "</snoozed-notifications>";
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(xml_string.getBytes())), null);
+ mSnoozeHelper.readXml(parser);
+ assertEquals("Should read the notification context from xml and it should be `uri",
+ "uri", mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+ 0, "pkg", "key"));
+ }
+
+ @Test
+ public void testReadValidSnoozedFromCorrectly_timeDeadline()
+ throws XmlPullParserException, IOException {
+ NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r, 999999999);
+ XmlSerializer serializer = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ mSnoozeHelper.writeXml(serializer);
+ serializer.endDocument();
+ serializer.flush();
+
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), "utf-8");
+ mSnoozeHelper.readXml(parser);
+ assertTrue("Should read the notification time from xml and it should be more than zero",
+ 0 < mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+ 0, "pkg", r.getKey()).doubleValue());
+ }
+
+
+ @Test
+ public void testReadExpiredSnoozedNotification() throws
+ XmlPullParserException, IOException, InterruptedException {
+ NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r, 0);
+ // Thread.sleep(100);
+ XmlSerializer serializer = new FastXmlSerializer();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+ serializer.startDocument(null, true);
+ mSnoozeHelper.writeXml(serializer);
+ serializer.endDocument();
+ serializer.flush();
+ Thread.sleep(10);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(new BufferedInputStream(
+ new ByteArrayInputStream(baos.toByteArray())), "utf-8");
+ mSnoozeHelper.readXml(parser);
+ int systemUser = UserHandle.SYSTEM.getIdentifier();
+ assertTrue("Should see a past time returned",
+ System.currentTimeMillis() > mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+ systemUser, "pkg", r.getKey()).longValue());
+ }
+
+ @Test
+ public void testCleanupContextShouldRemovePersistedRecord() {
+ NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r, "context");
+ mSnoozeHelper.cleanupPersistedContext(r.sbn.getKey());
+ assertNull(mSnoozeHelper.getSnoozeContextForUnpostedNotification(
+ r.getUser().getIdentifier(),
+ r.sbn.getPackageName(),
+ r.sbn.getKey()
+ ));
+ }
+
+ @Test
+ public void testReadNoneSnoozedNotification() throws XmlPullParserException,
+ IOException, InterruptedException {
+ NotificationRecord r = getNotificationRecord(
+ "pkg", 1, "one", UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r, 0);
+
+ assertEquals("should see a zero value for unsnoozed notification",
+ 0L,
+ mSnoozeHelper.getSnoozeTimeForUnpostedNotification(
+ UserHandle.SYSTEM.getIdentifier(),
+ "not_my_package", r.getKey()).longValue());
+ }
+
+ @Test
public void testSnoozeForTime() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
mSnoozeHelper.snooze(r, 1000);
@@ -84,7 +207,7 @@
@Test
public void testSnooze() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
- mSnoozeHelper.snooze(r);
+ mSnoozeHelper.snooze(r, (String) null);
verify(mAm, never()).setExactAndAllowWhileIdle(
anyInt(), anyLong(), any(PendingIntent.class));
assertTrue(mSnoozeHelper.isSnoozed(
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index 63b9198..6c78f6f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -28,6 +28,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
@@ -140,7 +141,8 @@
public void testNotResumeHomeStackOnRemovingDisplay() {
// Create a display which supports system decoration and allows reparenting stacks to
// another display when the display is removed.
- final ActivityDisplay display = spy(createNewActivityDisplay());
+ final ActivityDisplay display = createNewActivityDisplay();
+ spyOn(display);
doReturn(false).when(display).shouldDestroyContentOnRemove();
doReturn(true).when(display).supportsSystemDecorations();
mRootActivityContainer.addChild(display, ActivityDisplay.POSITION_TOP);
@@ -304,14 +306,18 @@
ACTIVITY_TYPE_STANDARD, ON_TOP);
final ActivityStack stack4 = display.createStack(WINDOWING_MODE_FULLSCREEN,
ACTIVITY_TYPE_STANDARD, ON_TOP);
- final TaskRecord task1 = new TaskBuilder(mService.mStackSupervisor).setStack(
- stack1).setTaskId(1).build();
- final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor).setStack(
- stack2).setTaskId(2).build();
- final TaskRecord task3 = new TaskBuilder(mService.mStackSupervisor).setStack(
- stack3).setTaskId(3).build();
- final TaskRecord task4 = new TaskBuilder(mService.mStackSupervisor).setStack(
- stack4).setTaskId(4).build();
+ final TaskRecord task1 = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(stack1)
+ .build();
+ final TaskRecord task2 = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(stack2)
+ .build();
+ final TaskRecord task3 = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(stack3)
+ .build();
+ final TaskRecord task4 = new TaskBuilder(mService.mStackSupervisor)
+ .setStack(stack4)
+ .build();
// Reordering stacks while removing stacks.
doAnswer(invocation -> {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index 23bae88..977dd8e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -61,13 +61,13 @@
private ActivityMetricsLaunchObserver mLaunchObserver;
private ActivityMetricsLaunchObserverRegistry mLaunchObserverRegistry;
- private TestActivityStack mStack;
+ private ActivityStack mStack;
private TaskRecord mTask;
private ActivityRecord mActivityRecord;
private ActivityRecord mActivityRecordTrampoline;
@Before
- public void setUpAMLO() throws Exception {
+ public void setUpAMLO() {
mLaunchObserver = mock(ActivityMetricsLaunchObserver.class);
// ActivityStackSupervisor always creates its own instance of ActivityMetricsLogger.
@@ -78,15 +78,19 @@
// Sometimes we need an ActivityRecord for ActivityMetricsLogger to do anything useful.
// This seems to be the easiest way to create an ActivityRecord.
- mStack = mRootActivityContainer.getDefaultDisplay().createStack(
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
- mActivityRecord = new ActivityBuilder(mService).setTask(mTask).build();
+ mStack = new StackBuilder(mRootActivityContainer)
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setOnTop(true)
+ .setCreateActivity(true)
+ .build();
+ mTask = mStack.topTask();
+ mActivityRecord = mTask.getTopActivity();
mActivityRecordTrampoline = new ActivityBuilder(mService).setTask(mTask).build();
}
@After
- public void tearDownAMLO() throws Exception {
+ public void tearDownAMLO() {
if (mLaunchObserverRegistry != null) { // Don't NPE if setUp failed.
mLaunchObserverRegistry.unregisterLaunchObserver(mLaunchObserver);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 7b252cb..eaffd77 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -16,10 +16,17 @@
package com.android.server.wm;
+import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
+import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.os.Process.NOBODY_UID;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_90;
+import static android.view.WindowManager.TRANSIT_TASK_CLOSE;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
@@ -28,18 +35,24 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_CANCELLED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REMOVED;
import static com.android.server.wm.ActivityRecord.FINISH_RESULT_REQUESTED;
+import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
+import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
import static com.android.server.wm.ActivityStack.ActivityState.INITIALIZING;
+import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityStack.ActivityState.STARTED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPING;
+import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_MOVING;
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_INVISIBLE;
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE;
@@ -54,6 +67,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.never;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -71,6 +85,7 @@
import android.platform.test.annotations.Presubmit;
import android.util.MergedConfiguration;
import android.util.MutableBoolean;
+import android.view.DisplayInfo;
import android.view.IRemoteAnimationFinishedCallback;
import android.view.IRemoteAnimationRunner.Stub;
import android.view.RemoteAnimationAdapter;
@@ -79,7 +94,7 @@
import androidx.test.filters.MediumTest;
import com.android.internal.R;
-import com.android.server.wm.utils.WmDisplayCutout;
+import com.android.server.wm.ActivityStack.ActivityState;
import org.junit.Before;
import org.junit.Test;
@@ -96,13 +111,13 @@
@MediumTest
@Presubmit
public class ActivityRecordTests extends ActivityTestsBase {
- private TestActivityStack mStack;
+ private ActivityStack mStack;
private TaskRecord mTask;
private ActivityRecord mActivity;
@Before
public void setUp() throws Exception {
- mStack = (TestActivityStack) new StackBuilder(mRootActivityContainer).build();
+ mStack = new StackBuilder(mRootActivityContainer).build();
mTask = mStack.getChildAt(0);
mActivity = mTask.getTopActivity();
@@ -113,13 +128,13 @@
@Test
public void testStackCleanupOnClearingTask() {
mActivity.setTask(null);
- assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1);
+ verify(mStack, times(1)).onActivityRemovedFromStack(any());
}
@Test
public void testStackCleanupOnActivityRemoval() {
mTask.removeActivity(mActivity);
- assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 1);
+ verify(mStack, times(1)).onActivityRemovedFromStack(any());
}
@Test
@@ -134,7 +149,7 @@
final TaskRecord newTask = new TaskBuilder(mService.mStackSupervisor).setStack(mStack)
.build();
mActivity.reparent(newTask, 0, null /*reason*/);
- assertEquals(mStack.onActivityRemovedFromStackInvocationCount(), 0);
+ verify(mStack, times(0)).onActivityRemovedFromStack(any());
}
@Test
@@ -181,7 +196,7 @@
assertTrue(mActivity.isState(STARTED));
mStack.mTranslucentActivityWaiting = null;
- topActivity.changeWindowTranslucency(false);
+ topActivity.setOccludesParent(false);
mActivity.setState(STOPPED, "testPausingWhenVisibleFromStopped behind non-opaque");
mActivity.makeVisibleIfNeeded(null /* starting */, true /* reportToClient */);
assertTrue(mActivity.isState(STARTED));
@@ -261,8 +276,8 @@
public void testNewParentConfigurationIncrementsSeq() {
final Configuration newConfig = new Configuration(
mTask.getRequestedOverrideConfiguration());
- newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
- ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+ newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
+ ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
mTask.onRequestedOverrideConfigurationChanged(newConfig);
@@ -277,7 +292,7 @@
.getRequestedOverrideConfiguration();
final Configuration newConfig = new Configuration();
- newConfig.orientation = Configuration.ORIENTATION_PORTRAIT;
+ newConfig.orientation = ORIENTATION_PORTRAIT;
final int prevSeq = mActivity.getMergedOverrideConfiguration().seq;
mActivity.onRequestedOverrideConfigurationChanged(newConfig);
@@ -293,10 +308,11 @@
mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
mActivity.getConfiguration()));
- mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION;
+ mActivity.info.configChanges &= ~CONFIG_ORIENTATION;
final Configuration newConfig = new Configuration(mTask.getConfiguration());
- newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
- ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+ newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
+ ? ORIENTATION_LANDSCAPE
+ : ORIENTATION_PORTRAIT;
mTask.onRequestedOverrideConfigurationChanged(newConfig);
mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
@@ -315,13 +331,14 @@
mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
mActivity.getConfiguration()));
- mActivity.info.configChanges &= ~ActivityInfo.CONFIG_ORIENTATION;
+ mActivity.info.configChanges &= ~CONFIG_ORIENTATION;
final Configuration newConfig = new Configuration(mTask.getConfiguration());
- newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
- ? Configuration.ORIENTATION_LANDSCAPE : Configuration.ORIENTATION_PORTRAIT;
+ newConfig.orientation = newConfig.orientation == ORIENTATION_PORTRAIT
+ ? ORIENTATION_LANDSCAPE
+ : ORIENTATION_PORTRAIT;
mTask.onRequestedOverrideConfigurationChanged(newConfig);
- doReturn(true).when(mTask.getTask()).isDragResizing();
+ doReturn(true).when(mTask.mTask).isDragResizing();
mActivity.mRelaunchReason = ActivityTaskManagerService.RELAUNCH_REASON_NONE;
@@ -355,30 +372,39 @@
@Test
public void testSetRequestedOrientationUpdatesConfiguration() throws Exception {
+ mActivity = new ActivityBuilder(mService)
+ .setTask(mTask)
+ .setConfigChanges(CONFIG_ORIENTATION | CONFIG_SCREEN_LAYOUT)
+ .build();
mActivity.setState(ActivityStack.ActivityState.RESUMED, "Testing");
- mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
mActivity.getConfiguration()));
- mActivity.info.configChanges |= ActivityInfo.CONFIG_ORIENTATION;
final Configuration newConfig = new Configuration(mActivity.getConfiguration());
- newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
- ? Configuration.ORIENTATION_LANDSCAPE
- : Configuration.ORIENTATION_PORTRAIT;
+ final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
+ final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
+ if (newConfig.orientation == ORIENTATION_PORTRAIT) {
+ newConfig.orientation = ORIENTATION_LANDSCAPE;
+ newConfig.screenWidthDp = longSide;
+ newConfig.screenHeightDp = shortSide;
+ } else {
+ newConfig.orientation = ORIENTATION_PORTRAIT;
+ newConfig.screenWidthDp = shortSide;
+ newConfig.screenHeightDp = longSide;
+ }
// Mimic the behavior that display doesn't handle app's requested orientation.
- doAnswer(invocation -> {
- mTask.onConfigurationChanged(newConfig);
- return null;
- }).when(mActivity.mAppWindowToken).setOrientation(anyInt(), any(), any());
+ final DisplayContent dc = mTask.mTask.getDisplayContent();
+ doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
+ doReturn(false).when(dc).handlesOrientationChangeFromDescendant();
final int requestedOrientation;
switch (newConfig.orientation) {
- case Configuration.ORIENTATION_LANDSCAPE:
- requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+ case ORIENTATION_LANDSCAPE:
+ requestedOrientation = SCREEN_ORIENTATION_LANDSCAPE;
break;
- case Configuration.ORIENTATION_PORTRAIT:
+ case ORIENTATION_PORTRAIT:
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
break;
default:
@@ -421,24 +447,33 @@
@Test
public void testPushConfigurationWhenLaunchTaskBehind() throws Exception {
+ mActivity = new ActivityBuilder(mService)
+ .setTask(mTask)
+ .setLaunchTaskBehind(true)
+ .setConfigChanges(CONFIG_ORIENTATION)
+ .build();
mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
- final TestActivityStack stack = (TestActivityStack) new StackBuilder(mRootActivityContainer)
- .build();
+ final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
try {
- stack.setIsTranslucent(false);
+ doReturn(false).when(stack).isStackTranslucent(any());
assertFalse(mStack.shouldBeVisible(null /* starting */));
- mTask.onRequestedOverrideConfigurationChanged(mTask.getConfiguration());
mActivity.setLastReportedConfiguration(new MergedConfiguration(new Configuration(),
mActivity.getConfiguration()));
- mActivity.mLaunchTaskBehind = true;
- mActivity.info.configChanges |= ActivityInfo.CONFIG_ORIENTATION;
final Configuration newConfig = new Configuration(mActivity.getConfiguration());
- newConfig.orientation = newConfig.orientation == Configuration.ORIENTATION_PORTRAIT
- ? Configuration.ORIENTATION_LANDSCAPE
- : Configuration.ORIENTATION_PORTRAIT;
+ final int shortSide = Math.min(newConfig.screenWidthDp, newConfig.screenHeightDp);
+ final int longSide = Math.max(newConfig.screenWidthDp, newConfig.screenHeightDp);
+ if (newConfig.orientation == ORIENTATION_PORTRAIT) {
+ newConfig.orientation = ORIENTATION_LANDSCAPE;
+ newConfig.screenWidthDp = longSide;
+ newConfig.screenHeightDp = shortSide;
+ } else {
+ newConfig.orientation = ORIENTATION_PORTRAIT;
+ newConfig.screenWidthDp = shortSide;
+ newConfig.screenHeightDp = longSide;
+ }
mTask.onConfigurationChanged(newConfig);
@@ -457,7 +492,7 @@
@Test
public void testShouldPauseWhenMakeClientVisible() {
ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
- topActivity.changeWindowTranslucency(false);
+ topActivity.setOccludesParent(false);
mActivity.setState(ActivityStack.ActivityState.STOPPED, "Testing");
mActivity.makeClientVisible();
assertEquals(STARTED, mActivity.getState());
@@ -468,6 +503,7 @@
setupDisplayContentForCompatDisplayInsets();
final int decorHeight = 200; // e.g. The device has cutout.
final DisplayPolicy policy = setupDisplayAndParentSize(600, 800).getDisplayPolicy();
+ spyOn(policy);
doAnswer(invocationOnMock -> {
final int rotation = invocationOnMock.<Integer>getArgument(0);
final Rect insets = invocationOnMock.<Rect>getArgument(4);
@@ -482,7 +518,7 @@
doReturn(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)
.when(mActivity.mAppWindowToken).getOrientationIgnoreVisibility();
- mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = 1;
ensureActivityConfiguration();
// The parent configuration doesn't change since the first resolved configuration, so the
@@ -506,19 +542,32 @@
@Test
public void testSizeCompatMode_FixedScreenConfigurationWhenMovingToDisplay() {
// Initialize different bounds on a new display.
- final ActivityDisplay newDisplay = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
- newDisplay.getWindowConfiguration().setAppBounds(new Rect(0, 0, 1000, 2000));
- newDisplay.getConfiguration().densityDpi = 300;
+ final Rect newDisplayBounds = new Rect(0, 0, 1000, 2000);
+ DisplayInfo info = new DisplayInfo();
+ mService.mContext.getDisplay().getDisplayInfo(info);
+ info.logicalWidth = newDisplayBounds.width();
+ info.logicalHeight = newDisplayBounds.height();
+ info.logicalDensityDpi = 300;
- mTask.getConfiguration().densityDpi = 200;
- prepareFixedAspectRatioUnresizableActivity();
+ final ActivityDisplay newDisplay =
+ addNewActivityDisplayAt(info, ActivityDisplay.POSITION_TOP);
+
+ final Configuration c =
+ new Configuration(mStack.getDisplay().getRequestedOverrideConfiguration());
+ c.densityDpi = 200;
+ mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
+ mActivity = new ActivityBuilder(mService)
+ .setTask(mTask)
+ .setResizeMode(RESIZE_MODE_UNRESIZEABLE)
+ .setMaxAspectRatio(1.5f)
+ .build();
+ mActivity.visible = true;
final Rect originalBounds = new Rect(mActivity.getBounds());
final int originalDpi = mActivity.getConfiguration().densityDpi;
// Move the non-resizable activity to the new display.
mStack.reparent(newDisplay, true /* onTop */, false /* displayRemoved */);
- ensureActivityConfiguration();
assertEquals(originalBounds, mActivity.getBounds());
assertEquals(originalDpi, mActivity.getConfiguration().densityDpi);
@@ -531,9 +580,9 @@
when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
mTask.getWindowConfiguration().setAppBounds(mStack.getDisplay().getBounds());
- mTask.getConfiguration().orientation = Configuration.ORIENTATION_PORTRAIT;
+ mTask.getConfiguration().orientation = ORIENTATION_PORTRAIT;
mActivity.info.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
- mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
ensureActivityConfiguration();
final Rect originalBounds = new Rect(mActivity.getBounds());
@@ -549,7 +598,7 @@
public void testSizeCompatMode_FixedScreenLayoutSizeBits() {
final int fixedScreenLayout = Configuration.SCREENLAYOUT_LONG_NO
| Configuration.SCREENLAYOUT_SIZE_NORMAL;
- mTask.getConfiguration().screenLayout = fixedScreenLayout
+ mTask.getRequestedOverrideConfiguration().screenLayout = fixedScreenLayout
| Configuration.SCREENLAYOUT_LAYOUTDIR_LTR;
prepareFixedAspectRatioUnresizableActivity();
@@ -579,7 +628,7 @@
mTask.getWindowConfiguration().setAppBounds(new Rect(0, 0, 600, 1200));
// Simulate the display changes orientation.
- doReturn(ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION
+ doReturn(ActivityInfo.CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION
| ActivityInfo.CONFIG_WINDOW_CONFIGURATION)
.when(display).getLastOverrideConfigurationChanges();
mActivity.onConfigurationChanged(mTask.getConfiguration());
@@ -689,12 +738,11 @@
* incorrect state.
*/
@Test
- public void testFinishActivityLocked_cancelled() {
+ public void testFinishActivityIfPossible_cancelled() {
// Mark activity as finishing
mActivity.finishing = true;
assertEquals("Duplicate finish request must be ignored", FINISH_RESULT_CANCELLED,
- mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
- false /* oomAdj */));
+ mActivity.finishIfPossible("test", false /* oomAdj */));
assertTrue(mActivity.finishing);
assertTrue(mActivity.isInStackLocked());
@@ -702,21 +750,19 @@
mActivity.finishing = false;
mActivity.setTask(null);
assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_CANCELLED,
- mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
- false /* oomAdj */));
+ mActivity.finishIfPossible("test", false /* oomAdj */));
assertFalse(mActivity.finishing);
}
/**
- * Verify that activity finish request is requested, but not executed immediately if activity is
+ * Verify that activity finish request is placed, but not executed immediately if activity is
* not ready yet.
*/
@Test
- public void testFinishActivityLocked_requested() {
+ public void testFinishActivityIfPossible_requested() {
mActivity.finishing = false;
- assertEquals("Currently resumed activity be paused removal", FINISH_RESULT_REQUESTED,
- mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
- false /* oomAdj */));
+ assertEquals("Currently resumed activity must be prepared removal", FINISH_RESULT_REQUESTED,
+ mActivity.finishIfPossible("test", false /* oomAdj */));
assertTrue(mActivity.finishing);
assertTrue(mActivity.isInStackLocked());
@@ -725,8 +771,7 @@
mActivity.finishing = false;
mActivity.setState(STOPPED, "test");
assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REQUESTED,
- mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
- false /* oomAdj */));
+ mActivity.finishIfPossible("test", false /* oomAdj */));
assertTrue(mActivity.finishing);
assertTrue(mActivity.isInStackLocked());
}
@@ -735,7 +780,7 @@
* Verify that activity finish request removes activity immediately if it's ready.
*/
@Test
- public void testFinishActivityLocked_removed() {
+ public void testFinishActivityIfPossible_removed() {
// Prepare the activity record to be ready for immediate removal. It should be invisible and
// have no process. Otherwise, request to finish it will send a message to client first.
mActivity.setState(STOPPED, "test");
@@ -745,38 +790,318 @@
// this will cause NPE when updating task's process.
mActivity.app = null;
assertEquals("Activity outside of task/stack cannot be finished", FINISH_RESULT_REMOVED,
- mActivity.finishActivityLocked(0 /* resultCode */, null /* resultData */, "test",
- false /* oomAdj */));
+ mActivity.finishIfPossible("test", false /* oomAdj */));
assertTrue(mActivity.finishing);
assertFalse(mActivity.isInStackLocked());
}
+ /**
+ * Verify that resumed activity is paused due to finish request.
+ */
+ @Test
+ public void testFinishActivityIfPossible_resumedStartsPausing() {
+ mActivity.finishing = false;
+ mActivity.setState(RESUMED, "test");
+ assertEquals("Currently resumed activity must be paused before removal",
+ FINISH_RESULT_REQUESTED, mActivity.finishIfPossible("test", false /* oomAdj */));
+ assertEquals(PAUSING, mActivity.getState());
+ verify(mActivity).setVisibility(eq(false));
+ verify(mActivity.getDisplay().mDisplayContent)
+ .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+ }
+
+ /**
+ * Verify that finish request will be completed immediately for non-resumed activity.
+ */
+ @Test
+ public void testFinishActivityIfPossible_nonResumedFinishCompletesImmediately() {
+ final ActivityState[] states = {INITIALIZING, STARTED, PAUSED, STOPPING, STOPPED};
+ for (ActivityState state : states) {
+ mActivity.finishing = false;
+ mActivity.setState(state, "test");
+ reset(mActivity);
+ assertEquals("Finish must be requested", FINISH_RESULT_REQUESTED,
+ mActivity.finishIfPossible("test", false /* oomAdj */));
+ verify(mActivity).completeFinishing(anyString());
+ }
+ }
+
+ /**
+ * Verify that finishing will not be completed in PAUSING state.
+ */
+ @Test
+ public void testFinishActivityIfPossible_pausing() {
+ mActivity.finishing = false;
+ mActivity.setState(PAUSING, "test");
+ assertEquals("Finish must be requested", FINISH_RESULT_REQUESTED,
+ mActivity.finishIfPossible("test", false /* oomAdj */));
+ verify(mActivity, never()).completeFinishing(anyString());
+ }
+
+ /**
+ * Verify that finish request for resumed activity will prepare an app transition but not
+ * execute it immediately.
+ */
+ @Test
+ public void testFinishActivityIfPossible_visibleResumedPreparesAppTransition() {
+ mActivity.finishing = false;
+ mActivity.visible = true;
+ mActivity.setState(RESUMED, "test");
+ mActivity.finishIfPossible("test", false /* oomAdj */);
+
+ verify(mActivity).setVisibility(eq(false));
+ verify(mActivity.getDisplay().mDisplayContent)
+ .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+ verify(mActivity.getDisplay().mDisplayContent, never()).executeAppTransition();
+ }
+
+ /**
+ * Verify that finish request for paused activity will prepare and execute an app transition.
+ */
+ @Test
+ public void testFinishActivityIfPossible_visibleNotResumedExecutesAppTransition() {
+ mActivity.finishing = false;
+ mActivity.visible = true;
+ mActivity.setState(PAUSED, "test");
+ mActivity.finishIfPossible("test", false /* oomAdj */);
+
+ verify(mActivity).setVisibility(eq(false));
+ verify(mActivity.getDisplay().mDisplayContent)
+ .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+ verify(mActivity.getDisplay().mDisplayContent).executeAppTransition();
+ }
+
+ /**
+ * Verify that finish request for non-visible activity will not prepare any transitions.
+ */
+ @Test
+ public void testFinishActivityIfPossible_nonVisibleNoAppTransition() {
+ // Put an activity on top of test activity to make it invisible and prevent us from
+ // accidentally resuming the topmost one again.
+ new ActivityBuilder(mService).build();
+ mActivity.visible = false;
+ mActivity.setState(STOPPED, "test");
+
+ mActivity.finishIfPossible("test", false /* oomAdj */);
+
+ verify(mActivity.getDisplay().mDisplayContent, never())
+ .prepareAppTransition(eq(TRANSIT_TASK_CLOSE), eq(false) /* alwaysKeepCurrent */);
+ }
+
+ /**
+ * Verify that complete finish request for non-finishing activity is invalid.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testCompleteFinishing_failNotFinishing() {
+ mActivity.finishing = false;
+ mActivity.completeFinishing("test");
+ }
+
+ /**
+ * Verify that complete finish request for resumed activity is invalid.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testCompleteFinishing_failResumed() {
+ mActivity.setState(RESUMED, "test");
+ mActivity.completeFinishing("test");
+ }
+
+ /**
+ * Verify that finish request for pausing activity must be a no-op - activity will finish
+ * once it completes pausing.
+ */
+ @Test
+ public void testCompleteFinishing_pausing() {
+ mActivity.setState(PAUSING, "test");
+ mActivity.finishing = true;
+
+ assertEquals("Activity must not be removed immediately - waiting for paused",
+ mActivity, mActivity.completeFinishing("test"));
+ assertEquals(PAUSING, mActivity.getState());
+ verify(mActivity, never()).destroyIfPossible(anyString());
+ }
+
+ /**
+ * Verify that complete finish request for visible activity must be delayed before the next one
+ * becomes visible.
+ */
+ @Test
+ public void testCompleteFinishing_waitForNextVisible() {
+ final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+ topActivity.visible = true;
+ topActivity.nowVisible = true;
+ topActivity.finishing = true;
+ topActivity.setState(PAUSED, "true");
+ // Mark the bottom activity as not visible, so that we will wait for it before removing
+ // the top one.
+ mActivity.visible = false;
+ mActivity.nowVisible = false;
+ mActivity.setState(STOPPED, "test");
+
+ assertEquals("Activity must not be removed immediately - waiting for next visible",
+ topActivity, topActivity.completeFinishing("test"));
+ assertEquals("Activity must be stopped to make next one visible", STOPPING,
+ topActivity.getState());
+ assertTrue("Activity must be stopped to make next one visible",
+ topActivity.mStackSupervisor.mStoppingActivities.contains(topActivity));
+ verify(topActivity, never()).destroyIfPossible(anyString());
+ }
+
+ /**
+ * Verify that complete finish request for invisible activity must not be delayed.
+ */
+ @Test
+ public void testCompleteFinishing_noWaitForNextVisible_alreadyInvisible() {
+ final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+ topActivity.visible = false;
+ topActivity.nowVisible = false;
+ topActivity.finishing = true;
+ topActivity.setState(PAUSED, "true");
+ // Mark the bottom activity as not visible, so that we would wait for it before removing
+ // the top one.
+ mActivity.visible = false;
+ mActivity.nowVisible = false;
+ mActivity.setState(STOPPED, "test");
+
+ topActivity.completeFinishing("test");
+
+ verify(topActivity).destroyIfPossible(anyString());
+ }
+
+ /**
+ * Verify that paused finishing activity will be added to finishing list and wait for next one
+ * to idle.
+ */
+ @Test
+ public void testCompleteFinishing_waitForIdle() {
+ final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+ topActivity.visible = true;
+ topActivity.nowVisible = true;
+ topActivity.finishing = true;
+ topActivity.setState(PAUSED, "true");
+ // Mark the bottom activity as already visible, so that there is no need to wait for it.
+ mActivity.visible = true;
+ mActivity.nowVisible = true;
+ mActivity.setState(RESUMED, "test");
+
+ topActivity.completeFinishing("test");
+
+ verify(topActivity).addToFinishingAndWaitForIdle();
+ }
+
+ /**
+ * Verify that complete finish request for visible activity must not be delayed if the next one
+ * is already visible and it's not the focused stack.
+ */
+ @Test
+ public void testCompleteFinishing_noWaitForNextVisible_stopped() {
+ final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+ topActivity.visible = false;
+ topActivity.nowVisible = false;
+ topActivity.finishing = true;
+ topActivity.setState(STOPPED, "true");
+ // Mark the bottom activity as already visible, so that there is no need to wait for it.
+ mActivity.visible = true;
+ mActivity.nowVisible = true;
+ mActivity.setState(RESUMED, "test");
+
+ topActivity.completeFinishing("test");
+
+ verify(topActivity).destroyIfPossible(anyString());
+ }
+
+ /**
+ * Verify that complete finish request for visible activity must not be delayed if the next one
+ * is already visible and it's not the focused stack.
+ */
+ @Test
+ public void testCompleteFinishing_noWaitForNextVisible_nonFocusedStack() {
+ final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
+ topActivity.visible = true;
+ topActivity.nowVisible = true;
+ topActivity.finishing = true;
+ topActivity.setState(PAUSED, "true");
+ // Mark the bottom activity as already visible, so that there is no need to wait for it.
+ mActivity.visible = true;
+ mActivity.nowVisible = true;
+ mActivity.setState(RESUMED, "test");
+
+ // Add another stack to become focused and make the activity there visible. This way it
+ // simulates finishing in non-focused stack in split-screen.
+ final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
+ stack.getChildAt(0).getChildAt(0).nowVisible = true;
+
+ topActivity.completeFinishing("test");
+
+ verify(topActivity).destroyIfPossible(anyString());
+ }
+
+ /**
+ * Verify destroy activity request completes successfully.
+ */
+ @Test
+ public void testDestroyIfPossible() {
+ doReturn(false).when(mRootActivityContainer).resumeFocusedStacksTopActivities();
+ spyOn(mStack);
+ mActivity.destroyIfPossible("test");
+
+ assertEquals(DESTROYING, mActivity.getState());
+ assertTrue(mActivity.finishing);
+ verify(mStack).destroyActivityLocked(eq(mActivity), eq(true) /* removeFromApp */,
+ anyString());
+ }
+
+ /**
+ * Verify that complete finish request for visible activity must not destroy it immediately if
+ * it is the last running activity on a display with a home stack. We must wait for home
+ * activity to come up to avoid a black flash in this case.
+ */
+ @Test
+ public void testDestroyIfPossible_lastActivityAboveEmptyHomeStack() {
+ // Empty the home stack.
+ final ActivityStack homeStack = mActivity.getDisplay().getHomeStack();
+ for (TaskRecord t : homeStack.getAllTasks()) {
+ homeStack.removeTask(t, "test", REMOVE_TASK_MODE_DESTROYING);
+ }
+ mActivity.finishing = true;
+ doReturn(false).when(mRootActivityContainer).resumeFocusedStacksTopActivities();
+ spyOn(mStack);
+
+ // Try to destroy the last activity above the home stack.
+ mActivity.destroyIfPossible("test");
+
+ // Verify that the activity was not actually destroyed, but waits for next one to come up
+ // instead.
+ verify(mStack, never()).destroyActivityLocked(eq(mActivity), eq(true) /* removeFromApp */,
+ anyString());
+ assertEquals(FINISHING, mActivity.getState());
+ assertTrue(mActivity.mStackSupervisor.mFinishingActivities.contains(mActivity));
+ }
+
/** Setup {@link #mActivity} as a size-compat-mode-able activity without fixed orientation. */
private void prepareFixedAspectRatioUnresizableActivity() {
setupDisplayContentForCompatDisplayInsets();
- when(mActivity.mAppWindowToken.getOrientationIgnoreVisibility()).thenReturn(
- ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
- mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+ mActivity.info.resizeMode = RESIZE_MODE_UNRESIZEABLE;
mActivity.info.maxAspectRatio = 1.5f;
+ mActivity.visible = true;
ensureActivityConfiguration();
}
private void setupDisplayContentForCompatDisplayInsets() {
final Rect displayBounds = mStack.getDisplay().getBounds();
- final DisplayContent displayContent = setupDisplayAndParentSize(
- displayBounds.width(), displayBounds.height());
- doReturn(mock(DisplayPolicy.class)).when(displayContent).getDisplayPolicy();
- doReturn(mock(WmDisplayCutout.class)).when(displayContent)
- .calculateDisplayCutoutForRotation(anyInt());
+ setupDisplayAndParentSize(displayBounds.width(), displayBounds.height());
}
private DisplayContent setupDisplayAndParentSize(int width, int height) {
- // The DisplayContent is already a mocked object.
final DisplayContent displayContent = mStack.getDisplay().mDisplayContent;
displayContent.mBaseDisplayWidth = width;
displayContent.mBaseDisplayHeight = height;
- mTask.getWindowConfiguration().setAppBounds(0, 0, width, height);
- mTask.getWindowConfiguration().setRotation(ROTATION_0);
+ final Configuration c =
+ new Configuration(mStack.getDisplay().getRequestedOverrideConfiguration());
+ c.windowConfiguration.setBounds(new Rect(0, 0, width, height));
+ c.windowConfiguration.setAppBounds(0, 0, width, height);
+ c.windowConfiguration.setRotation(ROTATION_0);
+ mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
return displayContent;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index e5278d8..fd552fc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.app.Activity.RESULT_CANCELED;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -29,11 +28,10 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityStack.ActivityState.DESTROYING;
import static com.android.server.wm.ActivityStack.ActivityState.FINISHING;
-import static com.android.server.wm.ActivityStack.ActivityState.PAUSED;
import static com.android.server.wm.ActivityStack.ActivityState.PAUSING;
import static com.android.server.wm.ActivityStack.ActivityState.RESUMED;
import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
@@ -44,6 +42,7 @@
import static com.android.server.wm.ActivityStack.STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
import static com.google.common.truth.Truth.assertThat;
@@ -83,8 +82,9 @@
@Before
public void setUp() throws Exception {
mDefaultDisplay = mRootActivityContainer.getDefaultDisplay();
- mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
- true /* onTop */));
+ mStack = mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
+ true /* onTop */);
+ spyOn(mStack);
mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
}
@@ -140,10 +140,8 @@
final ActivityStack destStack = mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final TaskRecord destTask = new TaskBuilder(mSupervisor).setStack(destStack).build();
-
- mTask.removeActivity(r);
- destTask.addActivityToTop(r);
+ mTask.reparent(destStack, true /*toTop*/, REPARENT_MOVE_STACK_TO_FRONT, false, false,
+ "testResumedActivityFromActivityReparenting");
assertNull(mStack.getResumedActivity());
assertEquals(r, destStack.getResumedActivity());
@@ -313,45 +311,50 @@
@Test
public void testShouldBeVisible_Fullscreen() {
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ // Add an activity to the pinned stack so it isn't considered empty for visibility check.
+ final ActivityRecord pinnedActivity = new ActivityBuilder(mService)
+ .setCreateTask(true)
+ .setStack(pinnedStack)
+ .build();
assertTrue(homeStack.shouldBeVisible(null /* starting */));
assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
- final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Home stack shouldn't be visible behind an opaque fullscreen stack, but pinned stack
// should be visible since it is always on-top.
- fullscreenStack.setIsTranslucent(false);
+ doReturn(false).when(fullscreenStack).isStackTranslucent(any());
assertFalse(homeStack.shouldBeVisible(null /* starting */));
assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
assertTrue(fullscreenStack.shouldBeVisible(null /* starting */));
// Home stack should be visible behind a translucent fullscreen stack.
- fullscreenStack.setIsTranslucent(true);
+ doReturn(true).when(fullscreenStack).isStackTranslucent(any());
assertTrue(homeStack.shouldBeVisible(null /* starting */));
assertTrue(pinnedStack.shouldBeVisible(null /* starting */));
}
@Test
public void testShouldBeVisible_SplitScreen() {
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
// Home stack should always be fullscreen for this test.
- homeStack.setSupportsSplitScreen(false);
- final TestActivityStack splitScreenPrimary =
+ doReturn(false).when(homeStack).supportsSplitScreenWindowingMode();
+ final ActivityStack splitScreenPrimary =
createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final TestActivityStack splitScreenSecondary =
+ final ActivityStack splitScreenSecondary =
createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Home stack shouldn't be visible if both halves of split-screen are opaque.
- splitScreenPrimary.setIsTranslucent(false);
- splitScreenSecondary.setIsTranslucent(false);
+ doReturn(false).when(splitScreenPrimary).isStackTranslucent(any());
+ doReturn(false).when(splitScreenSecondary).isStackTranslucent(any());
assertFalse(homeStack.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -362,7 +365,7 @@
splitScreenSecondary.getVisibility(null /* starting */));
// Home stack should be visible if one of the halves of split-screen is translucent.
- splitScreenPrimary.setIsTranslucent(true);
+ doReturn(true).when(splitScreenPrimary).isStackTranslucent(any());
assertTrue(homeStack.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -373,12 +376,12 @@
assertEquals(STACK_VISIBILITY_VISIBLE,
splitScreenSecondary.getVisibility(null /* starting */));
- final TestActivityStack splitScreenSecondary2 =
+ final ActivityStack splitScreenSecondary2 =
createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// First split-screen secondary shouldn't be visible behind another opaque split-split
// secondary.
- splitScreenSecondary2.setIsTranslucent(false);
+ doReturn(false).when(splitScreenSecondary2).isStackTranslucent(any());
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
assertEquals(STACK_VISIBILITY_INVISIBLE,
@@ -388,7 +391,7 @@
// First split-screen secondary should be visible behind another translucent split-screen
// secondary.
- splitScreenSecondary2.setIsTranslucent(true);
+ doReturn(true).when(splitScreenSecondary2).isStackTranslucent(any());
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary2.shouldBeVisible(null /* starting */));
assertEquals(STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,
@@ -396,11 +399,11 @@
assertEquals(STACK_VISIBILITY_VISIBLE,
splitScreenSecondary2.getVisibility(null /* starting */));
- final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
// Split-screen stacks shouldn't be visible behind an opaque fullscreen stack.
- assistantStack.setIsTranslucent(false);
+ doReturn(false).when(assistantStack).isStackTranslucent(any());
assertTrue(assistantStack.shouldBeVisible(null /* starting */));
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -415,7 +418,7 @@
splitScreenSecondary2.getVisibility(null /* starting */));
// Split-screen stacks should be visible behind a translucent fullscreen stack.
- assistantStack.setIsTranslucent(true);
+ doReturn(true).when(assistantStack).isStackTranslucent(any());
assertTrue(assistantStack.shouldBeVisible(null /* starting */));
assertTrue(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertTrue(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -430,9 +433,9 @@
splitScreenSecondary2.getVisibility(null /* starting */));
// Assistant stack shouldn't be visible behind translucent split-screen stack
- assistantStack.setIsTranslucent(false);
- splitScreenPrimary.setIsTranslucent(true);
- splitScreenSecondary2.setIsTranslucent(true);
+ doReturn(false).when(assistantStack).isStackTranslucent(any());
+ doReturn(true).when(splitScreenPrimary).isStackTranslucent(any());
+ doReturn(true).when(splitScreenSecondary2).isStackTranslucent(any());
splitScreenSecondary2.moveToFront("testShouldBeVisible_SplitScreen");
splitScreenPrimary.moveToFront("testShouldBeVisible_SplitScreen");
assertFalse(assistantStack.shouldBeVisible(null /* starting */));
@@ -450,10 +453,10 @@
@Test
public void testGetVisibility_FullscreenBehindTranslucent() {
- final TestActivityStack bottomStack =
+ final ActivityStack bottomStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final TestActivityStack translucentStack =
+ final ActivityStack translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
@@ -465,13 +468,13 @@
@Test
public void testGetVisibility_FullscreenBehindTranslucentAndOpaque() {
- final TestActivityStack bottomStack =
+ final ActivityStack bottomStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final TestActivityStack translucentStack =
+ final ActivityStack translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- final TestActivityStack opaqueStack =
+ final ActivityStack opaqueStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
@@ -483,13 +486,13 @@
@Test
public void testGetVisibility_FullscreenBehindOpaqueAndTranslucent() {
- final TestActivityStack bottomStack =
+ final ActivityStack bottomStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final TestActivityStack opaqueStack =
+ final ActivityStack opaqueStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final TestActivityStack translucentStack =
+ final ActivityStack translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
@@ -502,10 +505,10 @@
@Test
public void testGetVisibility_FullscreenTranslucentBehindTranslucent() {
- final TestActivityStack bottomTranslucentStack =
+ final ActivityStack bottomTranslucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- final TestActivityStack translucentStack =
+ final ActivityStack translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
@@ -517,10 +520,10 @@
@Test
public void testGetVisibility_FullscreenTranslucentBehindOpaque() {
- final TestActivityStack bottomTranslucentStack =
+ final ActivityStack bottomTranslucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
- final TestActivityStack opaqueStack =
+ final ActivityStack opaqueStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
@@ -531,10 +534,10 @@
@Test
public void testGetVisibility_FullscreenBehindTranslucentAndPip() {
- final TestActivityStack bottomStack =
+ final ActivityStack bottomStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
false /* translucent */);
- final TestActivityStack translucentStack =
+ final ActivityStack translucentStack =
createStandardStackForVisibilityTest(WINDOWING_MODE_FULLSCREEN,
true /* translucent */);
final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
@@ -544,22 +547,34 @@
bottomStack.getVisibility(null /* starting */));
assertEquals(STACK_VISIBILITY_VISIBLE,
translucentStack.getVisibility(null /* starting */));
+ // Add an activity to the pinned stack so it isn't considered empty for visibility check.
+ final ActivityRecord pinnedActivity = new ActivityBuilder(mService)
+ .setCreateTask(true)
+ .setStack(pinnedStack)
+ .build();
assertEquals(STACK_VISIBILITY_VISIBLE, pinnedStack.getVisibility(null /* starting */));
}
@Test
public void testShouldBeVisible_Finishing() {
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final TestActivityStack translucentStack = createStackForShouldBeVisibleTest(
+ ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
+ if (topRunningHomeActivity == null) {
+ topRunningHomeActivity = new ActivityBuilder(mService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build();
+ }
+
+ final ActivityStack translucentStack = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- translucentStack.setIsTranslucent(true);
+ doReturn(true).when(translucentStack).isStackTranslucent(any());
assertTrue(homeStack.shouldBeVisible(null /* starting */));
assertTrue(translucentStack.shouldBeVisible(null /* starting */));
- final ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
topRunningHomeActivity.finishing = true;
final ActivityRecord topRunningTranslucentActivity =
translucentStack.topRunningActivityLocked();
@@ -577,13 +592,13 @@
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
mDefaultDisplay.removeChild(mStack);
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- homeStack.setIsTranslucent(false);
- fullscreenStack.setIsTranslucent(false);
+ doReturn(false).when(homeStack).isStackTranslucent(any());
+ doReturn(false).when(fullscreenStack).isStackTranslucent(any());
// Ensure that we don't move the home stack if it is already behind the top fullscreen stack
int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -596,13 +611,13 @@
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() {
mDefaultDisplay.removeChild(mStack);
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- homeStack.setIsTranslucent(false);
- fullscreenStack.setIsTranslucent(true);
+ doReturn(false).when(homeStack).isStackTranslucent(any());
+ doReturn(true).when(fullscreenStack).isStackTranslucent(any());
// Ensure that we don't move the home stack if it is already behind the top fullscreen stack
int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -615,13 +630,13 @@
public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() {
mDefaultDisplay.removeChild(mStack);
- final TestActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- homeStack.setIsTranslucent(false);
- fullscreenStack.setIsTranslucent(false);
+ doReturn(false).when(homeStack).isStackTranslucent(any());
+ doReturn(false).when(fullscreenStack).isStackTranslucent(any());
// Ensure we don't move the home stack if it is already on top
int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -634,20 +649,20 @@
public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() {
mDefaultDisplay.removeChild(mStack);
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- homeStack.setIsTranslucent(false);
- fullscreenStack1.setIsTranslucent(false);
- fullscreenStack2.setIsTranslucent(false);
+ doReturn(false).when(homeStack).isStackTranslucent(any());
+ doReturn(false).when(fullscreenStack1).isStackTranslucent(any());
+ doReturn(false).when(fullscreenStack2).isStackTranslucent(any());
// Ensure that we move the home stack behind the bottom most fullscreen stack, ignoring the
// pinned stack
@@ -661,18 +676,18 @@
testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() {
mDefaultDisplay.removeChild(mStack);
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- homeStack.setIsTranslucent(false);
- fullscreenStack1.setIsTranslucent(false);
- fullscreenStack2.setIsTranslucent(true);
+ doReturn(false).when(homeStack).isStackTranslucent(any());
+ doReturn(false).when(fullscreenStack1).isStackTranslucent(any());
+ doReturn(true).when(fullscreenStack2).isStackTranslucent(any());
// Ensure that we move the home stack behind the bottom most non-translucent fullscreen
// stack
@@ -685,18 +700,18 @@
public void testMoveHomeStackBehindStack_BehindHomeStack() {
mDefaultDisplay.removeChild(mStack);
- final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- homeStack.setIsTranslucent(false);
- fullscreenStack1.setIsTranslucent(false);
- fullscreenStack2.setIsTranslucent(false);
+ doReturn(false).when(homeStack).isStackTranslucent(any());
+ doReturn(false).when(fullscreenStack1).isStackTranslucent(any());
+ doReturn(false).when(fullscreenStack2).isStackTranslucent(any());
// Ensure we don't move the home stack behind itself
int homeStackIndex = mDefaultDisplay.getIndexOf(homeStack);
@@ -708,19 +723,19 @@
public void testMoveHomeStackBehindStack() {
mDefaultDisplay.removeChild(mStack);
- final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack2 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack fullscreenStack3 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack3 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack fullscreenStack4 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack4 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
mDefaultDisplay.moveStackBehindStack(homeStack, fullscreenStack1);
@@ -735,13 +750,13 @@
@Test
public void testSetAlwaysOnTop() {
- final TestActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
final ActivityStack pinnedStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
assertEquals(pinnedStack, mDefaultDisplay.getStackAbove(homeStack));
- final TestActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest(
+ final ActivityStack alwaysOnTopStack = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
alwaysOnTopStack.setAlwaysOnTop(true);
@@ -749,13 +764,13 @@
// Ensure (non-pinned) always on top stack is put below pinned stack.
assertEquals(pinnedStack, mDefaultDisplay.getStackAbove(alwaysOnTopStack));
- final TestActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
+ final ActivityStack nonAlwaysOnTopStack = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
// Ensure non always on top stack is put below always on top stacks.
assertEquals(alwaysOnTopStack, mDefaultDisplay.getStackAbove(nonAlwaysOnTopStack));
- final TestActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
+ final ActivityStack alwaysOnTopStack2 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
alwaysOnTopStack2.setAlwaysOnTop(true);
@@ -780,18 +795,18 @@
@Test
public void testSplitScreenMoveToFront() {
- final TestActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(
+ final ActivityStack splitScreenPrimary = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(
+ final ActivityStack splitScreenSecondary = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
- final TestActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack assistantStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
- splitScreenPrimary.setIsTranslucent(false);
- splitScreenSecondary.setIsTranslucent(false);
- assistantStack.setIsTranslucent(false);
+ doReturn(false).when(splitScreenPrimary).isStackTranslucent(any());
+ doReturn(false).when(splitScreenSecondary).isStackTranslucent(any());
+ doReturn(false).when(assistantStack).isStackTranslucent(any());
assertFalse(splitScreenPrimary.shouldBeVisible(null /* starting */));
assertFalse(splitScreenSecondary.shouldBeVisible(null /* starting */));
@@ -804,18 +819,18 @@
assertFalse(assistantStack.shouldBeVisible(null /* starting */));
}
- private TestActivityStack createStandardStackForVisibilityTest(int windowingMode,
+ private ActivityStack createStandardStackForVisibilityTest(int windowingMode,
boolean translucent) {
- final TestActivityStack stack = createStackForShouldBeVisibleTest(mDefaultDisplay,
+ final ActivityStack stack = createStackForShouldBeVisibleTest(mDefaultDisplay,
windowingMode, ACTIVITY_TYPE_STANDARD, true /* onTop */);
- stack.setIsTranslucent(translucent);
+ doReturn(translucent).when(stack).isStackTranslucent(any());
return stack;
}
@SuppressWarnings("TypeParameterUnusedInFormals")
private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
- final T stack;
+ final ActivityStack stack;
if (activityType == ACTIVITY_TYPE_HOME) {
// Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
stack = mDefaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
@@ -825,11 +840,15 @@
mDefaultDisplay.positionChildAtBottom(stack);
}
} else {
- stack = display.createStack(windowingMode, activityType, onTop);
- final ActivityRecord r = new ActivityBuilder(mService).setUid(0).setStack(stack)
- .setCreateTask(true).build();
+ stack = new StackBuilder(mRootActivityContainer)
+ .setDisplay(display)
+ .setWindowingMode(windowingMode)
+ .setActivityType(activityType)
+ .setOnTop(onTop)
+ .setCreateActivity(true)
+ .build();
}
- return stack;
+ return (T) stack;
}
@Test
@@ -961,17 +980,21 @@
@Test
public void testAdjustFocusedStackToHomeWhenNoActivity() {
+ final ActivityStack homeStask = mDefaultDisplay.getHomeStack();
+ TaskRecord homeTask = homeStask.topTask();
+ if (homeTask == null) {
+ // Create home task if there isn't one.
+ homeTask = new TaskBuilder(mSupervisor).setStack(homeStask).build();
+ }
+
final ActivityRecord topActivity = new ActivityBuilder(mService).setTask(mTask).build();
mStack.moveToFront("testAdjustFocusedStack");
- final ActivityStack homeStask = mDefaultDisplay.getHomeStack();
- final TaskRecord homeTask = homeStask.topTask();
// Simulate that home activity has not been started or is force-stopped.
homeStask.removeTask(homeTask, "testAdjustFocusedStack", REMOVE_TASK_MODE_DESTROYING);
// Finish the only activity.
- topActivity.finishActivityLocked(RESULT_CANCELED /* resultCode */, null /* resultData */,
- "testAdjustFocusedStack", false /* oomAdj */);
+ topActivity.finishIfPossible("testAdjustFocusedStack", false /* oomAdj */);
// Although home stack is empty, it should still be the focused stack.
assertEquals(homeStask, mDefaultDisplay.getFocusedStack());
}
@@ -981,8 +1004,16 @@
final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
+ ActivityRecord activity = homeStack.topRunningActivityLocked();
+ if (activity == null) {
+ activity = new ActivityBuilder(mService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build();
+ }
+
// Home stack should not be destroyed immediately.
- final ActivityRecord activity1 = finishCurrentActivity(homeStack);
+ final ActivityRecord activity1 = finishTopActivity(homeStack);
assertEquals(FINISHING, activity1.getState());
}
@@ -998,25 +1029,24 @@
// There is still an activity1 in stack1 so the activity2 should be added to finishing list
// that will be destroyed until idle.
stack2.getTopActivity().visible = true;
- final ActivityRecord activity2 = finishCurrentActivity(stack2);
+ final ActivityRecord activity2 = finishTopActivity(stack2);
assertEquals(STOPPING, activity2.getState());
assertThat(mSupervisor.mStoppingActivities).contains(activity2);
// The display becomes empty. Since there is no next activity to be idle, the activity
// should be destroyed immediately with updating configuration to restore original state.
- final ActivityRecord activity1 = finishCurrentActivity(stack1);
+ final ActivityRecord activity1 = finishTopActivity(stack1);
assertEquals(DESTROYING, activity1.getState());
verify(mRootActivityContainer).ensureVisibilityAndConfig(eq(null) /* starting */,
eq(display.mDisplayId), anyBoolean(), anyBoolean());
}
- private ActivityRecord finishCurrentActivity(ActivityStack stack) {
+ private ActivityRecord finishTopActivity(ActivityStack stack) {
final ActivityRecord activity = stack.topRunningActivityLocked();
assertNotNull(activity);
- activity.setState(PAUSED, "finishCurrentActivity");
+ activity.setState(STOPPED, "finishTopActivity");
activity.makeFinishingLocked();
- activity.finishCurrentActivityLocked(ActivityRecord.FINISH_AFTER_VISIBLE,
- false /* oomAdj */, "finishCurrentActivity");
+ activity.completeFinishing("finishTopActivity");
return activity;
}
@@ -1068,7 +1098,7 @@
public void testStackOrderChangedOnPositionStack() {
StackOrderChangedListener listener = new StackOrderChangedListener();
try {
- final TestActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
+ final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
true /* onTop */);
mDefaultDisplay.registerStackOrderChangedListener(listener);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index 3d94467..81fbfe4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -79,6 +79,7 @@
import androidx.test.filters.SmallTest;
import com.android.server.wm.LaunchParamsController.LaunchParamsModifier;
+import com.android.server.wm.utils.MockTracker;
import org.junit.Before;
import org.junit.Test;
@@ -186,6 +187,19 @@
verifyStartActivityPreconditions(preconditions, 0 /*launchFlags*/, expectedResult);
}
+ private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
+ int expectedResult) {
+ // We track mocks created here because this is used in a single test
+ // (testStartActivityPreconditions) as a specific case, and mocks created inside it won't be
+ // used for other cases. To avoid extensive memory usage, we clean up all used mocks after
+ // each case. This is necessary because usually we only clean up mocks after a test
+ // finishes, but this test creates too many mocks that the intermediate memory usage can be
+ // ~0.8 GiB and thus very susceptible to OutOfMemoryException.
+ try (MockTracker tracker = new MockTracker()) {
+ verifyStartActivityPreconditionsUntracked(preconditions, launchFlags, expectedResult);
+ }
+ }
+
/**
* Excercises how the {@link ActivityStarter} reacts to various preconditions. The caller
* provides a bitmask of all the set conditions (such as {@link #PRECONDITION_NO_CALLER_APP})
@@ -197,7 +211,7 @@
* @param launchFlags The launch flags to be provided by the launch {@link Intent}.
* @param expectedResult The expected result from the launch.
*/
- private void verifyStartActivityPreconditions(int preconditions, int launchFlags,
+ private void verifyStartActivityPreconditionsUntracked(int preconditions, int launchFlags,
int expectedResult) {
final ActivityTaskManagerService service = mService;
final IPackageManager packageManager = mock(IPackageManager.class);
@@ -329,9 +343,6 @@
any(), any(), any(), anyInt(), anyInt(), anyInt(), any(),
anyBoolean(), anyBoolean(), any(), any(), any());
- // Use factory that only returns spy task.
- mockTaskRecordFactory();
-
if (mockGetLaunchStack) {
// Instrument the stack and task used.
final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
@@ -482,7 +493,7 @@
@Test
public void testTaskModeViolation() {
final ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
- ((TestActivityDisplay) display).removeAllTasks();
+ display.removeAllTasks();
assertNoTasks(display);
final ActivityStarter starter = prepareStarter(0);
@@ -676,18 +687,27 @@
doReturn(isCallingUidDeviceOwner).when(mService).isDeviceOwner(callingUid);
final ActivityOptions options = spy(ActivityOptions.makeBasic());
+ ActivityRecord[] outActivity = new ActivityRecord[1];
ActivityStarter starter = prepareStarter(FLAG_ACTIVITY_NEW_TASK)
.setCallingPackage("com.whatever.dude")
.setCaller(caller)
.setCallingUid(callingUid)
.setRealCallingUid(realCallingUid)
- .setActivityOptions(new SafeActivityOptions(options));
+ .setActivityOptions(new SafeActivityOptions(options))
+ .setOutActivity(outActivity);
final int result = starter.setReason("testBackgroundActivityStarts_" + name).execute();
assertEquals(ActivityStarter.getExternalResult(
shouldHaveAborted ? START_ABORTED : START_SUCCESS), result);
verify(options, times(shouldHaveAborted ? 1 : 0)).abort();
+
+ final ActivityRecord startedActivity = outActivity[0];
+ if (startedActivity != null && startedActivity.getTaskRecord() != null) {
+ // Remove the activity so it doesn't interfere with with subsequent activity launch
+ // tests from this method.
+ startedActivity.getTaskRecord().removeActivity(startedActivity);
+ }
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index d8a9bb0..297aa7e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -45,8 +45,7 @@
/** Verify that activity is finished correctly upon request. */
@Test
public void testActivityFinish() {
- final TestActivityStack stack =
- (TestActivityStack) new StackBuilder(mRootActivityContainer).build();
+ final ActivityStack stack = new StackBuilder(mRootActivityContainer).build();
final ActivityRecord activity = stack.getChildAt(0).getTopActivity();
assertTrue("Activity must be finished", mService.finishActivity(activity.appToken,
0 /* resultCode */, null /* resultData */,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index ab2da2b..a5dc241 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -16,96 +16,58 @@
package com.android.server.wm;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.server.wm.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
-import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
-import android.app.ActivityManagerInternal;
import android.app.ActivityOptions;
-import android.app.AppOpsManager;
import android.app.IApplicationThread;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.res.Configuration;
import android.graphics.Rect;
-import android.hardware.display.DisplayManager;
-import android.hardware.display.DisplayManagerGlobal;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.PowerManager;
-import android.os.Process;
import android.os.UserHandle;
import android.service.voice.IVoiceInteractionSession;
import android.testing.DexmakerShareClassLoaderRule;
-import android.view.Display;
import android.view.DisplayInfo;
-import com.android.internal.app.IVoiceInteractor;
import com.android.server.AttributeCache;
-import com.android.server.ServiceThread;
-import com.android.server.am.ActivityManagerService;
-import com.android.server.am.PendingIntentController;
-import com.android.server.appop.AppOpsService;
-import com.android.server.firewall.IntentFirewall;
-import com.android.server.policy.PermissionPolicyInternal;
-import com.android.server.uri.UriGrantsManagerInternal;
-import com.android.server.wm.TaskRecord.TaskRecordFactory;
-import com.android.server.wm.utils.MockTracker;
-import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
-import org.mockito.invocation.InvocationOnMock;
-
-import java.io.File;
-import java.util.List;
-import java.util.function.Consumer;
/**
* A base class to handle common operations in activity related unit tests.
*/
class ActivityTestsBase {
- private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
@Rule
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
+ @Rule
+ public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule();
+
final Context mContext = getInstrumentation().getTargetContext();
- final TestInjector mTestInjector = new TestInjector(mContext);
ActivityTaskManagerService mService;
RootActivityContainer mRootActivityContainer;
ActivityStackSupervisor mSupervisor;
- private MockTracker mMockTracker;
-
// Default package name
static final String DEFAULT_COMPONENT_PACKAGE_NAME = "com.foo";
@@ -119,37 +81,18 @@
@Before
public void setUpBase() {
- mMockTracker = new MockTracker();
-
- mTestInjector.setUp();
-
- mService = new TestActivityTaskManagerService(mContext);
+ mService = mSystemServicesTestRule.getActivityTaskManagerService();
mSupervisor = mService.mStackSupervisor;
mRootActivityContainer = mService.mRootActivityContainer;
}
- @After
- public void tearDownBase() {
- mTestInjector.tearDown();
- if (mService != null) {
- mService.setWindowManager(null);
- mService = null;
- }
- if (sMockWindowManagerService != null) {
- reset(sMockWindowManagerService);
- }
-
- mMockTracker.close();
- mMockTracker = null;
- }
-
/** Creates a {@link TestActivityDisplay}. */
TestActivityDisplay createNewActivityDisplay() {
- return TestActivityDisplay.create(mSupervisor, sNextDisplayId++);
+ return TestActivityDisplay.create(mSupervisor);
}
TestActivityDisplay createNewActivityDisplay(DisplayInfo info) {
- return TestActivityDisplay.create(mSupervisor, sNextDisplayId++, info);
+ return TestActivityDisplay.create(mSupervisor, info);
}
/** Creates and adds a {@link TestActivityDisplay} to supervisor at the given position. */
@@ -166,25 +109,9 @@
return display;
}
- /**
- * Delegates task creation to {@link #TaskBuilder} to avoid the dependency of window hierarchy
- * when starting activity in unit tests.
- */
- void mockTaskRecordFactory(Consumer<TaskBuilder> taskBuilderSetup) {
- final TaskBuilder taskBuilder = new TaskBuilder(mSupervisor).setCreateStack(false);
- if (taskBuilderSetup != null) {
- taskBuilderSetup.accept(taskBuilder);
- }
- final TaskRecord task = taskBuilder.build();
- final TaskRecordFactory factory = mock(TaskRecordFactory.class);
- TaskRecord.setTaskRecordFactory(factory);
- doReturn(task).when(factory).create(any() /* service */, anyInt() /* taskId */,
- any() /* info */, any() /* intent */, any() /* voiceSession */,
- any() /* voiceInteractor */);
- }
-
- void mockTaskRecordFactory() {
- mockTaskRecordFactory(null /* taskBuilderSetup */);
+ /** Sets the default minimum task size to 1 so that tests can use small task sizes */
+ public void removeGlobalMinSizeRestriction() {
+ mService.mRootActivityContainer.mDefaultMinSizeOfResizeableTaskDp = 1;
}
/**
@@ -204,6 +131,11 @@
private ActivityStack mStack;
private int mActivityFlags;
private int mLaunchMode;
+ private int mResizeMode = RESIZE_MODE_RESIZEABLE;
+ private float mMaxAspectRatio;
+ private int mScreenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+ private boolean mLaunchTaskBehind;
+ private int mConfigChanges;
ActivityBuilder(ActivityTaskManagerService service) {
mService = service;
@@ -254,6 +186,31 @@
return this;
}
+ ActivityBuilder setResizeMode(int resizeMode) {
+ mResizeMode = resizeMode;
+ return this;
+ }
+
+ ActivityBuilder setMaxAspectRatio(float maxAspectRatio) {
+ mMaxAspectRatio = maxAspectRatio;
+ return this;
+ }
+
+ ActivityBuilder setScreenOrientation(int screenOrientation) {
+ mScreenOrientation = screenOrientation;
+ return this;
+ }
+
+ ActivityBuilder setLaunchTaskBehind(boolean launchTaskBehind) {
+ mLaunchTaskBehind = launchTaskBehind;
+ return this;
+ }
+
+ ActivityBuilder setConfigChanges(int configChanges) {
+ mConfigChanges = configChanges;
+ return this;
+ }
+
ActivityRecord build() {
if (mComponent == null) {
final int id = sCurrentActivityId++;
@@ -279,24 +236,31 @@
}
aInfo.flags |= mActivityFlags;
aInfo.launchMode = mLaunchMode;
+ aInfo.resizeMode = mResizeMode;
+ aInfo.maxAspectRatio = mMaxAspectRatio;
+ aInfo.screenOrientation = mScreenOrientation;
+ aInfo.configChanges |= mConfigChanges;
+
+ ActivityOptions options = null;
+ if (mLaunchTaskBehind) {
+ options = ActivityOptions.makeTaskLaunchBehind();
+ }
final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
0 /* launchedFromPid */, 0, null, intent, null,
aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
0 /* reqCode */, false /*componentSpecified*/, false /* rootVoiceInteraction */,
- mService.mStackSupervisor, null /* options */, null /* sourceRecord */);
+ mService.mStackSupervisor, options, null /* sourceRecord */);
spyOn(activity);
- activity.mAppWindowToken = mock(AppWindowToken.class);
- doCallRealMethod().when(activity.mAppWindowToken).getOrientationIgnoreVisibility();
- doCallRealMethod().when(activity.mAppWindowToken)
- .setOrientation(anyInt(), any(), any());
- doCallRealMethod().when(activity.mAppWindowToken).setOrientation(anyInt());
- doNothing().when(activity).removeWindowContainer();
- doReturn(mock(Configuration.class)).when(activity.mAppWindowToken)
- .getRequestedOverrideConfiguration();
-
if (mTaskRecord != null) {
- mTaskRecord.addActivityToTop(activity);
+ // fullscreen value is normally read from resources in ctor, so for testing we need
+ // to set it somewhere else since we can't mock resources.
+ activity.fullscreen = true;
+ activity.setTask(mTaskRecord);
+ activity.createAppWindowToken();
+ spyOn(activity.mAppWindowToken);
+ // Make visible by default...
+ activity.mAppWindowToken.setHidden(false);
}
final WindowProcessController wpc = new WindowProcessController(mService,
@@ -305,6 +269,9 @@
mock(WindowProcessListener.class));
wpc.setThread(mock(IApplicationThread.class));
activity.setProcess(wpc);
+
+ // Resume top activities to make sure all other signals in the system are connected.
+ mService.mRootActivityContainer.resumeFocusedStacksTopActivities();
return activity;
}
}
@@ -313,16 +280,13 @@
* Builder for creating new tasks.
*/
protected static class TaskBuilder {
- // Default package name
- static final String DEFAULT_PACKAGE = "com.bar";
-
private final ActivityStackSupervisor mSupervisor;
private ComponentName mComponent;
private String mPackage;
private int mFlags = 0;
// Task id 0 is reserved in ARC for the home app.
- private int mTaskId = 1;
+ private int mTaskId = SystemServicesTestRule.sNextTaskId++;
private int mUserId = 0;
private IVoiceInteractionSession mVoiceSession;
private boolean mCreateStack = true;
@@ -381,6 +345,7 @@
if (mStack == null && mCreateStack) {
mStack = mSupervisor.mRootActivityContainer.getDefaultDisplay().createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
+ spyOn(mStack);
}
final ActivityInfo aInfo = new ActivityInfo();
@@ -396,450 +361,22 @@
intent.setComponent(mComponent);
intent.setFlags(mFlags);
- final TestTaskRecord task = new TestTaskRecord(mSupervisor.mService, mTaskId, aInfo,
+ final TaskRecord task = new TaskRecord(mSupervisor.mService, mTaskId, aInfo,
intent /*intent*/, mVoiceSession, null /*_voiceInteractor*/);
+ spyOn(task);
task.userId = mUserId;
if (mStack != null) {
mStack.moveToFront("test");
mStack.addTask(task, true, "creating test task");
- task.setStack(mStack);
- task.setTask();
- mStack.getTaskStack().addChild(task.mTask, 0);
+ task.createTask(true, true);
+ spyOn(task.mTask);
}
task.touchActiveTime();
return task;
}
-
- private static class TestTaskRecord extends TaskRecord {
- TestTaskRecord(ActivityTaskManagerService service, int taskId, ActivityInfo info,
- Intent intent, IVoiceInteractionSession voiceSession,
- IVoiceInteractor voiceInteractor) {
- super(service, taskId, info, intent, voiceSession, voiceInteractor);
- }
-
- @Override
- void createTask(boolean onTop, boolean showForAllUsers) {
- setTask();
- }
-
- void setTask() {
- Task mockTask = mock(Task.class);
- mockTask.mTaskRecord = this;
- doCallRealMethod().when(mockTask).onDescendantOrientationChanged(any(), any());
- setTask(mock(Task.class));
- }
- }
- }
-
- protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
- private PackageManagerInternal mPmInternal;
- private PermissionPolicyInternal mPermissionPolicyInternal;
-
- // ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
- // We keep the reference in order to prevent creating it twice.
- ActivityStackSupervisor mTestStackSupervisor;
-
- ActivityDisplay mDefaultDisplay;
- AppOpsService mAppOpsService;
-
- TestActivityTaskManagerService(Context context) {
- super(context);
- spyOn(this);
-
- mUgmInternal = mock(UriGrantsManagerInternal.class);
- mAppOpsService = mock(AppOpsService.class);
-
- // Make sure permission checks aren't overridden.
- doReturn(AppOpsManager.MODE_DEFAULT)
- .when(mAppOpsService).noteOperation(anyInt(), anyInt(), anyString());
-
- mSupportsMultiWindow = true;
- mSupportsMultiDisplay = true;
- mSupportsSplitScreenMultiWindow = true;
- mSupportsFreeformWindowManagement = true;
- mSupportsPictureInPicture = true;
-
- final TestActivityManagerService am =
- new TestActivityManagerService(mTestInjector, this);
-
- spyOn(getLifecycleManager());
- spyOn(getLockTaskController());
- spyOn(getTaskChangeNotificationController());
- doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
- // allow background activity starts by default
- doReturn(true).when(this).isBackgroundActivityStartsEnabled();
- doNothing().when(this).updateCpuStats();
- }
-
- void setup(IntentFirewall intentFirewall, PendingIntentController intentController,
- ActivityManagerInternal amInternal, WindowManagerService wm, Looper looper) {
- mAmInternal = amInternal;
- initialize(intentFirewall, intentController, looper);
- initRootActivityContainerMocks(wm);
- setWindowManager(wm);
- createDefaultDisplay();
- }
-
- void initRootActivityContainerMocks(WindowManagerService wm) {
- spyOn(mRootActivityContainer);
- mRootActivityContainer.setWindowContainer(mock(RootWindowContainer.class));
- mRootActivityContainer.mWindowManager = wm;
- mRootActivityContainer.mDisplayManager =
- (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
- doNothing().when(mRootActivityContainer).setWindowManager(any());
- // Invoked during {@link ActivityStack} creation.
- doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay();
- // Always keep things awake.
- doReturn(true).when(mRootActivityContainer).hasAwakeDisplay();
- // Called when moving activity to pinned stack.
- doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(),
- anyBoolean());
- }
-
- void createDefaultDisplay() {
- // Create a default display and put a home stack on it so that we'll always have
- // something focusable.
- mDefaultDisplay = TestActivityDisplay.create(mStackSupervisor, DEFAULT_DISPLAY);
- spyOn(mDefaultDisplay);
- mRootActivityContainer.addChild(mDefaultDisplay, ActivityDisplay.POSITION_TOP);
- mDefaultDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
- final TaskRecord task = new TaskBuilder(mStackSupervisor)
- .setStack(mDefaultDisplay.getHomeStack()).build();
- new ActivityBuilder(this).setTask(task).build();
-
- doReturn(mDefaultDisplay).when(mRootActivityContainer).getDefaultDisplay();
- }
-
- @Override
- int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
- return userId;
- }
-
- @Override
- AppOpsService getAppOpsService() {
- return mAppOpsService;
- }
-
- @Override
- void updateCpuStats() {
- }
-
- @Override
- void updateBatteryStats(ActivityRecord component, boolean resumed) {
- }
-
- @Override
- void updateActivityUsageStats(ActivityRecord activity, int event) {
- }
-
- @Override
- protected ActivityStackSupervisor createStackSupervisor() {
- if (mTestStackSupervisor == null) {
- mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper());
- }
- return mTestStackSupervisor;
- }
-
- @Override
- PackageManagerInternal getPackageManagerInternalLocked() {
- if (mPmInternal == null) {
- mPmInternal = mock(PackageManagerInternal.class);
- doReturn(false)
- .when(mPmInternal)
- .isPermissionsReviewRequired(anyString(), anyInt());
- }
- return mPmInternal;
- }
-
- @Override
- PermissionPolicyInternal getPermissionPolicyInternal() {
- if (mPermissionPolicyInternal == null) {
- mPermissionPolicyInternal = mock(PermissionPolicyInternal.class);
- doReturn(true).when(mPermissionPolicyInternal).checkStartActivity(any(), anyInt(),
- any());
- }
- return mPermissionPolicyInternal;
- }
- }
-
- private static class TestInjector extends ActivityManagerService.Injector {
- private ServiceThread mHandlerThread;
-
- TestInjector(Context context) {
- super(context);
- }
-
- @Override
- public AppOpsService getAppOpsService(File file, Handler handler) {
- return null;
- }
-
- @Override
- public Handler getUiHandler(ActivityManagerService service) {
- return mHandlerThread.getThreadHandler();
- }
-
- @Override
- public boolean isNetworkRestrictedForUid(int uid) {
- return false;
- }
-
- void setUp() {
- mHandlerThread = new ServiceThread("ActivityTestsThread",
- Process.THREAD_PRIORITY_DEFAULT, true /* allowIo */);
- mHandlerThread.start();
- }
-
- void tearDown() {
- // Make sure there are no running messages and then quit the thread so the next test
- // won't be affected.
- mHandlerThread.getThreadHandler().runWithScissors(mHandlerThread::quit,
- 0 /* timeout */);
- }
- }
-
- // TODO: Replace this with a mock object since we are no longer in AMS package.
- /**
- * An {@link ActivityManagerService} subclass which provides a test
- * {@link ActivityStackSupervisor}.
- */
- class TestActivityManagerService extends ActivityManagerService {
-
- TestActivityManagerService(TestInjector testInjector, TestActivityTaskManagerService atm) {
- super(testInjector, testInjector.mHandlerThread);
- spyOn(this);
-
- mWindowManager = prepareMockWindowManager();
- mUgmInternal = mock(UriGrantsManagerInternal.class);
-
- atm.setup(mIntentFirewall, mPendingIntentController, new LocalService(), mWindowManager,
- testInjector.mHandlerThread.getLooper());
-
- mActivityTaskManager = atm;
- mAtmInternal = atm.mInternal;
-
- doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
- PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
- doReturn(mockPackageManager).when(this).getPackageManagerInternalLocked();
- doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
- doNothing().when(this).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
- }
- }
-
- /**
- * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
- * setup not available in the test environment. Also specifies an injector for
- */
- protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
- private KeyguardController mKeyguardController;
-
- TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
- super(service, looper);
- spyOn(this);
- mWindowManager = prepareMockWindowManager();
- mKeyguardController = mock(KeyguardController.class);
-
- // Do not schedule idle that may touch methods outside the scope of the test.
- doNothing().when(this).scheduleIdleLocked();
- doNothing().when(this).scheduleIdleTimeoutLocked(any());
- // unit test version does not handle launch wake lock
- doNothing().when(this).acquireLaunchWakelock();
- doReturn(mKeyguardController).when(this).getKeyguardController();
-
- mLaunchingActivityWakeLock = mock(PowerManager.WakeLock.class);
-
- initialize();
- }
-
- @Override
- public KeyguardController getKeyguardController() {
- return mKeyguardController;
- }
-
- @Override
- void setWindowManager(WindowManagerService wm) {
- mWindowManager = wm;
- }
- }
-
- protected static class TestActivityDisplay extends ActivityDisplay {
- private final ActivityStackSupervisor mSupervisor;
-
- static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) {
- return create(supervisor, displayId, new DisplayInfo());
- }
-
- static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId,
- DisplayInfo info) {
- if (displayId == DEFAULT_DISPLAY) {
- return new TestActivityDisplay(supervisor,
- supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId));
- }
- final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
- info, DEFAULT_DISPLAY_ADJUSTMENTS);
- return new TestActivityDisplay(supervisor, display);
- }
-
- TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
- super(supervisor.mService.mRootActivityContainer, display);
- // Normally this comes from display-properties as exposed by WM. Without that, just
- // hard-code to FULLSCREEN for tests.
- setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- mSupervisor = supervisor;
- }
-
- @SuppressWarnings("TypeParameterUnusedInFormals")
- @Override
- ActivityStack createStackUnchecked(int windowingMode, int activityType,
- int stackId, boolean onTop) {
- return new StackBuilder(mSupervisor.mRootActivityContainer).setDisplay(this)
- .setWindowingMode(windowingMode).setActivityType(activityType)
- .setStackId(stackId).setOnTop(onTop).setCreateActivity(false).build();
- }
-
- @Override
- protected DisplayContent createDisplayContent() {
- final DisplayContent displayContent = mock(DisplayContent.class);
- DockedStackDividerController divider = mock(DockedStackDividerController.class);
- doReturn(divider).when(displayContent).getDockedDividerController();
- return displayContent;
- }
-
- void removeAllTasks() {
- for (int i = 0; i < getChildCount(); i++) {
- final ActivityStack stack = getChildAt(i);
- for (TaskRecord task : (List<TaskRecord>) stack.getAllTasks()) {
- stack.removeTask(task, "removeAllTasks", REMOVE_TASK_MODE_DESTROYING);
- }
- }
- }
- }
-
- private static WindowManagerService sMockWindowManagerService;
-
- private static WindowManagerService prepareMockWindowManager() {
- if (sMockWindowManagerService == null) {
- sMockWindowManagerService = mock(WindowManagerService.class);
- }
-
- sMockWindowManagerService.mRoot = mock(RootWindowContainer.class);
-
- doAnswer((InvocationOnMock invocationOnMock) -> {
- final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
- if (runnable != null) {
- runnable.run();
- }
- return null;
- }).when(sMockWindowManagerService).inSurfaceTransaction(any());
-
- return sMockWindowManagerService;
- }
-
- /**
- * Overridden {@link ActivityStack} that tracks test metrics, such as the number of times a
- * method is called. Note that its functionality depends on the implementations of the
- * construction arguments.
- */
- protected static class TestActivityStack
- extends ActivityStack {
- private int mOnActivityRemovedFromStackCount = 0;
-
- static final int IS_TRANSLUCENT_UNSET = 0;
- static final int IS_TRANSLUCENT_FALSE = 1;
- static final int IS_TRANSLUCENT_TRUE = 2;
- private int mIsTranslucent = IS_TRANSLUCENT_UNSET;
-
- static final int SUPPORTS_SPLIT_SCREEN_UNSET = 0;
- static final int SUPPORTS_SPLIT_SCREEN_FALSE = 1;
- static final int SUPPORTS_SPLIT_SCREEN_TRUE = 2;
- private int mSupportsSplitScreen = SUPPORTS_SPLIT_SCREEN_UNSET;
-
- TestActivityStack(ActivityDisplay display, int stackId, ActivityStackSupervisor supervisor,
- int windowingMode, int activityType, boolean onTop, boolean createActivity) {
- super(display, stackId, supervisor, windowingMode, activityType, onTop);
- if (createActivity) {
- new ActivityBuilder(mService).setCreateTask(true).setStack(this).build();
- if (onTop) {
- // We move the task to front again in order to regain focus after activity
- // added to the stack. Or {@link ActivityDisplay#mPreferredTopFocusableStack}
- // could be other stacks (e.g. home stack).
- moveToFront("createActivityStack");
- } else {
- moveToBack("createActivityStack", null);
- }
- }
- }
-
- @Override
- void onActivityRemovedFromStack(ActivityRecord r) {
- mOnActivityRemovedFromStackCount++;
- super.onActivityRemovedFromStack(r);
- }
-
- // Returns the number of times {@link #onActivityRemovedFromStack} has been called
- int onActivityRemovedFromStackInvocationCount() {
- return mOnActivityRemovedFromStackCount;
- }
-
- @Override
- protected void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
- mTaskStack = mock(TaskStack.class);
-
- // Primary pinned stacks require a non-empty out bounds to be set or else all tasks
- // will be moved to the full screen stack.
- if (getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
- outBounds.set(0, 0, 100, 100);
- }
- }
-
- @Override
- TaskStack getTaskStack() {
- return mTaskStack;
- }
-
- void setIsTranslucent(boolean isTranslucent) {
- mIsTranslucent = isTranslucent ? IS_TRANSLUCENT_TRUE : IS_TRANSLUCENT_FALSE;
- }
-
- @Override
- boolean isStackTranslucent(ActivityRecord starting) {
- switch (mIsTranslucent) {
- case IS_TRANSLUCENT_TRUE:
- return true;
- case IS_TRANSLUCENT_FALSE:
- return false;
- case IS_TRANSLUCENT_UNSET:
- default:
- return super.isStackTranslucent(starting);
- }
- }
-
- void setSupportsSplitScreen(boolean supportsSplitScreen) {
- mSupportsSplitScreen = supportsSplitScreen
- ? SUPPORTS_SPLIT_SCREEN_TRUE : SUPPORTS_SPLIT_SCREEN_FALSE;
- }
-
- @Override
- public boolean supportsSplitScreenWindowingMode() {
- switch (mSupportsSplitScreen) {
- case SUPPORTS_SPLIT_SCREEN_TRUE:
- return true;
- case SUPPORTS_SPLIT_SCREEN_FALSE:
- return false;
- case SUPPORTS_SPLIT_SCREEN_UNSET:
- default:
- return super.supportsSplitScreenWindowingMode();
- }
- }
-
- @Override
- void startActivityLocked(ActivityRecord r, ActivityRecord focusedTopActivity,
- boolean newTask, boolean keepCurTransition,
- ActivityOptions options) {
- }
}
static class StackBuilder {
@@ -886,27 +423,45 @@
return this;
}
- @SuppressWarnings("TypeParameterUnusedInFormals")
ActivityStack build() {
final int stackId = mStackId >= 0 ? mStackId : mDisplay.getNextStackId();
+ final ActivityStack stack;
+ final ActivityStackSupervisor supervisor = mRootActivityContainer.mStackSupervisor;
if (mWindowingMode == WINDOWING_MODE_PINNED) {
- return new ActivityStack(mDisplay, stackId, mRootActivityContainer.mStackSupervisor,
+ stack = new ActivityStack(mDisplay, stackId, supervisor,
mWindowingMode, ACTIVITY_TYPE_STANDARD, mOnTop) {
@Override
Rect getDefaultPictureInPictureBounds(float aspectRatio) {
return new Rect(50, 50, 100, 100);
}
-
- @Override
- void createTaskStack(int displayId, boolean onTop, Rect outBounds) {
- mTaskStack = mock(TaskStack.class);
- }
};
} else {
- return new TestActivityStack(mDisplay, stackId,
- mRootActivityContainer.mStackSupervisor, mWindowingMode,
- mActivityType, mOnTop, mCreateActivity);
+ stack = new ActivityStack(mDisplay, stackId, supervisor,
+ mWindowingMode, mActivityType, mOnTop);
+
+ if (mCreateActivity) {
+ new ActivityBuilder(supervisor.mService)
+ .setCreateTask(true)
+ .setStack(stack)
+ .build();
+ if (mOnTop) {
+ // We move the task to front again in order to regain focus after activity
+ // added to the stack.
+ // Or {@link ActivityDisplay#mPreferredTopFocusableStack} could be other
+ // stacks (e.g. home stack).
+ stack.moveToFront("createActivityStack");
+ } else {
+ stack.moveToBack("createActivityStack", null);
+ }
+ }
}
+
+ spyOn(stack);
+ spyOn(stack.mTaskStack);
+ doNothing().when(stack).startActivityLocked(
+ any(), any(), anyBoolean(), anyBoolean(), any());
+
+ return stack;
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 20379a2..e71c8f4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -60,7 +60,7 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- translucentOpening.setFillsParent(false);
+ translucentOpening.setOccludesParent(false);
translucentOpening.setHidden(true);
mDisplayContent.mOpeningApps.add(behind);
mDisplayContent.mOpeningApps.add(translucentOpening);
@@ -78,7 +78,7 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final AppWindowToken translucentClosing = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- translucentClosing.setFillsParent(false);
+ translucentClosing.setOccludesParent(false);
mDisplayContent.mClosingApps.add(translucentClosing);
assertEquals(WindowManager.TRANSIT_TRANSLUCENT_ACTIVITY_CLOSE,
mAppTransitionController.maybeUpdateTransitToTranslucentAnim(
@@ -94,7 +94,7 @@
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final AppWindowToken translucentOpening = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
- translucentOpening.setFillsParent(false);
+ translucentOpening.setOccludesParent(false);
translucentOpening.setHidden(true);
mDisplayContent.mOpeningApps.add(behind);
mDisplayContent.mOpeningApps.add(translucentOpening);
@@ -110,10 +110,10 @@
synchronized (mWm.mGlobalLock) {
final AppWindowToken opening = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
- opening.setFillsParent(false);
+ opening.setOccludesParent(false);
final AppWindowToken closing = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
- closing.setFillsParent(false);
+ closing.setOccludesParent(false);
Task task = opening.getTask();
mDisplayContent.mOpeningApps.add(opening);
mDisplayContent.mClosingApps.add(closing);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index d1dc382..c162b6a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -30,12 +30,14 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import android.graphics.Rect;
import android.os.IBinder;
@@ -195,6 +197,7 @@
@Test
public void testCancelRemoteAnimationWhenFreeze() {
final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+ doReturn(false).when(dc).onDescendantOrientationChanged(any(), any());
final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
dc, "exiting app");
final AppWindowToken exitingAppToken = exitingAppWindow.mAppToken;
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
index d9566a3..e387e18 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenTests.java
@@ -36,7 +36,7 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_BEFORE_ANIM;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
@@ -152,10 +152,6 @@
@Test
@FlakyTest(bugId = 131005232)
public void testLandscapeSeascapeRotationByApp() {
- // Some plumbing to get the service ready for rotation updates.
- mWm.mDisplayReady = true;
- mWm.mDisplayEnabled = true;
-
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
attrs.setTitle("AppWindow");
@@ -185,25 +181,21 @@
@Test
public void testLandscapeSeascapeRotationByPolicy() {
- // Some plumbing to get the service ready for rotation updates.
- mWm.mDisplayReady = true;
- mWm.mDisplayEnabled = true;
-
- final DisplayRotation spiedRotation = spy(mDisplayContent.getDisplayRotation());
- mDisplayContent.setDisplayRotation(spiedRotation);
+ final DisplayRotation displayRotation = mDisplayContent.getDisplayRotation();
+ spyOn(displayRotation);
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(
TYPE_BASE_APPLICATION);
- attrs.setTitle("AppWindow");
+ attrs.setTitle("RotationByPolicy");
final WindowTestUtils.TestWindowState appWindow = createWindowState(attrs, mToken);
mToken.addWindow(appWindow);
// Set initial orientation and update.
- performRotation(spiedRotation, Surface.ROTATION_90);
+ performRotation(displayRotation, Surface.ROTATION_90);
appWindow.mResizeReported = false;
// Update the rotation to perform 180 degree rotation and check that resize was reported.
- performRotation(spiedRotation, Surface.ROTATION_270);
+ performRotation(displayRotation, Surface.ROTATION_270);
assertTrue(appWindow.mResizeReported);
appWindow.removeImmediately();
@@ -211,14 +203,7 @@
private void performRotation(DisplayRotation spiedRotation, int rotationToReport) {
doReturn(rotationToReport).when(spiedRotation).rotationForOrientation(anyInt(), anyInt());
- int oldRotation = mDisplayContent.getRotation();
mWm.updateRotation(false, false);
- // Must manually apply here since ATM doesn't know about the display during this test
- // (meaning it can't perform the normal sendNewConfiguration flow).
- mDisplayContent.applyRotationLocked(oldRotation, mDisplayContent.getRotation());
- // Prevent the next rotation from being deferred by animation.
- mWm.mAnimator.setScreenRotationAnimationLocked(mDisplayContent.getDisplayId(), null);
- mWm.mRoot.performSurfacePlacement(false /* recoveringMemory */);
}
@Test
@@ -266,14 +251,14 @@
public void testGetOrientation() {
mToken.setOrientation(SCREEN_ORIENTATION_LANDSCAPE);
- mToken.setFillsParent(false);
- // Can specify orientation if app doesn't fill parent.
+ mToken.setOccludesParent(false);
+ // Can specify orientation if app doesn't occludes parent.
assertEquals(SCREEN_ORIENTATION_LANDSCAPE, mToken.getOrientation());
- mToken.setFillsParent(true);
+ mToken.setOccludesParent(true);
mToken.setHidden(true);
mToken.sendingToBottom = true;
- // Can not specify orientation if app isn't visible even though it fills parent.
+ // Can not specify orientation if app isn't visible even though it occludes parent.
assertEquals(SCREEN_ORIENTATION_UNSET, mToken.getOrientation());
// Can specify orientation if the current orientation candidate is orientation behind.
assertEquals(SCREEN_ORIENTATION_LANDSCAPE,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 388658d..6289768 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -83,7 +83,6 @@
import androidx.test.filters.SmallTest;
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.server.wm.utils.WmDisplayCutout;
@@ -653,25 +652,27 @@
@Test
public void testOnDescendantOrientationRequestChanged() {
final DisplayContent dc = createNewDisplay();
- mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
+ dc.getDisplayRotation().setFixedToUserRotation(
+ DisplayRotation.FIXED_TO_USER_ROTATION_DISABLED);
final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
? SCREEN_ORIENTATION_PORTRAIT
: SCREEN_ORIENTATION_LANDSCAPE;
- final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
- window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
- window.mAppToken.setOrientation(newOrientation);
+ final ActivityStack stack =
+ new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
+ .setDisplay(dc.mAcitvityDisplay).build();
+ final ActivityRecord activity = stack.topTask().getTopActivity();
- ActivityRecord activityRecord = mock(ActivityRecord.class);
-
- assertTrue("Display should rotate to handle orientation request by default.",
- dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
+ activity.setRequestedOrientation(newOrientation);
final ArgumentCaptor<Configuration> captor = ArgumentCaptor.forClass(Configuration.class);
verify(dc.mAcitvityDisplay).updateDisplayOverrideConfigurationLocked(captor.capture(),
- same(activityRecord), anyBoolean(), same(null));
+ same(activity), anyBoolean(), same(null));
final Configuration newDisplayConfig = captor.getValue();
- assertEquals(Configuration.ORIENTATION_PORTRAIT, newDisplayConfig.orientation);
+ final int expectedOrientation = newOrientation == SCREEN_ORIENTATION_PORTRAIT
+ ? Configuration.ORIENTATION_PORTRAIT
+ : Configuration.ORIENTATION_LANDSCAPE;
+ assertEquals(expectedOrientation, newDisplayConfig.orientation);
}
@Test
@@ -679,22 +680,20 @@
final DisplayContent dc = createNewDisplay();
dc.getDisplayRotation().setFixedToUserRotation(
DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED);
- mWm.mAtmService.mRootActivityContainer = mock(RootActivityContainer.class);
final int newOrientation = dc.getLastOrientation() == SCREEN_ORIENTATION_LANDSCAPE
? SCREEN_ORIENTATION_PORTRAIT
: SCREEN_ORIENTATION_LANDSCAPE;
- final WindowState window = createWindow(null /* parent */, TYPE_BASE_APPLICATION, dc, "w");
- window.getTask().mTaskRecord = mock(TaskRecord.class, ExtendedMockito.RETURNS_DEEP_STUBS);
- window.mAppToken.setOrientation(newOrientation);
+ final ActivityStack stack =
+ new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
+ .setDisplay(dc.mAcitvityDisplay).build();
+ final ActivityRecord activity = stack.topTask().getTopActivity();
- ActivityRecord activityRecord = mock(ActivityRecord.class);
+ activity.setRequestedOrientation(newOrientation);
- assertFalse("Display shouldn't rotate to handle orientation request if fixed to"
- + " user rotation.",
- dc.onDescendantOrientationChanged(window.mToken.token, activityRecord));
verify(dc.mAcitvityDisplay, never()).updateDisplayOverrideConfigurationLocked(any(),
- eq(activityRecord), anyBoolean(), same(null));
+ eq(activity), anyBoolean(), same(null));
+ assertEquals(dc.getDisplayRotation().getUserRotation(), dc.getRotation());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
index bfede51..f6f8811 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java
@@ -95,7 +95,7 @@
public void setUp() throws Exception {
deleteRecursively(TEST_FOLDER);
- mWm.setSupportsFreeformWindowManagement(false);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = false;
mWm.setIsPc(false);
mWm.setForceDesktopModeOnExternalDisplays(false);
@@ -134,7 +134,7 @@
@Test
public void testPrimaryDisplayDefaultToFullscreen_HasFreeformSupport_NonPc_NoDesktopMode() {
- mWm.setSupportsFreeformWindowManagement(true);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
@@ -144,7 +144,7 @@
@Test
public void testPrimaryDisplayDefaultToFullscreen_HasFreeformSupport_NonPc_HasDesktopMode() {
- mWm.setSupportsFreeformWindowManagement(true);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mWm.setForceDesktopModeOnExternalDisplays(true);
mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
@@ -155,7 +155,7 @@
@Test
public void testPrimaryDisplayDefaultToFreeform_HasFreeformSupport_IsPc() {
- mWm.setSupportsFreeformWindowManagement(true);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mWm.setIsPc(true);
mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
@@ -168,7 +168,7 @@
public void testPrimaryDisplayUpdateToFreeform_HasFreeformSupport_IsPc() {
mTarget.applySettingsToDisplayLocked(mPrimaryDisplay);
- mWm.setSupportsFreeformWindowManagement(true);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mWm.setIsPc(true);
mTarget.updateSettingsForDisplay(mPrimaryDisplay);
@@ -187,7 +187,7 @@
@Test
public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_NonPc_NoDesktopMode() {
- mWm.setSupportsFreeformWindowManagement(true);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
@@ -197,7 +197,7 @@
@Test
public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_NonPc_HasDesktopMode() {
- mWm.setSupportsFreeformWindowManagement(true);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mWm.setForceDesktopModeOnExternalDisplays(true);
mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
@@ -208,7 +208,7 @@
@Test
public void testSecondaryDisplayDefaultToFreeform_HasFreeformSupport_IsPc() {
- mWm.setSupportsFreeformWindowManagement(true);
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mWm.setIsPc(true);
mTarget.applySettingsToDisplayLocked(mSecondaryDisplay);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index b28ae40..be2ee29 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -331,7 +331,9 @@
mController.layoutTask(task, null /* windowLayout */);
- assertEquals(expected, task.getBounds());
+ // TaskRecord will make adjustments to requested bounds. We only need to guarantee that the
+ // reuqested bounds are expected.
+ assertEquals(expected, task.getRequestedOverrideBounds());
}
/**
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index e4d3770..49d778f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -104,6 +104,7 @@
mDisplayUniqueId = "test:" + Integer.toString(sNextUniqueId++);
final DisplayInfo info = new DisplayInfo();
+ mService.mContext.getDisplay().getDisplayInfo(info);
info.uniqueId = mDisplayUniqueId;
mTestDisplay = createNewActivityDisplay(info);
mRootActivityContainer.addChild(mTestDisplay, ActivityDisplay.POSITION_TOP);
diff --git a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
index 63d9fb9..efd468f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/PinnedStackControllerTest.java
@@ -61,7 +61,7 @@
@Test
public void setShelfHeight_shelfVisibilityChangedTriggered() throws RemoteException {
- mWm.mSupportsPictureInPicture = true;
+ mWm.mAtmService.mSupportsPictureInPicture = true;
mWm.registerPinnedStackListener(DEFAULT_DISPLAY, mIPinnedStackListener);
verify(mIPinnedStackListener).onImeVisibilityChanged(false, 0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index b7a85d7..fb4e330 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -32,7 +32,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
@@ -53,14 +53,12 @@
import android.app.ActivityTaskManager;
import android.app.WindowConfiguration;
import android.content.ComponentName;
-import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
@@ -93,11 +91,8 @@
private static final int TEST_QUIET_USER_ID = 20;
private static final UserInfo DEFAULT_USER_INFO = new UserInfo();
private static final UserInfo QUIET_USER_INFO = new UserInfo();
- private static int sLastTaskId = 1;
- private static int sLastStackId = 1;
private static final int INVALID_STACK_ID = 999;
- private TestActivityTaskManagerService mTestService;
private ActivityDisplay mDisplay;
private ActivityDisplay mOtherDisplay;
private ActivityDisplay mSingleTaskDisplay;
@@ -115,13 +110,29 @@
@Before
public void setUp() throws Exception {
mTaskPersister = new TestTaskPersister(mContext.getFilesDir());
- mTestService = new MyTestActivityTaskManagerService(mContext);
- mRecentTasks = (TestRecentTasks) mTestService.getRecentTasks();
+
+ // Set testing displays
+ mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
+ mOtherDisplay = createNewActivityDisplay();
+ mSingleTaskDisplay = createNewActivityDisplay();
+ mSingleTaskDisplay.setDisplayToSingleTaskInstance();
+ mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
+ mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
+ mRootActivityContainer.addChild(mSingleTaskDisplay, ActivityDisplay.POSITION_TOP);
+
+ // Set the recent tasks we should use for testing in this class.
+ mRecentTasks = new TestRecentTasks(mService, mTaskPersister);
+ spyOn(mRecentTasks);
+ mService.setRecentTasks(mRecentTasks);
mRecentTasks.loadParametersFromResources(mContext.getResources());
- mRunningTasks = (TestRunningTasks) mTestService.mStackSupervisor.mRunningTasks;
- mHomeStack = mTestService.mRootActivityContainer.getDefaultDisplay().getOrCreateStack(
+
+ // Set the running tasks we should use for testing in this class.
+ mRunningTasks = new TestRunningTasks();
+ mService.mStackSupervisor.setRunningTasks(mRunningTasks);
+
+ mHomeStack = mDisplay.getOrCreateStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
- mStack = mTestService.mRootActivityContainer.getDefaultDisplay().createStack(
+ mStack = mDisplay.createStack(
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
mCallbacksRecorder = new CallbacksRecorder();
mRecentTasks.registerCallback(mCallbacksRecorder);
@@ -723,7 +734,7 @@
ActivityStack stack = mTasks.get(2).getStack();
stack.moveToFront("", mTasks.get(2));
- doReturn(stack).when(mTestService.mRootActivityContainer).getTopDisplayFocusedStack();
+ doReturn(stack).when(mService.mRootActivityContainer).getTopDisplayFocusedStack();
// Simulate the reset from the timeout
mRecentTasks.resetFreezeTaskListReorderingOnTimeout();
@@ -742,10 +753,9 @@
public void testBackStackTasks_expectNoTrim() {
mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
- final MyTestActivityStackSupervisor supervisor =
- (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
final ActivityStack homeStack = mDisplay.getHomeStack();
- final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack aboveHomeStack = mDisplay.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
// the tasks belong in stacks above the home stack
@@ -761,11 +771,11 @@
public void testBehindHomeStackTasks_expectTaskTrimmed() {
mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
- final MyTestActivityStackSupervisor supervisor =
- (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
- final ActivityStack behindHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack behindHomeStack = mDisplay.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
final ActivityStack homeStack = mDisplay.getHomeStack();
- final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
+ final ActivityStack aboveHomeStack = mDisplay.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
// the home stack is trimmed once a new task is added
@@ -783,10 +793,9 @@
public void testOtherDisplayTasks_expectNoTrim() {
mRecentTasks.setParameters(-1 /* min */, 1 /* max */, -1 /* ms */);
- final MyTestActivityStackSupervisor supervisor =
- (MyTestActivityStackSupervisor) mTestService.mStackSupervisor;
final ActivityStack homeStack = mDisplay.getHomeStack();
- final ActivityStack otherDisplayStack = new MyTestActivityStack(mOtherDisplay, supervisor);
+ final ActivityStack otherDisplayStack = mOtherDisplay.createStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
// Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
// removed
@@ -887,16 +896,16 @@
mStack.remove();
// The following APIs should not restore task from recents to the active list.
- assertNotRestoreTask(() -> mTestService.setFocusedTask(taskId));
- assertNotRestoreTask(() -> mTestService.startSystemLockTaskMode(taskId));
- assertNotRestoreTask(() -> mTestService.cancelTaskWindowTransition(taskId));
+ assertNotRestoreTask(() -> mService.setFocusedTask(taskId));
+ assertNotRestoreTask(() -> mService.startSystemLockTaskMode(taskId));
+ assertNotRestoreTask(() -> mService.cancelTaskWindowTransition(taskId));
assertNotRestoreTask(
- () -> mTestService.resizeTask(taskId, null /* bounds */, 0 /* resizeMode */));
+ () -> mService.resizeTask(taskId, null /* bounds */, 0 /* resizeMode */));
assertNotRestoreTask(
- () -> mTestService.setTaskWindowingMode(taskId, WINDOWING_MODE_FULLSCREEN,
+ () -> mService.setTaskWindowingMode(taskId, WINDOWING_MODE_FULLSCREEN,
false/* toTop */));
assertNotRestoreTask(
- () -> mTestService.setTaskWindowingModeSplitScreenPrimary(taskId,
+ () -> mService.setTaskWindowingModeSplitScreenPrimary(taskId,
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT,
false /* toTop */, false /* animate */, null /* initialBounds */,
true /* showRecents */));
@@ -910,7 +919,7 @@
mRecentTasks.remove(task);
TaskChangeNotificationController controller =
- mTestService.getTaskChangeNotificationController();
+ mService.getTaskChangeNotificationController();
verify(controller, times(2)).notifyTaskListUpdated();
}
@@ -923,7 +932,7 @@
// 2 calls - Once for add and once for remove
TaskChangeNotificationController controller =
- mTestService.getTaskChangeNotificationController();
+ mService.getTaskChangeNotificationController();
verify(controller, times(2)).notifyTaskListUpdated();
}
@@ -938,7 +947,7 @@
// 4 calls - Twice for add and twice for remove
TaskChangeNotificationController controller =
- mTestService.getTaskChangeNotificationController();
+ mService.getTaskChangeNotificationController();
verify(controller, times(4)).notifyTaskListUpdated();
}
@@ -980,7 +989,7 @@
@Test
public void testNotRecentsComponent_denyApiAccess() throws Exception {
- doReturn(PackageManager.PERMISSION_DENIED).when(mTestService)
+ doReturn(PackageManager.PERMISSION_DENIED).when(mService)
.checkGetTasksPermission(anyString(), anyInt(), anyInt());
// Expect the following methods to fail due to recents component not being set
mRecentTasks.setIsCallerRecentsOverride(TestRecentTasks.DENY_THROW_SECURITY_EXCEPTION);
@@ -992,7 +1001,7 @@
@Test
public void testRecentsComponent_allowApiAccessWithoutPermissions() {
- doReturn(PackageManager.PERMISSION_DENIED).when(mTestService)
+ doReturn(PackageManager.PERMISSION_DENIED).when(mService)
.checkGetTasksPermission(anyString(), anyInt(), anyInt());
// Set the recents component and ensure that the following calls do not fail
@@ -1002,62 +1011,62 @@
}
private void doTestRecentTasksApis(boolean expectCallable) {
- assertSecurityException(expectCallable, () -> mTestService.removeStack(INVALID_STACK_ID));
+ assertSecurityException(expectCallable, () -> mService.removeStack(INVALID_STACK_ID));
assertSecurityException(expectCallable,
- () -> mTestService.removeStacksInWindowingModes(
+ () -> mService.removeStacksInWindowingModes(
new int[]{WINDOWING_MODE_UNDEFINED}));
assertSecurityException(expectCallable,
- () -> mTestService.removeStacksWithActivityTypes(
+ () -> mService.removeStacksWithActivityTypes(
new int[]{ACTIVITY_TYPE_UNDEFINED}));
- assertSecurityException(expectCallable, () -> mTestService.removeTask(0));
+ assertSecurityException(expectCallable, () -> mService.removeTask(0));
assertSecurityException(expectCallable,
- () -> mTestService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
+ () -> mService.setTaskWindowingMode(0, WINDOWING_MODE_UNDEFINED, true));
assertSecurityException(expectCallable,
- () -> mTestService.moveTaskToStack(0, INVALID_STACK_ID, true));
+ () -> mService.moveTaskToStack(0, INVALID_STACK_ID, true));
assertSecurityException(expectCallable,
- () -> mTestService.setTaskWindowingModeSplitScreenPrimary(0,
+ () -> mService.setTaskWindowingModeSplitScreenPrimary(0,
SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
- assertSecurityException(expectCallable, () -> mTestService.dismissSplitScreenMode(true));
- assertSecurityException(expectCallable, () -> mTestService.dismissPip(true, 0));
+ assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true));
+ assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
assertSecurityException(expectCallable,
- () -> mTestService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
+ () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
assertSecurityException(expectCallable,
- () -> mTestService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0));
+ () -> mService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0));
assertSecurityException(expectCallable,
- () -> mTestService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
+ () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
new Rect()));
assertSecurityException(expectCallable,
- () -> mTestService.resizePinnedStack(new Rect(), new Rect()));
- assertSecurityException(expectCallable, () -> mTestService.getAllStackInfos());
+ () -> mService.resizePinnedStack(new Rect(), new Rect()));
+ assertSecurityException(expectCallable, () -> mService.getAllStackInfos());
assertSecurityException(expectCallable,
- () -> mTestService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
+ () -> mService.getStackInfo(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_UNDEFINED));
assertSecurityException(expectCallable, () -> {
try {
- mTestService.getFocusedStackInfo();
+ mService.getFocusedStackInfo();
} catch (RemoteException e) {
// Ignore
}
});
assertSecurityException(expectCallable,
- () -> mTestService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
+ () -> mService.moveTasksToFullscreenStack(INVALID_STACK_ID, true));
assertSecurityException(expectCallable,
- () -> mTestService.startActivityFromRecents(0, new Bundle()));
- assertSecurityException(expectCallable, () -> mTestService.getTaskSnapshot(0, true));
- assertSecurityException(expectCallable, () -> mTestService.registerTaskStackListener(null));
+ () -> mService.startActivityFromRecents(0, new Bundle()));
+ assertSecurityException(expectCallable, () -> mService.getTaskSnapshot(0, true));
+ assertSecurityException(expectCallable, () -> mService.registerTaskStackListener(null));
assertSecurityException(expectCallable,
- () -> mTestService.unregisterTaskStackListener(null));
- assertSecurityException(expectCallable, () -> mTestService.getTaskDescription(0));
- assertSecurityException(expectCallable, () -> mTestService.cancelTaskWindowTransition(0));
- assertSecurityException(expectCallable, () -> mTestService.startRecentsActivity(null, null,
+ () -> mService.unregisterTaskStackListener(null));
+ assertSecurityException(expectCallable, () -> mService.getTaskDescription(0));
+ assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0));
+ assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null,
null));
- assertSecurityException(expectCallable, () -> mTestService.cancelRecentsAnimation(true));
- assertSecurityException(expectCallable, () -> mTestService.stopAppSwitches());
- assertSecurityException(expectCallable, () -> mTestService.resumeAppSwitches());
+ assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation(true));
+ assertSecurityException(expectCallable, () -> mService.stopAppSwitches());
+ assertSecurityException(expectCallable, () -> mService.resumeAppSwitches());
}
private void testGetTasksApis(boolean expectCallable) {
- mTestService.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID);
- mTestService.getTasks(MAX_VALUE);
+ mService.getRecentTasks(MAX_VALUE, 0, TEST_USER_0_ID);
+ mService.getTasks(MAX_VALUE);
if (expectCallable) {
assertTrue(mRecentTasks.mLastAllowed);
assertTrue(mRunningTasks.mLastAllowed);
@@ -1072,10 +1081,9 @@
}
private TaskBuilder createTaskBuilder(String packageName, String className) {
- return new TaskBuilder(mTestService.mStackSupervisor)
+ return new TaskBuilder(mService.mStackSupervisor)
.setComponent(new ComponentName(packageName, className))
.setStack(mStack)
- .setTaskId(sLastTaskId++)
.setUserId(TEST_USER_0_ID);
}
@@ -1140,68 +1148,6 @@
}
}
- private class MyTestActivityTaskManagerService extends TestActivityTaskManagerService {
- MyTestActivityTaskManagerService(Context context) {
- super(context);
- }
-
- @Override
- protected RecentTasks createRecentTasks() {
- return spy(new TestRecentTasks(this, mTaskPersister));
- }
-
- @Override
- protected ActivityStackSupervisor createStackSupervisor() {
- if (mTestStackSupervisor == null) {
- mTestStackSupervisor = new MyTestActivityStackSupervisor(this, mH.getLooper());
- }
- return mTestStackSupervisor;
- }
-
- @Override
- void createDefaultDisplay() {
- super.createDefaultDisplay();
- mDisplay = mRootActivityContainer.getActivityDisplay(DEFAULT_DISPLAY);
- mOtherDisplay = TestActivityDisplay.create(mTestStackSupervisor, DEFAULT_DISPLAY + 1);
- mSingleTaskDisplay = TestActivityDisplay.create(mTestStackSupervisor,
- DEFAULT_DISPLAY + 2);
- mSingleTaskDisplay.setDisplayToSingleTaskInstance();
- mRootActivityContainer.addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
- mRootActivityContainer.addChild(mDisplay, ActivityDisplay.POSITION_TOP);
- mRootActivityContainer.addChild(mSingleTaskDisplay, ActivityDisplay.POSITION_TOP);
- }
- }
-
- private class MyTestActivityStackSupervisor extends TestActivityStackSupervisor {
- MyTestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
- super(service, looper);
- }
-
- @Override
- RunningTasks createRunningTasks() {
- mRunningTasks = new TestRunningTasks();
- return mRunningTasks;
- }
- }
-
- private static class MyTestActivityStack extends TestActivityStack {
- private ActivityDisplay mDisplay = null;
-
- MyTestActivityStack(ActivityDisplay display, ActivityStackSupervisor supervisor) {
- super(display, sLastStackId++, supervisor, WINDOWING_MODE_FULLSCREEN,
- ACTIVITY_TYPE_STANDARD, true /* onTop */, false /* createActivity */);
- mDisplay = display;
- }
-
- @Override
- ActivityDisplay getDisplay() {
- if (mDisplay != null) {
- return mDisplay;
- }
- return super.getDisplay();
- }
- }
-
private static class CallbacksRecorder implements Callbacks {
public final ArrayList<TaskRecord> mAdded = new ArrayList<>();
public final ArrayList<TaskRecord> mTrimmed = new ArrayList<>();
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 f5a1d75..9ca0180 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -30,6 +30,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMoreInteractions;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.wm.ActivityStackSupervisor.ON_TOP;
import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE;
import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_TO_ORIGINAL_POSITION;
@@ -132,8 +133,14 @@
@Test
public void testIncludedApps_expectTargetAndVisible() {
mWm.setRecentsAnimationController(mController);
- final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent,
- WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+ final ActivityStack homStack = mDisplayContent.mAcitvityDisplay.getOrCreateStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
+ final AppWindowToken homeAppWindow =
+ new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
+ .setStack(homStack)
+ .setCreateTask(true)
+ .build()
+ .mAppWindowToken;
final AppWindowToken appWindow = createAppWindowToken(mDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
final AppWindowToken hiddenAppWindow = createAppWindowToken(mDisplayContent,
@@ -169,7 +176,7 @@
// Simulate the app transition finishing
mController.mAppTransitionListener.onAppTransitionStartingLocked(0, 0, 0, 0);
- verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false);
+ verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
}
@Test
@@ -201,7 +208,7 @@
spyOn(mController.mRecentScreenshotAnimator.mAnimatable);
mController.mRecentScreenshotAnimator.cancelAnimation();
verify(mController.mRecentScreenshotAnimator.mAnimatable).onAnimationLeashLost(any());
- verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, true, false);
+ verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false);
}
@Test
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 0e119e3..dcc295c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -74,8 +74,9 @@
@Before
public void setUp() throws Exception {
mRecentsAnimationController = mock(RecentsAnimationController.class);
- doReturn(mRecentsAnimationController).when(
- mService.mWindowManager).getRecentsAnimationController();
+ mService.mWindowManager.setRecentsAnimationController(mRecentsAnimationController);
+ doNothing().when(mService.mWindowManager).initializeRecentsAnimation(
+ anyInt(), any(), any(), anyInt(), any());
doReturn(true).when(mService.mWindowManager).canStartRecentsAnimation();
final RecentTasks recentTasks = mService.getRecentTasks();
@@ -107,16 +108,25 @@
assertTrue(recentActivity.visible);
// Simulate the animation is cancelled without changing the stack order.
- recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, true /* runSychronously */,
- false /* sendUserLeaveHint */);
+ recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */);
// The non-top recents activity should be invisible by the restored launch-behind state.
assertFalse(recentActivity.visible);
}
@Test
public void testPreloadRecentsActivity() {
- // Ensure that the fake recent component can be resolved by the recents intent.
- mockTaskRecordFactory(builder -> builder.setComponent(mRecentsComponent));
+ final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay();
+ final ActivityStack homeStack =
+ defaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+ defaultDisplay.positionChildAtTop(homeStack, false /* includingParents */);
+ ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
+ if (topRunningHomeActivity == null) {
+ topRunningHomeActivity = new ActivityBuilder(mService)
+ .setStack(homeStack)
+ .setCreateTask(true)
+ .build();
+ }
+
ActivityInfo aInfo = new ActivityInfo();
aInfo.applicationInfo = new ApplicationInfo();
aInfo.applicationInfo.uid = 10001;
@@ -204,6 +214,13 @@
ActivityStack homeStack = display.getHomeStack();
// Assume the home activity support recents.
ActivityRecord targetActivity = homeStack.getTopActivity();
+ if (targetActivity == null) {
+ targetActivity = new ActivityBuilder(mService)
+ .setCreateTask(true)
+ .setStack(homeStack)
+ .build();
+ }
+
// Put another home activity in home stack.
ActivityRecord anotherHomeActivity = new ActivityBuilder(mService)
.setComponent(new ComponentName(mContext.getPackageName(), "Home2"))
@@ -226,13 +243,12 @@
anotherHomeActivity.moveFocusableActivityToTop("launchAnotherHome");
// The current top activity is not the recents so the animation should be canceled.
- verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+ verify(mService.mWindowManager, times(1)).cancelRecentsAnimation(
eq(REORDER_KEEP_IN_PLACE), any() /* reason */);
// The test uses mocked RecentsAnimationController so we have to invoke the callback
// manually to simulate the flow.
- recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, true /* runSychronously */,
- false /* sendUserLeaveHint */);
+ recentsAnimation.onAnimationFinished(REORDER_KEEP_IN_PLACE, false /* sendUserLeaveHint */);
// We should restore the launch-behind of the original target activity.
assertFalse(targetActivity.mLaunchTaskBehind);
}
@@ -269,7 +285,7 @@
fullscreenStack.moveToFront("Activity start");
// Ensure that the recents animation was canceled by cancelAnimationSynchronously().
- verify(mService.mWindowManager, times(1)).cancelRecentsAnimationSynchronously(
+ verify(mService.mWindowManager, times(1)).cancelRecentsAnimation(
eq(REORDER_KEEP_IN_PLACE), any());
// Assume recents animation already started, set a state that cancel recents animation
@@ -314,7 +330,7 @@
fullscreenStack.remove();
// Ensure that the recents animation was NOT canceled
- verify(mService.mWindowManager, times(0)).cancelRecentsAnimationSynchronously(
+ verify(mService.mWindowManager, times(0)).cancelRecentsAnimation(
eq(REORDER_KEEP_IN_PLACE), any());
verify(mRecentsAnimationController, times(0)).setCancelOnNextTransitionStart();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index d4f24f9..539a79c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -97,7 +97,7 @@
*/
@Test
public void testRestoringInvalidTask() {
- ((TestActivityDisplay) mRootActivityContainer.getDefaultDisplay()).removeAllTasks();
+ mRootActivityContainer.getDefaultDisplay().removeAllTasks();
TaskRecord task = mRootActivityContainer.anyTaskForId(0 /*taskId*/,
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
assertNull(task);
@@ -304,21 +304,23 @@
*/
@Test
public void testResizeDockedStackForSplitScreenPrimary() {
- final Rect taskSize = new Rect(0, 0, 600, 600);
+ final Rect taskSize = new Rect(0, 0, 1000, 1000);
final Rect stackSize = new Rect(0, 0, 300, 300);
// Create primary split-screen stack with a task.
- final ActivityStack primaryStack = mRootActivityContainer.getDefaultDisplay()
- .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
- true /* onTop */);
- final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
+ final ActivityStack primaryStack = new StackBuilder(mRootActivityContainer)
+ .setActivityType(ACTIVITY_TYPE_STANDARD)
+ .setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)
+ .setOnTop(true)
+ .build();
+ final TaskRecord task = primaryStack.topTask();
// Resize dock stack.
mService.resizeDockedStack(stackSize, taskSize, null, null, null);
// Verify dock stack & its task bounds if is equal as resized result.
- assertEquals(primaryStack.getBounds(), stackSize);
- assertEquals(task.getBounds(), taskSize);
+ assertEquals(stackSize, primaryStack.getBounds());
+ assertEquals(taskSize, task.getBounds());
}
/**
@@ -328,8 +330,9 @@
public void testFindTaskToMoveToFrontWhenRecentsOnTop() {
// Create stack/task on default display.
final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
- final TestActivityStack targetStack = (TestActivityStack) new StackBuilder(
- mRootActivityContainer).setOnTop(false).build();
+ final ActivityStack targetStack = new StackBuilder(mRootActivityContainer)
+ .setOnTop(false)
+ .build();
final TaskRecord targetTask = targetStack.getChildAt(0);
// Create Recents on top of the display.
@@ -505,12 +508,10 @@
mockResolveSecondaryHomeActivity();
// Create secondary displays.
- final TestActivityDisplay secondDisplay = spy(createNewActivityDisplay());
+ final TestActivityDisplay secondDisplay = createNewActivityDisplay();
mRootActivityContainer.addChild(secondDisplay, POSITION_TOP);
doReturn(true).when(secondDisplay).supportsSystemDecorations();
- // Create mock tasks and other necessary mocks.
- mockTaskRecordFactory();
doReturn(true).when(mRootActivityContainer)
.ensureVisibilityAndConfig(any(), anyInt(), anyBoolean(), anyBoolean());
doReturn(true).when(mRootActivityContainer).canStartHomeOnDisplay(
@@ -621,7 +622,6 @@
info.applicationInfo.packageName = "android";
info.name = ResolverActivity.class.getName();
doReturn(info).when(mRootActivityContainer).resolveHomeActivity(anyInt(), any());
- mockTaskRecordFactory();
mRootActivityContainer.startHomeOnDisplay(0 /* userId */, "test", DEFAULT_DISPLAY);
final ActivityRecord resolverActivity = mRootActivityContainer.topRunningActivity();
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
index f51ce13..db105dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -96,7 +96,7 @@
mWm.getDefaultDisplayContentLocked().getWindowingMode());
mWm.mIsPc = true;
- mWm.mSupportsFreeformWindowManagement = true;
+ mWm.mAtmService.mSupportsFreeformWindowManagement = true;
mWm.mRoot.onSettingsRetrieved();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index df7c9a4..1ad0e00 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -16,6 +16,9 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.os.Process.THREAD_PRIORITY_DEFAULT;
import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareClassLoader;
import static android.view.Display.DEFAULT_DISPLAY;
@@ -25,67 +28,91 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyString;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.nullable;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
+import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManagerInternal;
import android.database.ContentObserver;
+import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.net.Uri;
import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
import android.os.PowerManagerInternal;
import android.os.PowerSaveState;
import android.os.UserHandle;
-import android.view.Display;
+import android.provider.DeviceConfig;
import android.view.InputChannel;
+import android.view.Surface;
+import android.view.SurfaceControl;
import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.server.AnimationThread;
+import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
+import com.android.server.ServiceThread;
import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerService;
+import com.android.server.appop.AppOpsService;
+import com.android.server.display.color.ColorDisplayService;
import com.android.server.input.InputManagerService;
+import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.wm.utils.MockTracker;
+import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.uri.UriGrantsManagerInternal;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
-import org.mockito.invocation.InvocationOnMock;
+import org.mockito.Mockito;
import org.mockito.quality.Strictness;
+import java.io.File;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * JUnit test rule to create a mock {@link WindowManagerService} instance for tests.
+ * JUnit test rule to correctly setting up system services like {@link WindowManagerService}
+ * and {@link ActivityTaskManagerService} for tests.
*/
public class SystemServicesTestRule implements TestRule {
private static final String TAG = SystemServicesTestRule.class.getSimpleName();
+ static int sNextDisplayId = DEFAULT_DISPLAY + 100;
+ static int sNextTaskId = 100;
+
private final AtomicBoolean mCurrentMessagesProcessed = new AtomicBoolean(false);
- private MockTracker mMockTracker;
+ private Context mContext;
private StaticMockitoSession mMockitoSession;
- private WindowManagerService mWindowManagerService;
- private TestWindowManagerPolicy mWindowManagerPolicy;
-
- /** {@link MockTracker} to track mocks created by {@link SystemServicesTestRule}. */
- private static class Tracker extends MockTracker {
- // This empty extended class is necessary since Mockito distinguishes a listener by it
- // class.
- }
+ ServiceThread mHandlerThread;
+ private ActivityManagerService mAmService;
+ private ActivityTaskManagerService mAtmService;
+ private WindowManagerService mWmService;
+ private TestWindowManagerPolicy mWMPolicy;
+ private WindowState.PowerManagerWrapper mPowerManagerWrapper;
+ private InputManagerService mImService;
+ /**
+ * Spied {@link SurfaceControl.Transaction} class than can be used to verify calls.
+ */
+ SurfaceControl.Transaction mTransaction;
@Override
public Statement apply(Statement base, Description description) {
@@ -103,8 +130,6 @@
}
private void setUp() {
- mMockTracker = new Tracker();
-
mMockitoSession = mockitoSession()
.spyStatic(LocalServices.class)
.mockStatic(LockGuard.class)
@@ -112,101 +137,220 @@
.strictness(Strictness.LENIENT)
.startMocking();
+ setUpSystemCore();
+ setUpLocalServices();
+ setUpActivityTaskManagerService();
+ setUpWindowManagerService();
+ }
+
+ private void setUpSystemCore() {
+ mHandlerThread = new ServiceThread(
+ "WmTestsThread", THREAD_PRIORITY_DEFAULT, true /* allowIo */);
+ mHandlerThread.start();
+
doReturn(mock(Watchdog.class)).when(Watchdog::getInstance);
- final Context context = getInstrumentation().getTargetContext();
- spyOn(context);
+ mContext = getInstrumentation().getTargetContext();
+ spyOn(mContext);
- doReturn(null).when(context)
+ doReturn(null).when(mContext)
.registerReceiver(nullable(BroadcastReceiver.class), any(IntentFilter.class));
- doReturn(null).when(context)
+ doReturn(null).when(mContext)
.registerReceiverAsUser(any(BroadcastReceiver.class), any(UserHandle.class),
any(IntentFilter.class), nullable(String.class), nullable(Handler.class));
- final ContentResolver contentResolver = context.getContentResolver();
+ final ContentResolver contentResolver = mContext.getContentResolver();
spyOn(contentResolver);
doNothing().when(contentResolver)
.registerContentObserver(any(Uri.class), anyBoolean(), any(ContentObserver.class),
anyInt());
+ }
- final AppOpsManager appOpsManager = mock(AppOpsManager.class);
- doReturn(appOpsManager).when(context)
- .getSystemService(eq(Context.APP_OPS_SERVICE));
+ private void setUpLocalServices() {
+ // Tear down any local services just in case.
+ tearDownLocalServices();
+ // UriGrantsManagerInternal
+ final UriGrantsManagerInternal ugmi = mock(UriGrantsManagerInternal.class);
+ LocalServices.addService(UriGrantsManagerInternal.class, ugmi);
+
+ // AppOpsManager
+ final AppOpsManager aom = mock(AppOpsManager.class);
+ doReturn(aom).when(mContext).getSystemService(eq(Context.APP_OPS_SERVICE));
+
+ // DisplayManagerInternal
final DisplayManagerInternal dmi = mock(DisplayManagerInternal.class);
doReturn(dmi).when(() -> LocalServices.getService(eq(DisplayManagerInternal.class)));
+ // ColorDisplayServiceInternal
+ final ColorDisplayService.ColorDisplayServiceInternal cds =
+ mock(ColorDisplayService.ColorDisplayServiceInternal.class);
+ doReturn(cds).when(() -> LocalServices.getService(
+ eq(ColorDisplayService.ColorDisplayServiceInternal.class)));
+
+ final UsageStatsManagerInternal usmi = mock(UsageStatsManagerInternal.class);
+ LocalServices.addService(UsageStatsManagerInternal.class, usmi);
+
+ // PackageManagerInternal
+ final PackageManagerInternal packageManagerInternal = mock(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, packageManagerInternal);
+ doReturn(false).when(packageManagerInternal).isPermissionsReviewRequired(
+ anyString(), anyInt());
+ doReturn(null).when(packageManagerInternal).getDefaultHomeActivity(anyInt());
+
+ // PowerManagerInternal
final PowerManagerInternal pmi = mock(PowerManagerInternal.class);
final PowerSaveState state = new PowerSaveState.Builder().build();
doReturn(state).when(pmi).getLowPowerState(anyInt());
doReturn(pmi).when(() -> LocalServices.getService(eq(PowerManagerInternal.class)));
- final ActivityManagerInternal ami = mock(ActivityManagerInternal.class);
- doReturn(ami).when(() -> LocalServices.getService(eq(ActivityManagerInternal.class)));
+ // PermissionPolicyInternal
+ final PermissionPolicyInternal ppi = mock(PermissionPolicyInternal.class);
+ LocalServices.addService(PermissionPolicyInternal.class, ppi);
+ doReturn(true).when(ppi).checkStartActivity(any(), anyInt(), any());
- final ActivityTaskManagerInternal atmi = mock(ActivityTaskManagerInternal.class);
- doAnswer((InvocationOnMock invocationOnMock) -> {
- final Runnable runnable = invocationOnMock.getArgument(0);
- if (runnable != null) {
- runnable.run();
- }
- return null;
- }).when(atmi).notifyKeyguardFlagsChanged(nullable(Runnable.class), anyInt());
- doReturn(atmi).when(() -> LocalServices.getService(eq(ActivityTaskManagerInternal.class)));
-
- final InputManagerService ims = mock(InputManagerService.class);
+ // InputManagerService
+ mImService = mock(InputManagerService.class);
// InputChannel is final and can't be mocked.
final InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
if (input != null && input.length > 1) {
- doReturn(input[1]).when(ims).monitorInput(anyString(), anyInt());
+ doReturn(input[1]).when(mImService).monitorInput(anyString(), anyInt());
}
- final ActivityTaskManagerService atms = mock(ActivityTaskManagerService.class);
- final TaskChangeNotificationController taskChangeNotificationController = mock(
- TaskChangeNotificationController.class);
- doReturn(taskChangeNotificationController).when(atms).getTaskChangeNotificationController();
- final WindowManagerGlobalLock wmLock = new WindowManagerGlobalLock();
- doReturn(wmLock).when(atms).getGlobalLock();
+ // StatusBarManagerInternal
+ final StatusBarManagerInternal sbmi = mock(StatusBarManagerInternal.class);
+ doReturn(sbmi).when(() -> LocalServices.getService(eq(StatusBarManagerInternal.class)));
+ }
- mWindowManagerPolicy = new TestWindowManagerPolicy(this::getWindowManagerService);
- mWindowManagerService = WindowManagerService.main(
- context, ims, false, false, mWindowManagerPolicy, atms, StubTransaction::new);
+ private void setUpActivityTaskManagerService() {
+ // ActivityManagerService
+ mAmService = new ActivityManagerService(
+ new AMTestInjector(mContext, mHandlerThread), mHandlerThread);
+ spyOn(mAmService);
+ doReturn(mock(IPackageManager.class)).when(mAmService).getPackageManager();
+ doNothing().when(mAmService).grantEphemeralAccessLocked(
+ anyInt(), any(), anyInt(), anyInt());
- mWindowManagerService.onInitReady();
+ // ActivityManagerInternal
+ final ActivityManagerInternal amInternal = mAmService.mInternal;
+ spyOn(amInternal);
+ doNothing().when(amInternal).trimApplications();
+ doNothing().when(amInternal).updateCpuStats();
+ doNothing().when(amInternal).updateOomAdj();
+ doNothing().when(amInternal).updateBatteryStats(any(), anyInt(), anyInt(), anyBoolean());
+ doNothing().when(amInternal).updateActivityUsageStats(
+ any(), anyInt(), anyInt(), any(), any());
+ doNothing().when(amInternal).startProcess(
+ any(), any(), anyBoolean(), anyBoolean(), any(), any());
+ doNothing().when(amInternal).updateOomLevelsForDisplay(anyInt());
+ LocalServices.addService(ActivityManagerInternal.class, amInternal);
- final Display display = mWindowManagerService.mDisplayManager.getDisplay(DEFAULT_DISPLAY);
- // Display creation is driven by the ActivityManagerService via
- // ActivityStackSupervisor. We emulate those steps here.
- DisplayContent displayContent = mWindowManagerService.mRoot
- .createDisplayContent(display, mock(ActivityDisplay.class));
- displayContent.reconfigureDisplayLocked();
+ mAtmService = new TestActivityTaskManagerService(mContext, mAmService);
+ LocalServices.addService(ActivityTaskManagerInternal.class, mAtmService.getAtmInternal());
+ }
- mMockTracker.stopTracking();
+ private void setUpWindowManagerService() {
+ mPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
+ mWMPolicy = new TestWindowManagerPolicy(this::getWindowManagerService,
+ mPowerManagerWrapper);
+ mWmService = WindowManagerService.main(
+ mContext, mImService, false, false, mWMPolicy, mAtmService, StubTransaction::new);
+ spyOn(mWmService);
+
+ // Setup factory classes to prevent calls to native code.
+ mTransaction = spy(StubTransaction.class);
+ // Return a spied Transaction class than can be used to verify calls.
+ mWmService.mTransactionFactory = () -> mTransaction;
+ // Return a SurfaceControl.Builder class that creates mocked SurfaceControl instances.
+ mWmService.mSurfaceBuilderFactory = (unused) -> new MockSurfaceControlBuilder();
+ // Return mocked Surface instances.
+ mWmService.mSurfaceFactory = () -> mock(Surface.class);
+ mWmService.mSurfaceAnimationRunner = new SurfaceAnimationRunner(
+ null, null, mTransaction, mWmService.mPowerManagerInternal);
+
+ mWmService.onInitReady();
+ mAmService.setWindowManager(mWmService);
+ mWmService.mDisplayEnabled = true;
+ mWmService.mDisplayReady = true;
+ // Set configuration for default display
+ mWmService.getDefaultDisplayContentLocked().reconfigureDisplayLocked();
+
+ // Mock root, some default display, and home stack.
+ spyOn(mWmService.mRoot);
+ final ActivityDisplay display = mAtmService.mRootActivityContainer.getDefaultDisplay();
+ spyOn(display);
+ spyOn(display.mDisplayContent);
+ final ActivityStack homeStack = display.getStack(
+ WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
+ spyOn(homeStack);
+ spyOn(homeStack.mTaskStack);
}
private void tearDown() {
waitUntilWindowManagerHandlersIdle();
- removeLocalServices();
- mWindowManagerService = null;
- mWindowManagerPolicy = null;
+ // Unregister display listener from root to avoid issues with subsequent tests.
+ mContext.getSystemService(DisplayManager.class)
+ .unregisterDisplayListener(mAtmService.mRootActivityContainer);
+ // ProptertiesChangesListener is registered in the constructor of WindowManagerService to
+ // a static object, so we need to clean it up in tearDown(), even though we didn't set up
+ // in tests.
+ DeviceConfig.removeOnPropertiesChangedListener(mWmService.mPropertiesChangedListener);
+ mWmService = null;
+ mWMPolicy = null;
+ mPowerManagerWrapper = null;
+
+ tearDownLocalServices();
+ tearDownSystemCore();
+
+ // Needs to explicitly dispose current static threads because there could be messages
+ // scheduled at a later time, and all mocks are invalid when it's executed.
+ DisplayThread.dispose();
+ AnimationThread.dispose();
+ // Reset priority booster because animation thread has been changed.
+ WindowManagerService.sThreadPriorityBooster = new WindowManagerThreadPriorityBooster();
+
+ Mockito.framework().clearInlineMocks();
+ }
+
+ private void tearDownSystemCore() {
if (mMockitoSession != null) {
mMockitoSession.finishMocking();
mMockitoSession = null;
}
- if (mMockTracker != null) {
- mMockTracker.close();
- mMockTracker = null;
+ if (mHandlerThread != null) {
+ // Make sure there are no running messages and then quit the thread so the next test
+ // won't be affected.
+ mHandlerThread.getThreadHandler().runWithScissors(mHandlerThread::quit,
+ 0 /* timeout */);
}
}
- private static void removeLocalServices() {
+ private static void tearDownLocalServices() {
+ LocalServices.removeServiceForTest(DisplayManagerInternal.class);
+ LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
LocalServices.removeServiceForTest(WindowManagerInternal.class);
LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
+ LocalServices.removeServiceForTest(PermissionPolicyInternal.class);
+ LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
+ LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+ LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
}
WindowManagerService getWindowManagerService() {
- return mWindowManagerService;
+ return mWmService;
+ }
+
+ ActivityTaskManagerService getActivityTaskManagerService() {
+ return mAtmService;
+ }
+
+ WindowState.PowerManagerWrapper getPowerManagerWrapper() {
+ return mPowerManagerWrapper;
}
void cleanupWindowManagerHandlers() {
@@ -229,6 +373,7 @@
waitHandlerIdle(wm.mH);
waitHandlerIdle(wm.mAnimationHandler);
waitHandlerIdle(SurfaceAnimationThread.getHandler());
+ waitHandlerIdle(mHandlerThread.getThreadHandler());
}
private void waitHandlerIdle(Handler handler) {
@@ -251,4 +396,121 @@
}
}
}
+
+ protected class TestActivityTaskManagerService extends ActivityTaskManagerService {
+ // ActivityStackSupervisor may be created more than once while setting up AMS and ATMS.
+ // We keep the reference in order to prevent creating it twice.
+ ActivityStackSupervisor mTestStackSupervisor;
+
+ TestActivityTaskManagerService(Context context, ActivityManagerService ams) {
+ super(context);
+ spyOn(this);
+
+ mSupportsMultiWindow = true;
+ mSupportsMultiDisplay = true;
+ mSupportsSplitScreenMultiWindow = true;
+ mSupportsFreeformWindowManagement = true;
+ mSupportsPictureInPicture = true;
+
+ doReturn(mock(IPackageManager.class)).when(this).getPackageManager();
+ // allow background activity starts by default
+ doReturn(true).when(this).isBackgroundActivityStartsEnabled();
+ doNothing().when(this).updateCpuStats();
+
+ // AppOpsService
+ final AppOpsService aos = mock(AppOpsService.class);
+ doReturn(aos).when(this).getAppOpsService();
+ // Make sure permission checks aren't overridden.
+ doReturn(AppOpsManager.MODE_DEFAULT)
+ .when(aos).noteOperation(anyInt(), anyInt(), anyString());
+
+ setUsageStatsManager(LocalServices.getService(UsageStatsManagerInternal.class));
+ ams.mActivityTaskManager = this;
+ ams.mAtmInternal = mInternal;
+ onActivityManagerInternalAdded();
+ initialize(
+ ams.mIntentFirewall, ams.mPendingIntentController, mHandlerThread.getLooper());
+ spyOn(getLifecycleManager());
+ spyOn(getLockTaskController());
+ spyOn(getTaskChangeNotificationController());
+ initRootActivityContainerMocks();
+ }
+
+ void initRootActivityContainerMocks() {
+ spyOn(mRootActivityContainer);
+ // Invoked during {@link ActivityStack} creation.
+ doNothing().when(mRootActivityContainer).updateUIDsPresentOnDisplay();
+ // Always keep things awake.
+ doReturn(true).when(mRootActivityContainer).hasAwakeDisplay();
+ // Called when moving activity to pinned stack.
+ doNothing().when(mRootActivityContainer).ensureActivitiesVisible(any(), anyInt(),
+ anyBoolean());
+ }
+
+ @Override
+ int handleIncomingUser(int callingPid, int callingUid, int userId, String name) {
+ return userId;
+ }
+
+ @Override
+ protected ActivityStackSupervisor createStackSupervisor() {
+ if (mTestStackSupervisor == null) {
+ mTestStackSupervisor = new TestActivityStackSupervisor(this, mH.getLooper());
+ }
+ return mTestStackSupervisor;
+ }
+ }
+
+ /**
+ * An {@link ActivityStackSupervisor} which stubs out certain methods that depend on
+ * setup not available in the test environment. Also specifies an injector for
+ */
+ protected class TestActivityStackSupervisor extends ActivityStackSupervisor {
+
+ TestActivityStackSupervisor(ActivityTaskManagerService service, Looper looper) {
+ super(service, looper);
+ spyOn(this);
+
+ // Do not schedule idle that may touch methods outside the scope of the test.
+ doNothing().when(this).scheduleIdleLocked();
+ doNothing().when(this).scheduleIdleTimeoutLocked(any());
+ // unit test version does not handle launch wake lock
+ doNothing().when(this).acquireLaunchWakelock();
+ doReturn(mock(KeyguardController.class)).when(this).getKeyguardController();
+
+ mLaunchingActivityWakeLock = mock(PowerManager.WakeLock.class);
+
+ initialize();
+ }
+ }
+
+ // TODO: Can we just mock this?
+ private static class AMTestInjector extends ActivityManagerService.Injector {
+ private ServiceThread mHandlerThread;
+
+ AMTestInjector(Context context, ServiceThread handlerThread) {
+ super(context);
+ mHandlerThread = handlerThread;
+ }
+
+ @Override
+ public Context getContext() {
+ return getInstrumentation().getTargetContext();
+ }
+
+ @Override
+ public AppOpsService getAppOpsService(File file, Handler handler) {
+ return null;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return mHandlerThread.getThreadHandler();
+ }
+
+ @Override
+ public boolean isNetworkRestrictedForUid(int uid) {
+ return false;
+ }
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 2cebebf..bcff704 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -1277,17 +1277,18 @@
}
private ActivityRecord createSourceActivity(TestActivityDisplay display) {
- final TestActivityStack stack = display.createStack(display.getWindowingMode(),
+ final ActivityStack stack = display.createStack(display.getWindowingMode(),
ACTIVITY_TYPE_STANDARD, true);
return new ActivityBuilder(mService).setStack(stack).setCreateTask(true).build();
}
private void addFreeformTaskTo(TestActivityDisplay display, Rect bounds) {
- final TestActivityStack stack = display.createStack(display.getWindowingMode(),
+ final ActivityStack stack = display.createStack(display.getWindowingMode(),
ACTIVITY_TYPE_STANDARD, true);
stack.setWindowingMode(WINDOWING_MODE_FREEFORM);
final TaskRecord task = new TaskBuilder(mSupervisor).setStack(stack).build();
- task.setBounds(bounds);
+ // Just work around the unnecessary adjustments for bounds.
+ task.getWindowConfiguration().setBounds(bounds);
}
private void assertEquivalentWindowingMode(int expected, int actual, int parentWindowingMode) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index a0302f6..c83401b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -28,14 +28,19 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.util.DisplayMetrics.DENSITY_DEFAULT;
import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_90;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+import static com.android.server.policy.WindowManagerPolicy.USER_ROTATION_FREE;
+import static com.android.server.wm.DisplayRotation.FIXED_TO_USER_ROTATION_ENABLED;
import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertEquals;
@@ -64,6 +69,7 @@
import android.util.Xml;
import android.view.DisplayInfo;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.MediumTest;
import com.android.internal.app.IVoiceInteractor;
@@ -102,6 +108,7 @@
public void setUp() throws Exception {
TaskRecord.setTaskRecordFactory(null);
mParentBounds = new Rect(10 /*left*/, 30 /*top*/, 80 /*right*/, 60 /*bottom*/);
+ removeGlobalMinSizeRestriction();
}
@Test
@@ -165,6 +172,7 @@
/** Ensures that bounds on freeform stacks are not clipped. */
@Test
+ @FlakyTest(bugId = 137879065)
public void testAppBounds_FreeFormBounds() {
final Rect freeFormBounds = new Rect(mParentBounds);
freeFormBounds.offset(10, 10);
@@ -174,6 +182,7 @@
/** Ensures that fully contained bounds are not clipped. */
@Test
+ @FlakyTest(bugId = 137879065)
public void testAppBounds_ContainedBounds() {
final Rect insetBounds = new Rect(mParentBounds);
insetBounds.inset(5, 5, 5, 5);
@@ -182,6 +191,7 @@
}
@Test
+ @FlakyTest(bugId = 137879065)
public void testFitWithinBounds() {
final Rect parentBounds = new Rect(10, 10, 200, 200);
ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
@@ -221,6 +231,7 @@
/** Tests that the task bounds adjust properly to changes between FULLSCREEN and FREEFORM */
@Test
+ @FlakyTest(bugId = 137879065)
public void testBoundsOnModeChangeFreeformToFullscreen() {
ActivityDisplay display = mService.mRootActivityContainer.getDefaultDisplay();
ActivityStack stack = new StackBuilder(mRootActivityContainer).setDisplay(display)
@@ -248,18 +259,6 @@
}
/**
- * This is a temporary hack to trigger an onConfigurationChange at the task level after an
- * orientation is requested. Normally this is done by the onDescendentOrientationChanged call
- * up the WM hierarchy, but since the WM hierarchy is mocked out, it doesn't happen here.
- * TODO: remove this when we either get a WM hierarchy or when hierarchies are merged.
- */
- private void setActivityRequestedOrientation(ActivityRecord activity, int orientation) {
- activity.setRequestedOrientation(orientation);
- ConfigurationContainer taskRecord = activity.getParent();
- taskRecord.onConfigurationChanged(taskRecord.getParent().getConfiguration());
- }
-
- /**
* Tests that a task with forced orientation has orientation-consistent bounds within the
* parent.
*/
@@ -268,49 +267,48 @@
final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
final Rect fullScreenBoundsPort = new Rect(0, 0, 1080, 1920);
DisplayInfo info = new DisplayInfo();
+ mService.mContext.getDisplay().getDisplayInfo(info);
info.logicalWidth = fullScreenBounds.width();
info.logicalHeight = fullScreenBounds.height();
ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
assertTrue(mRootActivityContainer.getActivityDisplay(display.mDisplayId) != null);
- // Override display orientation. Normally this is available via DisplayContent, but DC
- // is mocked-out.
- display.getRequestedOverrideConfiguration().orientation =
- Configuration.ORIENTATION_LANDSCAPE;
- display.onRequestedOverrideConfigurationChanged(
- display.getRequestedOverrideConfiguration());
+ // Fix the display orientation to landscape which is the natural rotation (0) for the test
+ // display.
+ final DisplayRotation dr = display.mDisplayContent.getDisplayRotation();
+ dr.setFixedToUserRotation(FIXED_TO_USER_ROTATION_ENABLED);
+ dr.setUserRotation(USER_ROTATION_FREE, ROTATION_0);
+
ActivityStack stack = new StackBuilder(mRootActivityContainer)
.setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
TaskRecord task = stack.getChildAt(0);
ActivityRecord root = task.getTopActivity();
- assertEquals(root, task.getTopActivity());
assertEquals(fullScreenBounds, task.getBounds());
// Setting app to fixed portrait fits within parent
- setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT);
+ root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertEquals(root, task.getRootActivity());
assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
- assertTrue(task.getBounds().width() < task.getBounds().height());
+ assertThat(task.getBounds().width()).isLessThan(task.getBounds().height());
assertEquals(fullScreenBounds.height(), task.getBounds().height());
// Top activity gets used
ActivityRecord top = new ActivityBuilder(mService).setTask(task).setStack(stack).build();
assertEquals(top, task.getTopActivity());
- setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE);
- assertTrue(task.getBounds().width() > task.getBounds().height());
+ top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
assertEquals(task.getBounds().width(), fullScreenBounds.width());
// Setting app to unspecified restores
- setActivityRequestedOrientation(top, SCREEN_ORIENTATION_UNSPECIFIED);
+ top.setRequestedOrientation(SCREEN_ORIENTATION_UNSPECIFIED);
assertEquals(fullScreenBounds, task.getBounds());
// Setting app to fixed landscape and changing display
- setActivityRequestedOrientation(top, SCREEN_ORIENTATION_LANDSCAPE);
- // simulate display orientation changing (normally done via DisplayContent)
- display.getRequestedOverrideConfiguration().orientation =
- Configuration.ORIENTATION_PORTRAIT;
- display.setBounds(fullScreenBoundsPort);
- assertTrue(task.getBounds().width() > task.getBounds().height());
+ top.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
+ // Fix the display orientation to portrait which is 90 degrees for the test display.
+ dr.setUserRotation(USER_ROTATION_FREE, ROTATION_90);
+
+ assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
// in FREEFORM, no constraint
@@ -323,7 +321,7 @@
// FULLSCREEN letterboxes bounds
stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- assertTrue(task.getBounds().width() > task.getBounds().height());
+ assertThat(task.getBounds().width()).isGreaterThan(task.getBounds().height());
assertEquals(fullScreenBoundsPort.width(), task.getBounds().width());
// FREEFORM restores bounds as before
@@ -335,6 +333,7 @@
public void testIgnoresForcedOrientationWhenParentHandles() {
final Rect fullScreenBounds = new Rect(0, 0, 1920, 1080);
DisplayInfo info = new DisplayInfo();
+ mService.mContext.getDisplay().getDisplayInfo(info);
info.logicalWidth = fullScreenBounds.width();
info.logicalHeight = fullScreenBounds.height();
ActivityDisplay display = addNewActivityDisplayAt(info, POSITION_TOP);
@@ -355,7 +354,7 @@
// Setting app to fixed portrait fits within parent, but TaskRecord shouldn't adjust the
// bounds because its parent says it will handle it at a later time.
- setActivityRequestedOrientation(root, SCREEN_ORIENTATION_PORTRAIT);
+ root.setRequestedOrientation(SCREEN_ORIENTATION_PORTRAIT);
assertEquals(root, task.getRootActivity());
assertEquals(SCREEN_ORIENTATION_PORTRAIT, task.getRootActivity().getOrientation());
assertEquals(fullScreenBounds, task.getBounds());
@@ -646,7 +645,7 @@
final ActivityRecord activity0 = task0.getChildAt(0);
final TaskRecord task1 = getTestTask();
- final ActivityRecord activity1 = task0.getChildAt(0);
+ final ActivityRecord activity1 = task1.getChildAt(0);
assertEquals(task0.taskId,
ActivityRecord.getTaskForActivityLocked(activity0.appToken, false /* onlyRoot */));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
new file mode 100644
index 0000000..c143969
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/TestActivityDisplay.java
@@ -0,0 +1,101 @@
+/*
+ * 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.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyBoolean;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
+import android.hardware.display.DisplayManagerGlobal;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+class TestActivityDisplay extends ActivityDisplay {
+ private final ActivityStackSupervisor mSupervisor;
+
+ static TestActivityDisplay create(ActivityStackSupervisor supervisor) {
+ return create(supervisor, SystemServicesTestRule.sNextDisplayId++);
+ }
+
+ static TestActivityDisplay create(ActivityStackSupervisor supervisor, DisplayInfo info) {
+ return create(supervisor, SystemServicesTestRule.sNextDisplayId++, info);
+ }
+
+ static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId) {
+ final DisplayInfo info = new DisplayInfo();
+ supervisor.mService.mContext.getDisplay().getDisplayInfo(info);
+ return create(supervisor, displayId, info);
+ }
+
+ static TestActivityDisplay create(ActivityStackSupervisor supervisor, int displayId,
+ DisplayInfo info) {
+ if (displayId == DEFAULT_DISPLAY) {
+ synchronized (supervisor.mService.mGlobalLock) {
+ return new TestActivityDisplay(supervisor,
+ supervisor.mRootActivityContainer.mDisplayManager.getDisplay(displayId));
+ }
+ }
+ final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
+ info, DEFAULT_DISPLAY_ADJUSTMENTS);
+
+ synchronized (supervisor.mService.mGlobalLock) {
+ return new TestActivityDisplay(supervisor, display);
+ }
+ }
+
+ private TestActivityDisplay(ActivityStackSupervisor supervisor, Display display) {
+ super(supervisor.mService.mRootActivityContainer, display);
+ // Normally this comes from display-properties as exposed by WM. Without that, just
+ // hard-code to FULLSCREEN for tests.
+ setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ mSupervisor = supervisor;
+ spyOn(this);
+ spyOn(mDisplayContent);
+ doAnswer(invocation -> {
+ // Bypass all the rotation animation and display freezing stuff for testing and just
+ // set the rotation we want for the display
+ final DisplayContent dc = mDisplayContent;
+ final int oldRotation = dc.getRotation();
+ final int rotation = dc.getDisplayRotation().rotationForOrientation(
+ dc.getLastOrientation(), oldRotation);
+ if (oldRotation == rotation) {
+ return false;
+ }
+ dc.setLayoutNeeded();
+ dc.setRotation(rotation);
+ return true;
+ }).when(mDisplayContent).updateRotationUnchecked(anyBoolean());
+ }
+
+ @SuppressWarnings("TypeParameterUnusedInFormals")
+ @Override
+ ActivityStack createStackUnchecked(int windowingMode, int activityType,
+ int stackId, boolean onTop) {
+ return new ActivityTestsBase.StackBuilder(mSupervisor.mRootActivityContainer)
+ .setDisplay(this)
+ .setWindowingMode(windowingMode)
+ .setActivityType(activityType)
+ .setStackId(stackId)
+ .setOnTop(onTop)
+ .setCreateActivity(false)
+ .build();
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 2e5ce69..bb89446 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -40,12 +40,14 @@
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.WindowState.PowerManagerWrapper;
import java.io.PrintWriter;
import java.util.function.Supplier;
class TestWindowManagerPolicy implements WindowManagerPolicy {
private final Supplier<WindowManagerService> mWmSupplier;
+ private final PowerManagerWrapper mPowerManagerWrapper;
int mRotationToReport = 0;
boolean mKeyguardShowingAndNotOccluded = false;
@@ -53,8 +55,10 @@
private Runnable mRunnableWhenAddingSplashScreen;
- TestWindowManagerPolicy(Supplier<WindowManagerService> wmSupplier) {
+ TestWindowManagerPolicy(Supplier<WindowManagerService> wmSupplier,
+ PowerManagerWrapper powerManagerWrapper) {
mWmSupplier = wmSupplier;
+ mPowerManagerWrapper = powerManagerWrapper;
}
@Override
@@ -121,7 +125,7 @@
doReturn(mock(IBinder.class)).when(iWindow).asBinder();
window = WindowTestsBase.createWindow(null, TYPE_APPLICATION_STARTING, atoken,
"Starting window", 0 /* ownerId */, false /* internalWindows */, wm,
- mock(Session.class), iWindow);
+ mock(Session.class), iWindow, mPowerManagerWrapper);
atoken.startingWindow = window;
}
if (mRunnableWhenAddingSplashScreen != null) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index acfc2ea..921f105 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -27,6 +27,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.reset;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -113,6 +114,7 @@
@Test
public void testAddChildSetsSurfacePosition() {
+ reset(mTransaction);
try (MockSurfaceBuildingContainer top = new MockSurfaceBuildingContainer(mWm)) {
WindowContainer child = new WindowContainer(mWm);
child.setBounds(1, 1, 10, 10);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 06bcdf8..60cefe8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -57,6 +57,7 @@
private final IWindow mIWindow = new TestIWindow();
private final Rect mEmptyRect = new Rect();
+ private DisplayContent mTestDisplayContent;
static class FrameTestWindowState extends WindowState {
boolean mDockedResizingForTest = false;
@@ -77,6 +78,9 @@
@Before
public void setUp() throws Exception {
mStubStack = mock(TaskStack.class);
+ DisplayInfo testDisplayInfo = new DisplayInfo(mDisplayInfo);
+ testDisplayInfo.displayCutout = null;
+ mTestDisplayContent = createNewDisplay(testDisplayInfo);
}
// Do not use this function directly in the tests below. Instead, use more explicit function
@@ -100,6 +104,10 @@
assertRect(w.getStableInsets(), left, top, right, bottom);
}
+ private void assertFrame(WindowState w, Rect frame) {
+ assertEquals(w.getFrameLw(), frame);
+ }
+
private void assertFrame(WindowState w, int left, int top, int right, int bottom) {
assertRect(w.getFrameLw(), left, top, right, bottom);
}
@@ -380,9 +388,10 @@
}
@Test
+ @FlakyTest(bugId = 137879065)
public void testLayoutLetterboxedWindow() {
// First verify task behavior in multi-window mode.
- final DisplayInfo displayInfo = mWm.getDefaultDisplayContentLocked().getDisplayInfo();
+ final DisplayInfo displayInfo = mTestDisplayContent.getDisplayInfo();
final int logicalWidth = displayInfo.logicalWidth;
final int logicalHeight = displayInfo.logicalHeight;
@@ -413,13 +422,14 @@
final Rect cf = new Rect(xInset, 0, logicalWidth - xInset, logicalHeight);
Configuration config = new Configuration(w.mAppToken.getRequestedOverrideConfiguration());
config.windowConfiguration.setBounds(cf);
+ config.windowConfiguration.setAppBounds(cf);
w.mAppToken.onRequestedOverrideConfigurationChanged(config);
pf.set(0, 0, logicalWidth, logicalHeight);
task.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
task.setBounds(null);
windowFrames.setFrames(pf, pf, pf, cf, cf, pf, cf, mEmptyRect);
w.computeFrameLw();
- assertFrame(w, cf.left, cf.top, cf.right, cf.bottom);
+ assertFrame(w, cf);
assertContentFrame(w, cf);
assertContentInset(w, 0, 0, 0, 0);
}
@@ -483,7 +493,7 @@
w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
task.setWindowingMode(WINDOWING_MODE_FREEFORM);
- DisplayContent dc = mWm.getDefaultDisplayContentLocked();
+ DisplayContent dc = mTestDisplayContent;
dc.mInputMethodTarget = w;
WindowState mockIme = mock(WindowState.class);
Mockito.doReturn(true).when(mockIme).isVisibleNow();
@@ -537,7 +547,7 @@
attrs.width = width;
attrs.height = height;
- AppWindowToken token = createAppWindowToken(mWm.getDefaultDisplayContentLocked(),
+ AppWindowToken token = createAppWindowToken(mTestDisplayContent,
WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
FrameTestWindowState ws = new FrameTestWindowState(mWm, mIWindow, token, attrs);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 36698ea..b731628 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -346,24 +346,28 @@
firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
- reset(sPowerManagerWrapper);
+ final WindowState.PowerManagerWrapper powerManagerWrapper =
+ mSystemServicesTestRule.getPowerManagerWrapper();
+ reset(powerManagerWrapper);
firstWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
- verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
- reset(sPowerManagerWrapper);
+ reset(powerManagerWrapper);
secondWindow.prepareWindowToDisplayDuringRelayout(false /*wasVisible*/);
- verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
}
private void testPrepareWindowToDisplayDuringRelayout(WindowState appWindow,
boolean expectedWakeupCalled, boolean expectedCurrentLaunchCanTurnScreenOn) {
- reset(sPowerManagerWrapper);
+ final WindowState.PowerManagerWrapper powerManagerWrapper =
+ mSystemServicesTestRule.getPowerManagerWrapper();
+ reset(powerManagerWrapper);
appWindow.prepareWindowToDisplayDuringRelayout(false /* wasVisible */);
if (expectedWakeupCalled) {
- verify(sPowerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManagerWrapper).wakeUp(anyLong(), anyInt(), anyString());
} else {
- verify(sPowerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString());
+ verify(powerManagerWrapper, never()).wakeUp(anyLong(), anyInt(), anyString());
}
// If wakeup is expected to be called, the currentLaunchCanTurnScreenOn should be false
// because the state will be consumed.
@@ -517,13 +521,19 @@
@Test
public void testGetTransformationMatrix() {
+ final int PARENT_WINDOW_OFFSET = 1;
+ final int DISPLAY_IN_PARENT_WINDOW_OFFSET = 2;
+ final int WINDOW_OFFSET = 3;
+ final float OFFSET_SUM =
+ PARENT_WINDOW_OFFSET + DISPLAY_IN_PARENT_WINDOW_OFFSET + WINDOW_OFFSET;
+
synchronized (mWm.mGlobalLock) {
final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
- win0.getFrameLw().offsetTo(1, 0);
final DisplayContent dc = createNewDisplay();
+ win0.getFrameLw().offsetTo(PARENT_WINDOW_OFFSET, 0);
dc.reparentDisplayContent(win0, win0.getSurfaceControl());
- dc.updateLocation(win0, 2, 0);
+ dc.updateLocation(win0, DISPLAY_IN_PARENT_WINDOW_OFFSET, 0);
final float[] values = new float[9];
final Matrix matrix = new Matrix();
@@ -531,12 +541,12 @@
final WindowState win1 = createWindow(null, TYPE_APPLICATION, dc, "win1");
win1.mHasSurface = true;
win1.mSurfaceControl = mock(SurfaceControl.class);
- win1.getFrameLw().offsetTo(3, 0);
+ win1.getFrameLw().offsetTo(WINDOW_OFFSET, 0);
win1.updateSurfacePosition(t);
win1.getTransformationMatrix(values, matrix);
matrix.getValues(values);
- assertEquals(6f, values[Matrix.MTRANS_X], 0f);
+ assertEquals(OFFSET_SUM, values[Matrix.MTRANS_X], 0f);
assertEquals(0f, values[Matrix.MTRANS_Y], 0f);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index dc461d1..d1cf1c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -20,8 +20,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.os.Process.SYSTEM_UID;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -38,33 +36,27 @@
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
+import static com.android.server.wm.ActivityDisplay.POSITION_TOP;
import static org.mockito.Mockito.mock;
-
import android.content.Context;
import android.content.res.Configuration;
-import android.hardware.display.DisplayManagerGlobal;
import android.testing.DexmakerShareClassLoaderRule;
import android.util.Log;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.IWindow;
-import android.view.Surface;
import android.view.SurfaceControl.Transaction;
import android.view.WindowManager;
import com.android.server.AttributeCache;
-import com.android.server.wm.utils.MockTracker;
import org.junit.After;
-import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
-import java.io.IOException;
import java.util.HashSet;
import java.util.LinkedList;
@@ -79,10 +71,6 @@
WindowManagerService mWm;
private final IWindow mIWindow = new TestIWindow();
private Session mMockSession;
- // The default display is removed in {@link #setUp} and then we iterate over all displays to
- // make sure we don't collide with any existing display. If we run into no other display, the
- // added display should be treated as default. This cannot be the default display
- private static int sNextDisplayId = DEFAULT_DISPLAY + 1;
static int sNextStackId = 1000;
/** Non-default display. */
@@ -99,8 +87,6 @@
WindowState mChildAppWindowBelow;
HashSet<WindowState> mCommonWindows;
- private MockTracker mMockTracker;
-
/**
* Spied {@link Transaction} class than can be used to verify calls.
*/
@@ -112,49 +98,27 @@
@Rule
public final SystemServicesTestRule mSystemServicesTestRule = new SystemServicesTestRule();
- static WindowState.PowerManagerWrapper sPowerManagerWrapper;
-
@BeforeClass
public static void setUpOnceBase() {
AttributeCache.init(getInstrumentation().getTargetContext());
-
- sPowerManagerWrapper = mock(WindowState.PowerManagerWrapper.class);
- }
-
- @AfterClass
- public static void tearDownOnceBase() throws IOException {
- sPowerManagerWrapper = null;
}
@Before
public void setUpBase() {
- mMockTracker = new MockTracker();
-
// If @Before throws an exception, the error isn't logged. This will make sure any failures
// in the set up are clear. This can be removed when b/37850063 is fixed.
try {
mMockSession = mock(Session.class);
- mTransaction = spy(StubTransaction.class);
final Context context = getInstrumentation().getTargetContext();
mWm = mSystemServicesTestRule.getWindowManagerService();
-
- // Setup factory classes to prevent calls to native code.
-
- // Return a spied Transaction class than can be used to verify calls.
- mWm.mTransactionFactory = () -> mTransaction;
- // Return a SurfaceControl.Builder class that creates mocked SurfaceControl instances.
- mWm.mSurfaceBuilderFactory = (unused) -> new MockSurfaceControlBuilder();
- // Return mocked Surface instances.
- mWm.mSurfaceFactory = () -> mock(Surface.class);
+ mTransaction = mSystemServicesTestRule.mTransaction;
beforeCreateDisplay();
context.getDisplay().getDisplayInfo(mDisplayInfo);
mDisplayContent = createNewDisplay();
- mWm.mDisplayEnabled = true;
- mWm.mDisplayReady = true;
// Set-up some common windows.
mCommonWindows = new HashSet<>();
@@ -211,12 +175,6 @@
nonCommonWindows.pollLast().removeImmediately();
}
- for (int i = mWm.mRoot.mChildren.size() - 1; i >= 0; --i) {
- final DisplayContent displayContent = mWm.mRoot.mChildren.get(i);
- if (!displayContent.isDefaultDisplay) {
- displayContent.removeImmediately();
- }
- }
// Remove app transition & window freeze timeout callbacks to prevent unnecessary
// actions after test.
mWm.getDefaultDisplayContentLocked().mAppTransition
@@ -230,11 +188,7 @@
} catch (Exception e) {
Log.e(TAG, "Failed to tear down test", e);
throw e;
- } finally {
- mMockTracker.close();
- mMockTracker = null;
}
-
}
private WindowState createCommonWindow(WindowState parent, int type, String name) {
@@ -356,12 +310,13 @@
WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
int ownerId, boolean ownerCanAddInternalSystemWindow) {
return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow,
- mWm, mMockSession, mIWindow);
+ mWm, mMockSession, mIWindow, mSystemServicesTestRule.getPowerManagerWrapper());
}
static WindowState createWindow(WindowState parent, int type, WindowToken token,
String name, int ownerId, boolean ownerCanAddInternalSystemWindow,
- WindowManagerService service, Session session, IWindow iWindow) {
+ WindowManagerService service, Session session, IWindow iWindow,
+ WindowState.PowerManagerWrapper powerManagerWrapper) {
synchronized (service.mGlobalLock) {
final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
attrs.setTitle(name);
@@ -369,7 +324,7 @@
final WindowState w = new WindowState(service, session, iWindow, token, parent,
OP_NONE,
0, attrs, VISIBLE, ownerId, ownerCanAddInternalSystemWindow,
- sPowerManagerWrapper);
+ powerManagerWrapper);
// TODO: Probably better to make this call in the WindowState ctor to avoid errors with
// adding it to the token...
token.addWindow(w);
@@ -408,13 +363,11 @@
}
/** Creates a {@link DisplayContent} and adds it to the system. */
- DisplayContent createNewDisplay(DisplayInfo displayInfo) {
- final int displayId = sNextDisplayId++;
- final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
- displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
- synchronized (mWm.mGlobalLock) {
- return new DisplayContent(display, mWm, mock(ActivityDisplay.class));
- }
+ DisplayContent createNewDisplay(DisplayInfo info) {
+ final ActivityDisplay display =
+ TestActivityDisplay.create(mWm.mAtmService.mStackSupervisor, info);
+ mWm.mAtmService.mRootActivityContainer.addChild(display, POSITION_TOP);
+ return display.mDisplayContent;
}
/**
@@ -428,17 +381,7 @@
DisplayInfo displayInfo = new DisplayInfo();
displayInfo.copyFrom(mDisplayInfo);
displayInfo.state = displayState;
- final int displayId = sNextDisplayId++;
- final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
- displayInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
- synchronized (mWm.mGlobalLock) {
- // Display creation is driven by DisplayWindowController via ActivityStackSupervisor.
- // We skip those steps here.
- final ActivityDisplay mockAd = mock(ActivityDisplay.class);
- final DisplayContent displayContent = mWm.mRoot.createDisplayContent(display, mockAd);
- displayContent.reconfigureDisplayLocked();
- return displayContent;
- }
+ return createNewDisplay(displayInfo);
}
/** Creates a {@link com.android.server.wm.WindowTestUtils.TestWindowState} */
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java b/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java
index a6e675a..7f09482 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/MockTracker.java
@@ -89,7 +89,7 @@
for (final Object mock : mMocks.keySet()) {
if (MockUtil.isMock(mock)) {
- Mockito.reset(mock);
+ mMockitoFramework.clearInlineMock(mock);
}
}
mMocks.clear();
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index 43fb304..42a5501 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -692,7 +692,7 @@
}
/**
- * Contains all sent text-based SMS messages in the SMS app.
+ * Contains all draft text-based SMS messages in the SMS app.
*/
public static final class Draft implements BaseColumns, TextBasedSmsColumns {
@@ -808,7 +808,15 @@
}
/**
- * Contains all sent text-based SMS messages in the SMS app.
+ * Contains a view of SMS conversations (also referred to as threads). This is similar to
+ * {@link Threads}, but only includes SMS messages and columns relevant to SMS
+ * conversations.
+ * <p>
+ * Note that this view ignores any information about MMS messages, it is a
+ * view of conversations as if MMS messages did not exist at all. This means that all
+ * relevant information, such as snippets and message count, will ignore any MMS messages
+ * that might be in the same thread through other views and present only data based on the
+ * SMS messages in that thread.
*/
public static final class Conversations
implements BaseColumns, TextBasedSmsColumns {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 60de214..4fcda63 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2656,25 +2656,34 @@
"call_waiting_service_class_int";
/**
- * This configuration allow the system UI to display different 5G icon for different 5G status.
+ * This configuration allow the system UI to display different 5G icon for different 5G
+ * scenario.
*
- * There are four 5G status:
+ * There are five 5G scenarios:
* 1. connected_mmwave: device currently connected to 5G cell as the secondary cell and using
* millimeter wave.
* 2. connected: device currently connected to 5G cell as the secondary cell but not using
* millimeter wave.
- * 3. not_restricted: device camped on a network that has 5G capability(not necessary to connect
- * a 5G cell as a secondary cell) and the use of 5G is not restricted.
- * 4. restricted: device camped on a network that has 5G capability(not necessary to connect a
+ * 3. not_restricted_rrc_idle: device camped on a network that has 5G capability(not necessary
+ * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
+ * currently in IDLE state.
+ * 4. not_restricted_rrc_con: device camped on a network that has 5G capability(not necessary
+ * to connect a 5G cell as a secondary cell) and the use of 5G is not restricted and RRC
+ * currently in CONNECTED state.
+ * 5. restricted: device camped on a network that has 5G capability(not necessary to connect a
* 5G cell as a secondary cell) but the use of 5G is restricted.
*
* The configured string contains multiple key-value pairs separated by comma. For each pair,
* the key and value is separated by a colon. The key is corresponded to a 5G status above and
* the value is the icon name. Use "None" as the icon name if no icon should be shown in a
- * specific 5G status.
+ * specific 5G scenario. If the scenario is "None", config can skip this key and value.
*
- * Here is an example of the configuration:
- * "connected_mmwave:5GPlus,connected:5G,not_restricted:None,restricted:None"
+ * Icon name options: "5G_Plus", "5G".
+ *
+ * Here is an example:
+ * UE want to display 5G_Plus icon for scenario#1, and 5G icon for scenario#2; otherwise no
+ * define.
+ * The configuration is: "connected_mmwave:5G_Plus,connected:5G"
*
* @hide
*/
@@ -3131,6 +3140,13 @@
public static final String KEY_SUPPORT_WPS_OVER_IMS_BOOL =
"support_wps_over_ims_bool";
+ /**
+ * Holds the list of carrier certificate hashes. Note that each carrier has its own certificates
+ * @hide
+ */
+ public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY =
+ "carrier_certificate_string_array";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -3509,7 +3525,7 @@
sDefaults.putBoolean(KEY_USE_CALLER_ID_USSD_BOOL, false);
sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
- "connected_mmwave:None,connected:5G,not_restricted:None,restricted:None");
+ "connected_mmwave:5G,connected:5G");
sDefaults.putInt(KEY_5G_ICON_DISPLAY_GRACE_PERIOD_SEC_INT, 0);
sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
@@ -3537,7 +3553,7 @@
sDefaults.putStringArray(KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY, new String[0]);
sDefaults.putBoolean(KEY_USE_USIM_BOOL, false);
sDefaults.putBoolean(KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, false);
- sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, false);
+ sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, true);
sDefaults.putString(KEY_SMART_FORWARDING_CONFIG_COMPONENT_NAME_STRING, "");
sDefaults.putBoolean(KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN,
false);
@@ -3552,6 +3568,7 @@
});
sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
sDefaults.putAll(Ims.getDefaults());
+ sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null);
}
/**
diff --git a/telephony/java/android/telephony/CellIdentity.java b/telephony/java/android/telephony/CellIdentity.java
index 258a873..432978d 100644
--- a/telephony/java/android/telephony/CellIdentity.java
+++ b/telephony/java/android/telephony/CellIdentity.java
@@ -17,6 +17,7 @@
package android.telephony;
import android.annotation.CallSuper;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -61,7 +62,7 @@
mType = type;
// Only allow INT_MAX if unknown string mcc/mnc
- if (mcc == null || mcc.matches("^[0-9]{3}$")) {
+ if (mcc == null || isMcc(mcc)) {
mMccStr = mcc;
} else if (mcc.isEmpty() || mcc.equals(String.valueOf(Integer.MAX_VALUE))) {
// If the mccStr is empty or unknown, set it as null.
@@ -73,7 +74,7 @@
log("invalid MCC format: " + mcc);
}
- if (mnc == null || mnc.matches("^[0-9]{2,3}$")) {
+ if (mnc == null || isMnc(mnc)) {
mMncStr = mnc;
} else if (mnc.isEmpty() || mnc.equals(String.valueOf(Integer.MAX_VALUE))) {
// If the mncStr is empty or unknown, set it as null.
@@ -262,4 +263,30 @@
if ((value < rangeMin || value > rangeMax) && value != special) return CellInfo.UNAVAILABLE;
return value;
}
+
+ /** @hide */
+ private static boolean isMcc(@NonNull String mcc) {
+ // ensure no out of bounds indexing
+ if (mcc.length() != 3) return false;
+
+ // Character.isDigit allows all unicode digits, not just [0-9]
+ for (int i = 0; i < 3; i++) {
+ if (mcc.charAt(i) < '0' || mcc.charAt(i) > '9') return false;
+ }
+
+ return true;
+ }
+
+ /** @hide */
+ private static boolean isMnc(@NonNull String mnc) {
+ // ensure no out of bounds indexing
+ if (mnc.length() < 2 || mnc.length() > 3) return false;
+
+ // Character.isDigit allows all unicode digits, not just [0-9]
+ for (int i = 0; i < mnc.length(); i++) {
+ if (mnc.charAt(i) < '0' || mnc.charAt(i) > '9') return false;
+ }
+
+ return true;
+ }
}
diff --git a/telephony/java/android/telephony/NetworkServiceCallback.java b/telephony/java/android/telephony/NetworkServiceCallback.java
index 1c64bcd..89b9665 100644
--- a/telephony/java/android/telephony/NetworkServiceCallback.java
+++ b/telephony/java/android/telephony/NetworkServiceCallback.java
@@ -24,7 +24,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
/**
* Network service callback. Object of this class is passed to NetworkServiceProvider upon
@@ -61,11 +60,11 @@
/** Request failed */
public static final int RESULT_ERROR_FAILED = 5;
- private final WeakReference<INetworkServiceCallback> mCallback;
+ private final INetworkServiceCallback mCallback;
/** @hide */
public NetworkServiceCallback(INetworkServiceCallback callback) {
- mCallback = new WeakReference<>(callback);
+ mCallback = callback;
}
/**
@@ -78,15 +77,14 @@
*/
public void onRequestNetworkRegistrationInfoComplete(int result,
@Nullable NetworkRegistrationInfo state) {
- INetworkServiceCallback callback = mCallback.get();
- if (callback != null) {
+ if (mCallback != null) {
try {
- callback.onRequestNetworkRegistrationInfoComplete(result, state);
+ mCallback.onRequestNetworkRegistrationInfoComplete(result, state);
} catch (RemoteException e) {
Rlog.e(mTag, "Failed to onRequestNetworkRegistrationInfoComplete on the remote");
}
} else {
- Rlog.e(mTag, "Weak reference of callback is null.");
+ Rlog.e(mTag, "onRequestNetworkRegistrationInfoComplete callback is null.");
}
}
}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index a8491d3..36e8123 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -38,6 +38,7 @@
import android.util.DisplayMetrics;
import android.util.Log;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -147,7 +148,14 @@
* The access rules for this subscription, if it is embedded and defines any.
*/
@Nullable
- private UiccAccessRule[] mAccessRules;
+ private UiccAccessRule[] mNativeAccessRules;
+
+ /**
+ * The carrier certificates for this subscription that are saved in carrier configs.
+ * The other carrier certificates are embedded on Uicc and stored as part of mNativeAccessRules.
+ */
+ @Nullable
+ private UiccAccessRule[] mCarrierConfigAccessRules;
/**
* The string ID of the SIM card. It is the ICCID of the active profile for a UICC card and the
@@ -206,12 +214,12 @@
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
- @Nullable UiccAccessRule[] accessRules, String cardString) {
+ @Nullable UiccAccessRule[] nativeAccessRules, String cardString) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
- roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, -1,
false, null, false, TelephonyManager.UNKNOWN_CARRIER_ID,
SubscriptionManager.PROFILE_CLASS_DEFAULT,
- SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null);
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null);
}
/**
@@ -220,12 +228,12 @@
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
- @Nullable UiccAccessRule[] accessRules, String cardString, boolean isOpportunistic,
- @Nullable String groupUUID, int carrierId, int profileClass) {
+ @Nullable UiccAccessRule[] nativeAccessRules, String cardString,
+ boolean isOpportunistic, @Nullable String groupUUID, int carrierId, int profileClass) {
this(id, iccId, simSlotIndex, displayName, carrierName, nameSource, iconTint, number,
- roaming, icon, mcc, mnc, countryIso, isEmbedded, accessRules, cardString, -1,
+ roaming, icon, mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, -1,
isOpportunistic, groupUUID, false, carrierId, profileClass,
- SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null);
+ SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM, null, null);
}
/**
@@ -234,9 +242,10 @@
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
- @Nullable UiccAccessRule[] accessRules, String cardString, int cardId,
+ @Nullable UiccAccessRule[] nativeAccessRules, String cardString, int cardId,
boolean isOpportunistic, @Nullable String groupUUID, boolean isGroupDisabled,
- int carrierId, int profileClass, int subType, @Nullable String groupOwner) {
+ int carrierId, int profileClass, int subType, @Nullable String groupOwner,
+ @Nullable UiccAccessRule[] carrierConfigAccessRules) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
@@ -251,7 +260,7 @@
this.mMnc = mnc;
this.mCountryIso = countryIso;
this.mIsEmbedded = isEmbedded;
- this.mAccessRules = accessRules;
+ this.mNativeAccessRules = nativeAccessRules;
this.mCardString = cardString;
this.mCardId = cardId;
this.mIsOpportunistic = isOpportunistic;
@@ -261,6 +270,7 @@
this.mProfileClass = profileClass;
this.mSubscriptionType = subType;
this.mGroupOwner = groupOwner;
+ this.mCarrierConfigAccessRules = carrierConfigAccessRules;
}
/**
@@ -566,7 +576,8 @@
if (!isEmbedded()) {
throw new UnsupportedOperationException("Not an embedded subscription");
}
- if (mAccessRules == null) {
+ List<UiccAccessRule> allAccessRules = getAllAccessRules();
+ if (allAccessRules == null) {
return false;
}
PackageManager packageManager = context.getPackageManager();
@@ -576,7 +587,7 @@
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalArgumentException("Unknown package: " + packageName, e);
}
- for (UiccAccessRule rule : mAccessRules) {
+ for (UiccAccessRule rule : allAccessRules) {
if (rule.getCarrierPrivilegeStatus(packageInfo)
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
return true;
@@ -586,7 +597,10 @@
}
/**
- * @return the {@link UiccAccessRule}s dictating who is authorized to manage this subscription.
+ * @return the {@link UiccAccessRule}s that are stored in Uicc, dictating who
+ * is authorized to manage this subscription.
+ * TODO and fix it properly in R / master: either deprecate this and have 3 APIs
+ * native + carrier + all, or have this return all by default.
* @throws UnsupportedOperationException if this subscription is not embedded.
* @hide
*/
@@ -595,8 +609,25 @@
if (!isEmbedded()) {
throw new UnsupportedOperationException("Not an embedded subscription");
}
- if (mAccessRules == null) return null;
- return Arrays.asList(mAccessRules);
+ if (mNativeAccessRules == null) return null;
+ return Arrays.asList(mNativeAccessRules);
+ }
+
+ /**
+ * @return the {@link UiccAccessRule}s that are both stored on Uicc and in carrierConfigs
+ * dictating who is authorized to manage this subscription.
+ * @hide
+ */
+ public @Nullable List<UiccAccessRule> getAllAccessRules() {
+ if (!isEmbedded()) {
+ throw new UnsupportedOperationException("Not an embedded subscription");
+ }
+ List<UiccAccessRule> merged = new ArrayList<>();
+ if (mNativeAccessRules != null) merged.addAll(getAccessRules());
+ if (mCarrierConfigAccessRules != null) {
+ merged.addAll(Arrays.asList(mCarrierConfigAccessRules));
+ }
+ return merged.isEmpty() ? null : merged;
}
/**
@@ -651,7 +682,7 @@
String countryIso = source.readString();
Bitmap iconBitmap = source.readParcelable(Bitmap.class.getClassLoader());
boolean isEmbedded = source.readBoolean();
- UiccAccessRule[] accessRules = source.createTypedArray(UiccAccessRule.CREATOR);
+ UiccAccessRule[] nativeAccessRules = source.createTypedArray(UiccAccessRule.CREATOR);
String cardString = source.readString();
int cardId = source.readInt();
boolean isOpportunistic = source.readBoolean();
@@ -663,11 +694,14 @@
String[] ehplmns = source.readStringArray();
String[] hplmns = source.readStringArray();
String groupOwner = source.readString();
+ UiccAccessRule[] carrierConfigAccessRules = source.createTypedArray(
+ UiccAccessRule.CREATOR);
SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName,
carrierName, nameSource, iconTint, number, dataRoaming, iconBitmap, mcc, mnc,
- countryIso, isEmbedded, accessRules, cardString, cardId, isOpportunistic,
- groupUUID, isGroupDisabled, carrierid, profileClass, subType, groupOwner);
+ countryIso, isEmbedded, nativeAccessRules, cardString, cardId, isOpportunistic,
+ groupUUID, isGroupDisabled, carrierid, profileClass, subType, groupOwner,
+ carrierConfigAccessRules);
info.setAssociatedPlmns(ehplmns, hplmns);
return info;
}
@@ -694,7 +728,7 @@
dest.writeString(mCountryIso);
dest.writeParcelable(mIconBitmap, flags);
dest.writeBoolean(mIsEmbedded);
- dest.writeTypedArray(mAccessRules, flags);
+ dest.writeTypedArray(mNativeAccessRules, flags);
dest.writeString(mCardString);
dest.writeInt(mCardId);
dest.writeBoolean(mIsOpportunistic);
@@ -706,6 +740,7 @@
dest.writeStringArray(mEhplmns);
dest.writeStringArray(mHplmns);
dest.writeString(mGroupOwner);
+ dest.writeTypedArray(mCarrierConfigAccessRules, flags);
}
@Override
@@ -736,9 +771,9 @@
+ " carrierId=" + mCarrierId + " displayName=" + mDisplayName
+ " carrierName=" + mCarrierName + " nameSource=" + mNameSource
+ " iconTint=" + mIconTint + " mNumber=" + Rlog.pii(Build.IS_DEBUGGABLE, mNumber)
- + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc=" + mMcc
- + " mnc=" + mMnc + " mCountryIso=" + mCountryIso + " isEmbedded=" + mIsEmbedded
- + " accessRules=" + Arrays.toString(mAccessRules)
+ + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc
+ + " mnc " + mMnc + "mCountryIso=" + mCountryIso + " isEmbedded " + mIsEmbedded
+ + " nativeAccessRules " + Arrays.toString(mNativeAccessRules)
+ " cardString=" + cardStringToPrint + " cardId=" + mCardId
+ " isOpportunistic=" + mIsOpportunistic + " mGroupUUID=" + mGroupUUID
+ " mIsGroupDisabled=" + mIsGroupDisabled
@@ -746,14 +781,15 @@
+ " ehplmns=" + Arrays.toString(mEhplmns)
+ " hplmns=" + Arrays.toString(mHplmns)
+ " subscriptionType=" + mSubscriptionType
- + " mGroupOwner=" + mGroupOwner + "}";
+ + " mGroupOwner=" + mGroupOwner
+ + " carrierConfigAccessRules=" + mCarrierConfigAccessRules + "}";
}
@Override
public int hashCode() {
return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc,
- mCountryIso, mCardString, mCardId, mDisplayName, mCarrierName, mAccessRules,
+ mCountryIso, mCardString, mCardId, mDisplayName, mCarrierName, mNativeAccessRules,
mIsGroupDisabled, mCarrierId, mProfileClass, mGroupOwner);
}
@@ -789,7 +825,7 @@
&& Objects.equals(mGroupOwner, toCompare.mGroupOwner)
&& TextUtils.equals(mDisplayName, toCompare.mDisplayName)
&& TextUtils.equals(mCarrierName, toCompare.mCarrierName)
- && Arrays.equals(mAccessRules, toCompare.mAccessRules)
+ && Arrays.equals(mNativeAccessRules, toCompare.mNativeAccessRules)
&& mProfileClass == toCompare.mProfileClass
&& Arrays.equals(mEhplmns, toCompare.mEhplmns)
&& Arrays.equals(mHplmns, toCompare.mHplmns);
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 8c53c51..a84c916 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -571,6 +571,16 @@
public static final String ACCESS_RULES = "access_rules";
/**
+ * TelephonyProvider column name for the encoded {@link UiccAccessRule}s from
+ * {@link UiccAccessRule#encodeRules} but for the rules that come from CarrierConfigs.
+ * Only present if there are access rules in CarrierConfigs
+ * <p>TYPE: BLOB
+ * @hide
+ */
+ public static final String ACCESS_RULES_FROM_CARRIER_CONFIGS =
+ "access_rules_from_carrier_configs";
+
+ /**
* TelephonyProvider column name identifying whether an embedded subscription is on a removable
* card. Such subscriptions are marked inaccessible as soon as the current card is removed.
* Otherwise, they will remain accessible unless explicitly deleted. Only present if
@@ -2601,7 +2611,7 @@
if (!info.isEmbedded()) {
throw new IllegalArgumentException("Not an embedded subscription");
}
- if (info.getAccessRules() == null) {
+ if (info.getAllAccessRules() == null) {
return false;
}
PackageManager packageManager = mContext.getPackageManager();
@@ -2611,7 +2621,7 @@
} catch (PackageManager.NameNotFoundException e) {
throw new IllegalArgumentException("Unknown package: " + packageName, e);
}
- for (UiccAccessRule rule : info.getAccessRules()) {
+ for (UiccAccessRule rule : info.getAllAccessRules()) {
if (rule.getCarrierPrivilegeStatus(packageInfo)
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
return true;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 35b435d..553bff2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -11047,6 +11047,8 @@
* The {@link #EXTRA_NETWORK_COUNTRY} extra indicates the country code of the current
* network returned by {@link #getNetworkCountryIso()}.
*
+ * <p>There may be a delay of several minutes before reporting that no country is detected.
+ *
* @see #EXTRA_NETWORK_COUNTRY
* @see #getNetworkCountryIso()
*/
diff --git a/telephony/java/android/telephony/data/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 89d30c0d..11dc78a 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -27,7 +27,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
import java.util.List;
/**
@@ -64,11 +63,11 @@
/** Request sent in illegal state */
public static final int RESULT_ERROR_ILLEGAL_STATE = 4;
- private final WeakReference<IDataServiceCallback> mCallback;
+ private final IDataServiceCallback mCallback;
/** @hide */
public DataServiceCallback(IDataServiceCallback callback) {
- mCallback = new WeakReference<>(callback);
+ mCallback = callback;
}
/**
@@ -80,14 +79,15 @@
*/
public void onSetupDataCallComplete(@ResultCode int result,
@Nullable DataCallResponse response) {
- IDataServiceCallback callback = mCallback.get();
- if (callback != null) {
+ if (mCallback != null) {
try {
if (DBG) Rlog.d(TAG, "onSetupDataCallComplete");
- callback.onSetupDataCallComplete(result, response);
+ mCallback.onSetupDataCallComplete(result, response);
} catch (RemoteException e) {
Rlog.e(TAG, "Failed to onSetupDataCallComplete on the remote");
}
+ } else {
+ Rlog.e(TAG, "onSetupDataCallComplete: callback is null!");
}
}
@@ -98,14 +98,15 @@
* @param result The result code. Must be one of the {@link ResultCode}.
*/
public void onDeactivateDataCallComplete(@ResultCode int result) {
- IDataServiceCallback callback = mCallback.get();
- if (callback != null) {
+ if (mCallback != null) {
try {
if (DBG) Rlog.d(TAG, "onDeactivateDataCallComplete");
- callback.onDeactivateDataCallComplete(result);
+ mCallback.onDeactivateDataCallComplete(result);
} catch (RemoteException e) {
Rlog.e(TAG, "Failed to onDeactivateDataCallComplete on the remote");
}
+ } else {
+ Rlog.e(TAG, "onDeactivateDataCallComplete: callback is null!");
}
}
@@ -116,13 +117,14 @@
* @param result The result code. Must be one of the {@link ResultCode}.
*/
public void onSetInitialAttachApnComplete(@ResultCode int result) {
- IDataServiceCallback callback = mCallback.get();
- if (callback != null) {
+ if (mCallback != null) {
try {
- callback.onSetInitialAttachApnComplete(result);
+ mCallback.onSetInitialAttachApnComplete(result);
} catch (RemoteException e) {
Rlog.e(TAG, "Failed to onSetInitialAttachApnComplete on the remote");
}
+ } else {
+ Rlog.e(TAG, "onSetInitialAttachApnComplete: callback is null!");
}
}
@@ -133,13 +135,14 @@
* @param result The result code. Must be one of the {@link ResultCode}.
*/
public void onSetDataProfileComplete(@ResultCode int result) {
- IDataServiceCallback callback = mCallback.get();
- if (callback != null) {
+ if (mCallback != null) {
try {
- callback.onSetDataProfileComplete(result);
+ mCallback.onSetDataProfileComplete(result);
} catch (RemoteException e) {
Rlog.e(TAG, "Failed to onSetDataProfileComplete on the remote");
}
+ } else {
+ Rlog.e(TAG, "onSetDataProfileComplete: callback is null!");
}
}
@@ -153,13 +156,14 @@
*/
public void onRequestDataCallListComplete(@ResultCode int result,
@NonNull List<DataCallResponse> dataCallList) {
- IDataServiceCallback callback = mCallback.get();
- if (callback != null) {
+ if (mCallback != null) {
try {
- callback.onRequestDataCallListComplete(result, dataCallList);
+ mCallback.onRequestDataCallListComplete(result, dataCallList);
} catch (RemoteException e) {
Rlog.e(TAG, "Failed to onRequestDataCallListComplete on the remote");
}
+ } else {
+ Rlog.e(TAG, "onRequestDataCallListComplete: callback is null!");
}
}
@@ -170,14 +174,15 @@
* @param dataCallList List of the current active data connection.
*/
public void onDataCallListChanged(@NonNull List<DataCallResponse> dataCallList) {
- IDataServiceCallback callback = mCallback.get();
- if (callback != null) {
+ if (mCallback != null) {
try {
if (DBG) Rlog.d(TAG, "onDataCallListChanged");
- callback.onDataCallListChanged(dataCallList);
+ mCallback.onDataCallListChanged(dataCallList);
} catch (RemoteException e) {
Rlog.e(TAG, "Failed to onDataCallListChanged on the remote");
}
+ } else {
+ Rlog.e(TAG, "onDataCallListChanged: callback is null!");
}
}
}
diff --git a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
index e9a177d..4e1ff8f 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfoAsyncQuery.java
@@ -32,9 +32,11 @@
import android.os.UserManager;
import android.provider.ContactsContract.PhoneLookup;
import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
import android.telephony.Rlog;
import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
import java.util.ArrayList;
import java.util.List;
@@ -80,6 +82,10 @@
* classes.
*/
private static final class CookieWrapper {
+ @UnsupportedAppUsage
+ private CookieWrapper() {
+ }
+
public OnQueryCompleteListener listener;
public Object cookie;
public int event;
@@ -527,6 +533,7 @@
/**
* Releases the relevant data.
*/
+ @UnsupportedAppUsage
private void release() {
mHandler.mContext = null;
mHandler.mQueryUri = null;
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index cde6db4..e1113eb 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -17,6 +17,8 @@
import com.android.internal.util.Protocol;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
/**
* @hide
*/
@@ -37,20 +39,34 @@
* RETRYING or CONNECTING: CONNECTING
* CONNECTED : CONNECTED or DISCONNECTING
*/
+ @UnsupportedAppUsage(implicitMember =
+ "values()[Lcom/android/internal/telephony/DctConstants$State;")
public enum State {
+ @UnsupportedAppUsage
IDLE,
+ @UnsupportedAppUsage
CONNECTING,
+ @UnsupportedAppUsage
RETRYING,
+ @UnsupportedAppUsage
CONNECTED,
+ @UnsupportedAppUsage
DISCONNECTING,
+ @UnsupportedAppUsage
FAILED,
}
+ @UnsupportedAppUsage(implicitMember =
+ "values()[Lcom/android/internal/telephony/DctConstants$Activity;")
public enum Activity {
NONE,
+ @UnsupportedAppUsage
DATAIN,
+ @UnsupportedAppUsage
DATAOUT,
+ @UnsupportedAppUsage
DATAINANDOUT,
+ @UnsupportedAppUsage
DORMANT
}
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index a75096f..5fb4e90 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -16,19 +16,17 @@
package com.android.internal.telephony;
+import android.annotation.UnsupportedAppUsage;
import android.content.res.Resources;
+import android.os.Build;
+import android.telephony.Rlog;
import android.text.TextUtils;
import android.util.SparseIntArray;
-import android.annotation.UnsupportedAppUsage;
-import android.os.Build;
-import android.telephony.Rlog;
+import com.android.internal.R;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
-import com.android.internal.telephony.SmsConstants;
-import com.android.internal.R;
-
import java.util.ArrayList;
import java.util.List;
@@ -83,6 +81,11 @@
* data.
*/
public static class TextEncodingDetails {
+
+ @UnsupportedAppUsage
+ public TextEncodingDetails() {
+ }
+
/**
*The number of SMS's required to encode the text.
*/
diff --git a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
index 5b509b7..15e7fc2 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl
@@ -27,6 +27,7 @@
/**
* Retrieves the unique device ID, e.g., IMEI for GSM phones.
*/
+ @UnsupportedAppUsage
String getDeviceId(String callingPackage);
/**
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index a65acac..9f1a2f7 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -94,6 +94,7 @@
* @param callingPackage the name of the package making the call.
* @return returns true if the radio is on.
*/
+ @UnsupportedAppUsage
boolean isRadioOn(String callingPackage);
/**
@@ -1254,6 +1255,7 @@
* <p>Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
+ @UnsupportedAppUsage
String getDeviceId(String callingPackage);
/**
diff --git a/telephony/java/com/android/internal/telephony/IccCardConstants.java b/telephony/java/com/android/internal/telephony/IccCardConstants.java
index d57f9af..6ff27b1 100644
--- a/telephony/java/com/android/internal/telephony/IccCardConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccCardConstants.java
@@ -17,6 +17,8 @@
import android.telephony.TelephonyManager;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
/**
* {@hide}
*/
@@ -65,15 +67,26 @@
*
* The ordinal values much match {@link TelephonyManager#SIM_STATE_UNKNOWN} ...
*/
+ @UnsupportedAppUsage(implicitMember =
+ "values()[Lcom/android/internal/telephony/IccCardConstants$State;")
public enum State {
+ @UnsupportedAppUsage
UNKNOWN, /** ordinal(0) == {@See TelephonyManager#SIM_STATE_UNKNOWN} */
+ @UnsupportedAppUsage
ABSENT, /** ordinal(1) == {@See TelephonyManager#SIM_STATE_ABSENT} */
+ @UnsupportedAppUsage
PIN_REQUIRED, /** ordinal(2) == {@See TelephonyManager#SIM_STATE_PIN_REQUIRED} */
+ @UnsupportedAppUsage
PUK_REQUIRED, /** ordinal(3) == {@See TelephonyManager#SIM_STATE_PUK_REQUIRED} */
+ @UnsupportedAppUsage
NETWORK_LOCKED, /** ordinal(4) == {@See TelephonyManager#SIM_STATE_NETWORK_LOCKED} */
+ @UnsupportedAppUsage
READY, /** ordinal(5) == {@See TelephonyManager#SIM_STATE_READY} */
+ @UnsupportedAppUsage
NOT_READY, /** ordinal(6) == {@See TelephonyManager#SIM_STATE_NOT_READY} */
+ @UnsupportedAppUsage
PERM_DISABLED, /** ordinal(7) == {@See TelephonyManager#SIM_STATE_PERM_DISABLED} */
+ @UnsupportedAppUsage
CARD_IO_ERROR, /** ordinal(8) == {@See TelephonyManager#SIM_STATE_CARD_IO_ERROR} */
CARD_RESTRICTED,/** ordinal(9) == {@See TelephonyManager#SIM_STATE_CARD_RESTRICTED} */
LOADED; /** ordinal(9) == {@See TelephonyManager#SIM_STATE_LOADED} */
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index d5061a3..ee1a476 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -15,6 +15,8 @@
*/
package com.android.internal.telephony;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
/**
* @hide
*/
@@ -31,8 +33,12 @@
* ringing or waiting.</li>
* </ul>
*/
+ @UnsupportedAppUsage(implicitMember =
+ "values()[Lcom/android/internal/telephony/PhoneConstants$State;")
public enum State {
- IDLE, RINGING, OFFHOOK;
+ @UnsupportedAppUsage IDLE,
+ @UnsupportedAppUsage RINGING,
+ @UnsupportedAppUsage OFFHOOK;
};
/**
@@ -46,8 +52,13 @@
* in 2G network</li>
* </ul>
*/
+ @UnsupportedAppUsage(implicitMember =
+ "values()[Lcom/android/internal/telephony/PhoneConstants$DataState;")
public enum DataState {
- CONNECTED, CONNECTING, DISCONNECTED, SUSPENDED;
+ @UnsupportedAppUsage CONNECTED,
+ @UnsupportedAppUsage CONNECTING,
+ @UnsupportedAppUsage DISCONNECTED,
+ @UnsupportedAppUsage SUSPENDED;
};
public static final String STATE_KEY = "state";
@@ -69,9 +80,13 @@
public static final int LTE_ON_CDMA_TRUE = RILConstants.LTE_ON_CDMA_TRUE;
// Number presentation type for caller id display (From internal/Connection.java)
+ @UnsupportedAppUsage
public static final int PRESENTATION_ALLOWED = 1; // normal
+ @UnsupportedAppUsage
public static final int PRESENTATION_RESTRICTED = 2; // block by user
+ @UnsupportedAppUsage
public static final int PRESENTATION_UNKNOWN = 3; // no specified or unknown by network
+ @UnsupportedAppUsage
public static final int PRESENTATION_PAYPHONE = 4; // show pay phone info
// Sim activation type
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 5205973..03ea920 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -18,6 +18,8 @@
import android.telephony.TelephonyManager;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
/**
* {@hide}
*/
@@ -230,6 +232,7 @@
/** NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA */
int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 33;
+ @UnsupportedAppUsage
int PREFERRED_NETWORK_MODE = Integer.parseInt(TelephonyManager.getTelephonyProperty(0,
"ro.telephony.default_network", Integer.toString(NETWORK_MODE_WCDMA_PREF)));
diff --git a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
index 49c737f..2cdf2f6 100644
--- a/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
+++ b/telephony/java/com/android/internal/telephony/Sms7BitEncodingTranslator.java
@@ -16,27 +16,28 @@
package com.android.internal.telephony;
-import android.telephony.Rlog;
-import android.os.Build;
-import android.util.SparseIntArray;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
-import android.telephony.SmsManager;
-import android.telephony.TelephonyManager;
+import android.os.Build;
+import android.telephony.Rlog;
+import android.util.SparseIntArray;
-import com.android.internal.util.XmlUtils;
import com.android.internal.telephony.cdma.sms.UserData;
+import com.android.internal.util.XmlUtils;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
+import dalvik.annotation.compat.UnsupportedAppUsage;
public class Sms7BitEncodingTranslator {
private static final String TAG = "Sms7BitEncodingTranslator";
+ @UnsupportedAppUsage
private static final boolean DBG = Build.IS_DEBUGGABLE ;
private static boolean mIs7BitTranslationTableLoaded = false;
private static SparseIntArray mTranslationTable = null;
+ @UnsupportedAppUsage
private static SparseIntArray mTranslationTableCommon = null;
+ @UnsupportedAppUsage
private static SparseIntArray mTranslationTableGSM = null;
+ @UnsupportedAppUsage
private static SparseIntArray mTranslationTableCDMA = null;
// Parser variables
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index f00035c..f10398f 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -48,6 +48,8 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
@@ -91,6 +93,7 @@
/**
* Name of this SMS app for display.
*/
+ @UnsupportedAppUsage
private String mApplicationName;
/**
@@ -224,6 +227,7 @@
* Implement ACTION_SENDTO intent.
* Support smsto Uri scheme.
*/
+ @UnsupportedAppUsage
public static Collection<SmsApplicationData> getApplicationCollection(Context context) {
return getApplicationCollectionAsUser(context, getIncomingUserId(context));
}
@@ -576,6 +580,7 @@
* Sets the specified package as the default SMS/MMS application. The caller of this method
* needs to have permission to set AppOps and write to secure settings.
*/
+ @UnsupportedAppUsage
public static void setDefaultApplication(String packageName, Context context) {
setDefaultApplicationAsUser(packageName, context, getIncomingUserId(context));
}
@@ -823,6 +828,7 @@
sSmsPackageMonitor.register(context, context.getMainLooper(), UserHandle.ALL, false);
}
+ @UnsupportedAppUsage
private static void configurePreferredActivity(PackageManager packageManager,
ComponentName componentName, int userId) {
// Add the four activity preferences we want to direct to this app.
@@ -865,6 +871,7 @@
* Returns SmsApplicationData for this package if this package is capable of being set as the
* default SMS application.
*/
+ @UnsupportedAppUsage
public static SmsApplicationData getSmsApplicationData(String packageName, Context context) {
Collection<SmsApplicationData> applications = getApplicationCollection(context);
return getApplicationForPackage(applications, packageName);
@@ -876,6 +883,7 @@
* @param updateIfNeeded update the default app if there is no valid default app configured.
* @return component name of the app and class to deliver SMS messages to
*/
+ @UnsupportedAppUsage
public static ComponentName getDefaultSmsApplication(Context context, boolean updateIfNeeded) {
return getDefaultSmsApplicationAsUser(context, updateIfNeeded, getIncomingUserId(context));
}
@@ -910,6 +918,7 @@
* @param updateIfNeeded update the default app if there is no valid default app configured.
* @return component name of the app and class to deliver MMS messages to
*/
+ @UnsupportedAppUsage
public static ComponentName getDefaultMmsApplication(Context context, boolean updateIfNeeded) {
int userId = getIncomingUserId(context);
final long token = Binder.clearCallingIdentity();
@@ -933,6 +942,7 @@
* @param updateIfNeeded update the default app if there is no valid default app configured.
* @return component name of the app and class to direct Respond Via Message intent to
*/
+ @UnsupportedAppUsage
public static ComponentName getDefaultRespondViaMessageApplication(Context context,
boolean updateIfNeeded) {
int userId = getIncomingUserId(context);
@@ -1033,6 +1043,7 @@
* <p>
* Caller must pass in the correct user context if calling from a singleton service.
*/
+ @UnsupportedAppUsage
public static boolean shouldWriteMessageForPackage(String packageName, Context context) {
if (SmsManager.getDefault().getAutoPersisting()) {
return true;
@@ -1047,6 +1058,7 @@
* @param packageName the name of the package to be checked
* @return true if the package is default sms app or bluetooth
*/
+ @UnsupportedAppUsage
public static boolean isDefaultSmsApplication(Context context, String packageName) {
if (packageName == null) {
return false;
diff --git a/telephony/java/com/android/internal/telephony/SmsHeader.java b/telephony/java/com/android/internal/telephony/SmsHeader.java
index 9fe1718..dd77b01 100644
--- a/telephony/java/com/android/internal/telephony/SmsHeader.java
+++ b/telephony/java/com/android/internal/telephony/SmsHeader.java
@@ -16,13 +16,12 @@
package com.android.internal.telephony;
-import com.android.internal.telephony.SmsConstants;
+import android.annotation.UnsupportedAppUsage;
+
import com.android.internal.util.HexDump;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
-
-import android.annotation.UnsupportedAppUsage;
import java.util.ArrayList;
/**
@@ -74,6 +73,10 @@
public static class PortAddrs {
@UnsupportedAppUsage
+ public PortAddrs() {
+ }
+
+ @UnsupportedAppUsage
public int destPort;
@UnsupportedAppUsage
public int origPort;
@@ -82,6 +85,10 @@
public static class ConcatRef {
@UnsupportedAppUsage
+ public ConcatRef() {
+ }
+
+ @UnsupportedAppUsage
public int refNumber;
@UnsupportedAppUsage
public int seqNumber;
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index ffdc4b6..f0687b4 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -16,18 +16,17 @@
package com.android.internal.telephony;
-import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
-import com.android.internal.telephony.SmsConstants;
-import com.android.internal.telephony.SmsHeader;
-import java.text.BreakIterator;
-import java.util.Arrays;
-
import android.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.provider.Telephony;
import android.telephony.SmsMessage;
import android.text.Emoji;
+import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+
+import java.text.BreakIterator;
+import java.util.Arrays;
+
/**
* Base class declaring the specific methods and members for SmsMessage.
* {@hide}
@@ -102,6 +101,10 @@
@UnsupportedAppUsage
public int mMessageRef;
+ @UnsupportedAppUsage
+ public SmsMessageBase() {
+ }
+
// TODO(): This class is duplicated in SmsMessage.java. Refactor accordingly.
public static abstract class SubmitPduBase {
@UnsupportedAppUsage
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index dd9b242..ef485071 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -16,6 +16,8 @@
package com.android.internal.telephony;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
/**
* Contains a list of string constants used to get or set telephone properties
* in the system. You can use {@link android.os.SystemProperties os.SystemProperties}
@@ -101,6 +103,7 @@
* provider of the SIM. 5 or 6 decimal digits.
* Availability: SIM state must be "READY"
*/
+ @UnsupportedAppUsage
static String PROPERTY_ICC_OPERATOR_NUMERIC = "gsm.sim.operator.numeric";
/** PROPERTY_ICC_OPERATOR_ALPHA is also known as the SPN, or Service Provider Name.
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index e2a8913b..6b3126d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -19,17 +19,17 @@
import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
import android.content.res.Resources;
-import android.os.Parcel;
import android.os.SystemProperties;
import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
import android.telephony.SmsCbLocation;
import android.telephony.SmsCbMessage;
import android.telephony.cdma.CdmaSmsCbProgramData;
-import android.telephony.Rlog;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
+import com.android.internal.telephony.Sms7BitEncodingTranslator;
import com.android.internal.telephony.SmsAddress;
import com.android.internal.telephony.SmsConstants;
import com.android.internal.telephony.SmsHeader;
@@ -43,7 +43,8 @@
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.util.BitwiseInputStream;
import com.android.internal.util.HexDump;
-import com.android.internal.telephony.Sms7BitEncodingTranslator;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
@@ -109,7 +110,9 @@
private static final int PRIORITY_URGENT = 0x2;
private static final int PRIORITY_EMERGENCY = 0x3;
+ @UnsupportedAppUsage
private SmsEnvelope mEnvelope;
+ @UnsupportedAppUsage
private BearerData mBearerData;
/** @hide */
@@ -119,15 +122,20 @@
createPdu();
}
+ @UnsupportedAppUsage
public SmsMessage() {}
public static class SubmitPdu extends SubmitPduBase {
+ @UnsupportedAppUsage
+ public SubmitPdu() {
+ }
}
/**
* Create an SmsMessage from a raw PDU.
* Note: In CDMA the PDU is just a byte representation of the received Sms.
*/
+ @UnsupportedAppUsage
public static SmsMessage createFromPdu(byte[] pdu) {
SmsMessage msg = new SmsMessage();
@@ -153,6 +161,7 @@
*
* @hide
*/
+ @UnsupportedAppUsage
public static SmsMessage createFromEfRecord(int index, byte[] data) {
try {
SmsMessage msg = new SmsMessage();
@@ -219,6 +228,7 @@
* Returns null on encode error.
* @hide
*/
+ @UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
boolean statusReportRequested, SmsHeader smsHeader) {
return getSubmitPdu(scAddr, destAddr, message, statusReportRequested, smsHeader, -1);
@@ -239,6 +249,7 @@
* Returns null on encode error.
* @hide
*/
+ @UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, String message,
boolean statusReportRequested, SmsHeader smsHeader, int priority) {
@@ -269,6 +280,7 @@
* address, if applicable, and the encoded message.
* Returns null on encode error.
*/
+ @UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String scAddr, String destAddr, int destPort,
byte[] data, boolean statusReportRequested) {
@@ -306,6 +318,7 @@
* address, if applicable, and the encoded message.
* Returns null on encode error.
*/
+ @UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
boolean statusReportRequested) {
return privateGetSubmitPdu(destAddr, statusReportRequested, userData);
@@ -322,6 +335,7 @@
* address, if applicable, and the encoded message.
* Returns null on encode error.
*/
+ @UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String destAddr, UserData userData,
boolean statusReportRequested, int priority) {
return privateGetSubmitPdu(destAddr, statusReportRequested, userData, priority);
@@ -393,6 +407,7 @@
}
/** Return true iff the bearer data message type is DELIVERY_ACK. */
+ @UnsupportedAppUsage
@Override
public boolean isStatusReportMessage() {
return (mBearerData.messageType == BearerData.MESSAGE_TYPE_DELIVERY_ACK);
@@ -415,6 +430,7 @@
* @param isEntireMsg indicates if this is entire msg or a segment in multipart msg
* @return TextEncodingDetails
*/
+ @UnsupportedAppUsage
public static TextEncodingDetails calculateLength(CharSequence messageBody,
boolean use7bitOnly, boolean isEntireMsg) {
CharSequence newMsgBody = null;
@@ -437,6 +453,7 @@
* {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_VMN},
* {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_WAP}
*/
+ @UnsupportedAppUsage
public int getTeleService() {
return mEnvelope.teleService;
}
@@ -448,6 +465,7 @@
* {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_BROADCAST},
* {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#MESSAGE_TYPE_ACKNOWLEDGE},
*/
+ @UnsupportedAppUsage
public int getMessageType() {
// NOTE: mEnvelope.messageType is not set correctly for cell broadcasts with some RILs.
// Use the service category parameter to detect CMAS and other cell broadcast messages.
@@ -680,6 +698,7 @@
/**
* Parses a SMS message from its BearerData stream.
*/
+ @UnsupportedAppUsage
public void parseSms() {
// Message Waiting Info Record defined in 3GPP2 C.S-0005, 3.7.5.6
// It contains only an 8-bit number with the number of messages waiting
@@ -816,6 +835,7 @@
* binder-call, and hence should be thread-safe, it has been
* synchronized.
*/
+ @UnsupportedAppUsage
public synchronized static int getNextMessageId() {
// Testing and dialog with partners has indicated that
// msgId==0 is (sometimes?) treated specially by lower levels.
@@ -840,6 +860,7 @@
* Creates BearerData and Envelope from parameters for a Submit SMS.
* @return byte stream for SubmitPdu.
*/
+ @UnsupportedAppUsage
private static SubmitPdu privateGetSubmitPdu(String destAddrStr, boolean statusReportRequested,
UserData userData) {
return privateGetSubmitPdu(destAddrStr, statusReportRequested, userData, -1);
@@ -1025,6 +1046,7 @@
/** This function shall be called to get the number of voicemails.
* @hide
*/
+ @UnsupportedAppUsage
public int getNumOfVoicemails() {
return mBearerData.numberOfMessages;
}
@@ -1036,6 +1058,7 @@
* @return byte array uniquely identifying the message.
* @hide
*/
+ @UnsupportedAppUsage
public byte[] getIncomingSmsFingerprint() {
ByteArrayOutputStream output = new ByteArrayOutputStream();
diff --git a/telephony/java/com/android/internal/telephony/cdma/UserData.java b/telephony/java/com/android/internal/telephony/cdma/UserData.java
index f879560..7187ae4 100644
--- a/telephony/java/com/android/internal/telephony/cdma/UserData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/UserData.java
@@ -21,6 +21,8 @@
import com.android.internal.telephony.SmsHeader;
import com.android.internal.util.HexDump;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
public class UserData {
/**
@@ -92,6 +94,7 @@
public static final int PRINTABLE_ASCII_MIN_INDEX = 0x20;
public static final int ASCII_NL_INDEX = 0x0A;
public static final int ASCII_CR_INDEX = 0x0D;
+ @UnsupportedAppUsage
public static final SparseIntArray charToAscii = new SparseIntArray();
static {
for (int i = 0; i < ASCII_MAP.length; i++) {
@@ -101,6 +104,10 @@
charToAscii.put('\r', ASCII_CR_INDEX);
}
+ @UnsupportedAppUsage
+ public UserData() {
+ }
+
/*
* TODO(cleanup): Move this very generic functionality somewhere
* more general.
@@ -131,12 +138,15 @@
/**
* Contains the data header of the user data
*/
+ @UnsupportedAppUsage
public SmsHeader userDataHeader;
/**
* Contains the data encoding type for the SMS message
*/
+ @UnsupportedAppUsage
public int msgEncoding;
+ @UnsupportedAppUsage
public boolean msgEncodingSet = false;
public int msgType;
@@ -146,13 +156,16 @@
*/
public int paddingBits;
+ @UnsupportedAppUsage
public int numFields;
/**
* Contains the user data of a SMS message
* (See 3GPP2 C.S0015-B, v2, 4.5.2)
*/
+ @UnsupportedAppUsage
public byte[] payload;
+ @UnsupportedAppUsage
public String payloadStr;
@Override
diff --git a/telephony/java/com/android/internal/telephony/cdma/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
similarity index 99%
rename from telephony/java/com/android/internal/telephony/cdma/BearerData.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 9e6f19f..2181bc4 100644
--- a/telephony/java/com/android/internal/telephony/cdma/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -17,10 +17,10 @@
package com.android.internal.telephony.cdma.sms;
import android.content.res.Resources;
+import android.telephony.Rlog;
import android.telephony.SmsCbCmasInfo;
import android.telephony.cdma.CdmaSmsCbProgramData;
import android.telephony.cdma.CdmaSmsCbProgramResults;
-import android.telephony.Rlog;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
@@ -31,6 +31,8 @@
import com.android.internal.util.BitwiseInputStream;
import com.android.internal.util.BitwiseOutputStream;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
@@ -95,6 +97,7 @@
* (Special rules apply for WAP-messages.)
* (See 3GPP2 C.S0015-B, v2, 4.5.1)
*/
+ @UnsupportedAppUsage
public int messageId;
/**
@@ -106,7 +109,9 @@
public static final int PRIORITY_URGENT = 0x2;
public static final int PRIORITY_EMERGENCY = 0x3;
+ @UnsupportedAppUsage
public boolean priorityIndicatorSet = false;
+ @UnsupportedAppUsage
public int priority = PRIORITY_NORMAL;
/**
@@ -144,6 +149,7 @@
public static final int DISPLAY_MODE_USER = 0x2;
public boolean displayModeSet = false;
+ @UnsupportedAppUsage
public int displayMode = DISPLAY_MODE_DEFAULT;
/**
@@ -207,6 +213,7 @@
* presence of a UDH in the structured data, any existing setting
* will be overwritten.
*/
+ @UnsupportedAppUsage
public boolean hasUserDataHeader;
/**
@@ -214,6 +221,7 @@
* (e.g. padding bits, user data, user data header, etc)
* (See 3GPP2 C.S.0015-B, v2, 4.5.2)
*/
+ @UnsupportedAppUsage
public UserData userData;
/**
@@ -226,6 +234,10 @@
public boolean userResponseCodeSet = false;
public int userResponseCode;
+ @UnsupportedAppUsage
+ public BearerData() {
+ }
+
/**
* 6-byte-field, see 3GPP2 C.S0015-B, v2, 4.5.4
*/
@@ -244,6 +256,7 @@
private ZoneId mZoneId;
+ @UnsupportedAppUsage
public TimeStamp() {
mZoneId = ZoneId.systemDefault(); // 3GPP2 timestamps use the local timezone
}
@@ -295,6 +308,7 @@
}
}
+ @UnsupportedAppUsage
public TimeStamp msgCenterTimeStamp;
public TimeStamp validityPeriodAbsolute;
public TimeStamp deferredDeliveryTimeAbsolute;
@@ -383,6 +397,7 @@
private static class CodingException extends Exception {
+ @UnsupportedAppUsage
public CodingException(String s) {
super(s);
}
@@ -476,6 +491,7 @@
outStream.skip(3);
}
+ @UnsupportedAppUsage
private static int countAsciiSeptets(CharSequence msg, boolean force) {
int msgLen = msg.length();
if (force) return msgLen;
@@ -518,6 +534,7 @@
return ted;
}
+ @UnsupportedAppUsage
private static byte[] encode7bitAscii(String msg, boolean force)
throws CodingException
{
@@ -949,6 +966,7 @@
*
* @return byte array of raw encoded SMS bearer data.
*/
+ @UnsupportedAppUsage
public static byte[] encode(BearerData bData) {
bData.hasUserDataHeader = ((bData.userData != null) &&
(bData.userData.userDataHeader != null));
@@ -1200,6 +1218,7 @@
}
}
+ @UnsupportedAppUsage
private static void decodeUserDataPayload(UserData userData, boolean hasUserDataHeader)
throws CodingException
{
@@ -1845,6 +1864,7 @@
* @return the number of bits to read from the stream
* @throws CodingException if the specified encoding is not supported
*/
+ @UnsupportedAppUsage
private static int getBitsForNumFields(int msgEncoding, int numFields)
throws CodingException {
switch (msgEncoding) {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
similarity index 97%
rename from telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
index d27a758..a82b975 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsAddress.java
@@ -16,11 +16,11 @@
package com.android.internal.telephony.cdma.sms;
+import android.annotation.UnsupportedAppUsage;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.SmsAddress;
-import com.android.internal.telephony.cdma.sms.UserData;
import com.android.internal.util.HexDump;
public class CdmaSmsAddress extends SmsAddress {
@@ -33,6 +33,7 @@
static public final int DIGIT_MODE_4BIT_DTMF = 0x00;
static public final int DIGIT_MODE_8BIT_CHAR = 0x01;
+ @UnsupportedAppUsage
public int digitMode;
/**
@@ -43,6 +44,7 @@
static public final int NUMBER_MODE_NOT_DATA_NETWORK = 0x00;
static public final int NUMBER_MODE_DATA_NETWORK = 0x01;
+ @UnsupportedAppUsage
public int numberMode;
/**
@@ -70,6 +72,7 @@
* This field shall be set to the number of address digits
* (See 3GPP2 C.S0015-B, v2, 3.4.3.3)
*/
+ @UnsupportedAppUsage
public int numberOfDigits;
/**
@@ -83,6 +86,7 @@
//static protected final int NUMBERING_PLAN_TELEX = 0x4;
//static protected final int NUMBERING_PLAN_PRIVATE = 0x9;
+ @UnsupportedAppUsage
public int numberPlan;
/**
@@ -91,6 +95,7 @@
* respectively.
*/
+ @UnsupportedAppUsage
public CdmaSmsAddress(){
}
@@ -194,6 +199,7 @@
* common punctuation. For alpha addresses, the string is cleaned
* up by removing whitespace.
*/
+ @UnsupportedAppUsage
public static CdmaSmsAddress parse(String address) {
CdmaSmsAddress addr = new CdmaSmsAddress();
addr.address = address;
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSmsSubaddress.java b/telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java
similarity index 100%
rename from telephony/java/com/android/internal/telephony/cdma/CdmaSmsSubaddress.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/CdmaSmsSubaddress.java
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
similarity index 96%
rename from telephony/java/com/android/internal/telephony/cdma/SmsEnvelope.java
rename to telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
index de93b57..6af174c 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsEnvelope.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
@@ -17,7 +17,7 @@
package com.android.internal.telephony.cdma.sms;
-import com.android.internal.telephony.cdma.sms.CdmaSmsSubaddress;
+import android.annotation.UnsupportedAppUsage;
public final class SmsEnvelope {
/**
@@ -69,6 +69,7 @@
* or receiving the message.
* (See 3GPP2 C.S0015-B, v2, 3.4.3.1)
*/
+ @UnsupportedAppUsage
public int teleService = TELESERVICE_NOT_SET;
/**
@@ -76,6 +77,7 @@
* by the SMS message.
* (See 3GPP2 C.S0015-B, v2, 3.4.3.2)
*/
+ @UnsupportedAppUsage
public int serviceCategory;
/**
@@ -126,8 +128,10 @@
* encoded bearer data
* (See 3GPP2 C.S0015-B, v2, 3.4.3.7)
*/
+ @UnsupportedAppUsage
public byte[] bearerData;
+ @UnsupportedAppUsage
public SmsEnvelope() {
// nothing to see here
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
index bd8c83e..17f69b3 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
@@ -18,10 +18,13 @@
import android.telephony.PhoneNumberUtils;
-import java.text.ParseException;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.SmsAddress;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
+import java.text.ParseException;
+
public class GsmSmsAddress extends SmsAddress {
static final int OFFSET_ADDRESS_LENGTH = 0;
@@ -38,7 +41,7 @@
* (addressLength + 1) / 2"
* @throws ParseException
*/
-
+ @UnsupportedAppUsage
public GsmSmsAddress(byte[] data, int offset, int length) throws ParseException {
origBytes = new byte[length];
System.arraycopy(data, offset, origBytes, 0, length);
@@ -136,6 +139,7 @@
* address indicating a "set" of "indicator 1" of type "voice message
* waiting"
*/
+ @UnsupportedAppUsage
public boolean isCphsVoiceMessageSet() {
// 0x11 means "set" "voice message waiting" "indicator 1"
return isCphsVoiceMessageIndicatorAddress()
@@ -148,6 +152,7 @@
* address indicating a "clear" of "indicator 1" of type "voice message
* waiting"
*/
+ @UnsupportedAppUsage
public boolean isCphsVoiceMessageClear() {
// 0x10 means "clear" "voice message waiting" "indicator 1"
return isCphsVoiceMessageIndicatorAddress()
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index 0dbc186..996edfc 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -19,6 +19,8 @@
import android.telephony.SmsCbCmasInfo;
import android.telephony.SmsCbEtwsInfo;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
import java.util.Arrays;
/**
@@ -74,6 +76,7 @@
private final int mSerialNumber;
/** The Message Identifier in 3GPP is the same as the Service Category in CDMA. */
+ @UnsupportedAppUsage
private final int mMessageIdentifier;
private final int mDataCodingScheme;
@@ -90,6 +93,7 @@
/** CMAS warning notification info. */
private final SmsCbCmasInfo mCmasInfo;
+ @UnsupportedAppUsage
public SmsCbHeader(byte[] pdu) throws IllegalArgumentException {
if (pdu == null || pdu.length < PDU_HEADER_LENGTH) {
throw new IllegalArgumentException("Illegal PDU");
@@ -183,14 +187,17 @@
}
}
+ @UnsupportedAppUsage
int getGeographicalScope() {
return mGeographicalScope;
}
+ @UnsupportedAppUsage
int getSerialNumber() {
return mSerialNumber;
}
+ @UnsupportedAppUsage
int getServiceCategory() {
return mMessageIdentifier;
}
@@ -199,10 +206,12 @@
return mDataCodingScheme;
}
+ @UnsupportedAppUsage
int getPageIndex() {
return mPageIndex;
}
+ @UnsupportedAppUsage
int getNumberOfPages() {
return mNrOfPages;
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 5667387..5bb818b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -16,18 +16,29 @@
package com.android.internal.telephony.gsm;
+import static com.android.internal.telephony.SmsConstants.ENCODING_16BIT;
+import static com.android.internal.telephony.SmsConstants.ENCODING_7BIT;
+import static com.android.internal.telephony.SmsConstants.ENCODING_8BIT;
+import static com.android.internal.telephony.SmsConstants.ENCODING_KSC5601;
+import static com.android.internal.telephony.SmsConstants.ENCODING_UNKNOWN;
+import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_BYTES;
+import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_SEPTETS;
+import static com.android.internal.telephony.SmsConstants.MessageClass;
+
+import android.content.res.Resources;
import android.telephony.PhoneNumberUtils;
import android.telephony.Rlog;
-import android.content.res.Resources;
import android.text.TextUtils;
import com.android.internal.telephony.EncodeException;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
-import com.android.internal.telephony.uicc.IccUtils;
+import com.android.internal.telephony.Sms7BitEncodingTranslator;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
-import com.android.internal.telephony.Sms7BitEncodingTranslator;
+import com.android.internal.telephony.uicc.IccUtils;
+
+import dalvik.annotation.compat.UnsupportedAppUsage;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
@@ -35,16 +46,6 @@
import java.time.LocalDateTime;
import java.time.ZoneOffset;
-import static com.android.internal.telephony.SmsConstants.MessageClass;
-import static com.android.internal.telephony.SmsConstants.ENCODING_UNKNOWN;
-import static com.android.internal.telephony.SmsConstants.ENCODING_7BIT;
-import static com.android.internal.telephony.SmsConstants.ENCODING_8BIT;
-import static com.android.internal.telephony.SmsConstants.ENCODING_16BIT;
-import static com.android.internal.telephony.SmsConstants.ENCODING_KSC5601;
-import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_SEPTETS;
-import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_BYTES;
-import static com.android.internal.telephony.SmsConstants.MAX_USER_DATA_BYTES_WITH_HEADER;
-
/**
* A Short Message Service message.
*
@@ -100,11 +101,18 @@
private static final int INVALID_VALIDITY_PERIOD = -1;
public static class SubmitPdu extends SubmitPduBase {
+ @UnsupportedAppUsage
+ public SubmitPdu() {}
+ }
+
+ @UnsupportedAppUsage
+ public SmsMessage() {
}
/**
* Create an SmsMessage from a raw PDU.
*/
+ @UnsupportedAppUsage
public static SmsMessage createFromPdu(byte[] pdu) {
try {
SmsMessage msg = new SmsMessage();
@@ -169,6 +177,7 @@
*
* @hide
*/
+ @UnsupportedAppUsage
public static SmsMessage createFromEfRecord(int index, byte[] data) {
try {
SmsMessage msg = new SmsMessage();
@@ -259,6 +268,7 @@
* Returns null on encode error.
* @hide
*/
+ @UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, String message,
boolean statusReportRequested, byte[] header) {
@@ -281,6 +291,7 @@
* Returns null on encode error.
* @hide
*/
+ @UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, String message,
boolean statusReportRequested, byte[] header, int encoding,
@@ -304,6 +315,7 @@
* Returns null on encode error.
* @hide
*/
+ @UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, String message,
boolean statusReportRequested, byte[] header, int encoding,
@@ -444,6 +456,7 @@
* @throws UnsupportedEncodingException
* @throws EncodeException if String is too large to encode
*/
+ @UnsupportedAppUsage
private static byte[] encodeUCS2(String message, byte[] header)
throws UnsupportedEncodingException, EncodeException {
byte[] userData, textPart;
@@ -478,6 +491,7 @@
* address, if applicable, and the encoded message.
* Returns null on encode error.
*/
+ @UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, String message,
boolean statusReportRequested) {
@@ -496,6 +510,7 @@
* address, if applicable, and the encoded message.
* Returns null on encode error.
*/
+ @UnsupportedAppUsage
public static SubmitPdu getSubmitPdu(String scAddress,
String destinationAddress, String message,
boolean statusReportRequested, int validityPeriod) {
@@ -576,6 +591,7 @@
* @param ret <code>SubmitPdu</code> containing the encoded SC
* address, if applicable, and the encoded message. Returns null on encode error.
*/
+ @UnsupportedAppUsage
private static ByteArrayOutputStream getSubmitPduHead(
String scAddress, String destinationAddress, byte mtiByte,
boolean statusReportRequested, SubmitPdu ret) {
@@ -622,12 +638,16 @@
}
private static class PduParser {
+ @UnsupportedAppUsage
byte mPdu[];
+ @UnsupportedAppUsage
int mCur;
SmsHeader mUserDataHeader;
byte[] mUserData;
+ @UnsupportedAppUsage
int mUserDataSeptetPadding;
+ @UnsupportedAppUsage
PduParser(byte[] pdu) {
mPdu = pdu;
mCur = 0;
@@ -667,6 +687,7 @@
/**
* returns non-sign-extended byte value
*/
+ @UnsupportedAppUsage
int getByte() {
return mPdu[mCur++] & 0xff;
}
@@ -808,6 +829,7 @@
*
* @return the user data payload, not including the headers
*/
+ @UnsupportedAppUsage
byte[] getUserData() {
return mUserData;
}
@@ -864,6 +886,7 @@
* @param byteCount the number of bytes in the user data payload
* @return a String with the decoded characters
*/
+ @UnsupportedAppUsage
String getUserDataUCS2(int byteCount) {
String ret;
@@ -912,6 +935,7 @@
* @param use7bitOnly ignore (but still count) illegal characters if true
* @return TextEncodingDetails
*/
+ @UnsupportedAppUsage
public static TextEncodingDetails calculateLength(CharSequence msgBody,
boolean use7bitOnly) {
CharSequence newMsgBody = null;
@@ -959,6 +983,7 @@
}
/** {@inheritDoc} */
+ @UnsupportedAppUsage
@Override
public boolean isMWIClearMessage() {
if (mIsMwi && !mMwiSense) {
@@ -970,6 +995,7 @@
}
/** {@inheritDoc} */
+ @UnsupportedAppUsage
@Override
public boolean isMWISetMessage() {
if (mIsMwi && mMwiSense) {
@@ -981,6 +1007,7 @@
}
/** {@inheritDoc} */
+ @UnsupportedAppUsage
@Override
public boolean isMwiDontStore() {
if (mIsMwi && mMwiDontStore) {
@@ -1000,12 +1027,14 @@
}
/** {@inheritDoc} */
+ @UnsupportedAppUsage
@Override
public int getStatus() {
return mStatus;
}
/** {@inheritDoc} */
+ @UnsupportedAppUsage
@Override
public boolean isStatusReportMessage() {
return mIsStatusReportMessage;
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 6b3df94..9c69e2d 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -24,6 +24,8 @@
import com.android.internal.telephony.GsmAlphabet;
+import dalvik.annotation.compat.UnsupportedAppUsage;
+
import java.io.UnsupportedEncodingException;
/**
@@ -46,6 +48,7 @@
*
* Stops on invalid BCD value, returning string so far
*/
+ @UnsupportedAppUsage
public static String
bcdToString(byte[] data, int offset, int length) {
StringBuilder ret = new StringBuilder(length*2);
@@ -173,6 +176,7 @@
/**
* Decode cdma byte into String.
*/
+ @UnsupportedAppUsage
public static String
cdmaBcdToString(byte[] data, int offset, int length) {
StringBuilder ret = new StringBuilder(length);
@@ -208,6 +212,7 @@
* assume the digit is set to 0 but shall store the entire field
* exactly as received"
*/
+ @UnsupportedAppUsage
public static int
gsmBcdByteToInt(byte b) {
int ret = 0;
@@ -230,6 +235,7 @@
* is in the least significant nibble and the most significant
* is in the most significant nibble.
*/
+ @UnsupportedAppUsage
public static int
cdmaBcdByteToInt(byte b) {
int ret = 0;
@@ -281,6 +287,7 @@
* contain a 16 bit number which defines the complete 16 bit
* base pointer to a "half page" in the UCS2 code space...
*/
+ @UnsupportedAppUsage
public static String
adnStringFieldToString(byte[] data, int offset, int length) {
if (length == 0) {
@@ -372,6 +379,7 @@
return GsmAlphabet.gsm8BitUnpackedToString(data, offset, length, defaultCharset.trim());
}
+ @UnsupportedAppUsage
public static int
hexCharToInt(char c) {
if (c >= '0' && c <= '9') return (c - '0');
@@ -391,6 +399,7 @@
*
* @throws RuntimeException on invalid format
*/
+ @UnsupportedAppUsage
public static byte[]
hexStringToBytes(String s) {
byte[] ret;
@@ -417,6 +426,7 @@
*
* @return hex string representation of bytes array
*/
+ @UnsupportedAppUsage
public static String
bytesToHexString(byte[] bytes) {
if (bytes == null) return null;
@@ -444,6 +454,7 @@
* "offset" points to "octet 3", the coding scheme byte
* empty string returned on decode error
*/
+ @UnsupportedAppUsage
public static String
networkNameToString(byte[] data, int offset, int length) {
String ret;
@@ -494,6 +505,7 @@
* @param length The length of image body
* @return The bitmap
*/
+ @UnsupportedAppUsage
public static Bitmap parseToBnW(byte[] data, int length){
int valueIndex = 0;
int width = data[valueIndex++] & 0xFF;
@@ -536,6 +548,7 @@
* @param transparency with or without transparency
* @return The color bitmap
*/
+ @UnsupportedAppUsage
public static Bitmap parseToRGB(byte[] data, int length,
boolean transparency) {
int valueIndex = 0;
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
index 2c0432a..720c1af 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/RollbackTest.java
@@ -929,7 +929,6 @@
*/
@Test
public void testBadUpdateRollback() throws Exception {
- BroadcastReceiver crashCountReceiver = null;
Context context = InstrumentationRegistry.getContext();
try {
InstallUtils.adoptShellPermissionIdentity(
@@ -937,7 +936,7 @@
Manifest.permission.DELETE_PACKAGES,
Manifest.permission.MANAGE_ROLLBACKS,
Manifest.permission.TEST_MANAGE_ROLLBACKS,
- Manifest.permission.KILL_BACKGROUND_PROCESSES,
+ Manifest.permission.FORCE_STOP_PACKAGES,
Manifest.permission.RESTART_PACKAGES);
RollbackManager rm = RollbackUtils.getRollbackManager();
@@ -967,7 +966,7 @@
RollbackBroadcastReceiver rollbackReceiver = new RollbackBroadcastReceiver();
// Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback
- crashCountReceiver = RollbackUtils.sendCrashBroadcast(context, TestApp.A, 5);
+ RollbackUtils.sendCrashBroadcast(TestApp.A, 5);
// Verify we received a broadcast for the rollback.
rollbackReceiver.take();
@@ -981,9 +980,6 @@
assertThat(InstallUtils.getInstalledVersion(TestApp.B)).isEqualTo(2);
} finally {
InstallUtils.dropShellPermissionIdentity();
- if (crashCountReceiver != null) {
- context.unregisterReceiver(crashCountReceiver);
- }
}
}
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index db81831..9e6ac8e 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -25,7 +25,6 @@
import android.Manifest;
import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -77,7 +76,7 @@
Manifest.permission.INSTALL_PACKAGES,
Manifest.permission.DELETE_PACKAGES,
Manifest.permission.TEST_MANAGE_ROLLBACKS,
- Manifest.permission.KILL_BACKGROUND_PROCESSES);
+ Manifest.permission.FORCE_STOP_PACKAGES);
}
/**
@@ -135,17 +134,8 @@
*/
@Test
public void testBadApkOnlyTriggerRollback() throws Exception {
- BroadcastReceiver crashCountReceiver = null;
- Context context = InstrumentationRegistry.getContext();
-
- try {
- // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback
- crashCountReceiver = RollbackUtils.sendCrashBroadcast(context, TestApp.A, 5);
- } finally {
- if (crashCountReceiver != null) {
- context.unregisterReceiver(crashCountReceiver);
- }
- }
+ // Crash TestApp.A PackageWatchdog#TRIGGER_FAILURE_COUNT times to trigger rollback
+ RollbackUtils.sendCrashBroadcast(TestApp.A, 5);
// We expect the device to be rebooted automatically. Wait for that to
// happen. At that point, the host test driver will wait for the
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 8c522f4..c030c3e 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -469,6 +469,8 @@
if (emulateInterfaceStatusChanged) {
assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
+ verify(mWifiManager).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
}
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
@@ -534,6 +536,8 @@
verify(mNMService, times(1)).startTethering(any(String[].class));
verifyNoMoreInteractions(mNMService);
verify(mWifiManager).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+ verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
@@ -554,6 +558,8 @@
.setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).stopTethering();
verify(mNMService, times(1)).setIpForwardingEnabled(false);
+ verify(mWifiManager, times(3)).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
// Asking for the last error after the per-interface state machine
@@ -739,6 +745,8 @@
assertEquals(1, mTetheringDependencies.isTetheringSupportedCalls);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_AVAILABLE_TETHER);
+ verify(mWifiManager).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
}
@@ -768,6 +776,8 @@
verify(mNMService, times(1)).startTethering(any(String[].class));
verifyNoMoreInteractions(mNMService);
verify(mWifiManager).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+ verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
verifyNoMoreInteractions(mWifiManager);
verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
@@ -805,6 +815,8 @@
.setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class));
verify(mNMService, times(1)).stopTethering();
verify(mNMService, times(1)).setIpForwardingEnabled(false);
+ verify(mWifiManager, times(3)).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
verifyNoMoreInteractions(mNMService);
verifyNoMoreInteractions(mWifiManager);
// Asking for the last error after the per-interface state machine
@@ -842,6 +854,8 @@
verify(mNetd, times(1)).interfaceSetCfg(argThat(p -> TEST_WLAN_IFNAME.equals(p.ifName)));
verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME);
verify(mWifiManager).updateInterfaceIpState(
+ TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_UNSPECIFIED);
+ verify(mWifiManager).updateInterfaceIpState(
TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
// TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
diff --git a/tools/aapt2/optimize/ResourceDeduper.cpp b/tools/aapt2/optimize/ResourceDeduper.cpp
index 78ebcb9..0278b43 100644
--- a/tools/aapt2/optimize/ResourceDeduper.cpp
+++ b/tools/aapt2/optimize/ResourceDeduper.cpp
@@ -63,13 +63,14 @@
// Compare compatible configs for this entry and ensure the values are
// equivalent.
const ConfigDescription& node_configuration = node_value->config;
- for (const auto& sibling : entry_->values) {
- if (!sibling->value) {
+ for (const auto& sibling : parent->children()) {
+ ResourceConfigValue* sibling_value = sibling->value();
+ if (!sibling_value->value) {
// Sibling was already removed.
continue;
}
- if (node_configuration.IsCompatibleWith(sibling->config) &&
- !node_value->value->Equals(sibling->value.get())) {
+ if (node_configuration.IsCompatibleWith(sibling_value->config) &&
+ !node_value->value->Equals(sibling_value->value.get())) {
// The configurations are compatible, but the value is
// different, so we can't remove this value.
return;
diff --git a/tools/aapt2/optimize/ResourceDeduper_test.cpp b/tools/aapt2/optimize/ResourceDeduper_test.cpp
index 2e098ae..048e318 100644
--- a/tools/aapt2/optimize/ResourceDeduper_test.cpp
+++ b/tools/aapt2/optimize/ResourceDeduper_test.cpp
@@ -80,11 +80,58 @@
.Build();
ASSERT_TRUE(ResourceDeduper().Consume(context.get(), table.get()));
+ EXPECT_THAT(table, HasValue("android:string/keep", default_config));
EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_config));
EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_v21_config));
EXPECT_THAT(table, HasValue("android:string/keep", land_config));
}
+TEST(ResourceDeduperTest, SameValuesAreDedupedIncompatibleSiblings) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ const ConfigDescription default_config = {};
+ const ConfigDescription ldrtl_config = test::ParseConfigOrDie("ldrtl");
+ const ConfigDescription ldrtl_night_config = test::ParseConfigOrDie("ldrtl-night");
+ // Chosen because this configuration is not compatible with ldrtl-night.
+ const ConfigDescription ldrtl_notnight_config = test::ParseConfigOrDie("ldrtl-notnight");
+
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddString("android:string/keep", ResourceId{}, default_config, "keep")
+ .AddString("android:string/keep", ResourceId{}, ldrtl_config, "dedupe")
+ .AddString("android:string/keep", ResourceId{}, ldrtl_night_config, "dedupe")
+ .AddString("android:string/keep", ResourceId{}, ldrtl_notnight_config, "keep2")
+ .Build();
+
+ ASSERT_TRUE(ResourceDeduper().Consume(context.get(), table.get()));
+ EXPECT_THAT(table, HasValue("android:string/keep", default_config));
+ EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_config));
+ EXPECT_THAT(table, Not(HasValue("android:string/keep", ldrtl_night_config)));
+ EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_notnight_config));
+}
+
+TEST(ResourceDeduperTest, SameValuesAreDedupedCompatibleNonSiblings) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
+ const ConfigDescription default_config = {};
+ const ConfigDescription ldrtl_config = test::ParseConfigOrDie("ldrtl");
+ const ConfigDescription ldrtl_night_config = test::ParseConfigOrDie("ldrtl-night");
+ // Chosen because this configuration is compatible with ldrtl.
+ const ConfigDescription land_config = test::ParseConfigOrDie("land");
+
+ std::unique_ptr<ResourceTable> table =
+ test::ResourceTableBuilder()
+ .AddString("android:string/keep", ResourceId{}, default_config, "keep")
+ .AddString("android:string/keep", ResourceId{}, ldrtl_config, "dedupe")
+ .AddString("android:string/keep", ResourceId{}, ldrtl_night_config, "dedupe")
+ .AddString("android:string/keep", ResourceId{}, land_config, "keep2")
+ .Build();
+
+ ASSERT_TRUE(ResourceDeduper().Consume(context.get(), table.get()));
+ EXPECT_THAT(table, HasValue("android:string/keep", default_config));
+ EXPECT_THAT(table, HasValue("android:string/keep", ldrtl_config));
+ EXPECT_THAT(table, Not(HasValue("android:string/keep", ldrtl_night_config)));
+ EXPECT_THAT(table, HasValue("android:string/keep", land_config));
+}
+
TEST(ResourceDeduperTest, LocalesValuesAreKept) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
const ConfigDescription default_config = {};
diff --git a/tools/processors/staledataclass/Android.bp b/tools/processors/staledataclass/Android.bp
index c81d410..58a7d34 100644
--- a/tools/processors/staledataclass/Android.bp
+++ b/tools/processors/staledataclass/Android.bp
@@ -13,6 +13,8 @@
static_libs: [
"codegen-version-info",
],
+ // The --add-modules/exports flags below don't work for kotlinc yet, so pin this module to Java language level 8 (see b/139342589):
+ java_version: "1.8",
openjdk9: {
javacflags: [
"--add-modules=jdk.compiler",
diff --git a/wifi/java/android/net/wifi/rtt/ResponderLocation.java b/wifi/java/android/net/wifi/rtt/ResponderLocation.java
index e1d82f8..970a75d 100644
--- a/wifi/java/android/net/wifi/rtt/ResponderLocation.java
+++ b/wifi/java/android/net/wifi/rtt/ResponderLocation.java
@@ -605,11 +605,11 @@
// Negative 2's complement value
// Note: The Math.pow(...) method cannot return a NaN value because the bitFieldSize
// for Lat or Lng is limited to exactly 34 bits.
- angle = Math.scalb(fields[offset] - Math.pow(2, bitFieldSizes[offset]),
+ angle = Math.scalb((double) fields[offset] - Math.pow(2, bitFieldSizes[offset]),
-LATLNG_FRACTION_BITS);
} else {
// Positive 2's complement value
- angle = Math.scalb(fields[offset], -LATLNG_FRACTION_BITS);
+ angle = Math.scalb((double) fields[offset], -LATLNG_FRACTION_BITS);
}
if (angle > limit) {
angle = limit;
@@ -732,10 +732,11 @@
int maxBssidIndicator = (int) buffer[SUBELEMENT_BSSID_MAX_INDICATOR_INDEX] & BYTE_MASK;
int bssidListLength = (buffer.length - 1) / BYTES_IN_A_BSSID;
- // Check the max number of BSSIDs agrees with the list length.
- if (maxBssidIndicator != bssidListLength) {
- return false;
- }
+ // The maxBSSIDIndicator is ignored. Its use is still being clarified in 802.11REVmd,
+ // which is not published at this time. This field will be used in a future
+ // release of Android after 802.11REVmd is public. Here, we interpret the following
+ // params as an explicit list of BSSIDs.
+
int bssidOffset = SUBELEMENT_BSSID_LIST_INDEX;
for (int i = 0; i < bssidListLength; i++) {
diff --git a/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java b/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java
index 47c30409..b02eebb 100644
--- a/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/ResponderLocationTest.java
@@ -37,7 +37,7 @@
*/
@RunWith(JUnit4.class)
public class ResponderLocationTest {
- private static final double LATLNG_TOLERANCE_DEGREES = 0.00001;
+ private static final double LATLNG_TOLERANCE_DEGREES = 0.000_000_05D; // 5E-8 = 6mm of meridian
private static final double ALT_TOLERANCE_METERS = 0.01;
private static final double HEIGHT_TOLERANCE_METERS = 0.01;
private static final int INDEX_ELEMENT_TYPE = 2;
@@ -103,7 +103,7 @@
private static final byte[] sTestBssidListSE = {
(byte) 0x07, // Subelement BSSID list
(byte) 13, // length dependent on number of BSSIDs in list
- (byte) 0x02, // Number of BSSIDs in list
+ (byte) 0x00, // List is explicit; no expansion of list required
(byte) 0x01, // BSSID #1 (MSB)
(byte) 0x02,
(byte) 0x03,
@@ -266,11 +266,11 @@
assertTrue(valid);
assertTrue(lciValid);
assertFalse(zValid);
- assertEquals(0.0009765625, responderLocation.getLatitudeUncertainty());
- assertEquals(-33.857009, responderLocation.getLatitude(),
+ assertEquals(0.0009765625D, responderLocation.getLatitudeUncertainty());
+ assertEquals(-33.8570095D, responderLocation.getLatitude(),
LATLNG_TOLERANCE_DEGREES);
- assertEquals(0.0009765625, responderLocation.getLongitudeUncertainty());
- assertEquals(151.215200, responderLocation.getLongitude(),
+ assertEquals(0.0009765625D, responderLocation.getLongitudeUncertainty());
+ assertEquals(151.2152005D, responderLocation.getLongitude(),
LATLNG_TOLERANCE_DEGREES);
assertEquals(1, responderLocation.getAltitudeType());
assertEquals(64.0, responderLocation.getAltitudeUncertainty());
@@ -282,11 +282,11 @@
assertEquals(1, responderLocation.getLciVersion());
// Testing Location Object
- assertEquals(-33.857009, location.getLatitude(),
+ assertEquals(-33.8570095D, location.getLatitude(),
LATLNG_TOLERANCE_DEGREES);
- assertEquals(151.215200, location.getLongitude(),
+ assertEquals(151.2152005D, location.getLongitude(),
LATLNG_TOLERANCE_DEGREES);
- assertEquals((0.0009765625 + 0.0009765625) / 2, location.getAccuracy(),
+ assertEquals((0.0009765625D + 0.0009765625D) / 2, location.getAccuracy(),
LATLNG_TOLERANCE_DEGREES);
assertEquals(11.2, location.getAltitude(), ALT_TOLERANCE_METERS);
assertEquals(64.0, location.getVerticalAccuracyMeters(), ALT_TOLERANCE_METERS);