Merge "Add DelegatingTransport which delegates all transport methods to its delegate"
diff --git a/Android.bp b/Android.bp
index bef3251..d5fc957 100644
--- a/Android.bp
+++ b/Android.bp
@@ -899,8 +899,10 @@
packages_to_document = [
"android",
+ "dalvik",
"java",
"javax",
+ "junit",
"org.apache.http",
"org.json",
"org.w3c.dom",
@@ -931,7 +933,7 @@
"sdk-dir",
"api-versions-jars-dir",
],
- previous_api: ":last-released-public-api",
+ previous_api: ":last-released-public-api-for-metalava-annotations",
merge_annotations_dirs: [
"metalava-manual",
"ojluni-annotated-sdk-stubs",
@@ -988,7 +990,7 @@
local_sourcepaths: frameworks_base_subdirs,
installable: false,
annotations_enabled: true,
- previous_api: ":last-released-public-api",
+ previous_api: ":last-released-public-api-for-metalava-annotations",
merge_annotations_dirs: [
"metalava-manual",
"ojluni-annotated-sdk-stubs",
@@ -1324,7 +1326,7 @@
installable: false,
sdk_version: "core_platform",
annotations_enabled: true,
- previous_api: ":last-released-public-api",
+ previous_api: ":last-released-public-api-for-metalava-annotations",
merge_annotations_dirs: [
"metalava-manual",
"ojluni-annotated-sdk-stubs",
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 a633350..329d4b7 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -56,8 +56,6 @@
import android.os.BatteryStatsInternal;
import android.os.Binder;
import android.os.Handler;
-import android.os.IThermalService;
-import android.os.IThermalStatusListener;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
@@ -66,7 +64,6 @@
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.SystemClock;
-import android.os.Temperature;
import android.os.UserHandle;
import android.os.UserManagerInternal;
import android.os.WorkSource;
@@ -81,7 +78,6 @@
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
import com.android.internal.util.ArrayUtils;
@@ -105,6 +101,8 @@
import com.android.server.job.controllers.StateController;
import com.android.server.job.controllers.StorageController;
import com.android.server.job.controllers.TimeController;
+import com.android.server.job.restrictions.JobRestriction;
+import com.android.server.job.restrictions.ThermalStatusRestriction;
import libcore.util.EmptyArray;
@@ -186,12 +184,12 @@
private final DeviceIdleJobsController mDeviceIdleJobsController;
/** Needed to get remaining quota time. */
private final QuotaController mQuotaController;
-
- /** Need directly for receiving thermal events */
- private IThermalService mThermalService;
- /** Thermal constraint. */
- @GuardedBy("mLock")
- private boolean mThermalConstraint = false;
+ /**
+ * List of restrictions.
+ * Note: do not add to or remove from this list at runtime except in the constructor, because we
+ * do not synchronize access to this list.
+ */
+ private final List<JobRestriction> mJobRestrictions;
/**
* Queue of pending jobs. The JobServiceContext class will receive jobs from this list
@@ -285,19 +283,6 @@
}
}
- /**
- * Thermal event received from Thermal Service
- */
- private final class ThermalStatusListener extends IThermalStatusListener.Stub {
- @Override public void onStatusChange(int status) {
- // Throttle for Temperature.THROTTLING_SEVERE and above
- synchronized (mLock) {
- mThermalConstraint = status >= Temperature.THROTTLING_SEVERE;
- }
- onControllerStateChanged();
- }
- }
-
static class MaxJobCounts {
private final KeyValueListParser.IntValue mTotal;
private final KeyValueListParser.IntValue mMaxBg;
@@ -1292,6 +1277,10 @@
mQuotaController = new QuotaController(this);
mControllers.add(mQuotaController);
+ // Create restrictions
+ mJobRestrictions = new ArrayList<>();
+ mJobRestrictions.add(new ThermalStatusRestriction(this));
+
// If the job store determined that it can't yet reschedule persisted jobs,
// we need to start watching the clock.
if (!mJobs.jobTimesInflatedValid()) {
@@ -1383,15 +1372,9 @@
// Remove any jobs that are not associated with any of the current users.
cancelJobsForNonExistentUsers();
- // Register thermal callback
- mThermalService = IThermalService.Stub.asInterface(
- ServiceManager.getService(Context.THERMAL_SERVICE));
- if (mThermalService != null) {
- try {
- mThermalService.registerThermalStatusListener(new ThermalStatusListener());
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to register thermal callback.", e);
- }
+
+ for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
+ mJobRestrictions.get(i).onSystemServicesReady();
}
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
synchronized (mLock) {
@@ -1833,9 +1816,28 @@
}
}
- private boolean isJobThermalConstrainedLocked(JobStatus job) {
- return mThermalConstraint && job.hasConnectivityConstraint()
- && (evaluateJobPriorityLocked(job) < JobInfo.PRIORITY_FOREGROUND_APP);
+ /**
+ * Check if a job is restricted by any of the declared {@link JobRestriction}s.
+ * Note, that the jobs with {@link JobInfo#PRIORITY_FOREGROUND_APP} priority or higher may not
+ * be restricted, thus we won't even perform the check, but simply return null early.
+ *
+ * @param job to be checked
+ * @return the first {@link JobRestriction} restricting the given job that has been found; null
+ * - if passes all the restrictions or has priority {@link JobInfo#PRIORITY_FOREGROUND_APP}
+ * or higher.
+ */
+ private JobRestriction checkIfRestricted(JobStatus job) {
+ if (evaluateJobPriorityLocked(job) >= JobInfo.PRIORITY_FOREGROUND_APP) {
+ // Jobs with PRIORITY_FOREGROUND_APP or higher should not be restricted
+ return null;
+ }
+ for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
+ final JobRestriction restriction = mJobRestrictions.get(i);
+ if (restriction.isJobRestricted(job)) {
+ return restriction;
+ }
+ }
+ return null;
}
private void stopNonReadyActiveJobsLocked() {
@@ -1849,10 +1851,13 @@
serviceContext.cancelExecutingJobLocked(
JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED,
"cancelled due to unsatisfied constraints");
- } else if (isJobThermalConstrainedLocked(running)) {
- serviceContext.cancelExecutingJobLocked(
- JobParameters.REASON_DEVICE_THERMAL,
- "cancelled due to thermal condition");
+ } else {
+ final JobRestriction restriction = checkIfRestricted(running);
+ if (restriction != null) {
+ final int reason = restriction.getReason();
+ serviceContext.cancelExecutingJobLocked(reason,
+ "restricted due to " + JobParameters.getReasonName(reason));
+ }
}
}
}
@@ -2089,7 +2094,7 @@
return false;
}
- if (isJobThermalConstrainedLocked(job)) {
+ if (checkIfRestricted(job) != null) {
return false;
}
@@ -2170,7 +2175,7 @@
return false;
}
- if (isJobThermalConstrainedLocked(job)) {
+ if (checkIfRestricted(job) != null) {
return false;
}
@@ -2982,9 +2987,12 @@
pw.print(" In parole?: ");
pw.print(mInParole);
pw.println();
- pw.print(" In thermal throttling?: ");
- pw.print(mThermalConstraint);
- pw.println();
+
+ for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
+ pw.print(" ");
+ mJobRestrictions.get(i).dumpConstants(pw);
+ pw.println();
+ }
pw.println();
pw.println("Started users: " + Arrays.toString(mStartedUsers));
@@ -3005,14 +3013,30 @@
job.dump(pw, " ", true, nowElapsed);
+
+ pw.print(" Restricted due to:");
+ final boolean isRestricted = checkIfRestricted(job) != null;
+ if (isRestricted) {
+ for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
+ final JobRestriction restriction = mJobRestrictions.get(i);
+ if (restriction.isJobRestricted(job)) {
+ final int reason = restriction.getReason();
+ pw.write(" " + JobParameters.getReasonName(reason) + "[" + reason + "]");
+ }
+ }
+ } else {
+ pw.print(" none");
+ }
+ pw.println(".");
+
pw.print(" Ready: ");
pw.print(isReadyToBeExecutedLocked(job));
pw.print(" (job=");
pw.print(job.isReady());
pw.print(" user=");
pw.print(areUsersStartedLocked(job));
- pw.print(" !thermal=");
- pw.print(!isJobThermalConstrainedLocked(job));
+ pw.print(" !restricted=");
+ pw.print(!isRestricted);
pw.print(" !pending=");
pw.print(!mPendingJobs.contains(job));
pw.print(" !active=");
@@ -3152,7 +3176,9 @@
proto.end(settingsToken);
proto.write(JobSchedulerServiceDumpProto.IN_PAROLE, mInParole);
- proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mThermalConstraint);
+ for (int i = mJobRestrictions.size() - 1; i >= 0; i--) {
+ mJobRestrictions.get(i).dumpConstants(proto);
+ }
for (int u : mStartedUsers) {
proto.write(JobSchedulerServiceDumpProto.STARTED_USERS, u);
@@ -3179,8 +3205,8 @@
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.ARE_USERS_STARTED,
areUsersStartedLocked(job));
proto.write(
- JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_THERMAL_CONSTRAINED,
- isJobThermalConstrainedLocked(job));
+ JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_RESTRICTED,
+ checkIfRestricted(job) != null);
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_PENDING,
mPendingJobs.contains(job));
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_JOB_CURRENTLY_ACTIVE,
@@ -3190,6 +3216,16 @@
proto.write(JobSchedulerServiceDumpProto.RegisteredJob.IS_COMPONENT_USABLE,
isComponentUsable(job));
+ for (JobRestriction restriction : mJobRestrictions) {
+ final long restrictionsToken = proto.start(
+ JobSchedulerServiceDumpProto.RegisteredJob.RESTRICTIONS);
+ proto.write(JobSchedulerServiceDumpProto.JobRestriction.REASON,
+ restriction.getReason());
+ proto.write(JobSchedulerServiceDumpProto.JobRestriction.IS_RESTRICTING,
+ restriction.isJobRestricted(job));
+ proto.end(restrictionsToken);
+ }
+
proto.end(rjToken);
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
new file mode 100644
index 0000000..e180c55
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/JobRestriction.java
@@ -0,0 +1,72 @@
+/*
+ * 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.job.restrictions;
+
+import android.app.job.JobInfo;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.controllers.JobStatus;
+
+/**
+ * Used by {@link JobSchedulerService} to impose additional restrictions regarding whether jobs
+ * should be scheduled or not based on the state of the system/device.
+ * Every restriction is associated with exactly one reason (from {@link
+ * android.app.job.JobParameters#JOB_STOP_REASON_CODES}), which could be retrieved using {@link
+ * #getReason()}.
+ * Note, that this is not taken into account for the jobs that have priority
+ * {@link JobInfo#PRIORITY_FOREGROUND_APP} or higher.
+ */
+public abstract class JobRestriction {
+
+ final JobSchedulerService mService;
+ private final int mReason;
+
+ JobRestriction(JobSchedulerService service, int reason) {
+ mService = service;
+ mReason = reason;
+ }
+
+ /**
+ * Called when the system boot phase has reached
+ * {@link com.android.server.SystemService#PHASE_SYSTEM_SERVICES_READY}.
+ */
+ public void onSystemServicesReady() {
+ }
+
+ /**
+ * Called by {@link JobSchedulerService} to check if it may proceed with scheduling the job (in
+ * case all constraints are satisfied and all other {@link JobRestriction}s are fine with it)
+ *
+ * @param job to be checked
+ * @return false if the {@link JobSchedulerService} should not schedule this job at the moment,
+ * true - otherwise
+ */
+ public abstract boolean isJobRestricted(JobStatus job);
+
+ /** Dump any internal constants the Restriction may have. */
+ public abstract void dumpConstants(IndentingPrintWriter pw);
+
+ /** Dump any internal constants the Restriction may have. */
+ public abstract void dumpConstants(ProtoOutputStream proto);
+
+ /** @return reason code for the Restriction. */
+ public final int getReason() {
+ return mReason;
+ }
+}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
new file mode 100644
index 0000000..b97da59
--- /dev/null
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
@@ -0,0 +1,81 @@
+/*
+ * 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.job.restrictions;
+
+import android.app.job.JobParameters;
+import android.content.Context;
+import android.os.IThermalService;
+import android.os.IThermalStatusListener;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.Temperature;
+import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobSchedulerServiceDumpProto;
+import com.android.server.job.controllers.JobStatus;
+
+public class ThermalStatusRestriction extends JobRestriction {
+ private static final String TAG = "ThermalStatusRestriction";
+
+ private volatile boolean mIsThermalRestricted = false;
+
+ public ThermalStatusRestriction(JobSchedulerService service) {
+ super(service, JobParameters.REASON_DEVICE_THERMAL);
+ }
+
+ @Override
+ public void onSystemServicesReady() {
+ final IThermalService thermalService = IThermalService.Stub.asInterface(
+ ServiceManager.getService(Context.THERMAL_SERVICE));
+ if (thermalService != null) {
+ try {
+ thermalService.registerThermalStatusListener(new IThermalStatusListener.Stub() {
+ @Override
+ public void onStatusChange(int status) {
+ final boolean shouldBeActive = status >= Temperature.THROTTLING_SEVERE;
+ if (mIsThermalRestricted == shouldBeActive) {
+ return;
+ }
+ mIsThermalRestricted = shouldBeActive;
+ mService.onControllerStateChanged();
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to register thermal callback.", e);
+ }
+ }
+ }
+
+ @Override
+ public boolean isJobRestricted(JobStatus job) {
+ return mIsThermalRestricted && job.hasConnectivityConstraint();
+ }
+
+ @Override
+ public void dumpConstants(IndentingPrintWriter pw) {
+ pw.print("In thermal throttling?: ");
+ pw.print(mIsThermalRestricted);
+ }
+
+ @Override
+ public void dumpConstants(ProtoOutputStream proto) {
+ proto.write(JobSchedulerServiceDumpProto.IN_THERMAL, mIsThermalRestricted);
+ }
+}
diff --git a/api/current.txt b/api/current.txt
index b4d110e..d688220 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -29463,7 +29463,8 @@
}
public class AudioGroup {
- ctor public AudioGroup();
+ ctor @Deprecated public AudioGroup();
+ ctor public AudioGroup(@Nullable android.content.Context);
method public void clear();
method public int getMode();
method public android.net.rtp.AudioStream[] getStreams();
diff --git a/api/system-current.txt b/api/system-current.txt
index 0d55b81..d31bfeb 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1663,7 +1663,8 @@
field public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT = 4096; // 0x1000
field public static final int FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT = 8192; // 0x2000
field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40
- field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
+ field public static final int FLAG_PERMISSION_REVOKED_COMPAT = 8; // 0x8
+ field @Deprecated public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
field public static final int FLAG_PERMISSION_SYSTEM_FIXED = 16; // 0x10
field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2
field public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 512; // 0x200
@@ -1731,7 +1732,7 @@
method public void onPermissionsChanged(int);
}
- @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
+ @IntDef(prefix={"FLAG_PERMISSION_"}, value={android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET, android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED, android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT, android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION, android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE, android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface PackageManager.PermissionFlags {
}
public class PermissionGroupInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
diff --git a/api/test-current.txt b/api/test-current.txt
index 34312f6..51b569f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -21,6 +21,10 @@
field public static final String WRITE_OBB = "android.permission.WRITE_OBB";
}
+ public static final class Manifest.permission_group {
+ field public static final String UNDEFINED = "android.permission-group.UNDEFINED";
+ }
+
public static final class R.bool {
field public static final int config_perDisplayFocusEnabled = 17891332; // 0x1110004
}
@@ -739,7 +743,8 @@
field public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT = 4096; // 0x1000
field public static final int FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT = 8192; // 0x2000
field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40
- field public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
+ field public static final int FLAG_PERMISSION_REVOKED_COMPAT = 8; // 0x8
+ field @Deprecated public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
field public static final int FLAG_PERMISSION_SYSTEM_FIXED = 16; // 0x10
field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index d4d5871..4c77ba4 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -69,6 +69,7 @@
static_libs: [
"libandroidfw",
"libbase",
+ "libcutils",
"libutils",
"libziparchive",
],
@@ -121,6 +122,7 @@
static_libs: [
"libandroidfw",
"libbase",
+ "libcutils",
"libidmap2",
"liblog",
"libutils",
@@ -163,6 +165,7 @@
static_libs: [
"libandroidfw",
"libbase",
+ "libcutils",
"libidmap2",
"liblog",
"libutils",
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 43058d5..05ff490 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -99,6 +99,8 @@
"src/shell/shell_config.proto",
"src/shell/ShellSubscriber.cpp",
"src/socket/StatsSocketListener.cpp",
+ "src/state/StateManager.cpp",
+ "src/state/StateTracker.cpp",
"src/stats_log_util.cpp",
"src/statscompanion_util.cpp",
"src/statsd_config.proto",
@@ -253,6 +255,7 @@
"tests/metrics/ValueMetricProducer_test.cpp",
"tests/MetricsManager_test.cpp",
"tests/shell/ShellSubscriber_test.cpp",
+ "tests/state/StateTracker_test.cpp",
"tests/statsd_test_util.cpp",
"tests/StatsLogProcessor_test.cpp",
"tests/StatsService_test.cpp",
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index af8b3af..5e156bb 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -59,6 +59,16 @@
return JenkinsHashWhiten(hash);
}
+bool filterValues(const Matcher& matcherField, const vector<FieldValue>& values, Value* output) {
+ for (const auto& value : values) {
+ if (value.mField.matches(matcherField)) {
+ (*output) = value.mValue;
+ return true;
+ }
+ }
+ return false;
+}
+
bool filterValues(const vector<Matcher>& matcherFields, const vector<FieldValue>& values,
HashableDimensionKey* output) {
size_t num_matches = 0;
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 6f4941f..a123850 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -120,6 +120,13 @@
android::hash_t hashDimension(const HashableDimensionKey& key);
/**
+ * Returns true if a FieldValue field matches the matcher field.
+ * The value of the FieldValue is output.
+ */
+bool filterValues(const Matcher& matcherField, const std::vector<FieldValue>& values,
+ Value* output);
+
+/**
* Creating HashableDimensionKeys from FieldValues using matcher.
*
* This function may make modifications to the Field if the matcher has Position=FIRST,LAST or ALL
@@ -169,4 +176,4 @@
return android::JenkinsHashWhiten(hash);
}
};
-} // namespace std
\ No newline at end of file
+} // namespace std
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 1c7180f..b665a8b 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -266,7 +266,9 @@
IResultReceiver::asInterface(data.readStrongBinder());
err = command(in, out, err, args, resultReceiver);
- resultReceiver->send(err);
+ if (resultReceiver != nullptr) {
+ resultReceiver->send(err);
+ }
return NO_ERROR;
}
default: { return BnStatsManager::onTransact(code, data, reply, flags); }
@@ -411,13 +413,20 @@
return cmd_trigger_active_config_broadcast(out, args);
}
if (!args[0].compare(String8("data-subscribe"))) {
- if (mShellSubscriber == nullptr) {
- mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager);
+ {
+ std::lock_guard<std::mutex> lock(mShellSubscriberMutex);
+ if (mShellSubscriber == nullptr) {
+ mShellSubscriber = new ShellSubscriber(mUidMap, mPullerManager);
+ }
}
int timeoutSec = -1;
if (argCount >= 2) {
timeoutSec = atoi(args[1].c_str());
}
+ if (resultReceiver == nullptr) {
+ ALOGI("Null resultReceiver given, no subscription will be started");
+ return UNEXPECTED_NULL;
+ }
mShellSubscriber->startNewSubscription(in, out, resultReceiver, timeoutSec);
return NO_ERROR;
}
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 53b6ce9..9490948 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -432,6 +432,10 @@
sp<ShellSubscriber> mShellSubscriber;
+ /**
+ * Mutex for setting the shell subscriber
+ */
+ mutable mutex mShellSubscriberMutex;
std::shared_ptr<LogEventQueue> mEventQueue;
FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 78a6609..b71a86b 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -340,7 +340,7 @@
}
// Pulled events will start at field 10000.
- // Next: 10064
+ // Next: 10065
oneof pulled {
WifiBytesTransfer wifi_bytes_transfer = 10000;
WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001;
@@ -406,6 +406,7 @@
ProcessSystemIonHeapSize process_system_ion_heap_size = 10061;
SurfaceflingerStatsGlobalInfo surfaceflinger_stats_global_info = 10062;
SurfaceflingerStatsLayerInfo surfaceflinger_stats_layer_info = 10063;
+ ProcessMemorySnapshot process_memory_snapshot = 10064;
}
// DO NOT USE field numbers above 100,000 in AOSP.
@@ -3069,9 +3070,9 @@
* services/core/java/com/android/server/wm/Session.java
*/
message OverlayStateChanged {
- optional int32 uid = 1 [(is_uid) = true];
+ optional int32 uid = 1 [(state_field_option).option = PRIMARY, (is_uid) = true];
- optional string package_name = 2;
+ optional string package_name = 2 [(state_field_option).option = PRIMARY];
optional bool using_alert_window = 3;
@@ -3079,7 +3080,7 @@
ENTERED = 1;
EXITED = 2;
}
- optional State state = 4;
+ optional State state = 4 [(state_field_option).option = EXCLUSIVE];
}
/*
@@ -4108,6 +4109,46 @@
}
/*
+ * Logs the memory stats for a process.
+ *
+ * Pulled from StatsCompanionService for all managed processes (from ActivityManagerService)
+ * and for selected native processes.
+ */
+message ProcessMemorySnapshot {
+ // The uid if available. -1 means not available.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // The process name.
+ // Usually package name or process cmdline.
+ // Provided by ActivityManagerService or read from /proc/PID/cmdline.
+ optional string process_name = 2;
+
+ // The pid of the process.
+ // Allows to disambiguate instances of the process.
+ optional int32 pid = 3;
+
+ // The current OOM score adjustment value.
+ // Read from ProcessRecord for managed processes.
+ // Placeholder -1001 (OOM_SCORE_ADJ_MIN - 1, outside of allowed range) for native ones.
+ optional int32 oom_score_adj = 4;
+
+ // The current RSS of the process.
+ // VmRSS from /proc/pid/status.
+ optional int32 rss_in_kilobytes = 5;
+
+ // The current anon RSS of the process.
+ // RssAnon from /proc/pid/status.
+ optional int32 anon_rss_in_kilobytes = 6;
+
+ // The current swap size of the process.
+ // VmSwap from /proc/pid/status.
+ optional int32 swap_in_kilobytes = 7;
+
+ // The sum of rss_in_kilobytes and swap_in_kilobytes.
+ optional int32 anon_rss_and_swap_in_kilobytes = 8;
+}
+
+/*
* Elapsed real time from SystemClock.
*/
message SystemElapsedRealtime {
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 475f18a..f69e2d0 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -153,6 +153,9 @@
{.additiveFields = {3},
.puller =
new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_HIGH_WATER_MARK)}},
+ // process_memory_snapshot
+ {android::util::PROCESS_MEMORY_SNAPSHOT,
+ {.puller = new StatsCompanionServicePuller(android::util::PROCESS_MEMORY_SNAPSHOT)}},
// system_ion_heap_size
{android::util::SYSTEM_ION_HEAP_SIZE,
{.puller = new StatsCompanionServicePuller(android::util::SYSTEM_ION_HEAP_SIZE)}},
diff --git a/cmds/statsd/src/state/StateListener.h b/cmds/statsd/src/state/StateListener.h
new file mode 100644
index 0000000..a31690a
--- /dev/null
+++ b/cmds/statsd/src/state/StateListener.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <utils/RefBase.h>
+
+#include "HashableDimensionKey.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateListener : public virtual RefBase {
+public:
+ StateListener(){};
+
+ virtual ~StateListener(){};
+
+ /**
+ * Interface for handling a state change.
+ *
+ * The old and new state values map to the original state values.
+ * StateTrackers only track the original state values and are unaware
+ * of higher-level state groups. MetricProducers hold information on
+ * state groups and are responsible for mapping original state values to
+ * the correct state group.
+ *
+ * [atomId]: The id of the state atom
+ * [primaryKey]: The primary field values of the state atom
+ * [oldState]: Previous state value before state change
+ * [newState]: Current state value after state change
+ */
+ virtual void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
+ int newState) = 0;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
new file mode 100644
index 0000000..a3059c5
--- /dev/null
+++ b/cmds/statsd/src/state/StateManager.cpp
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#include "Log.h"
+
+#include "StateManager.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+StateManager& StateManager::getInstance() {
+ static StateManager sStateManager;
+ return sStateManager;
+}
+
+void StateManager::onLogEvent(const LogEvent& event) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) {
+ mStateTrackers[event.GetTagId()]->onLogEvent(event);
+ }
+}
+
+bool StateManager::registerListener(int stateAtomId, wp<StateListener> listener) {
+ std::lock_guard<std::mutex> lock(mMutex);
+
+ // Check if state tracker already exists
+ if (mStateTrackers.find(stateAtomId) == mStateTrackers.end()) {
+ // Create a new state tracker iff atom is a state atom
+ auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(stateAtomId);
+ if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
+ mStateTrackers[stateAtomId] = new StateTracker(stateAtomId, it->second);
+ } else {
+ ALOGE("StateManager cannot register listener, Atom %d is not a state atom",
+ stateAtomId);
+ return false;
+ }
+ }
+ mStateTrackers[stateAtomId]->registerListener(listener);
+ return true;
+}
+
+void StateManager::unregisterListener(int stateAtomId, wp<StateListener> listener) {
+ std::unique_lock<std::mutex> lock(mMutex);
+
+ // Hold the sp<> until the lock is released so that ~StateTracker() is
+ // not called while the lock is held.
+ sp<StateTracker> toRemove;
+
+ // Unregister listener from correct StateTracker
+ auto it = mStateTrackers.find(stateAtomId);
+ if (it != mStateTrackers.end()) {
+ it->second->unregisterListener(listener);
+
+ // Remove the StateTracker if it has no listeners
+ if (it->second->getListenersCount() == 0) {
+ toRemove = it->second;
+ mStateTrackers.erase(it);
+ }
+ } else {
+ ALOGE("StateManager cannot unregister listener, StateTracker for atom %d does not exist",
+ stateAtomId);
+ }
+ lock.unlock();
+}
+
+int StateManager::getState(int stateAtomId, const HashableDimensionKey& key) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) {
+ return mStateTrackers[stateAtomId]->getState(key);
+ }
+
+ return StateTracker::kStateUnknown;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h
new file mode 100644
index 0000000..ce60f14
--- /dev/null
+++ b/cmds/statsd/src/state/StateManager.h
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+#pragma once
+
+//#include <utils/Log.h>
+#include <utils/RefBase.h>
+#include "HashableDimensionKey.h"
+
+#include "state/StateListener.h"
+#include "state/StateTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateManager : public virtual RefBase {
+public:
+ StateManager(){};
+
+ ~StateManager(){};
+
+ // Returns a pointer to the single, shared StateManager object.
+ static StateManager& getInstance();
+
+ // Notifies the correct StateTracker of an event.
+ void onLogEvent(const LogEvent& event);
+
+ // Returns true if stateAtomId is the id of a state atom and notifies the
+ // correct StateTracker to register the listener. If the correct
+ // StateTracker does not exist, a new StateTracker is created.
+ bool registerListener(int stateAtomId, wp<StateListener> listener);
+
+ // Notifies the correct StateTracker to unregister a listener
+ // and removes the tracker if it no longer has any listeners.
+ void unregisterListener(int stateAtomId, wp<StateListener> listener);
+
+ // Queries the correct StateTracker for the state that is mapped to the given
+ // query key.
+ // If the StateTracker doesn't exist, returns StateTracker::kStateUnknown.
+ int getState(int stateAtomId, const HashableDimensionKey& queryKey);
+
+ inline int getStateTrackersCount() {
+ std::lock_guard<std::mutex> lock(mMutex);
+ return mStateTrackers.size();
+ }
+
+ inline int getListenersCount(int stateAtomId) {
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) {
+ return mStateTrackers[stateAtomId]->getListenersCount();
+ }
+ return -1;
+ }
+
+private:
+ mutable std::mutex mMutex;
+
+ // Maps state atom ids to StateTrackers
+ std::unordered_map<int, sp<StateTracker>> mStateTrackers;
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
new file mode 100644
index 0000000..5a91950
--- /dev/null
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+#define DEBUG true // STOPSHIP if true
+#include "Log.h"
+
+#include "stats_util.h"
+
+#include "StateTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+StateTracker::StateTracker(const int atomId,
+ const util::StateAtomFieldOptions& stateAtomInfo)
+ : mAtomId(atomId),
+ mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) {
+ // create matcher for each primary field
+ // TODO(tsaichristine): handle when primary field is first uid in chain
+ for (const auto& primary : stateAtomInfo.primaryFields) {
+ Matcher matcher = getSimpleMatcher(atomId, primary);
+ mPrimaryFields.push_back(matcher);
+ }
+
+ // TODO(tsaichristine): set default state, reset state, and nesting
+}
+
+void StateTracker::onLogEvent(const LogEvent& event) {
+ // parse event for primary field values i.e. primary key
+ HashableDimensionKey primaryKey;
+ if (mPrimaryFields.size() > 0) {
+ if (!filterValues(mPrimaryFields, event.getValues(), &primaryKey) ||
+ primaryKey.getValues().size() != mPrimaryFields.size()) {
+ ALOGE("StateTracker error extracting primary key from log event.");
+ handleReset();
+ return;
+ }
+ } else {
+ // atom has no primary fields
+ primaryKey = DEFAULT_DIMENSION_KEY;
+ }
+
+ // parse event for state value
+ Value state;
+ int32_t stateValue;
+ if (!filterValues(mStateField, event.getValues(), &state) || state.getType() != INT) {
+ ALOGE("StateTracker error extracting state from log event. Type: %d", state.getType());
+ handlePartialReset(primaryKey);
+ return;
+ }
+ stateValue = state.int_value;
+
+ if (stateValue == mResetState) {
+ VLOG("StateTracker Reset state: %s", state.toString().c_str());
+ handleReset();
+ }
+
+ // track and update state
+ int32_t oldState = 0;
+ int32_t newState = 0;
+ updateState(primaryKey, stateValue, &oldState, &newState);
+
+ // notify all listeners if state has changed
+ if (oldState != newState) {
+ VLOG("StateTracker updated state");
+ for (auto listener : mListeners) {
+ auto sListener = listener.promote(); // safe access to wp<>
+ if (sListener != nullptr) {
+ sListener->onStateChanged(mAtomId, primaryKey, oldState, newState);
+ }
+ }
+ } else {
+ VLOG("StateTracker NO updated state");
+ }
+}
+
+void StateTracker::registerListener(wp<StateListener> listener) {
+ mListeners.insert(listener);
+}
+
+void StateTracker::unregisterListener(wp<StateListener> listener) {
+ mListeners.erase(listener);
+}
+
+int StateTracker::getState(const HashableDimensionKey& queryKey) const {
+ if (queryKey.getValues().size() == mPrimaryFields.size()) {
+ auto it = mStateMap.find(queryKey);
+ if (it != mStateMap.end()) {
+ return it->second.state;
+ }
+ } else if (queryKey.getValues().size() > mPrimaryFields.size()) {
+ ALOGE("StateTracker query key size > primary key size is illegal");
+ } else {
+ ALOGE("StateTracker query key size < primary key size is not supported");
+ }
+ return mDefaultState;
+}
+
+void StateTracker::handleReset() {
+ VLOG("StateTracker handle reset");
+ for (const auto pair : mStateMap) {
+ for (auto l : mListeners) {
+ auto sl = l.promote();
+ if (sl != nullptr) {
+ sl->onStateChanged(mAtomId, pair.first, pair.second.state, mDefaultState);
+ }
+ }
+ }
+ mStateMap.clear();
+}
+
+void StateTracker::handlePartialReset(const HashableDimensionKey& primaryKey) {
+ VLOG("StateTracker handle partial reset");
+ if (mStateMap.find(primaryKey) != mStateMap.end()) {
+ mStateMap.erase(primaryKey);
+ }
+}
+
+void StateTracker::updateState(const HashableDimensionKey& primaryKey, const int32_t eventState,
+ int32_t* oldState, int32_t* newState) {
+ // get old state (either current state in map or default state)
+ auto it = mStateMap.find(primaryKey);
+ if (it != mStateMap.end()) {
+ *oldState = it->second.state;
+ } else {
+ *oldState = mDefaultState;
+ }
+
+ // update state map
+ if (eventState == mDefaultState) {
+ // remove (key, state) pair if state returns to default state
+ VLOG("\t StateTracker changed to default state")
+ mStateMap.erase(primaryKey);
+ } else {
+ mStateMap[primaryKey].state = eventState;
+ mStateMap[primaryKey].count = 1;
+ }
+ *newState = eventState;
+
+ // TODO(tsaichristine): support atoms with nested counting
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
new file mode 100644
index 0000000..f22706c
--- /dev/null
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <statslog.h>
+#include <utils/RefBase.h>
+#include "HashableDimensionKey.h"
+#include "logd/LogEvent.h"
+
+#include "state/StateListener.h"
+
+#include <unordered_map>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateTracker : public virtual RefBase {
+public:
+ StateTracker(const int atomId, const util::StateAtomFieldOptions& stateAtomInfo);
+
+ virtual ~StateTracker(){};
+
+ // Updates state map and notifies all listeners if a state change occurs.
+ // Checks if a state change has occurred by getting the state value from
+ // the log event and comparing the old and new states.
+ void onLogEvent(const LogEvent& event);
+
+ // Adds new listeners to set of StateListeners. If a listener is already
+ // registered, it is ignored.
+ void registerListener(wp<StateListener> listener);
+
+ void unregisterListener(wp<StateListener> listener);
+
+ // Returns the state value mapped to the given query key.
+ // If the key isn't mapped to a state or the key size doesn't match the
+ // primary key size, the default state is returned.
+ int getState(const HashableDimensionKey& queryKey) const;
+
+ inline int getListenersCount() const {
+ return mListeners.size();
+ }
+
+ const static int kStateUnknown = -1;
+
+private:
+ struct StateValueInfo {
+ int32_t state; // state value
+ int count; // nested count (only used for binary states)
+ };
+
+ const int32_t mAtomId; // id of the state atom being tracked
+
+ Matcher mStateField; // matches the atom's exclusive state field
+
+ std::vector<Matcher> mPrimaryFields; // matches the atom's primary fields
+
+ int32_t mDefaultState = kStateUnknown;
+
+ int32_t mResetState;
+
+ // Maps primary key to state value info
+ std::unordered_map<HashableDimensionKey, StateValueInfo> mStateMap;
+
+ // Set of all StateListeners (objects listening for state changes)
+ std::set<wp<StateListener>> mListeners;
+
+ // Reset all state values in map to default state
+ void handleReset();
+
+ // Reset only the state value mapped to primary key to default state
+ void handlePartialReset(const HashableDimensionKey& primaryKey);
+
+ // Update the StateMap based on the received state value.
+ // Store the old and new states.
+ void updateState(const HashableDimensionKey& primaryKey, const int32_t eventState,
+ int32_t* oldState, int32_t* newState);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
new file mode 100644
index 0000000..c89ffea
--- /dev/null
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -0,0 +1,356 @@
+/*
+ * 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 <gtest/gtest.h>
+#include "state/StateManager.h"
+#include "state/StateTracker.h"
+#include "state/StateListener.h"
+
+#include "tests/statsd_test_util.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Mock StateListener class for testing.
+ * Stores primary key and state pairs.
+ */
+class TestStateListener : public virtual StateListener {
+public:
+ TestStateListener(){};
+
+ virtual ~TestStateListener(){};
+
+ struct Update {
+ Update(const HashableDimensionKey& key, int state) : mKey(key), mState(state){};
+ HashableDimensionKey mKey;
+ int mState;
+ };
+
+ std::vector<Update> updates;
+
+ void onStateChanged(int stateAtomId, const HashableDimensionKey& primaryKey, int oldState,
+ int newState) {
+ updates.emplace_back(primaryKey, newState);
+ }
+};
+
+// START: build event functions.
+// State with no primary fields - ScreenStateChanged
+std::shared_ptr<LogEvent> buildScreenEvent(int state) {
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::SCREEN_STATE_CHANGED, 1000 /*timestamp*/);
+ event->write((int32_t)state);
+ event->init();
+ return event;
+}
+
+// State with one primary field - UidProcessStateChanged
+std::shared_ptr<LogEvent> buildUidProcessEvent(int uid, int state) {
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::UID_PROCESS_STATE_CHANGED, 1000 /*timestamp*/);
+ event->write((int32_t)uid);
+ event->write((int32_t)state);
+ event->init();
+ return event;
+}
+
+// State with multiple primary fields - OverlayStateChanged
+std::shared_ptr<LogEvent> buildOverlayEvent(int uid, const std::string& packageName, int state) {
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
+ event->write((int32_t)uid);
+ event->write(packageName);
+ event->write(true); // using_alert_window
+ event->write((int32_t)state);
+ event->init();
+ return event;
+}
+
+std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, int state) {
+ std::shared_ptr<LogEvent> event =
+ std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
+ event->write((int32_t)uid);
+ event->write(packageName);
+ event->write((int32_t)state);
+ event->init();
+ return event;
+}
+// END: build event functions.
+
+// START: get primary key functions
+void getUidProcessKey(int uid, HashableDimensionKey* key) {
+ int pos1[] = {1, 0, 0};
+ Field field1(27 /* atom id */, pos1, 0 /* depth */);
+ Value value1((int32_t)uid);
+
+ key->addValue(FieldValue(field1, value1));
+}
+
+void getOverlayKey(int uid, string packageName, HashableDimensionKey* key) {
+ int pos1[] = {1, 0, 0};
+ int pos2[] = {2, 0, 0};
+
+ Field field1(59 /* atom id */, pos1, 0 /* depth */);
+ Field field2(59 /* atom id */, pos2, 0 /* depth */);
+
+ Value value1((int32_t)uid);
+ Value value2(packageName);
+
+ key->addValue(FieldValue(field1, value1));
+ key->addValue(FieldValue(field2, value2));
+}
+// END: get primary key functions
+
+TEST(StateListenerTest, TestStateListenerWeakPointer) {
+ sp<TestStateListener> listener = new TestStateListener();
+ wp<TestStateListener> wListener = listener;
+ listener = nullptr; // let go of listener
+ EXPECT_TRUE(wListener.promote() == nullptr);
+}
+
+TEST(StateManagerTest, TestStateManagerGetInstance) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager& mgr = StateManager::getInstance();
+
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+}
+
+/**
+ * Test registering listeners to StateTrackers
+ *
+ * - StateManager will create a new StateTracker if it doesn't already exist
+ * and then register the listener to the StateTracker.
+ * - If a listener is already registered to a StateTracker, it is not added again.
+ * - StateTrackers are only created for atoms that are state atoms.
+ */
+TEST(StateTrackerTest, TestRegisterListener) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ sp<TestStateListener> listener2 = new TestStateListener();
+ StateManager mgr;
+
+ // Register listener to non-existing StateTracker
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Register listener to existing StateTracker
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Register already registered listener to existing StateTracker
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Register listener to non-state atom
+ mgr.registerListener(android::util::BATTERY_LEVEL_CHANGED, listener2);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+}
+
+/**
+ * Test unregistering listeners from StateTrackers
+ *
+ * - StateManager will unregister listeners from a StateTracker only if the
+ * StateTracker exists and the listener is registered to the StateTracker.
+ * - Once all listeners are removed from a StateTracker, the StateTracker
+ * is also removed.
+ */
+TEST(StateTrackerTest, TestUnregisterListener) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ sp<TestStateListener> listener2 = new TestStateListener();
+ StateManager mgr;
+
+ // Unregister listener from non-existing StateTracker
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ EXPECT_EQ(-1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Unregister non-registered listener from existing StateTracker
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+ mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Unregister second-to-last listener from StateTracker
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ EXPECT_EQ(1, mgr.getStateTrackersCount());
+ EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+ // Unregister last listener from StateTracker
+ mgr.unregisterListener(android::util::SCREEN_STATE_CHANGED, listener2);
+ EXPECT_EQ(0, mgr.getStateTrackersCount());
+ EXPECT_EQ(-1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states without primary keys.
+ */
+TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+
+ // log event
+ std::shared_ptr<LogEvent> event =
+ buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+ mgr.onLogEvent(*event);
+
+ // check listener was updated
+ EXPECT_EQ(1, listener1->updates.size());
+ EXPECT_EQ(DEFAULT_DIMENSION_KEY, listener1->updates[0].mKey);
+ EXPECT_EQ(2, listener1->updates[0].mState);
+
+ // check StateTracker was updated by querying for state
+ HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
+ EXPECT_EQ(2, mgr.getState(android::util::SCREEN_STATE_CHANGED, queryKey));
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states with primary keys.
+ */
+TEST(StateTrackerTest, TestStateChangeOnePrimaryField) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener1);
+
+ // log event
+ std::shared_ptr<LogEvent> event = buildUidProcessEvent(
+ 1000,
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
+ mgr.onLogEvent(*event);
+
+ // check listener was updated
+ EXPECT_EQ(1, listener1->updates.size());
+ EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(1002, listener1->updates[0].mState);
+
+ // check StateTracker was updated by querying for state
+ HashableDimensionKey queryKey;
+ getUidProcessKey(1000, &queryKey);
+ EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey));
+}
+
+TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1);
+
+ // log event
+ std::shared_ptr<LogEvent> event = buildOverlayEvent(1000, "package1", 1); // state: ENTERED
+ mgr.onLogEvent(*event);
+
+ // check listener update
+ EXPECT_EQ(1, listener1->updates.size());
+ EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
+ EXPECT_EQ(1, listener1->updates[0].mState);
+}
+
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged
+ * when there is an error extracting state from log event. Listener is not
+ * updated of state change.
+ */
+TEST(StateTrackerTest, TestStateChangeEventError) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1);
+
+ // log event
+ std::shared_ptr<LogEvent> event =
+ buildIncorrectOverlayEvent(1000, "package1", 1); // state: ENTERED
+ mgr.onLogEvent(*event);
+
+ // check listener update
+ EXPECT_EQ(0, listener1->updates.size());
+}
+
+TEST(StateTrackerTest, TestStateQuery) {
+ sp<TestStateListener> listener1 = new TestStateListener();
+ sp<TestStateListener> listener2 = new TestStateListener();
+ sp<TestStateListener> listener3 = new TestStateListener();
+ StateManager mgr;
+ mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+ mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener2);
+ mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener3);
+
+ std::shared_ptr<LogEvent> event1 = buildUidProcessEvent(
+ 1000,
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
+ std::shared_ptr<LogEvent> event2 = buildUidProcessEvent(
+ 1001,
+ android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value: 1003
+ std::shared_ptr<LogEvent> event3 = buildUidProcessEvent(
+ 1002,
+ android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000
+ std::shared_ptr<LogEvent> event4 = buildUidProcessEvent(
+ 1001,
+ android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002
+ std::shared_ptr<LogEvent> event5 =
+ buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON); // state value:
+ std::shared_ptr<LogEvent> event6 = buildOverlayEvent(1000, "package1", 1);
+ std::shared_ptr<LogEvent> event7 = buildOverlayEvent(1000, "package2", 2);
+
+ mgr.onLogEvent(*event1);
+ mgr.onLogEvent(*event2);
+ mgr.onLogEvent(*event3);
+ mgr.onLogEvent(*event5);
+ mgr.onLogEvent(*event5);
+ mgr.onLogEvent(*event6);
+ mgr.onLogEvent(*event7);
+
+ // Query for UidProcessState of uid 1001
+ HashableDimensionKey queryKey1;
+ getUidProcessKey(1001, &queryKey1);
+ EXPECT_EQ(1003, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+
+ // Query for UidProcessState of uid 1004 - not in state map
+ HashableDimensionKey queryKey2;
+ getUidProcessKey(1004, &queryKey2);
+ EXPECT_EQ(-1,
+ mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey2)); // default state
+
+ // Query for UidProcessState of uid 1001 - after change in state
+ mgr.onLogEvent(*event4);
+ EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+
+ // Query for ScreenState
+ EXPECT_EQ(2, mgr.getState(android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
+
+ // Query for OverlayState of uid 1000, package name "package2"
+ HashableDimensionKey queryKey3;
+ getOverlayKey(1000, "package2", &queryKey3);
+ EXPECT_EQ(2, mgr.getState(android::util::OVERLAY_STATE_CHANGED, queryKey3));
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 563174b..1649e8b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -853,12 +853,14 @@
public static final int OP_ACCESS_ACCESSIBILITY = 88;
/** @hide Read the device identifiers (IMEI / MEID, IMSI, SIM / Build serial) */
public static final int OP_READ_DEVICE_IDENTIFIERS = 89;
+ /** @hide Read location metadata from media */
+ public static final int OP_ACCESS_MEDIA_LOCATION = 90;
/** @hide Query all apps on device, regardless of declarations in the calling app manifest */
- public static final int OP_QUERY_ALL_PACKAGES = 90;
+ public static final int OP_QUERY_ALL_PACKAGES = 91;
/** @hide */
@UnsupportedAppUsage
- public static final int _NUM_OP = 91;
+ public static final int _NUM_OP = 92;
/** Access to coarse location information. */
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -1134,6 +1136,8 @@
/** @hide Has a legacy (non-isolated) view of storage. */
@SystemApi @TestApi
public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage";
+ /** @hide Read location metadata from media */
+ public static final String OPSTR_ACCESS_MEDIA_LOCATION = "android:access_media_location";
/** @hide Interact with accessibility. */
@SystemApi
@@ -1180,6 +1184,7 @@
// Storage
OP_READ_EXTERNAL_STORAGE,
OP_WRITE_EXTERNAL_STORAGE,
+ OP_ACCESS_MEDIA_LOCATION,
// Location
OP_COARSE_LOCATION,
OP_FINE_LOCATION,
@@ -1322,6 +1327,7 @@
OP_LEGACY_STORAGE, // LEGACY_STORAGE
OP_ACCESS_ACCESSIBILITY, // ACCESS_ACCESSIBILITY
OP_READ_DEVICE_IDENTIFIERS, // READ_DEVICE_IDENTIFIERS
+ OP_ACCESS_MEDIA_LOCATION, // ACCESS_MEDIA_LOCATION
OP_QUERY_ALL_PACKAGES, // QUERY_ALL_PACKAGES
};
@@ -1419,6 +1425,7 @@
OPSTR_LEGACY_STORAGE,
OPSTR_ACCESS_ACCESSIBILITY,
OPSTR_READ_DEVICE_IDENTIFIERS,
+ OPSTR_ACCESS_MEDIA_LOCATION,
OPSTR_QUERY_ALL_PACKAGES,
};
@@ -1517,6 +1524,7 @@
"LEGACY_STORAGE",
"ACCESS_ACCESSIBILITY",
"READ_DEVICE_IDENTIFIERS",
+ "ACCESS_MEDIA_LOCATION",
"QUERY_ALL_PACKAGES",
};
@@ -1616,6 +1624,7 @@
null, // no permission for OP_LEGACY_STORAGE
null, // no permission for OP_ACCESS_ACCESSIBILITY
null, // no direct permission for OP_READ_DEVICE_IDENTIFIERS
+ Manifest.permission.ACCESS_MEDIA_LOCATION,
null, // no permission for OP_QUERY_ALL_PACKAGES
};
@@ -1715,6 +1724,7 @@
null, // LEGACY_STORAGE
null, // ACCESS_ACCESSIBILITY
null, // READ_DEVICE_IDENTIFIERS
+ null, // ACCESS_MEDIA_LOCATION
null, // QUERY_ALL_PACKAGES
};
@@ -1813,6 +1823,7 @@
false, // LEGACY_STORAGE
false, // ACCESS_ACCESSIBILITY
false, // READ_DEVICE_IDENTIFIERS
+ false, // ACCESS_MEDIA_LOCATION
false, // QUERY_ALL_PACKAGES
};
@@ -1910,6 +1921,7 @@
AppOpsManager.MODE_DEFAULT, // LEGACY_STORAGE
AppOpsManager.MODE_ALLOWED, // ACCESS_ACCESSIBILITY
AppOpsManager.MODE_ERRORED, // READ_DEVICE_IDENTIFIERS
+ AppOpsManager.MODE_ALLOWED, // ALLOW_MEDIA_LOCATION
AppOpsManager.MODE_DEFAULT, // QUERY_ALL_PACKAGES
};
@@ -2011,6 +2023,7 @@
false, // LEGACY_STORAGE
false, // ACCESS_ACCESSIBILITY
false, // READ_DEVICE_IDENTIFIERS
+ false, // ACCESS_MEDIA_LOCATION
false, // QUERY_ALL_PACKAGES
};
diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java
index 64f886a..df6533a 100644
--- a/core/java/android/app/AsyncNotedAppOp.java
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -238,7 +238,7 @@
time = 1566503083973L,
codegenVersion = "1.0.0",
sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java",
- inputSignatures = "private final @android.annotation.IntRange(from=0L, to=90L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mNotingPackageName\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
+ inputSignatures = "private final @android.annotation.IntRange(from=0L, to=91L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mNotingPackageName\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/app/TEST_MAPPING b/core/java/android/app/TEST_MAPPING
index def1f45..35c7104 100644
--- a/core/java/android/app/TEST_MAPPING
+++ b/core/java/android/app/TEST_MAPPING
@@ -26,6 +26,15 @@
"include-filter": "com.android.server.appop"
}
]
+ },
+ {
+ "file_patterns": ["(/|^)AppOpsManager.java"],
+ "name": "CtsPermission2TestCases",
+ "options": [
+ {
+ "include-filter": "android.permission2.cts.RuntimePermissionProperties"
+ }
+ ]
}
],
"postsubmit": [
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 02cac23..64ddfc1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1551,7 +1551,8 @@
* scopes will be sent in an {@code ArrayList<String>} extra identified by the
* {@link #EXTRA_DELEGATION_SCOPES} key.
*
- * <p class=”note”> Note: This is a protected intent that can only be sent by the system.</p>
+ * <p class="note"><b>Note:</b> This is a protected intent that can only be sent by the
+ * system.</p>
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_APPLICATION_DELEGATION_SCOPES_CHANGED =
@@ -2609,6 +2610,7 @@
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
* @param quality The new desired quality. One of {@link #PASSWORD_QUALITY_UNSPECIFIED},
+ * {@link #PASSWORD_QUALITY_BIOMETRIC_WEAK},
* {@link #PASSWORD_QUALITY_SOMETHING}, {@link #PASSWORD_QUALITY_NUMERIC},
* {@link #PASSWORD_QUALITY_NUMERIC_COMPLEX}, {@link #PASSWORD_QUALITY_ALPHABETIC},
* {@link #PASSWORD_QUALITY_ALPHANUMERIC} or {@link #PASSWORD_QUALITY_COMPLEX}.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8dfe00a..6d88fea 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3078,8 +3078,11 @@
* because the app was updated to support runtime permissions, the
* the permission will be revoked in the upgrade process.
*
+ * @deprecated Renamed to {@link #FLAG_PERMISSION_REVOKED_COMPAT}.
+ *
* @hide
*/
+ @Deprecated
@SystemApi
@TestApi
public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 1 << 3;
@@ -3202,6 +3205,18 @@
public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 1 << 15;
/**
+ * Permission flag: The permission should have been revoked but is kept granted for
+ * compatibility. The data protected by the permission should be protected by a no-op (empty
+ * list, default error, etc) instead of crashing the client. The permission will be revoked if
+ * the app is upgraded to supports it.
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ public static final int FLAG_PERMISSION_REVOKED_COMPAT = FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+
+ /**
* Permission flags: Bitwise or of all permission flags allowing an
* exemption for a restricted permission.
* @hide
@@ -3241,7 +3256,8 @@
| FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT
| FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT
| FLAG_PERMISSION_APPLY_RESTRICTION
- | FLAG_PERMISSION_GRANTED_BY_ROLE;
+ | FLAG_PERMISSION_GRANTED_BY_ROLE
+ | FLAG_PERMISSION_REVOKED_COMPAT;
/**
* Injected activity in app that forwards user to setting activity of that app.
@@ -4017,7 +4033,8 @@
FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT,
FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT,
FLAG_PERMISSION_APPLY_RESTRICTION,
- FLAG_PERMISSION_GRANTED_BY_ROLE
+ FLAG_PERMISSION_GRANTED_BY_ROLE,
+ FLAG_PERMISSION_REVOKED_COMPAT
})
@Retention(RetentionPolicy.SOURCE)
public @interface PermissionFlags {}
@@ -7086,7 +7103,6 @@
case FLAG_PERMISSION_POLICY_FIXED: return "POLICY_FIXED";
case FLAG_PERMISSION_SYSTEM_FIXED: return "SYSTEM_FIXED";
case FLAG_PERMISSION_USER_SET: return "USER_SET";
- case FLAG_PERMISSION_REVOKE_ON_UPGRADE: return "REVOKE_ON_UPGRADE";
case FLAG_PERMISSION_USER_FIXED: return "USER_FIXED";
case FLAG_PERMISSION_REVIEW_REQUIRED: return "REVIEW_REQUIRED";
case FLAG_PERMISSION_REVOKE_WHEN_REQUESTED: return "REVOKE_WHEN_REQUESTED";
@@ -7097,6 +7113,7 @@
case FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT: return "RESTRICTION_UPGRADE_EXEMPT";
case FLAG_PERMISSION_APPLY_RESTRICTION: return "APPLY_RESTRICTION";
case FLAG_PERMISSION_GRANTED_BY_ROLE: return "GRANTED_BY_ROLE";
+ case FLAG_PERMISSION_REVOKED_COMPAT: return "REVOKED_COMPAT";
default: return Integer.toString(flag);
}
}
diff --git a/core/java/com/android/internal/policy/KeyInterceptionInfo.java b/core/java/com/android/internal/policy/KeyInterceptionInfo.java
new file mode 100644
index 0000000..964be01
--- /dev/null
+++ b/core/java/com/android/internal/policy/KeyInterceptionInfo.java
@@ -0,0 +1,35 @@
+/*
+ * 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.internal.policy;
+
+
+/**
+ * Stores a snapshot of window information used to decide whether to intercept a key event.
+ */
+public class KeyInterceptionInfo {
+ // Window layout params attributes.
+ public final int layoutParamsType;
+ public final int layoutParamsPrivateFlags;
+ // Debug friendly name to help identify the window
+ public final String windowTitle;
+
+ public KeyInterceptionInfo(int type, int flags, String title) {
+ layoutParamsType = type;
+ layoutParamsPrivateFlags = flags;
+ windowTitle = title;
+ }
+}
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index 79167ab..15b98af 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -48,6 +48,13 @@
repeated int32 started_users = 2;
+ message JobRestriction {
+ option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+ optional .android.app.job.StopReasonEnum reason = 1;
+ optional bool is_restricting = 2;
+ }
+
message RegisteredJob {
option (.android.msg_privacy).dest = DEST_AUTOMATIC;
@@ -56,20 +63,22 @@
optional bool is_job_ready_to_be_executed = 10;
// A job is ready to be executed if:
- // is_job_ready && are_users_started && !is_job_thermal_constrained && !is_job_pending &&
+ // is_job_ready && are_users_started && !is_job_restricted && !is_job_pending &&
// !is_job_currently_active && !is_uid_backing_up &&
// is_component_usable.
optional bool is_job_ready = 3;
optional bool are_users_started = 4;
- optional bool is_job_thermal_constrained = 11;
+ optional bool is_job_restricted = 11;
optional bool is_job_pending = 5;
optional bool is_job_currently_active = 6;
optional bool is_uid_backing_up = 7;
optional bool is_component_usable = 8;
+ repeated JobRestriction restrictions = 12;
+
reserved 9; // last_run_heartbeat
- // Next tag: 12
+ // Next tag: 13
}
repeated RegisteredJob registered_jobs = 3;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index e23c75e..1d590ea 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -643,6 +643,7 @@
<!-- Grouping for platform runtime permissions is not accessible to apps
@hide
@SystemApi
+ @TestApi
-->
<permission-group android:name="android.permission-group.UNDEFINED"
android:priority="100" />
diff --git a/core/res/TEST_MAPPING b/core/res/TEST_MAPPING
index ccd91a4..9185bae 100644
--- a/core/res/TEST_MAPPING
+++ b/core/res/TEST_MAPPING
@@ -5,6 +5,9 @@
"options": [
{
"include-filter": "android.permission2.cts.PermissionPolicyTest#platformPermissionPolicyIsUnaltered"
+ },
+ {
+ "include-filter": "android.permission2.cts.RuntimePermissionProperties"
}
]
}
diff --git a/data/etc/TEST_MAPPING b/data/etc/TEST_MAPPING
new file mode 100644
index 0000000..1a5db2f
--- /dev/null
+++ b/data/etc/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+ "presubmit": [
+ {
+ "file_patterns": ["(/|^)platform.xml"],
+ "name": "CtsPermission2TestCases",
+ "options": [
+ {
+ "include-filter": "android.permission2.cts.RuntimePermissionProperties"
+ }
+ ]
+ }
+ ]
+}
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index d66930a..2ab7845a 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -206,6 +206,10 @@
targetSdk="29">
<new-permission name="android.permission.ACCESS_BACKGROUND_LOCATION" />
</split-permission>
+ <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
+ targetSdk="29">
+ <new-permission name="android.permission.ACCESS_MEDIA_LOCATION" />
+ </split-permission>
<!-- This is a list of all the libraries available for application
code to link against. -->
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index b9945cc..96ac0f9 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -637,7 +637,7 @@
* @see #setOrientation(Orientation)
*/
public Orientation getOrientation() {
- return mGradientState.getOrientation();
+ return mGradientState.mOrientation;
}
/**
@@ -653,7 +653,7 @@
* @see #getOrientation()
*/
public void setOrientation(Orientation orientation) {
- mGradientState.setOrientation(orientation);
+ mGradientState.mOrientation = orientation;
mGradientIsDirty = true;
invalidateSelf();
}
@@ -1270,7 +1270,7 @@
if (st.mGradient == LINEAR_GRADIENT) {
final float level = st.mUseLevel ? getLevel() / 10000.0f : 1.0f;
- switch (st.getOrientation()) {
+ switch (st.mOrientation) {
case TOP_BOTTOM:
x0 = r.left; y0 = r.top;
x1 = x0; y1 = level * r.bottom;
@@ -1759,6 +1759,33 @@
int angle = (int) a.getFloat(R.styleable.GradientDrawableGradient_angle, st.mAngle);
st.mAngle = ((angle % 360) + 360) % 360; // offset negative angle measures
+ switch (st.mAngle) {
+ case 0:
+ st.mOrientation = Orientation.LEFT_RIGHT;
+ break;
+ case 45:
+ st.mOrientation = Orientation.BL_TR;
+ break;
+ case 90:
+ st.mOrientation = Orientation.BOTTOM_TOP;
+ break;
+ case 135:
+ st.mOrientation = Orientation.BR_TL;
+ break;
+ case 180:
+ st.mOrientation = Orientation.RIGHT_LEFT;
+ break;
+ case 225:
+ st.mOrientation = Orientation.TR_BL;
+ break;
+ case 270:
+ st.mOrientation = Orientation.TOP_BOTTOM;
+ break;
+ case 315:
+ st.mOrientation = Orientation.TL_BR;
+ break;
+ }
+
final TypedValue tv = a.peekValue(R.styleable.GradientDrawableGradient_gradientRadius);
if (tv != null) {
final float radius;
@@ -1981,7 +2008,7 @@
int[] mAttrPadding;
public GradientState(Orientation orientation, int[] gradientColors) {
- setOrientation(orientation);
+ mOrientation = orientation;
setGradientColors(gradientColors);
}
@@ -2184,93 +2211,11 @@
mCenterY = y;
}
- public void setOrientation(Orientation orientation) {
- // Update the angle here so that subsequent attempts to obtain the orientation
- // from the angle overwrite previously configured values during inflation
- mAngle = getAngleFromOrientation(orientation);
- mOrientation = orientation;
- }
-
@NonNull
public Orientation getOrientation() {
- updateGradientStateOrientation();
return mOrientation;
}
- /**
- * Update the orientation of the gradient based on the given angle only if the type is
- * {@link #LINEAR_GRADIENT}
- */
- private void updateGradientStateOrientation() {
- if (mGradient == LINEAR_GRADIENT) {
- int angle = mAngle;
- if (angle % 45 != 0) {
- throw new IllegalArgumentException("Linear gradient requires 'angle' attribute "
- + "to be a multiple of 45");
- }
-
- Orientation orientation;
- switch (angle) {
- case 0:
- orientation = Orientation.LEFT_RIGHT;
- break;
- case 45:
- orientation = Orientation.BL_TR;
- break;
- case 90:
- orientation = Orientation.BOTTOM_TOP;
- break;
- case 135:
- orientation = Orientation.BR_TL;
- break;
- case 180:
- orientation = Orientation.RIGHT_LEFT;
- break;
- case 225:
- orientation = Orientation.TR_BL;
- break;
- case 270:
- orientation = Orientation.TOP_BOTTOM;
- break;
- case 315:
- orientation = Orientation.TL_BR;
- break;
- default:
- // Should not get here as exception is thrown above if angle is not multiple
- // of 45 degrees
- orientation = Orientation.LEFT_RIGHT;
- break;
- }
- mOrientation = orientation;
- }
- }
-
- private int getAngleFromOrientation(@Nullable Orientation orientation) {
- if (orientation != null) {
- switch (orientation) {
- default:
- case LEFT_RIGHT:
- return 0;
- case BL_TR:
- return 45;
- case BOTTOM_TOP:
- return 90;
- case BR_TL:
- return 135;
- case RIGHT_LEFT:
- return 180;
- case TR_BL:
- return 225;
- case TOP_BOTTOM:
- return 270;
- case TL_BR:
- return 315;
- }
- } else {
- return 0;
- }
- }
-
public void setGradientColors(@Nullable int[] colors) {
mGradientColors = colors;
mSolidColors = null;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 67c181b..3010206 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -569,6 +569,7 @@
// Set up the overdraw canvas.
SkImageInfo offscreenInfo = SkImageInfo::MakeA8(surface->width(), surface->height());
sk_sp<SkSurface> offscreen = surface->makeSurface(offscreenInfo);
+ LOG_ALWAYS_FATAL_IF(!offscreen, "Failed to create offscreen SkSurface for overdraw viz.");
SkOverdrawCanvas overdrawCanvas(offscreen->getCanvas());
// Fake a redraw to replay the draw commands. This will increment the alpha channel
diff --git a/location/lib/Android.bp b/location/lib/Android.bp
index b36aa03..fe0f669 100644
--- a/location/lib/Android.bp
+++ b/location/lib/Android.bp
@@ -16,12 +16,11 @@
java_sdk_library {
name: "com.android.location.provider",
- srcs: [
- "java/**/*.java",
- ":framework-all-sources",
- ],
+ srcs: ["java/**/*.java"],
+ api_srcs: [":framework-all-sources"],
libs: [
"androidx.annotation_annotation",
+ "framework-all",
],
api_packages: ["com.android.location.provider"],
}
diff --git a/media/lib/signer/Android.bp b/media/lib/signer/Android.bp
index 2286c53..6b03e4d 100644
--- a/media/lib/signer/Android.bp
+++ b/media/lib/signer/Android.bp
@@ -16,9 +16,8 @@
java_sdk_library {
name: "com.android.mediadrm.signer",
- srcs: [
- "java/**/*.java",
- ":framework-all-sources",
- ],
+ srcs: ["java/**/*.java"],
+ api_srcs: [":framework-all-sources"],
+ libs: ["framework-all"],
api_packages: ["com.android.mediadrm.signer"],
}
diff --git a/packages/BackupEncryption/proto/key_value_listing.proto b/packages/BackupEncryption/proto/key_value_listing.proto
new file mode 100644
index 0000000..001e697
--- /dev/null
+++ b/packages/BackupEncryption/proto/key_value_listing.proto
@@ -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
+ */
+
+syntax = "proto2";
+
+package android_backup_crypto;
+
+option java_package = "com.android.server.backup.encryption.protos";
+option java_outer_classname = "KeyValueListingProto";
+
+// An entry of a key-value pair.
+message KeyValueEntry {
+ // Plaintext key of the key-value pair.
+ optional string key = 1;
+ // SHA-256 MAC of the plaintext of the chunk containing the pair
+ optional bytes hash = 2;
+}
+
+// Describes the key/value pairs currently in the backup blob, mapping from the
+// plaintext key to the hash of the chunk containing the pair.
+//
+// This is local state stored on the device. It is never sent to the
+// backup server. See ChunkOrdering for how the device restores the
+// key-value pairs in the correct order.
+message KeyValueListing {
+ repeated KeyValueEntry entries = 1;
+}
diff --git a/packages/BackupEncryption/proto/key_value_pair.proto b/packages/BackupEncryption/proto/key_value_pair.proto
new file mode 100644
index 0000000..177fa30
--- /dev/null
+++ b/packages/BackupEncryption/proto/key_value_pair.proto
@@ -0,0 +1,31 @@
+/*
+ * 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
+ */
+
+syntax = "proto2";
+
+package android_backup_crypto;
+
+option java_package = "com.android.server.backup.encryption.protos";
+option java_outer_classname = "KeyValuePairProto";
+
+// Serialized form of a key-value pair, when it is to be encrypted in a blob.
+// The backup blob for a key-value database consists of repeated encrypted
+// key-value pairs like this, in a randomized order. See ChunkOrdering for how
+// these are then reconstructed during a restore.
+message KeyValuePair {
+ optional string key = 1;
+ optional bytes value = 2;
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java
new file mode 100644
index 0000000..56e1c05
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutput.java
@@ -0,0 +1,111 @@
+/*
+ * 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.encryption.kv;
+
+import static com.android.internal.util.Preconditions.checkState;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ChunkHasher;
+import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
+import com.android.server.backup.encryption.tasks.DecryptedChunkOutput;
+
+import java.io.IOException;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Builds a key value backup set from plaintext chunks. Computes a digest over the sorted SHA-256
+ * hashes of the chunks.
+ */
+public class DecryptedChunkKvOutput implements DecryptedChunkOutput {
+ @VisibleForTesting static final String DIGEST_ALGORITHM = "SHA-256";
+
+ private final ChunkHasher mChunkHasher;
+ private final List<KeyValuePairProto.KeyValuePair> mUnsortedPairs = new ArrayList<>();
+ private final List<ChunkHash> mUnsortedHashes = new ArrayList<>();
+ private boolean mClosed;
+
+ /** Constructs a new instance which computers the digest using the given hasher. */
+ public DecryptedChunkKvOutput(ChunkHasher chunkHasher) {
+ mChunkHasher = chunkHasher;
+ }
+
+ @Override
+ public DecryptedChunkOutput open() {
+ // As we don't have any resources there is nothing to open.
+ return this;
+ }
+
+ @Override
+ public void processChunk(byte[] plaintextBuffer, int length)
+ throws IOException, InvalidKeyException {
+ checkState(!mClosed, "Cannot process chunk after close()");
+ KeyValuePairProto.KeyValuePair kvPair = new KeyValuePairProto.KeyValuePair();
+ KeyValuePairProto.KeyValuePair.mergeFrom(kvPair, plaintextBuffer, 0, length);
+ mUnsortedPairs.add(kvPair);
+ // TODO(b/71492289): Update ChunkHasher to accept offset and length so we don't have to copy
+ // the buffer into a smaller array.
+ mUnsortedHashes.add(mChunkHasher.computeHash(Arrays.copyOf(plaintextBuffer, length)));
+ }
+
+ @Override
+ public void close() {
+ // As we don't have any resources there is nothing to close.
+ mClosed = true;
+ }
+
+ @Override
+ public byte[] getDigest() throws NoSuchAlgorithmException {
+ checkState(mClosed, "Must close() before getDigest()");
+ MessageDigest digest = getMessageDigest();
+ Collections.sort(mUnsortedHashes);
+ for (ChunkHash hash : mUnsortedHashes) {
+ digest.update(hash.getHash());
+ }
+ return digest.digest();
+ }
+
+ private static MessageDigest getMessageDigest() throws NoSuchAlgorithmException {
+ return MessageDigest.getInstance(DIGEST_ALGORITHM);
+ }
+
+ /**
+ * Returns the key value pairs from the backup, sorted lexicographically by key.
+ *
+ * <p>You must call {@link #close} first.
+ */
+ public List<KeyValuePairProto.KeyValuePair> getPairs() {
+ checkState(mClosed, "Must close() before getPairs()");
+ Collections.sort(
+ mUnsortedPairs,
+ new Comparator<KeyValuePairProto.KeyValuePair>() {
+ @Override
+ public int compare(
+ KeyValuePairProto.KeyValuePair o1, KeyValuePairProto.KeyValuePair o2) {
+ return o1.key.compareTo(o2.key);
+ }
+ });
+ return mUnsortedPairs;
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
new file mode 100644
index 0000000..b3518e1
--- /dev/null
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/kv/KeyValueListingBuilder.java
@@ -0,0 +1,77 @@
+/*
+ * 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.encryption.kv;
+
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * Builds a {@link KeyValueListingProto.KeyValueListing}, which is a nano proto and so has no
+ * builder.
+ */
+public class KeyValueListingBuilder {
+ private final List<KeyValueListingProto.KeyValueEntry> mEntries = new ArrayList<>();
+
+ /** Adds a new pair entry to the listing. */
+ public KeyValueListingBuilder addPair(String key, ChunkHash hash) {
+ checkArgument(key.length() != 0, "Key must have non-zero length");
+ checkNotNull(hash, "Hash must not be null");
+
+ KeyValueListingProto.KeyValueEntry entry = new KeyValueListingProto.KeyValueEntry();
+ entry.key = key;
+ entry.hash = hash.getHash();
+ mEntries.add(entry);
+
+ return this;
+ }
+
+ /** Adds all pairs contained in a map, where the map is from key to hash. */
+ public KeyValueListingBuilder addAll(Map<String, ChunkHash> map) {
+ for (Entry<String, ChunkHash> entry : map.entrySet()) {
+ addPair(entry.getKey(), entry.getValue());
+ }
+
+ return this;
+ }
+
+ /** Returns a new listing containing all the pairs added so far. */
+ public KeyValueListingProto.KeyValueListing build() {
+ if (mEntries.size() == 0) {
+ return emptyListing();
+ }
+
+ KeyValueListingProto.KeyValueListing listing = new KeyValueListingProto.KeyValueListing();
+ listing.entries = new KeyValueListingProto.KeyValueEntry[mEntries.size()];
+ mEntries.toArray(listing.entries);
+ return listing;
+ }
+
+ /** Returns a new listing which does not contain any pairs. */
+ public static KeyValueListingProto.KeyValueListing emptyListing() {
+ KeyValueListingProto.KeyValueListing listing = new KeyValueListingProto.KeyValueListing();
+ listing.entries = KeyValueListingProto.KeyValueEntry.emptyArray();
+ return listing;
+ }
+}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
index e3df3c1..f67f100 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
@@ -19,6 +19,7 @@
import java.io.Closeable;
import java.io.IOException;
import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
/**
* Accepts the plaintext bytes of decrypted chunks and writes them to some output. Also keeps track
@@ -30,7 +31,7 @@
*
* @return {@code this}, to allow use with try-with-resources
*/
- DecryptedChunkOutput open() throws IOException;
+ DecryptedChunkOutput open() throws IOException, NoSuchAlgorithmException;
/**
* Writes the plaintext bytes of chunk to whatever output the implementation chooses. Also
@@ -43,12 +44,13 @@
* at index 0.
* @param length The length in bytes of the plaintext contained in {@code plaintextBuffer}.
*/
- void processChunk(byte[] plaintextBuffer, int length) throws IOException, InvalidKeyException;
+ void processChunk(byte[] plaintextBuffer, int length)
+ throws IOException, InvalidKeyException, NoSuchAlgorithmException;
/**
* Returns the message digest of all the chunks processed by {@link #processChunk}.
*
* <p>You must call {@link Closeable#close()} before calling this method.
*/
- byte[] getDigest();
+ byte[] getDigest() throws NoSuchAlgorithmException;
}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutputTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutputTest.java
new file mode 100644
index 0000000..215e1cb
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/DecryptedChunkKvOutputTest.java
@@ -0,0 +1,164 @@
+/*
+ * 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.encryption.kv;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+import static org.testng.Assert.assertThrows;
+
+import android.os.Debug;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunking.ChunkHasher;
+import com.android.server.backup.encryption.protos.nano.KeyValuePairProto;
+
+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 java.security.MessageDigest;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Stream;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class DecryptedChunkKvOutputTest {
+ private static final String TEST_KEY_1 = "key_1";
+ private static final String TEST_KEY_2 = "key_2";
+ private static final byte[] TEST_VALUE_1 = {1, 2, 3};
+ private static final byte[] TEST_VALUE_2 = {10, 11, 12, 13};
+ private static final byte[] TEST_PAIR_1 = toByteArray(createPair(TEST_KEY_1, TEST_VALUE_1));
+ private static final byte[] TEST_PAIR_2 = toByteArray(createPair(TEST_KEY_2, TEST_VALUE_2));
+ private static final int TEST_BUFFER_SIZE = Math.max(TEST_PAIR_1.length, TEST_PAIR_2.length);
+
+ @Mock private ChunkHasher mChunkHasher;
+ private DecryptedChunkKvOutput mOutput;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ when(mChunkHasher.computeHash(any()))
+ .thenAnswer(invocation -> fakeHash(invocation.getArgument(0)));
+ mOutput = new DecryptedChunkKvOutput(mChunkHasher);
+ }
+
+ @Test
+ public void open_returnsInstance() throws Exception {
+ assertThat(mOutput.open()).isEqualTo(mOutput);
+ }
+
+ @Test
+ public void processChunk_alreadyClosed_throws() throws Exception {
+ mOutput.open();
+ mOutput.close();
+
+ assertThrows(
+ IllegalStateException.class,
+ () -> mOutput.processChunk(TEST_PAIR_1, TEST_PAIR_1.length));
+ }
+
+ @Test
+ public void getDigest_beforeClose_throws() throws Exception {
+ // TODO: b/141356823 We should add a test which calls .open() here
+ assertThrows(IllegalStateException.class, () -> mOutput.getDigest());
+ }
+
+ @Test
+ public void getDigest_returnsDigestOfSortedHashes() throws Exception {
+ mOutput.open();
+ Debug.waitForDebugger();
+ mOutput.processChunk(Arrays.copyOf(TEST_PAIR_1, TEST_BUFFER_SIZE), TEST_PAIR_1.length);
+ mOutput.processChunk(Arrays.copyOf(TEST_PAIR_2, TEST_BUFFER_SIZE), TEST_PAIR_2.length);
+ mOutput.close();
+
+ byte[] actualDigest = mOutput.getDigest();
+
+ MessageDigest digest = MessageDigest.getInstance(DecryptedChunkKvOutput.DIGEST_ALGORITHM);
+ Stream.of(TEST_PAIR_1, TEST_PAIR_2)
+ .map(DecryptedChunkKvOutputTest::fakeHash)
+ .sorted(Comparator.naturalOrder())
+ .forEachOrdered(hash -> digest.update(hash.getHash()));
+ assertThat(actualDigest).isEqualTo(digest.digest());
+ }
+
+ @Test
+ public void getPairs_beforeClose_throws() throws Exception {
+ // TODO: b/141356823 We should add a test which calls .open() here
+ assertThrows(IllegalStateException.class, () -> mOutput.getPairs());
+ }
+
+ @Test
+ public void getPairs_returnsPairsSortedByKey() throws Exception {
+ mOutput.open();
+ // Write out of order to check that it sorts the chunks.
+ mOutput.processChunk(Arrays.copyOf(TEST_PAIR_2, TEST_BUFFER_SIZE), TEST_PAIR_2.length);
+ mOutput.processChunk(Arrays.copyOf(TEST_PAIR_1, TEST_BUFFER_SIZE), TEST_PAIR_1.length);
+ mOutput.close();
+
+ List<KeyValuePairProto.KeyValuePair> pairs = mOutput.getPairs();
+
+ assertThat(
+ isInOrder(
+ pairs,
+ Comparator.comparing(
+ (KeyValuePairProto.KeyValuePair pair) -> pair.key)))
+ .isTrue();
+ assertThat(pairs).hasSize(2);
+ assertThat(pairs.get(0).key).isEqualTo(TEST_KEY_1);
+ assertThat(pairs.get(0).value).isEqualTo(TEST_VALUE_1);
+ assertThat(pairs.get(1).key).isEqualTo(TEST_KEY_2);
+ assertThat(pairs.get(1).value).isEqualTo(TEST_VALUE_2);
+ }
+
+ private static KeyValuePairProto.KeyValuePair createPair(String key, byte[] value) {
+ KeyValuePairProto.KeyValuePair pair = new KeyValuePairProto.KeyValuePair();
+ pair.key = key;
+ pair.value = value;
+ return pair;
+ }
+
+ private boolean isInOrder(
+ List<KeyValuePairProto.KeyValuePair> list,
+ Comparator<KeyValuePairProto.KeyValuePair> comparator) {
+ if (list.size() < 2) {
+ return true;
+ }
+
+ List<KeyValuePairProto.KeyValuePair> sortedList = new ArrayList<>(list);
+ Collections.sort(sortedList, comparator);
+ return list.equals(sortedList);
+ }
+
+ private static byte[] toByteArray(KeyValuePairProto.KeyValuePair nano) {
+ return KeyValuePairProto.KeyValuePair.toByteArray(nano);
+ }
+
+ private static ChunkHash fakeHash(byte[] data) {
+ return new ChunkHash(Arrays.copyOf(data, ChunkHash.HASH_LENGTH_BYTES));
+ }
+}
diff --git a/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/KeyValueListingBuilderTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/KeyValueListingBuilderTest.java
new file mode 100644
index 0000000..acc6628
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/kv/KeyValueListingBuilderTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.encryption.kv;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.protos.nano.KeyValueListingProto;
+
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import java.util.Arrays;
+
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class KeyValueListingBuilderTest {
+ private static final String TEST_KEY_1 = "test_key_1";
+ private static final String TEST_KEY_2 = "test_key_2";
+ private static final ChunkHash TEST_HASH_1 =
+ new ChunkHash(Arrays.copyOf(new byte[] {1, 2}, ChunkHash.HASH_LENGTH_BYTES));
+ private static final ChunkHash TEST_HASH_2 =
+ new ChunkHash(Arrays.copyOf(new byte[] {5, 6}, ChunkHash.HASH_LENGTH_BYTES));
+
+ private KeyValueListingBuilder mBuilder;
+
+ @Before
+ public void setUp() {
+ mBuilder = new KeyValueListingBuilder();
+ }
+
+ @Test
+ public void addPair_nullKey_throws() {
+ assertThrows(NullPointerException.class, () -> mBuilder.addPair(null, TEST_HASH_1));
+ }
+
+ @Test
+ public void addPair_emptyKey_throws() {
+ assertThrows(IllegalArgumentException.class, () -> mBuilder.addPair("", TEST_HASH_1));
+ }
+
+ @Test
+ public void addPair_nullHash_throws() {
+ assertThrows(NullPointerException.class, () -> mBuilder.addPair(TEST_KEY_1, null));
+ }
+
+ @Test
+ public void build_noPairs_buildsEmptyListing() {
+ KeyValueListingProto.KeyValueListing listing = mBuilder.build();
+
+ assertThat(listing.entries).isEmpty();
+ }
+
+ @Test
+ public void build_returnsCorrectListing() {
+ mBuilder.addPair(TEST_KEY_1, TEST_HASH_1);
+
+ KeyValueListingProto.KeyValueListing listing = mBuilder.build();
+
+ assertThat(listing.entries.length).isEqualTo(1);
+ assertThat(listing.entries[0].key).isEqualTo(TEST_KEY_1);
+ assertThat(listing.entries[0].hash).isEqualTo(TEST_HASH_1.getHash());
+ }
+
+ @Test
+ public void addAll_addsAllPairsInMap() {
+ ImmutableMap<String, ChunkHash> pairs =
+ new ImmutableMap.Builder<String, ChunkHash>()
+ .put(TEST_KEY_1, TEST_HASH_1)
+ .put(TEST_KEY_2, TEST_HASH_2)
+ .build();
+
+ mBuilder.addAll(pairs);
+ KeyValueListingProto.KeyValueListing listing = mBuilder.build();
+
+ assertThat(listing.entries.length).isEqualTo(2);
+ assertThat(listing.entries[0].key).isEqualTo(TEST_KEY_1);
+ assertThat(listing.entries[0].hash).isEqualTo(TEST_HASH_1.getHash());
+ assertThat(listing.entries[1].key).isEqualTo(TEST_KEY_2);
+ assertThat(listing.entries[1].hash).isEqualTo(TEST_HASH_2.getHash());
+ }
+
+ @Test
+ public void emptyListing_returnsListingWithoutAnyPairs() {
+ KeyValueListingProto.KeyValueListing emptyListing = KeyValueListingBuilder.emptyListing();
+ assertThat(emptyListing.entries).isEmpty();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index bd91333..1c0e0b3 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.graphics.Rect;
import android.os.HandlerThread;
+import android.os.Trace;
import android.service.wallpaper.WallpaperService;
import android.util.Log;
import android.util.Size;
@@ -48,6 +49,7 @@
private static final int DELAY_FINISH_RENDERING = 1000;
private static final int INTERVAL_WAIT_FOR_RENDERING = 100;
private static final int PATIENCE_WAIT_FOR_RENDERING = 10;
+ private static final boolean DEBUG = true;
private HandlerThread mWorker;
@Override
@@ -125,6 +127,10 @@
@Override
public void onAmbientModeChanged(boolean inAmbientMode, long animationDuration) {
if (!mNeedTransition) return;
+ if (DEBUG) {
+ Log.d(TAG, "onAmbientModeChanged: inAmbient=" + inAmbientMode
+ + ", duration=" + animationDuration);
+ }
mWorker.getThreadHandler().post(
() -> mRenderer.updateAmbientMode(inAmbientMode, animationDuration));
if (inAmbientMode && animationDuration == 0) {
@@ -184,17 +190,32 @@
@Override
public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
+ if (DEBUG) {
+ Log.d(TAG, "onSurfaceRedrawNeeded: mNeedRedraw=" + mNeedRedraw);
+ }
+
mWorker.getThreadHandler().post(() -> {
if (mNeedRedraw) {
- preRender();
- requestRender();
- postRender();
+ drawFrame();
mNeedRedraw = false;
}
});
}
@Override
+ public void onVisibilityChanged(boolean visible) {
+ if (DEBUG) {
+ Log.d(TAG, "wallpaper visibility changes: " + visible);
+ }
+ }
+
+ private void drawFrame() {
+ preRender();
+ requestRender();
+ postRender();
+ }
+
+ @Override
public void onStatePostChange() {
// When back to home, we try to release EGL, which is preserved in lock screen or aod.
if (mController.getState() == StatusBarState.SHADE) {
@@ -205,7 +226,9 @@
@Override
public void preRender() {
// This method should only be invoked from worker thread.
+ Trace.beginSection("ImageWallpaper#preRender");
preRenderInternal();
+ Trace.endSection();
}
private void preRenderInternal() {
@@ -240,7 +263,9 @@
@Override
public void requestRender() {
// This method should only be invoked from worker thread.
+ Trace.beginSection("ImageWallpaper#requestRender");
requestRenderInternal();
+ Trace.endSection();
}
private void requestRenderInternal() {
@@ -263,8 +288,10 @@
@Override
public void postRender() {
// This method should only be invoked from worker thread.
+ Trace.beginSection("ImageWallpaper#postRender");
notifyWaitingThread();
scheduleFinishRendering();
+ Trace.endSection();
}
private void notifyWaitingThread() {
@@ -289,12 +316,14 @@
}
private void finishRendering() {
+ Trace.beginSection("ImageWallpaper#finishRendering");
if (mEglHelper != null) {
mEglHelper.destroyEglSurface();
if (!needPreserveEglContext()) {
mEglHelper.destroyEglContext();
}
}
+ Trace.endSection();
}
private boolean needPreserveEglContext() {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
index 0c4f051..b346a6e 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/ui/DefaultUiController.java
@@ -17,6 +17,7 @@
package com.android.systemui.assist.ui;
import static com.android.systemui.assist.AssistManager.DISMISS_REASON_INVOCATION_CANCELLED;
+import static com.android.systemui.assist.AssistManager.INVOCATION_TYPE_GESTURE;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -24,6 +25,7 @@
import android.content.Context;
import android.graphics.PixelFormat;
import android.metrics.LogMaker;
+import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
@@ -40,6 +42,8 @@
import com.android.systemui.SysUiServiceProvider;
import com.android.systemui.assist.AssistManager;
+import java.util.Locale;
+
/**
* Default UiController implementation. Shows white edge lights along the bottom of the phone,
* expanding from the corners to meet in the center.
@@ -50,6 +54,9 @@
private static final long ANIM_DURATION_MS = 200;
+ private static final boolean VERBOSE = Build.TYPE.toLowerCase(Locale.ROOT).contains("debug")
+ || Build.TYPE.toLowerCase(Locale.ROOT).equals("eng");
+
protected final FrameLayout mRoot;
protected InvocationLightsView mInvocationLightsView;
@@ -114,6 +121,7 @@
@Override // AssistManager.UiController
public void onGestureCompletion(float velocity) {
animateInvocationCompletion(AssistManager.INVOCATION_TYPE_GESTURE, velocity);
+ logInvocationProgressMetrics(INVOCATION_TYPE_GESTURE, 1, mInvocationInProgress);
}
@Override // AssistManager.UiController
@@ -127,16 +135,27 @@
updateAssistHandleVisibility();
}
- protected static void logInvocationProgressMetrics(
+ protected void logInvocationProgressMetrics(
int type, float progress, boolean invocationWasInProgress) {
// Logs assistant invocation start.
+ if (progress == 1f) {
+ if (VERBOSE) {
+ Log.v(TAG, "Invocation complete: type=" + type);
+ }
+ }
if (!invocationWasInProgress && progress > 0.f) {
+ if (VERBOSE) {
+ Log.v(TAG, "Invocation started: type=" + type);
+ }
MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT)
.setType(MetricsEvent.TYPE_ACTION)
.setSubtype(Dependency.get(AssistManager.class).toLoggingSubType(type)));
}
// Logs assistant invocation cancelled.
- if (invocationWasInProgress && progress == 0f) {
+ if (!mInvocationAnimator.isRunning() && invocationWasInProgress && progress == 0f) {
+ if (VERBOSE) {
+ Log.v(TAG, "Invocation cancelled: type=" + type);
+ }
MetricsLogger.action(new LogMaker(MetricsEvent.ASSISTANT)
.setType(MetricsEvent.TYPE_DISMISS)
.setSubtype(DISMISS_REASON_INVOCATION_CANCELLED));
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index bb8c7f1..4d3dc70 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -34,6 +34,7 @@
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.wakelock.DelayedWakeLock;
import com.android.systemui.util.wakelock.WakeLock;
@@ -97,7 +98,8 @@
DockManager dockManager) {
boolean allowPulseTriggers = true;
return new DozeTriggers(context, machine, host, alarmManager, config, params,
- sensorManager, handler, wakeLock, allowPulseTriggers, dockManager);
+ sensorManager, handler, wakeLock, allowPulseTriggers, dockManager,
+ new ProximitySensor(context, sensorManager));
}
private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 67eefc5..dfd83a5 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -42,9 +42,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
-import com.android.systemui.Dependency;
import com.android.systemui.plugins.SensorManagerPlugin;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
import com.android.systemui.util.sensors.ProximitySensor;
@@ -147,8 +145,7 @@
false /* touchscreen */, mConfig.getWakeLockScreenDebounce()),
};
- mProximitySensor = new ProximitySensor(
- context, sensorManager, Dependency.get(PluginManager.class));
+ mProximitySensor = new ProximitySensor(context, sensorManager);
mProximitySensor.register(
proximityEvent -> mProxCallback.accept(!proximityEvent.getNear()));
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 80d4b63..8eed71c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -72,7 +72,6 @@
private final AmbientDisplayConfiguration mConfig;
private final DozeParameters mDozeParameters;
private final AsyncSensorManager mSensorManager;
- private final Handler mHandler;
private final WakeLock mWakeLock;
private final boolean mAllowPulseTriggers;
private final UiModeManager mUiModeManager;
@@ -89,14 +88,14 @@
public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
AlarmManager alarmManager, AmbientDisplayConfiguration config,
DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler,
- WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager) {
+ WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager,
+ ProximitySensor proximitySensor) {
mContext = context;
mMachine = machine;
mDozeHost = dozeHost;
mConfig = config;
mDozeParameters = dozeParameters;
mSensorManager = sensorManager;
- mHandler = handler;
mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
@@ -104,9 +103,7 @@
dozeParameters.getPolicy());
mUiModeManager = mContext.getSystemService(UiModeManager.class);
mDockManager = dockManager;
- mProxCheck = new ProximitySensor.ProximityCheck(
- new ProximitySensor(mContext, mSensorManager, null),
- mHandler);
+ mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, handler);
}
private void onNotification(Runnable onPulseSuppressedListener) {
@@ -182,7 +179,7 @@
}
} else {
proximityCheckThenCall((result) -> {
- if (result) {
+ if (result != null && result) {
// In pocket, drop event.
return;
}
@@ -271,7 +268,7 @@
if (wake) {
proximityCheckThenCall((result) -> {
- if (result) {
+ if (result != null && result) {
// In pocket, drop event.
return;
}
@@ -380,7 +377,7 @@
mPulsePending = true;
proximityCheckThenCall((result) -> {
- if (result) {
+ if (result != null && result) {
// in pocket, abort pulse
DozeLog.tracePulseDropped(mContext, "inPocket");
mPulsePending = false;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index c9c6a0c..8eeb629 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -665,8 +665,7 @@
// Take an "interactive" bugreport.
MetricsLogger.action(mContext,
MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
- ActivityManager.getService().requestBugReport(
- ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
+ ActivityManager.getService().requestInteractiveBugReport();
} catch (RemoteException e) {
}
}
@@ -683,8 +682,7 @@
try {
// Take a "full" bugreport.
MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
- ActivityManager.getService().requestBugReport(
- ActivityManager.BUGREPORT_OPTION_FULL);
+ ActivityManager.getService().requestFullBugReport();
} catch (RemoteException e) {
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
index aac721e..6b5a780 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/EglHelper.java
@@ -63,6 +63,7 @@
// Below two constants make drawing at low priority, so other things can preempt our drawing.
private static final int EGL_CONTEXT_PRIORITY_LEVEL_IMG = 0x3100;
private static final int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103;
+ private static final boolean DEBUG = true;
private EGLDisplay mEglDisplay;
private EGLConfig mEglConfig;
@@ -146,6 +147,10 @@
* @return true if EglSurface is ready.
*/
public boolean createEglSurface(SurfaceHolder surfaceHolder) {
+ if (DEBUG) {
+ Log.d(TAG, "createEglSurface start");
+ }
+
if (hasEglDisplay()) {
mEglSurface = eglCreateWindowSurface(mEglDisplay, mEglConfig, surfaceHolder, null, 0);
} else {
@@ -163,6 +168,9 @@
return false;
}
+ if (DEBUG) {
+ Log.d(TAG, "createEglSurface done");
+ }
return true;
}
@@ -190,6 +198,10 @@
* @return true if EglContext is ready.
*/
public boolean createEglContext() {
+ if (DEBUG) {
+ Log.d(TAG, "createEglContext start");
+ }
+
int[] attrib_list = new int[] {EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_LOW_IMG, EGL_NONE};
if (hasEglDisplay()) {
@@ -203,6 +215,10 @@
Log.w(TAG, "eglCreateContext failed: " + GLUtils.getEGLErrorString(eglGetError()));
return false;
}
+
+ if (DEBUG) {
+ Log.d(TAG, "createEglContext done");
+ }
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
index b154e66..99c55f1 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
@@ -19,6 +19,7 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.util.Log;
import com.android.systemui.Interpolators;
@@ -30,6 +31,7 @@
private static final String TAG = ImageRevealHelper.class.getSimpleName();
private static final float MAX_REVEAL = 0f;
private static final float MIN_REVEAL = 1f;
+ private static final boolean DEBUG = true;
private final ValueAnimator mAnimator;
private final RevealStateListener mRevealListener;
@@ -57,6 +59,9 @@
@Override
public void onAnimationEnd(Animator animation) {
if (!mIsCanceled && mRevealListener != null) {
+ if (DEBUG) {
+ Log.d(TAG, "transition end");
+ }
mRevealListener.onRevealEnd();
}
mIsCanceled = false;
@@ -65,6 +70,9 @@
@Override
public void onAnimationStart(Animator animation) {
if (mRevealListener != null) {
+ if (DEBUG) {
+ Log.d(TAG, "transition start");
+ }
mRevealListener.onRevealStart(true /* animate */);
}
}
@@ -82,6 +90,9 @@
}
void updateAwake(boolean awake, long duration) {
+ if (DEBUG) {
+ Log.d(TAG, "updateAwake: awake=" + awake + ", duration=" + duration);
+ }
mAwake = awake;
mAnimator.setDuration(duration);
if (duration == 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
index 2960634..a8371e3 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageWallpaperRenderer.java
@@ -46,6 +46,7 @@
private static final String TAG = ImageWallpaperRenderer.class.getSimpleName();
private static final float SCALE_VIEWPORT_MIN = 1f;
private static final float SCALE_VIEWPORT_MAX = 1.1f;
+ private static final boolean DEBUG = true;
private final WallpaperManager mWallpaperManager;
private final ImageGLProgram mProgram;
@@ -107,6 +108,9 @@
}
private boolean loadBitmap() {
+ if (DEBUG) {
+ Log.d(TAG, "loadBitmap: mBitmap=" + mBitmap);
+ }
if (mWallpaperManager != null && mBitmap == null) {
mBitmap = mWallpaperManager.getBitmap();
mWallpaperManager.forgetLoadedWallpaper();
@@ -119,6 +123,9 @@
mSurfaceSize.set(0, 0, surfaceWidth, surfaceHeight);
}
}
+ if (DEBUG) {
+ Log.d(TAG, "loadBitmap done");
+ }
return mBitmap != null;
}
@@ -223,6 +230,7 @@
out.print(prefix); out.print("mXOffset="); out.print(mXOffset);
out.print(prefix); out.print("mYOffset="); out.print(mYOffset);
out.print(prefix); out.print("threshold="); out.print(mImageProcessHelper.getThreshold());
+ out.print(prefix); out.print("mReveal="); out.print(mImageRevealHelper.getReveal());
mWallpaper.dump(prefix, fd, out, args);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
index 00b764b..d9de59e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt
@@ -66,6 +66,9 @@
}
private fun updateListeningState() {
+ if (pickupSensor == null) {
+ return
+ }
val onKeyguard = keyguardUpdateMonitor.isKeyguardVisible &&
!statusBarStateController.isDozing
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index c48bdde..cce5bca 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -24,8 +24,8 @@
import android.os.Handler;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
-import com.android.systemui.shared.plugins.PluginManager;
import java.util.ArrayList;
import java.util.List;
@@ -47,7 +47,7 @@
private final float mMaxRange;
private List<ProximitySensorListener> mListeners = new ArrayList<>();
private String mTag = null;
- private ProximityEvent mLastEvent;
+ @VisibleForTesting ProximityEvent mLastEvent;
private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;
private boolean mPaused;
private boolean mRegistered;
@@ -64,8 +64,7 @@
};
@Inject
- public ProximitySensor(
- Context context, AsyncSensorManager sensorManager, PluginManager pluginManager) {
+ public ProximitySensor(Context context, AsyncSensorManager sensorManager) {
mSensorManager = sensorManager;
Sensor sensor = findBrightnessSensor(context);
@@ -146,17 +145,17 @@
return false;
}
- logDebug("Using brightness sensor? " + mUsingBrightnessSensor);
mListeners.add(listener);
registerInternal();
return true;
}
- private void registerInternal() {
+ protected void registerInternal() {
if (mRegistered || mPaused || mListeners.isEmpty()) {
return;
}
+ logDebug("Using brightness sensor? " + mUsingBrightnessSensor);
logDebug("Registering sensor listener");
mRegistered = true;
mSensorManager.registerListener(mSensorEventListener, mSensor, mSensorDelay);
@@ -175,7 +174,7 @@
}
}
- private void unregisterInternal() {
+ protected void unregisterInternal() {
if (!mRegistered) {
return;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index f7cd696..b0e3969 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -41,7 +41,9 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.FakeProximitySensor;
import com.android.systemui.util.sensors.FakeSensorManager;
+import com.android.systemui.util.sensors.ProximitySensor;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.util.wakelock.WakeLockFake;
@@ -60,6 +62,7 @@
private FakeSensorManager mSensors;
private Sensor mTapSensor;
private DockManager mDockManagerFake;
+ private FakeProximitySensor mProximitySensor;
@BeforeClass
public static void setupSuite() {
@@ -80,10 +83,11 @@
mDockManagerFake = mock(DockManager.class);
AsyncSensorManager asyncSensorManager =
new AsyncSensorManager(mSensors, null, new Handler());
+ mProximitySensor = new FakeProximitySensor(getContext(), asyncSensorManager);
mTriggers = new DozeTriggers(mContext, mMachine, mHost, alarmManager, config, parameters,
asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true,
- mDockManagerFake);
+ mDockManagerFake, mProximitySensor);
waitForSensorManager();
}
@@ -95,15 +99,17 @@
mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
clearInvocations(mMachine);
+ mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(true, 1));
mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
- mSensors.getFakeProximitySensor().sendProximityResult(false); /* Near */
+ mProximitySensor.alertListeners();
verify(mMachine, never()).requestState(any());
verify(mMachine, never()).requestPulse(anyInt());
mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
waitForSensorManager();
- mSensors.getFakeProximitySensor().sendProximityResult(true); /* Far */
+ mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(false, 2));
+ mProximitySensor.alertListeners();
verify(mMachine).requestPulse(anyInt());
}
@@ -139,6 +145,14 @@
verify(mDockManagerFake).removeListener(any());
}
+ @Test
+ public void testProximitySensorNotAvailablel() {
+ mProximitySensor.setSensorAvailable(false);
+ mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_LONG_PRESS, 100, 100, null);
+ mTriggers.onSensor(DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, 100, 100, new float[]{1});
+ mTriggers.onSensor(DozeLog.REASON_SENSOR_TAP, 100, 100, null);
+ }
+
private void waitForSensorManager() {
TestableLooper.get(this).processAllMessages();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
new file mode 100644
index 0000000..d7df96d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
@@ -0,0 +1,53 @@
+/*
+ * 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.util.sensors;
+
+import android.content.Context;
+
+public class FakeProximitySensor extends ProximitySensor {
+ private boolean mAvailable;
+ private boolean mPaused;
+
+ public FakeProximitySensor(Context context, AsyncSensorManager sensorManager) {
+ super(context, sensorManager);
+
+ mAvailable = true;
+ }
+
+ public void setSensorAvailable(boolean available) {
+ mAvailable = available;
+ }
+
+ public void setLastEvent(ProximityEvent event) {
+ mLastEvent = event;
+ }
+
+ @Override
+ public boolean getSensorAvailable() {
+ return mAvailable;
+ }
+
+ @Override
+ protected void registerInternal() {
+ // no-op
+ }
+
+ @Override
+ protected void unregisterInternal() {
+ // no-op
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
index 6d13408..fa943e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
@@ -47,7 +47,7 @@
AsyncSensorManager asyncSensorManager = new AsyncSensorManager(
sensorManager, null, new Handler());
mFakeProximitySensor = sensorManager.getFakeProximitySensor();
- mProximitySensor = new ProximitySensor(getContext(), asyncSensorManager, null);
+ mProximitySensor = new ProximitySensor(getContext(), asyncSensorManager);
}
@Test
diff --git a/services/Android.bp b/services/Android.bp
index 6953e86..60dd895 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -47,6 +47,11 @@
"compat-changeid-annotation-processor",
],
+ required: [
+ // Required by services.backup
+ "BackupEncryption",
+ ],
+
// Uncomment to enable output of certain warnings (deprecated, unchecked)
//javacflags: ["-Xlint"],
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index d46c626..058afd3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1016,18 +1016,23 @@
int runBugReport(PrintWriter pw) throws RemoteException {
String opt;
- int bugreportType = ActivityManager.BUGREPORT_OPTION_FULL;
+ boolean fullBugreport = true;
while ((opt=getNextOption()) != null) {
if (opt.equals("--progress")) {
- bugreportType = ActivityManager.BUGREPORT_OPTION_INTERACTIVE;
+ fullBugreport = false;
+ mInterface.requestInteractiveBugReport();
} else if (opt.equals("--telephony")) {
- bugreportType = ActivityManager.BUGREPORT_OPTION_TELEPHONY;
+ fullBugreport = false;
+ // no title and description specified
+ mInterface.requestTelephonyBugReport("" /* no title */, "" /* no descriptions */);
} else {
getErrPrintWriter().println("Error: Unknown option: " + opt);
return -1;
}
}
- mInterface.requestBugReport(bugreportType);
+ if (fullBugreport) {
+ mInterface.requestFullBugReport();
+ }
pw.println("Your lovely bug report is being created; please be patient.");
return 0;
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 159e5b8..f866314 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -807,7 +807,12 @@
public void binderDied() {
synchronized (AppOpsService.this) {
for (int i=mStartedOps.size()-1; i>=0; i--) {
- finishOperationLocked(mStartedOps.get(i), /*finishNested*/ true);
+ final Op op = mStartedOps.get(i);
+ finishOperationLocked(op, /*finishNested*/ true);
+ if (op.startNesting <= 0) {
+ scheduleOpActiveChangedIfNeededLocked(op.op, op.uidState.uid,
+ op.packageName, false);
+ }
}
mClients.remove(mAppToken);
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index e8198b9..6010b1dc 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -57,6 +57,13 @@
private final @NonNull AudioService mAudioService;
private final @NonNull Context mContext;
+ /** Forced device usage for communications sent to AudioSystem */
+ private int mForcedUseForComm;
+ /**
+ * Externally reported force device usage state returned by getters: always consistent
+ * with requests by setters */
+ private int mForcedUseForCommExt;
+
// Manages all connected devices, only ever accessed on the message loop
private final AudioDeviceInventory mDeviceInventory;
// Manages notifications to BT service
@@ -64,34 +71,24 @@
//-------------------------------------------------------------------
- /**
- * Lock to guard:
- * - any changes to the message queue: enqueueing or removing any message
- * - state of A2DP enabled
- * - force use for communication + SCO changes
- */
- private final Object mDeviceBrokerLock = new Object();
-
- @GuardedBy("mDeviceBrokerLock")
+ // we use a different lock than mDeviceStateLock so as not to create
+ // lock contention between enqueueing a message and handling them
+ private static final Object sLastDeviceConnectionMsgTimeLock = new Object();
+ @GuardedBy("sLastDeviceConnectionMsgTimeLock")
private static long sLastDeviceConnectMsgTime = 0;
+ // General lock to be taken whenever the state of the audio devices is to be checked or changed
+ private final Object mDeviceStateLock = new Object();
- /** Request to override default use of A2DP for media */
- @GuardedBy("mDeviceBrokerLock")
+ // Request to override default use of A2DP for media.
+ @GuardedBy("mDeviceStateLock")
private boolean mBluetoothA2dpEnabled;
- /** Forced device usage for communications sent to AudioSystem */
- @GuardedBy("mDeviceBrokerLock")
- private int mForcedUseForComm;
- /**
- * Externally reported force device usage state returned by getters: always consistent
- * with requests by setters */
- @GuardedBy("mDeviceBrokerLock")
- private int mForcedUseForCommExt;
-
+ // lock always taken when accessing AudioService.mSetModeDeathHandlers
+ // TODO do not "share" the lock between AudioService and BtHelpr, see b/123769055
+ /*package*/ final Object mSetModeLock = new Object();
//-------------------------------------------------------------------
- /** Normal constructor used by AudioService */
/*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service) {
mContext = context;
mAudioService = service;
@@ -130,37 +127,36 @@
// All post* methods are asynchronous
/*package*/ void onSystemReady() {
- mBtHelper.onSystemReady();
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onSystemReady();
+ }
+ }
}
/*package*/ void onAudioServerDied() {
// Restore forced usage for communications and record
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
AudioSystem.setParameters(
"BT_SCO=" + (mForcedUseForComm == AudioSystem.FORCE_BT_SCO ? "on" : "off"));
onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, "onAudioServerDied");
onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm, "onAudioServerDied");
-
- // restore devices
- sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
}
+ // restore devices
+ sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
}
/*package*/ void setForceUse_Async(int useCase, int config, String eventSource) {
- synchronized (mDeviceBrokerLock) {
- sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
- useCase, config, eventSource);
- }
+ sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
+ useCase, config, eventSource);
}
/*package*/ void toggleHdmiIfConnected_Async() {
- synchronized (mDeviceBrokerLock) {
- sendMsgNoDelay(MSG_TOGGLE_HDMI, SENDMSG_QUEUE);
- }
+ sendMsgNoDelay(MSG_TOGGLE_HDMI, SENDMSG_QUEUE);
}
/*package*/ void disconnectAllBluetoothProfiles() {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
mBtHelper.disconnectAllBluetoothProfiles();
}
}
@@ -172,11 +168,15 @@
* @param intent
*/
/*package*/ void receiveBtEvent(@NonNull Intent intent) {
- mBtHelper.receiveBtEvent(intent);
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.receiveBtEvent(intent);
+ }
+ }
}
/*package*/ void setBluetoothA2dpOn_Async(boolean on, String source) {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
if (mBluetoothA2dpEnabled == on) {
return;
}
@@ -196,7 +196,7 @@
* @return true if speakerphone state changed
*/
/*package*/ boolean setSpeakerphoneOn(boolean on, String eventSource) {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
final boolean wasOn = isSpeakerphoneOn();
if (on) {
if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
@@ -214,7 +214,7 @@
}
/*package*/ boolean isSpeakerphoneOn() {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
}
}
@@ -223,7 +223,9 @@
@AudioService.ConnectionState int state, String address, String name,
String caller) {
//TODO move logging here just like in setBluetooth* methods
- mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller);
+ }
}
private static final class BtDeviceConnectionInfo {
@@ -257,24 +259,27 @@
final BtDeviceConnectionInfo info = new BtDeviceConnectionInfo(device, state, profile,
suppressNoisyIntent, a2dpVolume);
- synchronized (mDeviceBrokerLock) {
- // when receiving a request to change the connection state of a device, this last
- // request is the source of truth, so cancel all previous requests
- mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
- device);
- mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION,
- device);
- mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
- device);
- mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
- device);
+ // when receiving a request to change the connection state of a device, this last request
+ // is the source of truth, so cancel all previous requests
+ removeAllA2dpConnectionEvents(device);
- sendLMsgNoDelay(
- state == BluetoothProfile.STATE_CONNECTED
- ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION
- : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
- SENDMSG_QUEUE, info);
- }
+ sendLMsgNoDelay(
+ state == BluetoothProfile.STATE_CONNECTED
+ ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION
+ : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
+ SENDMSG_QUEUE, info);
+ }
+
+ /** remove all previously scheduled connection and disconnection events for the given device */
+ private void removeAllA2dpConnectionEvents(@NonNull BluetoothDevice device) {
+ mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION,
+ device);
+ mBrokerHandler.removeMessages(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION,
+ device);
+ mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
+ device);
+ mBrokerHandler.removeMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
+ device);
}
private static final class HearingAidDeviceConnectionInfo {
@@ -300,27 +305,25 @@
boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) {
final HearingAidDeviceConnectionInfo info = new HearingAidDeviceConnectionInfo(
device, state, suppressNoisyIntent, musicDevice, eventSource);
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
- }
+ sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
}
// never called by system components
/*package*/ void setBluetoothScoOnByApp(boolean on) {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
}
}
/*package*/ boolean isBluetoothScoOnForApp() {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO;
}
}
/*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
//Log.i(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
if (on) {
// do not accept SCO ON if SCO audio is not connected
if (!mBtHelper.isBluetoothScoOn()) {
@@ -343,55 +346,58 @@
}
/*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
- return mDeviceInventory.startWatchingRoutes(observer);
-
+ synchronized (mDeviceStateLock) {
+ return mDeviceInventory.startWatchingRoutes(observer);
+ }
}
/*package*/ AudioRoutesInfo getCurAudioRoutes() {
- return mDeviceInventory.getCurAudioRoutes();
+ synchronized (mDeviceStateLock) {
+ return mDeviceInventory.getCurAudioRoutes();
+ }
}
/*package*/ boolean isAvrcpAbsoluteVolumeSupported() {
- return mBtHelper.isAvrcpAbsoluteVolumeSupported();
+ synchronized (mDeviceStateLock) {
+ return mBtHelper.isAvrcpAbsoluteVolumeSupported();
+ }
}
/*package*/ boolean isBluetoothA2dpOn() {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
return mBluetoothA2dpEnabled;
}
}
/*package*/ void postSetAvrcpAbsoluteVolumeIndex(int index) {
- synchronized (mDeviceBrokerLock) {
- sendIMsgNoDelay(MSG_I_SET_AVRCP_ABSOLUTE_VOLUME, SENDMSG_REPLACE, index);
- }
+ sendIMsgNoDelay(MSG_I_SET_AVRCP_ABSOLUTE_VOLUME, SENDMSG_REPLACE, index);
}
/*package*/ void postSetHearingAidVolumeIndex(int index, int streamType) {
- synchronized (mDeviceBrokerLock) {
- sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType);
- }
+ sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType);
}
/*package*/ void postDisconnectBluetoothSco(int exceptPid) {
- synchronized (mDeviceBrokerLock) {
- sendIMsgNoDelay(MSG_I_DISCONNECT_BT_SCO, SENDMSG_REPLACE, exceptPid);
- }
+ sendIMsgNoDelay(MSG_I_DISCONNECT_BT_SCO, SENDMSG_REPLACE, exceptPid);
}
/*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) {
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
+ sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
+ }
+
+ @GuardedBy("mSetModeLock")
+ /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
+ @NonNull String eventSource) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
}
}
- /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
- @NonNull String eventSource) {
- mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
- }
-
+ @GuardedBy("mSetModeLock")
/*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) {
- mBtHelper.stopBluetoothScoForClient(cb, eventSource);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.stopBluetoothScoForClient(cb, eventSource);
+ }
}
//---------------------------------------------------------------------
@@ -454,109 +460,77 @@
//---------------------------------------------------------------------
// Message handling on behalf of helper classes
/*package*/ void postBroadcastScoConnectionState(int state) {
- synchronized (mDeviceBrokerLock) {
- sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state);
- }
+ sendIMsgNoDelay(MSG_I_BROADCAST_BT_CONNECTION_STATE, SENDMSG_QUEUE, state);
}
/*package*/ void postBroadcastBecomingNoisy() {
- synchronized (mDeviceBrokerLock) {
- sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE);
- }
+ sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE);
}
/*package*/ void postA2dpSinkConnection(@AudioService.BtProfileConnectionState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
- synchronized (mDeviceBrokerLock) {
- sendILMsg(state == BluetoothA2dp.STATE_CONNECTED
- ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED
- : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
- SENDMSG_QUEUE,
- state, btDeviceInfo, delay);
- }
+ sendILMsg(state == BluetoothA2dp.STATE_CONNECTED
+ ? MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED
+ : MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
+ SENDMSG_QUEUE,
+ state, btDeviceInfo, delay);
}
/*package*/ void postA2dpSourceConnection(@AudioService.BtProfileConnectionState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
- synchronized (mDeviceBrokerLock) {
- sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE,
- state, btDeviceInfo, delay);
- }
+ sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE,
+ state, btDeviceInfo, delay);
}
/*package*/ void postSetWiredDeviceConnectionState(
AudioDeviceInventory.WiredDeviceConnectionState connectionState, int delay) {
- synchronized (mDeviceBrokerLock) {
- sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE,
- connectionState, delay);
- }
+ sendLMsg(MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE, SENDMSG_QUEUE, connectionState, delay);
}
/*package*/ void postSetHearingAidConnectionState(
@AudioService.BtProfileConnectionState int state,
@NonNull BluetoothDevice device, int delay) {
- synchronized (mDeviceBrokerLock) {
- sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE,
- state,
- device,
- delay);
- }
+ sendILMsg(MSG_IL_SET_HEARING_AID_CONNECTION_STATE, SENDMSG_QUEUE,
+ state,
+ device,
+ delay);
}
/*package*/ void postDisconnectA2dp() {
- synchronized (mDeviceBrokerLock) {
- sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE);
- }
+ sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE);
}
/*package*/ void postDisconnectA2dpSink() {
- synchronized (mDeviceBrokerLock) {
- sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE);
- }
+ sendMsgNoDelay(MSG_DISCONNECT_A2DP_SINK, SENDMSG_QUEUE);
}
/*package*/ void postDisconnectHearingAid() {
- synchronized (mDeviceBrokerLock) {
- sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE);
- }
+ sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE);
}
/*package*/ void postDisconnectHeadset() {
- synchronized (mDeviceBrokerLock) {
- sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE);
- }
+ sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE);
}
/*package*/ void postBtA2dpProfileConnected(BluetoothA2dp a2dpProfile) {
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile);
- }
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile);
}
/*package*/ void postBtA2dpSinkProfileConnected(BluetoothProfile profile) {
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile);
- }
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile);
}
/*package*/ void postBtHeasetProfileConnected(BluetoothHeadset headsetProfile) {
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE,
- headsetProfile);
- }
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE, headsetProfile);
}
/*package*/ void postBtHearingAidProfileConnected(BluetoothHearingAid hearingAidProfile) {
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE,
- hearingAidProfile);
- }
+ sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE,
+ hearingAidProfile);
}
/*package*/ void postScoClientDied(Object obj) {
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
- }
+ sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
}
//---------------------------------------------------------------------
@@ -571,7 +545,7 @@
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
.append(Binder.getCallingPid()).append(" src:").append(source).toString();
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
mBluetoothA2dpEnabled = on;
mBrokerHandler.removeMessages(MSG_IIL_SET_FORCE_BT_A2DP_USE);
onSetForceUse(
@@ -583,85 +557,71 @@
/*package*/ boolean handleDeviceConnection(boolean connect, int device, String address,
String deviceName) {
- return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName);
+ synchronized (mDeviceStateLock) {
+ return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName);
+ }
}
/*package*/ void postSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
- synchronized (mDeviceBrokerLock) {
- sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state,
- btDeviceInfo);
- }
+ sendILMsgNoDelay(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE, state,
+ btDeviceInfo);
}
/*package*/ void handleFailureToConnectToBtHeadsetService(int delay) {
- synchronized (mDeviceBrokerLock) {
- sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay);
- }
+ sendMsg(MSG_BT_HEADSET_CNCT_FAILED, SENDMSG_REPLACE, delay);
}
/*package*/ void handleCancelFailureToConnectToBtHeadsetService() {
- synchronized (mDeviceBrokerLock) {
- mBrokerHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
- }
+ mBrokerHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
}
/*package*/ void postReportNewRoutes() {
- synchronized (mDeviceBrokerLock) {
- sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP);
- }
+ sendMsgNoDelay(MSG_REPORT_NEW_ROUTES, SENDMSG_NOOP);
}
/*package*/ void cancelA2dpDockTimeout() {
- synchronized (mDeviceBrokerLock) {
- mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
- }
+ mBrokerHandler.removeMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
}
- // FIXME: used by?
/*package*/ void postA2dpActiveDeviceChange(
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
- synchronized (mDeviceBrokerLock) {
- sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
- }
+ sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
}
/*package*/ boolean hasScheduledA2dpDockTimeout() {
- synchronized (mDeviceBrokerLock) {
- return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
- }
+ return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
}
// must be called synchronized on mConnectedDevices
/*package*/ boolean hasScheduledA2dpSinkConnectionState(BluetoothDevice btDevice) {
- synchronized (mDeviceBrokerLock) {
- return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
- new BtHelper.BluetoothA2dpDeviceInfo(btDevice))
- || mBrokerHandler.hasMessages(
- MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
- new BtHelper.BluetoothA2dpDeviceInfo(btDevice)));
- }
+ return (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED,
+ new BtHelper.BluetoothA2dpDeviceInfo(btDevice))
+ || mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED,
+ new BtHelper.BluetoothA2dpDeviceInfo(btDevice)));
}
/*package*/ void setA2dpDockTimeout(String address, int a2dpCodec, int delayMs) {
- synchronized (mDeviceBrokerLock) {
- sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
- }
+ sendILMsg(MSG_IL_BTA2DP_DOCK_TIMEOUT, SENDMSG_QUEUE, a2dpCodec, address, delayMs);
}
/*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
- mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
+ }
}
/*package*/ boolean getBluetoothA2dpEnabled() {
- synchronized (mDeviceBrokerLock) {
+ synchronized (mDeviceStateLock) {
return mBluetoothA2dpEnabled;
}
}
/*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) {
- return mBtHelper.getA2dpCodec(device);
+ synchronized (mDeviceStateLock) {
+ return mBtHelper.getA2dpCodec(device);
+ }
}
/*package*/ void dump(PrintWriter pw, String prefix) {
@@ -749,50 +709,68 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_RESTORE_DEVICES:
- mDeviceInventory.onRestoreDevices();
- mBtHelper.onAudioServerDiedRestoreA2dp();
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onRestoreDevices();
+ mBtHelper.onAudioServerDiedRestoreA2dp();
+ }
break;
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
- mDeviceInventory.onSetWiredDeviceConnectionState(
- (AudioDeviceInventory.WiredDeviceConnectionState) msg.obj);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onSetWiredDeviceConnectionState(
+ (AudioDeviceInventory.WiredDeviceConnectionState) msg.obj);
+ }
break;
case MSG_I_BROADCAST_BT_CONNECTION_STATE:
- mBtHelper.onBroadcastScoConnectionState(msg.arg1);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onBroadcastScoConnectionState(msg.arg1);
+ }
break;
case MSG_IIL_SET_FORCE_USE: // intended fall-through
case MSG_IIL_SET_FORCE_BT_A2DP_USE:
onSetForceUse(msg.arg1, msg.arg2, (String) msg.obj);
break;
case MSG_REPORT_NEW_ROUTES:
- mDeviceInventory.onReportNewRoutes();
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onReportNewRoutes();
+ }
break;
case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
- mDeviceInventory.onSetA2dpSinkConnectionState(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onSetA2dpSinkConnectionState(
+ (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
+ }
break;
case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
- mDeviceInventory.onSetA2dpSourceConnectionState(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onSetA2dpSourceConnectionState(
+ (BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
+ }
break;
case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
- mDeviceInventory.onSetHearingAidConnectionState(
- (BluetoothDevice) msg.obj, msg.arg1,
- mAudioService.getHearingAidStreamType());
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onSetHearingAidConnectionState(
+ (BluetoothDevice) msg.obj, msg.arg1,
+ mAudioService.getHearingAidStreamType());
+ }
break;
case MSG_BT_HEADSET_CNCT_FAILED:
- mBtHelper.resetBluetoothSco();
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.resetBluetoothSco();
+ }
+ }
break;
case MSG_IL_BTA2DP_DOCK_TIMEOUT:
// msg.obj == address of BTA2DP device
- mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
+ }
break;
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
final int a2dpCodec;
final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
- synchronized (mDeviceBrokerLock) {
- // FIXME why isn't the codec coming with the request? lock shouldn't be
- // needed here
+ synchronized (mDeviceStateLock) {
a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec),
@@ -803,48 +781,84 @@
onSendBecomingNoisyIntent();
break;
case MSG_II_SET_HEARING_AID_VOLUME:
- mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2);
+ }
break;
case MSG_I_SET_AVRCP_ABSOLUTE_VOLUME:
- mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
+ }
break;
case MSG_I_DISCONNECT_BT_SCO:
- mBtHelper.disconnectBluetoothSco(msg.arg1);
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.disconnectBluetoothSco(msg.arg1);
+ }
+ }
break;
case MSG_L_SCOCLIENT_DIED:
- mBtHelper.scoClientDied(msg.obj);
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.scoClientDied(msg.obj);
+ }
+ }
break;
case MSG_TOGGLE_HDMI:
- mDeviceInventory.onToggleHdmi();
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onToggleHdmi();
+ }
break;
case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
- mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
- (BtHelper.BluetoothA2dpDeviceInfo) msg.obj,
- BtHelper.EVENT_ACTIVE_DEVICE_CHANGE);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
+ (BtHelper.BluetoothA2dpDeviceInfo) msg.obj,
+ BtHelper.EVENT_ACTIVE_DEVICE_CHANGE);
+ }
break;
case MSG_DISCONNECT_A2DP:
- mDeviceInventory.disconnectA2dp();
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.disconnectA2dp();
+ }
break;
case MSG_DISCONNECT_A2DP_SINK:
- mDeviceInventory.disconnectA2dpSink();
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.disconnectA2dpSink();
+ }
break;
case MSG_DISCONNECT_BT_HEARING_AID:
- mDeviceInventory.disconnectHearingAid();
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.disconnectHearingAid();
+ }
break;
case MSG_DISCONNECT_BT_HEADSET:
- mBtHelper.disconnectHeadset();
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.disconnectHeadset();
+ }
+ }
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP:
- mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj);
+ }
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK:
- mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj);
+ }
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID:
- mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj);
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj);
+ }
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET:
- mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
+ }
+ }
break;
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION:
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: {
@@ -857,9 +871,11 @@
+ " addr=" + info.mDevice.getAddress()
+ " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy
+ " vol=" + info.mVolume)).printLog(TAG));
- mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
- info.mDevice, info.mState, info.mProfile, info.mSupprNoisy,
- AudioSystem.DEVICE_NONE, info.mVolume);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
+ info.mDevice, info.mState, info.mProfile, info.mSupprNoisy,
+ AudioSystem.DEVICE_NONE, info.mVolume);
+ }
} break;
case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: {
final HearingAidDeviceConnectionInfo info =
@@ -869,8 +885,10 @@
+ " addr=" + info.mDevice.getAddress()
+ " supprNoisy=" + info.mSupprNoisy
+ " src=" + info.mEventSource)).printLog(TAG));
- mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
- info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
+ info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
+ }
} break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
@@ -955,57 +973,46 @@
/** If the msg is already queued, queue this one and leave the old. */
private static final int SENDMSG_QUEUE = 2;
- @GuardedBy("mDeviceBrokerLock")
private void sendMsg(int msg, int existingMsgPolicy, int delay) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, null, delay);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendILMsg(int msg, int existingMsgPolicy, int arg, Object obj, int delay) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, obj, delay);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendLMsg(int msg, int existingMsgPolicy, Object obj, int delay) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, obj, delay);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendIMsg(int msg, int existingMsgPolicy, int arg, int delay) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, null, delay);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendMsgNoDelay(int msg, int existingMsgPolicy) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, null, 0);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendIMsgNoDelay(int msg, int existingMsgPolicy, int arg) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, null, 0);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendIIMsgNoDelay(int msg, int existingMsgPolicy, int arg1, int arg2) {
sendIILMsg(msg, existingMsgPolicy, arg1, arg2, null, 0);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendILMsgNoDelay(int msg, int existingMsgPolicy, int arg, Object obj) {
sendIILMsg(msg, existingMsgPolicy, arg, 0, obj, 0);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendLMsgNoDelay(int msg, int existingMsgPolicy, Object obj) {
sendIILMsg(msg, existingMsgPolicy, 0, 0, obj, 0);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendIILMsgNoDelay(int msg, int existingMsgPolicy, int arg1, int arg2, Object obj) {
sendIILMsg(msg, existingMsgPolicy, arg1, arg2, obj, 0);
}
- @GuardedBy("mDeviceBrokerLock")
private void sendIILMsg(int msg, int existingMsgPolicy, int arg1, int arg2, Object obj,
int delay) {
if (existingMsgPolicy == SENDMSG_REPLACE) {
@@ -1024,29 +1031,31 @@
Binder.restoreCallingIdentity(identity);
}
- long time = SystemClock.uptimeMillis() + delay;
+ synchronized (sLastDeviceConnectionMsgTimeLock) {
+ long time = SystemClock.uptimeMillis() + delay;
- switch (msg) {
- case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
- case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
- case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
- case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
- case MSG_IL_BTA2DP_DOCK_TIMEOUT:
- case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
- case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
- if (sLastDeviceConnectMsgTime >= time) {
- // add a little delay to make sure messages are ordered as expected
- time = sLastDeviceConnectMsgTime + 30;
- }
- sLastDeviceConnectMsgTime = time;
- break;
- default:
- break;
+ switch (msg) {
+ case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
+ case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED:
+ case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED:
+ case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
+ case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
+ case MSG_IL_BTA2DP_DOCK_TIMEOUT:
+ case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
+ case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
+ if (sLastDeviceConnectMsgTime >= time) {
+ // add a little delay to make sure messages are ordered as expected
+ time = sLastDeviceConnectMsgTime + 30;
+ }
+ sLastDeviceConnectMsgTime = time;
+ break;
+ default:
+ break;
+ }
+
+ mBrokerHandler.sendMessageAtTime(mBrokerHandler.obtainMessage(msg, arg1, arg2, obj),
+ time);
}
-
- mBrokerHandler.sendMessageAtTime(mBrokerHandler.obtainMessage(msg, arg1, arg2, obj),
- time);
}
//-------------------------------------------------------------
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 075842b..77b3fee 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -469,11 +469,12 @@
// List of binder death handlers for setMode() client processes.
// The last process to have called setMode() is at the top of the list.
- private final ArrayList<SetModeDeathHandler> mSetModeDeathHandlers =
+ // package-private so it can be accessed in AudioDeviceBroker.getSetModeDeathHandlers
+ //TODO candidate to be moved to separate class that handles synchronization
+ @GuardedBy("mDeviceBroker.mSetModeLock")
+ /*package*/ final ArrayList<SetModeDeathHandler> mSetModeDeathHandlers =
new ArrayList<SetModeDeathHandler>();
- private volatile int mCurrentModeOwnerPid = 0;
-
// true if boot sequence has been completed
private boolean mSystemReady;
// true if Intent.ACTION_USER_SWITCHED has ever been received
@@ -3149,10 +3150,15 @@
* @return 0 if nobody owns the mode
*/
/*package*/ int getModeOwnerPid() {
- return mCurrentModeOwnerPid;
+ int modeOwnerPid = 0;
+ try {
+ modeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
+ } catch (Exception e) {
+ // nothing to do, modeOwnerPid is not modified
+ }
+ return modeOwnerPid;
}
-
private class SetModeDeathHandler implements IBinder.DeathRecipient {
private IBinder mCb; // To be notified of client's death
private int mPid;
@@ -3166,7 +3172,7 @@
public void binderDied() {
int oldModeOwnerPid = 0;
int newModeOwnerPid = 0;
- synchronized (mSetModeDeathHandlers) {
+ synchronized (mDeviceBroker.mSetModeLock) {
Log.w(TAG, "setMode() client died");
if (!mSetModeDeathHandlers.isEmpty()) {
oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
@@ -3177,15 +3183,11 @@
} else {
newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, TAG);
}
-
- if (newModeOwnerPid != oldModeOwnerPid) {
- mCurrentModeOwnerPid = newModeOwnerPid;
- // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all SCO
- // connections not started by the application changing the mode when pid changes
- if (newModeOwnerPid != 0) {
- mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
- }
- }
+ }
+ // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
+ // SCO connections not started by the application changing the mode when pid changes
+ if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
+ mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
}
}
@@ -3208,17 +3210,15 @@
/** @see AudioManager#setMode(int) */
public void setMode(int mode, IBinder cb, String callingPackage) {
- if (DEBUG_MODE) {
- Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")");
- }
+ if (DEBUG_MODE) { Log.v(TAG, "setMode(mode=" + mode + ", callingPackage=" + callingPackage + ")"); }
if (!checkAudioSettingsPermission("setMode()")) {
return;
}
- if ((mode == AudioSystem.MODE_IN_CALL)
- && (mContext.checkCallingOrSelfPermission(
+ if ( (mode == AudioSystem.MODE_IN_CALL) &&
+ (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
- != PackageManager.PERMISSION_GRANTED)) {
+ != PackageManager.PERMISSION_GRANTED)) {
Log.w(TAG, "MODIFY_PHONE_STATE Permission Denial: setMode(MODE_IN_CALL) from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
return;
@@ -3230,7 +3230,7 @@
int oldModeOwnerPid = 0;
int newModeOwnerPid = 0;
- synchronized (mSetModeDeathHandlers) {
+ synchronized (mDeviceBroker.mSetModeLock) {
if (!mSetModeDeathHandlers.isEmpty()) {
oldModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
}
@@ -3238,21 +3238,17 @@
mode = mMode;
}
newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid(), callingPackage);
-
- if (newModeOwnerPid != oldModeOwnerPid) {
- mCurrentModeOwnerPid = newModeOwnerPid;
- // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
- // SCO connections not started by the application changing the mode when pid changes
- if (newModeOwnerPid != 0) {
- mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
- }
- }
+ }
+ // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
+ // SCO connections not started by the application changing the mode when pid changes
+ if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
+ mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
}
}
// setModeInt() returns a valid PID if the audio mode was successfully set to
// any mode other than NORMAL.
- @GuardedBy("mSetModeDeathHandlers")
+ @GuardedBy("mDeviceBroker.mSetModeLock")
private int setModeInt(int mode, IBinder cb, int pid, String caller) {
if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", caller="
+ caller + ")"); }
@@ -3591,7 +3587,9 @@
!mSystemReady) {
return;
}
- mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
+ synchronized (mDeviceBroker.mSetModeLock) {
+ mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
+ }
}
/** @see AudioManager#stopBluetoothSco() */
@@ -3603,7 +3601,9 @@
final String eventSource = new StringBuilder("stopBluetoothSco()")
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
.append(Binder.getCallingPid()).toString();
- mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
+ synchronized (mDeviceBroker.mSetModeLock) {
+ mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
+ }
}
@@ -4352,7 +4352,7 @@
// NOTE: Locking order for synchronized objects related to volume or ringer mode management:
// 1 mScoclient OR mSafeMediaVolumeState
- // 2 mSetModeDeathHandlers
+ // 2 mSetModeLock
// 3 mSettingsLock
// 4 VolumeStreamState.class
private class VolumeStreamState {
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 625b6b6..9f1a6bd 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -171,6 +171,8 @@
//----------------------------------------------------------------------
// Interface for AudioDeviceBroker
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onSystemReady() {
mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
resetBluetoothSco();
@@ -243,6 +245,8 @@
return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void receiveBtEvent(Intent intent) {
final String action = intent.getAction();
if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
@@ -329,6 +333,8 @@
*
* @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
*/
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
checkScoAudioState();
if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
@@ -337,6 +343,8 @@
clearAllScoClients(exceptPid, true);
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
@NonNull String eventSource) {
ScoClient client = getScoClient(cb, true);
@@ -356,6 +364,8 @@
Binder.restoreCallingIdentity(ident);
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
@NonNull String eventSource) {
ScoClient client = getScoClient(cb, false);
@@ -413,6 +423,8 @@
mDeviceBroker.postDisconnectHearingAid();
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void resetBluetoothSco() {
clearAllScoClients(0, false);
mScoAudioState = SCO_STATE_INACTIVE;
@@ -421,6 +433,8 @@
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void disconnectHeadset() {
setBtScoActiveDevice(null);
mBluetoothHeadset = null;
@@ -466,6 +480,8 @@
/*eventSource*/ "mBluetoothProfileServiceListener");
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) {
// Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
@@ -552,6 +568,8 @@
return result;
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private void setBtScoActiveDevice(BluetoothDevice btDevice) {
Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice);
@@ -634,6 +652,8 @@
};
//----------------------------------------------------------------------
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void scoClientDied(Object obj) {
final ScoClient client = (ScoClient) obj;
Log.w(TAG, "SCO client died");
@@ -664,6 +684,8 @@
mDeviceBroker.postScoClientDied(this);
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void incCount(int scoAudioMode) {
if (!requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode)) {
@@ -683,6 +705,8 @@
mStartcount++;
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void decCount() {
if (mStartcount == 0) {
@@ -702,6 +726,8 @@
}
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ // @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void clearCount(boolean stopSco) {
if (mStartcount != 0) {
@@ -738,6 +764,8 @@
return count;
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private boolean requestScoState(int state, int scoAudioMode) {
checkScoAudioState();
@@ -931,6 +959,8 @@
return null;
}
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private void clearAllScoClients(int exceptPid, boolean stopSco) {
ScoClient savedClient = null;
diff --git a/services/core/java/com/android/server/integrity/OWNERS b/services/core/java/com/android/server/integrity/OWNERS
new file mode 100644
index 0000000..019aa4f
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/OWNERS
@@ -0,0 +1,6 @@
+omernebil@google.com
+khelmy@google.com
+mdchurchill@google.com
+sturla@google.com
+songpan@google.com
+bjy@google.com
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 587862a..2a85c89 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -37,7 +37,7 @@
import static android.content.pm.PackageManager.DELETE_KEEP_DATA;
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
@@ -581,16 +581,6 @@
private static final String PACKAGE_SCHEME = "package";
- private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
-
- private static final String PRODUCT_OVERLAY_DIR = "/product/overlay";
-
- private static final String SYSTEM_EXT_OVERLAY_DIR = "/system_ext/overlay";
-
- private static final String ODM_OVERLAY_DIR = "/odm/overlay";
-
- private static final String OEM_OVERLAY_DIR = "/oem/overlay";
-
/** Canonical intent used to identify what counts as a "web browser" app */
private static final Intent sBrowserIntent;
static {
@@ -756,6 +746,26 @@
private final Injector mInjector;
/**
+ * The list of all system partitions that may contain packages in ascending order of
+ * specificity (the more generic, the earlier in the list a partition appears).
+ */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final List<SystemPartition> SYSTEM_PARTITIONS = Collections.unmodifiableList(
+ Arrays.asList(
+ new SystemPartition(Environment.getRootDirectory(), 0 /* scanFlag */,
+ true /* hasPriv */, false /* hasOverlays */),
+ new SystemPartition(Environment.getVendorDirectory(), SCAN_AS_VENDOR,
+ true /* hasPriv */, true /* hasOverlays */),
+ new SystemPartition(Environment.getOdmDirectory(), SCAN_AS_ODM,
+ true /* hasPriv */, true /* hasOverlays */),
+ new SystemPartition(Environment.getOemDirectory(), SCAN_AS_OEM,
+ false /* hasPriv */, true /* hasOverlays */),
+ new SystemPartition(Environment.getProductDirectory(), SCAN_AS_PRODUCT,
+ true /* hasPriv */, true /* hasOverlays */),
+ new SystemPartition(Environment.getSystemExtDirectory(), SCAN_AS_SYSTEM_EXT,
+ true /* hasPriv */, true /* hasOverlays */)));
+
+ /**
* Unit tests will instantiate, extend and/or mock to mock dependencies / behaviors.
*
* NOTE: All getters should return the same instance for every call.
@@ -1582,7 +1592,7 @@
private static final int USER_RUNTIME_GRANT_MASK =
FLAG_PERMISSION_USER_SET
| FLAG_PERMISSION_USER_FIXED
- | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ | FLAG_PERMISSION_REVOKED_COMPAT;
final @Nullable String mRequiredVerifierPackage;
final @NonNull String mRequiredInstallerPackage;
@@ -2552,6 +2562,51 @@
}
}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static class SystemPartition {
+ public final File folder;
+ public final int scanFlag;
+ public final File appFolder;
+ @Nullable
+ public final File privAppFolder;
+ @Nullable
+ public final File overlayFolder;
+
+ private SystemPartition(File folder, int scanFlag, boolean hasPrivApps,
+ boolean hasOverlays) {
+ this.folder = folder;
+ this.scanFlag = scanFlag;
+ this.appFolder = toCanonical(new File(folder, "app"));
+ this.privAppFolder = hasPrivApps ? toCanonical(new File(folder, "priv-app")) : null;
+ this.overlayFolder = hasOverlays ? toCanonical(new File(folder, "overlay")) : null;
+ }
+
+ public boolean containsPrivApp(File scanFile) {
+ return FileUtils.contains(privAppFolder, scanFile);
+ }
+
+ public boolean containsApp(File scanFile) {
+ return FileUtils.contains(appFolder, scanFile);
+ }
+
+ public boolean containsPath(String path) {
+ return path.startsWith(folder.getPath() + "/");
+ }
+
+ public boolean containsPrivPath(String path) {
+ return privAppFolder != null && path.startsWith(privAppFolder.getPath() + "/");
+ }
+
+ private static File toCanonical(File dir) {
+ try {
+ return dir.getCanonicalFile();
+ } catch (IOException e) {
+ // failed to look up canonical path, continue with original one
+ return dir;
+ }
+ }
+ }
+
public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
Trace.TRACE_TAG_PACKAGE_MANAGER);
@@ -2775,215 +2830,35 @@
// any apps.)
// For security and version matching reason, only consider overlay packages if they
// reside in the right directory.
- scanDirTracedLI(new File(VENDOR_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR,
- 0);
- scanDirTracedLI(new File(PRODUCT_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT,
- 0);
- scanDirTracedLI(new File(SYSTEM_EXT_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT,
- 0);
- scanDirTracedLI(new File(ODM_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_ODM,
- 0);
- scanDirTracedLI(new File(OEM_OVERLAY_DIR),
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_OEM,
- 0);
+ final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
+ final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
+ for (int i = SYSTEM_PARTITIONS.size() - 1; i >= 0; i--) {
+ final SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ if (partition.overlayFolder == null) {
+ continue;
+ }
+ scanDirTracedLI(partition.overlayFolder, systemParseFlags,
+ systemScanFlags | partition.scanFlag, 0);
+ }
mParallelPackageParserCallback.findStaticOverlayPackages();
- // Find base frameworks (resource packages without code).
- scanDirTracedLI(frameworkDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_NO_DEX
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRIVILEGED,
- 0);
+ scanDirTracedLI(frameworkDir, systemParseFlags,
+ systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0);
if (!mPackages.containsKey("android")) {
throw new IllegalStateException(
"Failed to load frameworks package; check log for warnings");
}
-
- // Collect privileged system packages.
- final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
- scanDirTracedLI(privilegedAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRIVILEGED,
- 0);
-
- // Collect ordinary system packages.
- final File systemAppDir = new File(Environment.getRootDirectory(), "app");
- scanDirTracedLI(systemAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM,
- 0);
-
- // Collect privileged vendor packages.
- File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app");
- try {
- privilegedVendorAppDir = privilegedVendorAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
+ for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
+ final SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ if (partition.privAppFolder != null) {
+ scanDirTracedLI(partition.privAppFolder, systemParseFlags,
+ systemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0);
+ }
+ scanDirTracedLI(partition.appFolder, systemParseFlags,
+ systemScanFlags | partition.scanFlag, 0);
}
- scanDirTracedLI(privilegedVendorAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR
- | SCAN_AS_PRIVILEGED,
- 0);
- // Collect ordinary vendor packages.
- File vendorAppDir = new File(Environment.getVendorDirectory(), "app");
- try {
- vendorAppDir = vendorAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(vendorAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR,
- 0);
-
- // Collect privileged odm packages. /odm is another vendor partition
- // other than /vendor.
- File privilegedOdmAppDir = new File(Environment.getOdmDirectory(),
- "priv-app");
- try {
- privilegedOdmAppDir = privilegedOdmAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(privilegedOdmAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR
- | SCAN_AS_PRIVILEGED,
- 0);
-
- // Collect ordinary odm packages. /odm is another vendor partition
- // other than /vendor.
- File odmAppDir = new File(Environment.getOdmDirectory(), "app");
- try {
- odmAppDir = odmAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(odmAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR,
- 0);
-
- // Collect all OEM packages.
- final File oemAppDir = new File(Environment.getOemDirectory(), "app");
- scanDirTracedLI(oemAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_OEM,
- 0);
-
- // Collected privileged /product packages.
- File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app");
- try {
- privilegedProductAppDir = privilegedProductAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(privilegedProductAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT
- | SCAN_AS_PRIVILEGED,
- 0);
-
- // Collect ordinary /product packages.
- File productAppDir = new File(Environment.getProductDirectory(), "app");
- try {
- productAppDir = productAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(productAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT,
- 0);
-
- // Collected privileged /system_ext packages.
- File privilegedSystemExtAppDir =
- new File(Environment.getSystemExtDirectory(), "priv-app");
- try {
- privilegedSystemExtAppDir =
- privilegedSystemExtAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(privilegedSystemExtAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT
- | SCAN_AS_PRIVILEGED,
- 0);
-
- // Collect ordinary /system_ext packages.
- File systemExtAppDir = new File(Environment.getSystemExtDirectory(), "app");
- try {
- systemExtAppDir = systemExtAppDir.getCanonicalFile();
- } catch (IOException e) {
- // failed to look up canonical path, continue with original one
- }
- scanDirTracedLI(systemExtAppDir,
- mDefParseFlags
- | PackageParser.PARSE_IS_SYSTEM_DIR,
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT,
- 0);
// Prune any system packages that no longer exist.
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
@@ -3151,89 +3026,26 @@
logCriticalInfo(Log.WARN, "Expected better " + packageName
+ " but never showed up; reverting to system");
- final @ParseFlags int reparseFlags;
- final @ScanFlags int rescanFlags;
- if (FileUtils.contains(privilegedAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRIVILEGED;
- } else if (FileUtils.contains(systemAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM;
- } else if (FileUtils.contains(privilegedVendorAppDir, scanFile)
- || FileUtils.contains(privilegedOdmAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR
- | SCAN_AS_PRIVILEGED;
- } else if (FileUtils.contains(vendorAppDir, scanFile)
- || FileUtils.contains(odmAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_VENDOR;
- } else if (FileUtils.contains(oemAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_OEM;
- } else if (FileUtils.contains(privilegedProductAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT
- | SCAN_AS_PRIVILEGED;
- } else if (FileUtils.contains(productAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_PRODUCT;
- } else if (FileUtils.contains(privilegedSystemExtAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT
- | SCAN_AS_PRIVILEGED;
- } else if (FileUtils.contains(systemExtAppDir, scanFile)) {
- reparseFlags =
- mDefParseFlags |
- PackageParser.PARSE_IS_SYSTEM_DIR;
- rescanFlags =
- scanFlags
- | SCAN_AS_SYSTEM
- | SCAN_AS_SYSTEM_EXT;
- } else {
+ @ParseFlags int reparseFlags = 0;
+ @ScanFlags int rescanFlags = 0;
+ for (int i1 = 0, size = SYSTEM_PARTITIONS.size(); i1 < size; i1++) {
+ SystemPartition partition = SYSTEM_PARTITIONS.get(i1);
+ if (partition.containsPrivApp(scanFile)) {
+ reparseFlags = systemParseFlags;
+ rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
+ | partition.scanFlag;
+ break;
+ }
+ if (partition.containsApp(scanFile)) {
+ reparseFlags = systemParseFlags;
+ rescanFlags = systemScanFlags | partition.scanFlag;
+ break;
+ }
+ }
+ if (rescanFlags == 0) {
Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
continue;
}
-
mSettings.enableSystemPackageLPw(packageName);
try {
@@ -18079,70 +17891,15 @@
}
static boolean locationIsPrivileged(String path) {
- try {
- final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
- final File privilegedVendorAppDir = new File(Environment.getVendorDirectory(), "priv-app");
- final File privilegedOdmAppDir = new File(Environment.getOdmDirectory(), "priv-app");
- final File privilegedProductAppDir = new File(Environment.getProductDirectory(), "priv-app");
- final File privilegedSystemExtAppDir =
- new File(Environment.getSystemExtDirectory(), "priv-app");
- return path.startsWith(privilegedAppDir.getCanonicalPath() + "/")
- || path.startsWith(privilegedVendorAppDir.getCanonicalPath() + "/")
- || path.startsWith(privilegedOdmAppDir.getCanonicalPath() + "/")
- || path.startsWith(privilegedProductAppDir.getCanonicalPath() + "/")
- || path.startsWith(privilegedSystemExtAppDir.getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
+ for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
+ SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ if (partition.containsPrivPath(path)) {
+ return true;
+ }
}
return false;
}
- static boolean locationIsOem(String path) {
- try {
- return path.startsWith(Environment.getOemDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
-
- static boolean locationIsVendor(String path) {
- try {
- return path.startsWith(Environment.getVendorDirectory().getCanonicalPath() + "/")
- || path.startsWith(Environment.getOdmDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
-
- static boolean locationIsProduct(String path) {
- try {
- return path.startsWith(Environment.getProductDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
-
- static boolean locationIsSystemExt(String path) {
- try {
- return path.startsWith(
- Environment.getSystemExtDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
-
- static boolean locationIsOdm(String path) {
- try {
- return path.startsWith(Environment.getOdmDirectory().getCanonicalPath() + "/");
- } catch (IOException e) {
- Slog.e(TAG, "Unable to access code path " + path);
- }
- return false;
- }
/*
* Tries to delete system package.
@@ -18253,23 +18010,15 @@
| PackageParser.PARSE_MUST_BE_APK
| PackageParser.PARSE_IS_SYSTEM_DIR;
@ScanFlags int scanFlags = SCAN_AS_SYSTEM;
- if (locationIsPrivileged(codePathString)) {
- scanFlags |= SCAN_AS_PRIVILEGED;
- }
- if (locationIsOem(codePathString)) {
- scanFlags |= SCAN_AS_OEM;
- }
- if (locationIsVendor(codePathString)) {
- scanFlags |= SCAN_AS_VENDOR;
- }
- if (locationIsProduct(codePathString)) {
- scanFlags |= SCAN_AS_PRODUCT;
- }
- if (locationIsSystemExt(codePathString)) {
- scanFlags |= SCAN_AS_SYSTEM_EXT;
- }
- if (locationIsOdm(codePathString)) {
- scanFlags |= SCAN_AS_ODM;
+ for (int i = 0, size = SYSTEM_PARTITIONS.size(); i < size; i++) {
+ SystemPartition partition = SYSTEM_PARTITIONS.get(i);
+ if (partition.containsPath(codePathString)) {
+ scanFlags |= partition.scanFlag;
+ if (partition.containsPrivPath(codePathString)) {
+ scanFlags |= SCAN_AS_PRIVILEGED;
+ }
+ break;
+ }
}
final File codePath = new File(codePathString);
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 1cea4ca..6b4ef69 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -318,7 +318,7 @@
// The APEX part of the session is activated, proceed with the installation of APKs.
try {
Slog.d(TAG, "Installing APK packages in session " + session.sessionId);
- installApksInSession(session, /* preReboot */ false);
+ installApksInSession(session);
} catch (PackageManagerException e) {
session.setStagedSessionFailed(e.error, e.getMessage());
@@ -410,72 +410,23 @@
return apkSession;
}
- private void commitApkSession(@NonNull PackageInstallerSession apkSession,
- PackageInstallerSession originalSession, boolean preReboot)
- throws PackageManagerException {
- final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED
- : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED;
- if (preReboot) {
- final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
- (Intent result) -> {
- int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status != PackageInstaller.STATUS_SUCCESS) {
- final String errorMessage = result.getStringExtra(
- PackageInstaller.EXTRA_STATUS_MESSAGE);
- Slog.e(TAG, "Failure to install APK staged session "
- + originalSession.sessionId + " [" + errorMessage + "]");
- originalSession.setStagedSessionFailed(errorCode, errorMessage);
- return;
- }
- mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
- originalSession.sessionId);
- });
- apkSession.commit(receiver.getIntentSender(), false);
- return;
- }
-
- if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
- // If rollback is available for this session, notify the rollback
- // manager of the apk session so it can properly enable rollback.
- final IRollbackManager rm = IRollbackManager.Stub.asInterface(
- ServiceManager.getService(Context.ROLLBACK_SERVICE));
- try {
- rm.notifyStagedApkSession(originalSession.sessionId, apkSession.sessionId);
- } catch (RemoteException re) {
- Slog.e(TAG, "Failed to notifyStagedApkSession for session: "
- + originalSession.sessionId, re);
- }
- }
-
- final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync();
- apkSession.commit(receiver.getIntentSender(), false);
- final Intent result = receiver.getResult();
- final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
- PackageInstaller.STATUS_FAILURE);
- if (status != PackageInstaller.STATUS_SUCCESS) {
- final String errorMessage = result.getStringExtra(
- PackageInstaller.EXTRA_STATUS_MESSAGE);
- Slog.e(TAG, "Failure to install APK staged session "
- + originalSession.sessionId + " [" + errorMessage + "]");
- throw new PackageManagerException(errorCode, errorMessage);
- }
- }
-
- private void installApksInSession(@NonNull PackageInstallerSession session,
- boolean preReboot) throws PackageManagerException {
+ /**
+ * Extract apks in the given session into a new session. Returns {@code null} if there is no
+ * apks in the given session. Only parent session is returned for multi-package session.
+ */
+ @Nullable
+ private PackageInstallerSession extractApksInSession(PackageInstallerSession session,
+ boolean preReboot) throws PackageManagerException {
final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED
: SessionInfo.STAGED_SESSION_ACTIVATION_FAILED;
if (!session.isMultiPackage() && !isApexSession(session)) {
- // APK single-packaged staged session. Do a regular install.
- PackageInstallerSession apkSession = createAndWriteApkSession(session, preReboot);
- commitApkSession(apkSession, session, preReboot);
+ return createAndWriteApkSession(session, preReboot);
} else if (session.isMultiPackage()) {
// For multi-package staged sessions containing APKs, we identify which child sessions
// contain an APK, and with those then create a new multi-package group of sessions,
// carrying over all the session parameters and unmarking them as staged. On commit the
// sessions will be installed atomically.
- List<PackageInstallerSession> childSessions;
+ final List<PackageInstallerSession> childSessions;
synchronized (mStagedSessions) {
childSessions =
Arrays.stream(session.getChildSessionIds())
@@ -487,18 +438,18 @@
}
if (childSessions.isEmpty()) {
// APEX-only multi-package staged session, nothing to do.
- return;
+ return null;
}
- PackageInstaller.SessionParams params = session.params.copy();
+ final PackageInstaller.SessionParams params = session.params.copy();
params.isStaged = false;
if (preReboot) {
params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
}
// TODO(b/129744602): use the userid from the original session.
- int apkParentSessionId = mPi.createSession(
+ final int apkParentSessionId = mPi.createSession(
params, session.getInstallerPackageName(),
0 /* UserHandle.SYSTEM */);
- PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId);
+ final PackageInstallerSession apkParentSession = mPi.getSession(apkParentSessionId);
try {
apkParentSession.open();
} catch (IOException e) {
@@ -519,9 +470,75 @@
"Failed to add a child session " + apkChildSession.sessionId);
}
}
- commitApkSession(apkParentSession, session, preReboot);
+ return apkParentSession;
}
- // APEX single-package staged session, nothing to do.
+ return null;
+ }
+
+ private void verifyApksInSession(PackageInstallerSession session)
+ throws PackageManagerException {
+
+ final PackageInstallerSession apksToVerify = extractApksInSession(
+ session, /* preReboot */ true);
+ if (apksToVerify == null) {
+ return;
+ }
+
+ final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
+ (Intent result) -> {
+ int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status != PackageInstaller.STATUS_SUCCESS) {
+ final String errorMessage = result.getStringExtra(
+ PackageInstaller.EXTRA_STATUS_MESSAGE);
+ Slog.e(TAG, "Failure to verify APK staged session "
+ + session.sessionId + " [" + errorMessage + "]");
+ session.setStagedSessionFailed(
+ SessionInfo.STAGED_SESSION_VERIFICATION_FAILED, errorMessage);
+ return;
+ }
+ mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
+ session.sessionId);
+ });
+
+ apksToVerify.commit(receiver.getIntentSender(), false);
+ }
+
+ private void installApksInSession(@NonNull PackageInstallerSession session)
+ throws PackageManagerException {
+
+ final PackageInstallerSession apksToInstall = extractApksInSession(
+ session, /* preReboot */ false);
+ if (apksToInstall == null) {
+ return;
+ }
+
+ if ((apksToInstall.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
+ // If rollback is available for this session, notify the rollback
+ // manager of the apk session so it can properly enable rollback.
+ final IRollbackManager rm = IRollbackManager.Stub.asInterface(
+ ServiceManager.getService(Context.ROLLBACK_SERVICE));
+ try {
+ rm.notifyStagedApkSession(session.sessionId, apksToInstall.sessionId);
+ } catch (RemoteException re) {
+ Slog.e(TAG, "Failed to notifyStagedApkSession for session: "
+ + session.sessionId, re);
+ }
+ }
+
+ final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync();
+ apksToInstall.commit(receiver.getIntentSender(), false);
+ final Intent result = receiver.getResult();
+ final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+ if (status != PackageInstaller.STATUS_SUCCESS) {
+ final String errorMessage = result.getStringExtra(
+ PackageInstaller.EXTRA_STATUS_MESSAGE);
+ Slog.e(TAG, "Failure to install APK staged session "
+ + session.sessionId + " [" + errorMessage + "]");
+ throw new PackageManagerException(
+ SessionInfo.STAGED_SESSION_ACTIVATION_FAILED, errorMessage);
+ }
}
void commitSession(@NonNull PackageInstallerSession session) {
@@ -836,8 +853,8 @@
Slog.d(TAG, "Running a pre-reboot verification for APKs in session "
+ session.sessionId + " by performing a dry-run install");
- // installApksInSession will notify the handler when APK verification is complete
- installApksInSession(session, /* preReboot */ true);
+ // verifyApksInSession will notify the handler when APK verification is complete
+ verifyApksInSession(session);
// TODO(b/118865310): abort the session on apexd.
} catch (PackageManagerException e) {
session.setStagedSessionFailed(e.error, e.getMessage());
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 3e655ed..793cdd2 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -190,6 +190,7 @@
static {
STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
}
private static final int MSG_READ_DEFAULT_PERMISSION_EXCEPTIONS = 1;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index a57321e..b831374 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -24,7 +24,7 @@
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
-import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
@@ -1514,7 +1514,7 @@
// These are flags that can change base on user actions.
final int userSettableMask = FLAG_PERMISSION_USER_SET
| FLAG_PERMISSION_USER_FIXED
- | FLAG_PERMISSION_REVOKE_ON_UPGRADE
+ | FLAG_PERMISSION_REVOKED_COMPAT
| FLAG_PERMISSION_REVIEW_REQUIRED;
final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED
@@ -1624,7 +1624,7 @@
final int uid = mPackageManagerInt.getPackageUid(packageName, 0, userId);
final int targetSdk = mPackageManagerInt.getUidTargetSdkVersion(uid);
final int flags = (targetSdk < Build.VERSION_CODES.M && bp.isRuntime())
- ? FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_REVOKE_ON_UPGRADE
+ ? FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_REVOKED_COMPAT
: 0;
updatePermissionFlagsInternal(
@@ -2536,8 +2536,8 @@
wasChanged = true;
}
- if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
- flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ if ((flags & FLAG_PERMISSION_REVOKED_COMPAT) != 0) {
+ flags &= ~FLAG_PERMISSION_REVOKED_COMPAT;
wasChanged = true;
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized
@@ -2556,7 +2556,7 @@
bp.getSourcePackageName())) {
if (!bp.isRemoved()) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED
- | FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ | FLAG_PERMISSION_REVOKED_COMPAT;
wasChanged = true;
}
}
@@ -2671,8 +2671,8 @@
wasChanged = true;
}
- if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
- flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ if ((flags & FLAG_PERMISSION_REVOKED_COMPAT) != 0) {
+ flags &= ~FLAG_PERMISSION_REVOKED_COMPAT;
wasChanged = true;
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized ||
diff --git a/services/core/java/com/android/server/policy/LegacyGlobalActions.java b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
index 9cb2441..bbee393b 100644
--- a/services/core/java/com/android/server/policy/LegacyGlobalActions.java
+++ b/services/core/java/com/android/server/policy/LegacyGlobalActions.java
@@ -370,8 +370,7 @@
// Take an "interactive" bugreport.
MetricsLogger.action(mContext,
MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
- ActivityManager.getService().requestBugReport(
- ActivityManager.BUGREPORT_OPTION_INTERACTIVE);
+ ActivityManager.getService().requestInteractiveBugReport();
} catch (RemoteException e) {
}
}
@@ -388,8 +387,7 @@
try {
// Take a "full" bugreport.
MetricsLogger.action(mContext, MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_FULL);
- ActivityManager.getService().requestBugReport(
- ActivityManager.BUGREPORT_OPTION_FULL);
+ ActivityManager.getService().requestFullBugReport();
} catch (RemoteException e) {
}
return false;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ab531899..88b1793 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -204,6 +204,7 @@
import com.android.internal.os.RoSystemProperties;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
+import com.android.internal.policy.KeyInterceptionInfo;
import com.android.internal.policy.PhoneWindow;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ArrayUtils;
@@ -1603,7 +1604,7 @@
mDisplayId = displayId;
}
- int handleHomeButton(WindowState win, KeyEvent event) {
+ int handleHomeButton(IBinder focusedToken, KeyEvent event) {
final boolean keyguardOn = keyguardOn();
final int repeatCount = event.getRepeatCount();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
@@ -1646,18 +1647,18 @@
return -1;
}
- // If a system window has focus, then it doesn't make sense
- // right now to interact with applications.
- WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
- if (attrs != null) {
- final int type = attrs.type;
- if (type == TYPE_KEYGUARD_DIALOG
- || (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
+ final KeyInterceptionInfo info =
+ mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
+ if (info != null) {
+ // If a system window has focus, then it doesn't make sense
+ // right now to interact with applications.
+ if (info.layoutParamsType == TYPE_KEYGUARD_DIALOG
+ || (info.layoutParamsPrivateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
// the "app" is keyguard, so give it the key
return 0;
}
for (int t : WINDOW_TYPES_WHERE_HOME_DOESNT_WORK) {
- if (type == t) {
+ if (info.layoutParamsType == t) {
// don't do anything, but also don't pass it to the app
return -1;
}
@@ -2598,8 +2599,9 @@
// TODO(b/117479243): handle it in InputPolicy
/** {@inheritDoc} */
@Override
- public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
- final long result = interceptKeyBeforeDispatchingInner(win, event, policyFlags);
+ public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
+ int policyFlags) {
+ final long result = interceptKeyBeforeDispatchingInner(focusedToken, event, policyFlags);
final int eventDisplayId = event.getDisplayId();
if (result == 0 && !mPerDisplayFocusEnabled
&& eventDisplayId != INVALID_DISPLAY && eventDisplayId != mTopFocusedDisplayId) {
@@ -2627,7 +2629,7 @@
return result;
}
- private long interceptKeyBeforeDispatchingInner(WindowState win, KeyEvent event,
+ private long interceptKeyBeforeDispatchingInner(IBinder focusedToken, KeyEvent event,
int policyFlags) {
final boolean keyguardOn = keyguardOn();
final int keyCode = event.getKeyCode();
@@ -2730,7 +2732,7 @@
handler = new DisplayHomeButtonHandler(displayId);
mDisplayHomeButtonHandlers.put(displayId, handler);
}
- return handler.handleHomeButton(win, event);
+ return handler.handleHomeButton(focusedToken, event);
} else if (keyCode == KeyEvent.KEYCODE_MENU) {
// Hijack modified menu keys for debugging features
final int chordBug = KeyEvent.META_SHIFT_ON;
@@ -3120,8 +3122,7 @@
|| Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1) {
try {
- ActivityManager.getService()
- .requestBugReport(ActivityManager.BUGREPORT_OPTION_FULL);
+ ActivityManager.getService().requestFullBugReport();
} catch (RemoteException e) {
Slog.e(TAG, "Error taking bugreport", e);
}
@@ -3131,10 +3132,15 @@
// TODO(b/117479243): handle it in InputPolicy
/** {@inheritDoc} */
@Override
- public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
+ public KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags) {
// Note: This method is only called if the initial down was unhandled.
if (DEBUG_INPUT) {
- Slog.d(TAG, "Unhandled key: win=" + win + ", action=" + event.getAction()
+ final KeyInterceptionInfo info =
+ mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
+ final String title = info == null ? "<unknown>" : info.windowTitle;
+ Slog.d(TAG, "Unhandled key: inputToken=" + focusedToken
+ + ", title=" + title
+ + ", action=" + event.getAction()
+ ", flags=" + event.getFlags()
+ ", keyCode=" + event.getKeyCode()
+ ", scanCode=" + event.getScanCode()
@@ -3173,7 +3179,7 @@
event.getDeviceId(), event.getScanCode(),
flags, event.getSource(), event.getDisplayId(), null);
- if (!interceptFallback(win, fallbackEvent, policyFlags)) {
+ if (!interceptFallback(focusedToken, fallbackEvent, policyFlags)) {
fallbackEvent.recycle();
fallbackEvent = null;
}
@@ -3197,11 +3203,12 @@
return fallbackEvent;
}
- private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {
+ private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent,
+ int policyFlags) {
int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);
if ((actions & ACTION_PASS_TO_USER) != 0) {
long delayMillis = interceptKeyBeforeDispatching(
- win, fallbackEvent, policyFlags);
+ focusedToken, fallbackEvent, policyFlags);
if (delayMillis == 0) {
return true;
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 6d9c710..01250db 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -173,7 +173,7 @@
/**
* Interface to the Window Manager state associated with a particular
- * window. You can hold on to an instance of this interface from the call
+ * window. You can hold on to an instance of this interface from the call
* to prepareAddWindow() until removeWindow().
*/
public interface WindowState {
@@ -1025,7 +1025,7 @@
* behavior for keys that can not be overridden by applications.
* This method is called from the input thread, with no locks held.
*
- * @param win The window that currently has focus. This is where the key
+ * @param focusedToken Client window token that currently has focus. This is where the key
* event will normally go.
* @param event The key event.
* @param policyFlags The policy flags associated with the key.
@@ -1034,7 +1034,7 @@
* milliseconds by which the key dispatch should be delayed before trying
* again.
*/
- public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags);
+ long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event, int policyFlags);
/**
* Called from the input dispatcher thread when an application did not handle
@@ -1043,14 +1043,14 @@
* <p>Allows you to define default global behavior for keys that were not handled
* by applications. This method is called from the input thread, with no locks held.
*
- * @param win The window that currently has focus. This is where the key
+ * @param focusedToken Client window token that currently has focus. This is where the key
* event will normally go.
* @param event The key event.
* @param policyFlags The policy flags associated with the key.
* @return Returns an alternate key event to redispatch as a fallback, or null to give up.
* The caller is responsible for recycling the key event.
*/
- public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags);
+ KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags);
/**
* Called when the top focused display is changed.
diff --git a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
index cae09ea3..3f9cc83b 100644
--- a/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
+++ b/services/core/java/com/android/server/rollback/AppDataRollbackHelper.java
@@ -23,17 +23,12 @@
import android.util.Slog;
import android.util.SparseLongArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.Installer;
import com.android.server.pm.Installer.InstallerException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
/**
* Encapsulates the logic for initiating userdata snapshots and rollbacks via installd.
@@ -56,6 +51,8 @@
* {@code userIds}. Updates said {@code packageRollbackInfo} with the inodes of the CE user data
* snapshot folders.
*/
+ @GuardedBy("rollback.getLock")
+ // TODO(b/136241838): Move into Rollback and synchronize there.
public void snapshotAppData(
int snapshotId, PackageRollbackInfo packageRollbackInfo, int[] userIds) {
for (int user : userIds) {
@@ -92,6 +89,8 @@
* to {@code packageRollbackInfo} are restricted to the removal or addition of {@code
* userId} to the list of pending backups or restores.
*/
+ @GuardedBy("rollback.getLock")
+ // TODO(b/136241838): Move into Rollback and synchronize there.
public boolean restoreAppData(int rollbackId, PackageRollbackInfo packageRollbackInfo,
int userId, int appId, String seInfo) {
int storageFlags = Installer.FLAG_STORAGE_DE;
@@ -135,6 +134,8 @@
* Deletes an app data snapshot with a given {@code rollbackId} for a specified package
* {@code packageName} for a given {@code user}.
*/
+ @GuardedBy("rollback.getLock")
+ // TODO(b/136241838): Move into Rollback and synchronize there.
public void destroyAppDataSnapshot(int rollbackId, PackageRollbackInfo packageRollbackInfo,
int user) {
int storageFlags = Installer.FLAG_STORAGE_DE;
@@ -156,141 +157,68 @@
}
/**
- * Computes the list of pending backups for {@code userId} given lists of rollbacks.
- * Packages pending backup for the given user are added to {@code pendingBackupPackages} along
- * with their corresponding {@code PackageRollbackInfo}.
+ * Commits the pending backups and restores for a given {@code userId} and {@code rollback}. If
+ * the rollback has a pending backup, it is updated with a mapping from {@code userId} to inode
+ * of the CE user data snapshot.
*
- * @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.
+ * @return true if any backups or restores were found for the userId
*/
- private static List<Rollback> computePendingBackups(int userId,
- Map<String, PackageRollbackInfo> pendingBackupPackages,
- List<Rollback> rollbacks) {
- List<Rollback> rollbacksWithPendingBackups = new ArrayList<>();
-
- 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 (rollbacksWithPendingBackups.indexOf(rollback) == -1) {
- rollbacksWithPendingBackups.add(rollback);
- }
- }
+ @GuardedBy("rollback.getLock")
+ boolean commitPendingBackupAndRestoreForUser(int userId, Rollback rollback) {
+ boolean foundBackupOrRestore = false;
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
+ boolean hasPendingBackup = false;
+ boolean hasPendingRestore = false;
+ final IntArray pendingBackupUsers = info.getPendingBackups();
+ if (pendingBackupUsers != null) {
+ if (pendingBackupUsers.indexOf(userId) != -1) {
+ hasPendingBackup = true;
+ foundBackupOrRestore = true;
}
}
- }
- return rollbacksWithPendingBackups;
- }
- /**
- * Computes the list of pending restores for {@code userId} given lists of rollbacks.
- * Packages pending restore are added to {@code pendingRestores} along with their corresponding
- * {@code PackageRollbackInfo}.
- *
- * @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<Rollback> computePendingRestores(int userId,
- Map<String, PackageRollbackInfo> pendingRestorePackages,
- List<Rollback> rollbacks) {
- List<Rollback> rollbacksWithPendingRestores = new ArrayList<>();
-
- 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 (rollbacksWithPendingRestores.indexOf(rollback) == -1) {
- rollbacksWithPendingRestores.add(rollback);
- }
- }
+ RestoreInfo ri = info.getRestoreInfo(userId);
+ if (ri != null) {
+ hasPendingRestore = true;
+ foundBackupOrRestore = true;
}
- }
- return rollbacksWithPendingRestores;
- }
-
- /**
- * 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 rollbacks with changes that should be stored on disk.
- */
- public Set<Rollback> commitPendingBackupAndRestoreForUser(int userId,
- List<Rollback> rollbacks) {
-
- final Map<String, PackageRollbackInfo> pendingBackupPackages = new HashMap<>();
- final List<Rollback> pendingBackups = computePendingBackups(userId,
- pendingBackupPackages, rollbacks);
-
- final Map<String, PackageRollbackInfo> pendingRestorePackages = new HashMap<>();
- final List<Rollback> pendingRestores = computePendingRestores(userId,
- pendingRestorePackages, rollbacks);
-
- // First remove unnecessary backups, i.e. when user did not unlock their phone between the
- // request to backup data and the request to restore it.
- Iterator<Map.Entry<String, PackageRollbackInfo>> iter =
- pendingBackupPackages.entrySet().iterator();
- while (iter.hasNext()) {
- PackageRollbackInfo backupPackage = iter.next().getValue();
- PackageRollbackInfo restorePackage =
- pendingRestorePackages.get(backupPackage.getPackageName());
- if (restorePackage != null) {
- backupPackage.removePendingBackup(userId);
- backupPackage.removePendingRestoreInfo(userId);
- iter.remove();
- pendingRestorePackages.remove(backupPackage.getPackageName());
+ if (hasPendingBackup && hasPendingRestore) {
+ // Remove unnecessary backup, i.e. when user did not unlock their phone between the
+ // request to backup data and the request to restore it.
+ info.removePendingBackup(userId);
+ info.removePendingRestoreInfo(userId);
+ continue;
}
- }
- if (!pendingBackupPackages.isEmpty()) {
- 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, rollback.info.getRollbackId(),
- Installer.FLAG_STORAGE_CE);
- info.putCeSnapshotInode(userId, ceSnapshotInode);
- pendingBackupUsers.remove(idx);
- } catch (InstallerException ie) {
- Slog.e(TAG,
- "Unable to create app data snapshot for: "
+ if (hasPendingBackup) {
+ int idx = pendingBackupUsers.indexOf(userId);
+ try {
+ long ceSnapshotInode = mInstaller.snapshotAppData(info.getPackageName(),
+ userId, rollback.info.getRollbackId(),
+ Installer.FLAG_STORAGE_CE);
+ info.putCeSnapshotInode(userId, ceSnapshotInode);
+ pendingBackupUsers.remove(idx);
+ } catch (InstallerException ie) {
+ Slog.e(TAG,
+ "Unable to create app data snapshot for: "
+ info.getPackageName() + ", userId: " + userId, ie);
- }
- }
+ }
+ }
+
+ if (hasPendingRestore) {
+ try {
+ mInstaller.restoreAppDataSnapshot(info.getPackageName(), ri.appId,
+ ri.seInfo, userId, rollback.info.getRollbackId(),
+ Installer.FLAG_STORAGE_CE);
+ info.removeRestoreInfo(ri);
+ } catch (InstallerException ie) {
+ Slog.e(TAG, "Unable to restore app data snapshot for: "
+ + info.getPackageName(), ie);
}
}
}
-
- if (!pendingRestorePackages.isEmpty()) {
- 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, rollback.info.getRollbackId(),
- Installer.FLAG_STORAGE_CE);
- info.removeRestoreInfo(ri);
- } catch (InstallerException ie) {
- Slog.e(TAG, "Unable to restore app data snapshot for: "
- + info.getPackageName(), ie);
- }
- }
- }
- }
- }
-
- final Set<Rollback> changed = new HashSet<>(pendingBackups);
- changed.addAll(pendingRestores);
- return changed;
+ return foundBackupOrRestore;
}
/**
diff --git a/services/core/java/com/android/server/rollback/Rollback.java b/services/core/java/com/android/server/rollback/Rollback.java
index 6769fe0..2dc4951 100644
--- a/services/core/java/com/android/server/rollback/Rollback.java
+++ b/services/core/java/com/android/server/rollback/Rollback.java
@@ -18,19 +18,27 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.content.rollback.PackageRollbackInfo;
import android.content.rollback.RollbackInfo;
+import com.android.internal.annotations.GuardedBy;
+
import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.ParseException;
import java.time.Instant;
import java.util.ArrayList;
+import java.util.List;
/**
- * Information about a rollback available for a set of atomically installed
- * packages.
+ * Information about a rollback available for a set of atomically installed packages.
+ *
+ * <p>When accessing the state of a Rollback object, the caller is responsible for synchronization.
+ * The lock object provided by {@link #getLock} should be acquired when accessing any of the mutable
+ * state of a Rollback, including from the {@link RollbackInfo} and any of the
+ * {@link PackageRollbackInfo} objects held within.
*/
class Rollback {
@IntDef(flag = true, prefix = { "ROLLBACK_STATE_" }, value = {
@@ -58,8 +66,18 @@
static final int ROLLBACK_STATE_COMMITTED = 3;
/**
- * The rollback info for this rollback.
+ * The session ID for the staged session if this rollback data represents a staged session,
+ * {@code -1} otherwise.
*/
+ private final int mStagedSessionId;
+
+ /**
+ * The rollback info for this rollback.
+ *
+ * <p>Any access to this field that touches any mutable state should be synchronized on
+ * {@link #getLock}.
+ */
+ @GuardedBy("getLock")
public final RollbackInfo info;
/**
@@ -74,23 +92,20 @@
* The timestamp is not applicable for all rollback states, but we make
* sure to keep it non-null to avoid potential errors there.
*/
+ @GuardedBy("mLock")
private @NonNull Instant mTimestamp;
/**
- * The session ID for the staged session if this rollback data represents a staged session,
- * {@code -1} otherwise.
- */
- private final int mStagedSessionId;
-
- /**
* The current state of the rollback.
* ENABLING, AVAILABLE, or COMMITTED.
*/
+ @GuardedBy("mLock")
private @RollbackState int mState;
/**
* The id of the post-reboot apk session for a staged install, if any.
*/
+ @GuardedBy("mLock")
private int mApkSessionId = -1;
/**
@@ -98,10 +113,17 @@
* for this rollback because it has just been committed but the rollback
* has not yet been fully applied.
*/
- // NOTE: All accesses to this field are from the RollbackManager handler thread.
+ @GuardedBy("mLock")
private boolean mRestoreUserDataInProgress = false;
/**
+ * Lock object to guard all access to Rollback state.
+ *
+ * @see #getLock
+ */
+ private final Object mLock = new Object();
+
+ /**
* Constructs a new, empty Rollback instance.
*
* @param rollbackId the id of the rollback.
@@ -135,8 +157,23 @@
}
/**
+ * Returns a lock object that should be acquired before accessing any Rollback state from
+ * {@link RollbackManagerServiceImpl}.
+ *
+ * <p>Note that while holding this lock, the lock for {@link RollbackManagerServiceImpl} should
+ * not be acquired (but it is ok to acquire this lock while already holding the lock for that
+ * class).
+ */
+ // TODO(b/136241838): Move rollback functionality into this class and synchronize on the lock
+ // internally. Remove this method once this has been done for all cases.
+ Object getLock() {
+ return mLock;
+ }
+
+ /**
* Whether the rollback is for rollback of a staged install.
*/
+ @GuardedBy("getLock")
boolean isStaged() {
return info.isStaged();
}
@@ -151,6 +188,7 @@
/**
* Returns the time when the upgrade occurred, for purposes of expiring rollback data.
*/
+ @GuardedBy("getLock")
Instant getTimestamp() {
return mTimestamp;
}
@@ -158,6 +196,7 @@
/**
* Sets the time at which upgrade occurred.
*/
+ @GuardedBy("getLock")
void setTimestamp(Instant timestamp) {
mTimestamp = timestamp;
}
@@ -173,6 +212,7 @@
/**
* Returns true if the rollback is in the ENABLING state.
*/
+ @GuardedBy("getLock")
boolean isEnabling() {
return mState == ROLLBACK_STATE_ENABLING;
}
@@ -180,6 +220,7 @@
/**
* Returns true if the rollback is in the AVAILABLE state.
*/
+ @GuardedBy("getLock")
boolean isAvailable() {
return mState == ROLLBACK_STATE_AVAILABLE;
}
@@ -187,6 +228,7 @@
/**
* Returns true if the rollback is in the COMMITTED state.
*/
+ @GuardedBy("getLock")
boolean isCommitted() {
return mState == ROLLBACK_STATE_COMMITTED;
}
@@ -194,6 +236,7 @@
/**
* Sets the state of the rollback to AVAILABLE.
*/
+ @GuardedBy("getLock")
void setAvailable() {
mState = ROLLBACK_STATE_AVAILABLE;
}
@@ -201,6 +244,7 @@
/**
* Sets the state of the rollback to COMMITTED.
*/
+ @GuardedBy("getLock")
void setCommitted() {
mState = ROLLBACK_STATE_COMMITTED;
}
@@ -208,6 +252,7 @@
/**
* Returns the id of the post-reboot apk session for a staged install, if any.
*/
+ @GuardedBy("getLock")
int getApkSessionId() {
return mApkSessionId;
}
@@ -215,6 +260,7 @@
/**
* Sets the id of the post-reboot apk session for a staged install.
*/
+ @GuardedBy("getLock")
void setApkSessionId(int apkSessionId) {
mApkSessionId = apkSessionId;
}
@@ -223,6 +269,7 @@
* Returns true if we are expecting the package manager to call restoreUserData for this
* rollback because it has just been committed but the rollback has not yet been fully applied.
*/
+ @GuardedBy("getLock")
boolean isRestoreUserDataInProgress() {
return mRestoreUserDataInProgress;
}
@@ -231,10 +278,65 @@
* Sets whether we are expecting the package manager to call restoreUserData for this
* rollback because it has just been committed but the rollback has not yet been fully applied.
*/
+ @GuardedBy("getLock")
void setRestoreUserDataInProgress(boolean restoreUserDataInProgress) {
mRestoreUserDataInProgress = restoreUserDataInProgress;
}
+ /**
+ * Returns true if this rollback includes the package with the provided {@code packageName}.
+ */
+ @GuardedBy("getLock")
+ boolean includesPackage(String packageName) {
+ for (PackageRollbackInfo info : info.getPackages()) {
+ if (info.getPackageName().equals(packageName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this rollback includes the package with the provided {@code packageName}
+ * with a <i>version rolled back from</i> that is not {@code versionCode}.
+ */
+ @GuardedBy("getLock")
+ boolean includesPackageWithDifferentVersion(String packageName, long versionCode) {
+ for (PackageRollbackInfo info : info.getPackages()) {
+ if (info.getPackageName().equals(packageName)
+ && info.getVersionRolledBackFrom().getLongVersionCode() != versionCode) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns a list containing the names of all the packages included in this rollback.
+ */
+ @GuardedBy("getLock")
+ List<String> getPackageNames() {
+ List<String> result = new ArrayList<>();
+ for (PackageRollbackInfo info : info.getPackages()) {
+ result.add(info.getPackageName());
+ }
+ return result;
+ }
+
+ /**
+ * Returns a list containing the names of all the apex packages included in this rollback.
+ */
+ @GuardedBy("getLock")
+ List<String> getApexPackageNames() {
+ List<String> result = new ArrayList<>();
+ for (PackageRollbackInfo info : info.getPackages()) {
+ if (info.isApex()) {
+ result.add(info.getPackageName());
+ }
+ }
+ return result;
+ }
+
static String rollbackStateToString(@RollbackState int state) {
switch (state) {
case Rollback.ROLLBACK_STATE_ENABLING: return "enabling";
@@ -254,6 +356,7 @@
throw new ParseException("Invalid rollback state: " + state, 0);
}
+ @GuardedBy("getLock")
String getStateAsString() {
return rollbackStateToString(mState);
}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 96d284b..e8e448a 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -282,8 +282,10 @@
List<RollbackInfo> rollbacks = new ArrayList<>();
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback rollback = mRollbacks.get(i);
- if (rollback.isAvailable()) {
- rollbacks.add(rollback.info);
+ synchronized (rollback.getLock()) {
+ if (rollback.isAvailable()) {
+ rollbacks.add(rollback.info);
+ }
}
}
return new ParceledListSlice<>(rollbacks);
@@ -298,8 +300,10 @@
List<RollbackInfo> rollbacks = new ArrayList<>();
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback rollback = mRollbacks.get(i);
- if (rollback.isCommitted()) {
- rollbacks.add(rollback.info);
+ synchronized (rollback.getLock()) {
+ if (rollback.isCommitted()) {
+ rollbacks.add(rollback.info);
+ }
}
}
return new ParceledListSlice<>(rollbacks);
@@ -332,8 +336,11 @@
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
- rollback.setTimestamp(rollback.getTimestamp().plusMillis(timeDifference));
- saveRollback(rollback);
+ synchronized (rollback.getLock()) {
+ rollback.setTimestamp(
+ rollback.getTimestamp().plusMillis(timeDifference));
+ saveRollback(rollback);
+ }
}
}
}
@@ -358,86 +365,94 @@
Slog.i(TAG, "Initiating rollback");
Rollback rollback = getRollbackForId(rollbackId);
- if (rollback == null || !rollback.isAvailable()) {
+ if (rollback == null) {
sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
"Rollback unavailable");
return;
}
-
- // Get a context for the caller to use to install the downgraded
- // version of the package.
- final Context context;
- try {
- context = mContext.createPackageContext(callerPackageName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
- "Invalid callerPackageName");
- return;
- }
-
- PackageManager pm = context.getPackageManager();
- try {
- PackageInstaller packageInstaller = pm.getPackageInstaller();
- PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
- PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- parentParams.setRequestDowngrade(true);
- parentParams.setMultiPackage();
- if (rollback.isStaged()) {
- parentParams.setStaged();
+ synchronized (rollback.getLock()) {
+ if (!rollback.isAvailable()) {
+ sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_ROLLBACK_UNAVAILABLE,
+ "Rollback unavailable");
+ return;
}
- int parentSessionId = packageInstaller.createSession(parentParams);
- PackageInstaller.Session parentSession = packageInstaller.openSession(parentSessionId);
+ // Get a context for the caller to use to install the downgraded
+ // version of the package.
+ final Context context;
+ try {
+ context = mContext.createPackageContext(callerPackageName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
+ "Invalid callerPackageName");
+ return;
+ }
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageManager pm = context.getPackageManager();
+ try {
+ PackageInstaller packageInstaller = pm.getPackageInstaller();
+ PackageInstaller.SessionParams parentParams = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
- // TODO: We can't get the installerPackageName for apex
- // (b/123920130). Is it okay to ignore the installer package
- // for apex?
- if (!info.isApex()) {
- String installerPackageName = pm.getInstallerPackageName(info.getPackageName());
- if (installerPackageName != null) {
- params.setInstallerPackageName(installerPackageName);
- }
- }
- params.setRequestDowngrade(true);
- params.setRequiredInstalledVersionCode(
- info.getVersionRolledBackFrom().getLongVersionCode());
+ parentParams.setRequestDowngrade(true);
+ parentParams.setMultiPackage();
if (rollback.isStaged()) {
- params.setStaged();
- }
- if (info.isApex()) {
- params.setInstallAsApex();
- }
- int sessionId = packageInstaller.createSession(params);
- PackageInstaller.Session session = packageInstaller.openSession(sessionId);
- File[] packageCodePaths = RollbackStore.getPackageCodePaths(
- rollback, info.getPackageName());
- if (packageCodePaths == null) {
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
- "Backup copy of package inaccessible");
- return;
+ parentParams.setStaged();
}
- for (File packageCodePath : packageCodePaths) {
- try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
- ParcelFileDescriptor.MODE_READ_ONLY)) {
- final long token = Binder.clearCallingIdentity();
- try {
- session.write(packageCodePath.getName(), 0, packageCodePath.length(),
- fd);
- } finally {
- Binder.restoreCallingIdentity(token);
+ int parentSessionId = packageInstaller.createSession(parentParams);
+ PackageInstaller.Session parentSession = packageInstaller.openSession(
+ parentSessionId);
+
+ 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
+ // (b/123920130). Is it okay to ignore the installer package
+ // for apex?
+ if (!info.isApex()) {
+ String installerPackageName =
+ pm.getInstallerPackageName(info.getPackageName());
+ if (installerPackageName != null) {
+ params.setInstallerPackageName(installerPackageName);
}
}
- }
- parentSession.addChildSessionId(sessionId);
- }
+ params.setRequestDowngrade(true);
+ params.setRequiredInstalledVersionCode(
+ info.getVersionRolledBackFrom().getLongVersionCode());
+ if (rollback.isStaged()) {
+ params.setStaged();
+ }
+ if (info.isApex()) {
+ params.setInstallAsApex();
+ }
+ int sessionId = packageInstaller.createSession(params);
+ PackageInstaller.Session session = packageInstaller.openSession(sessionId);
+ File[] packageCodePaths = RollbackStore.getPackageCodePaths(
+ rollback, info.getPackageName());
+ if (packageCodePaths == null) {
+ sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
+ "Backup copy of package inaccessible");
+ return;
+ }
- final LocalIntentReceiver receiver = new LocalIntentReceiver(
- (Intent result) -> {
- getHandler().post(() -> {
+ for (File packageCodePath : packageCodePaths) {
+ try (ParcelFileDescriptor fd = ParcelFileDescriptor.open(packageCodePath,
+ ParcelFileDescriptor.MODE_READ_ONLY)) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ session.write(packageCodePath.getName(), 0,
+ packageCodePath.length(),
+ fd);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+ parentSession.addChildSessionId(sessionId);
+ }
+
+ final LocalIntentReceiver receiver = new LocalIntentReceiver(
+ (Intent result) -> getHandler().post(() -> {
int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
PackageInstaller.STATUS_FAILURE);
@@ -450,21 +465,22 @@
// TODO: Should we just kill this rollback if
// commit failed? Why would we expect commit
// not to fail again?
- synchronized (mLock) {
- // TODO: Could this cause a rollback to be
- // resurrected if it should otherwise have
- // expired by now?
+ // TODO: Could this cause a rollback to be
+ // resurrected if it should otherwise have
+ // expired by now?
+ synchronized (rollback.getLock()) {
rollback.setAvailable();
rollback.setRestoreUserDataInProgress(false);
}
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE_INSTALL,
+ sendFailure(statusReceiver,
+ RollbackManager.STATUS_FAILURE_INSTALL,
"Rollback downgrade install failed: "
- + result.getStringExtra(
+ + result.getStringExtra(
PackageInstaller.EXTRA_STATUS_MESSAGE));
return;
}
- synchronized (mLock) {
+ synchronized (rollback.getLock()) {
if (!rollback.isStaged()) {
// All calls to restoreUserData should have
// completed by now for a non-staged install.
@@ -473,32 +489,31 @@
rollback.info.setCommittedSessionId(parentSessionId);
rollback.info.getCausePackages().addAll(causePackages);
+ RollbackStore.deletePackageCodePaths(rollback);
+ saveRollback(rollback);
}
- mRollbackStore.deletePackageCodePaths(rollback);
- saveRollback(rollback);
sendSuccess(statusReceiver);
Intent broadcast = new Intent(Intent.ACTION_ROLLBACK_COMMITTED);
for (UserInfo userInfo : UserManager.get(mContext).getUsers(true)) {
- mContext.sendBroadcastAsUser(broadcast, userInfo.getUserHandle(),
+ mContext.sendBroadcastAsUser(broadcast,
+ userInfo.getUserHandle(),
Manifest.permission.MANAGE_ROLLBACKS);
}
- });
- }
- );
+ })
+ );
- synchronized (mLock) {
rollback.setCommitted();
rollback.setRestoreUserDataInProgress(true);
+ parentSession.commit(receiver.getIntentSender());
+ } catch (IOException e) {
+ Slog.e(TAG, "Rollback failed", e);
+ sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
+ "IOException: " + e.toString());
+ return;
}
- parentSession.commit(receiver.getIntentSender());
- } catch (IOException e) {
- Slog.e(TAG, "Rollback failed", e);
- sendFailure(statusReceiver, RollbackManager.STATUS_FAILURE,
- "IOException: " + e.toString());
- return;
}
}
@@ -534,19 +549,17 @@
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- if (info.getPackageName().equals(packageName)) {
+ synchronized (rollback.getLock()) {
+ if (rollback.includesPackage(packageName)) {
iter.remove();
deleteRollback(rollback);
- break;
}
}
}
for (NewRollback newRollback : mNewRollbacks) {
- for (PackageRollbackInfo info : newRollback.rollback.info.getPackages()) {
- if (info.getPackageName().equals(packageName)) {
+ synchronized (newRollback.rollback.getLock()) {
+ if (newRollback.rollback.includesPackage(packageName)) {
newRollback.isCancelled = true;
- break;
}
}
}
@@ -578,12 +591,16 @@
rollbacks = new ArrayList<>(mRollbacks);
}
- final Set<Rollback> changed =
- mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(userId, rollbacks);
-
- for (Rollback rollback : changed) {
- saveRollback(rollback);
+ for (int i = 0; i < rollbacks.size(); i++) {
+ Rollback rollback = rollbacks.get(i);
+ synchronized (rollback.getLock()) {
+ if (mAppDataRollbackHelper.commitPendingBackupAndRestoreForUser(
+ userId, rollback)) {
+ saveRollback(rollback);
+ }
+ }
}
+
latch.countDown();
});
@@ -617,17 +634,15 @@
Set<String> apexPackageNames = new HashSet<>();
synchronized (mLock) {
for (Rollback rollback : mRollbacks) {
- if (rollback.isStaged()) {
- if (rollback.isEnabling()) {
- enabling.add(rollback);
- } else if (rollback.isRestoreUserDataInProgress()) {
- restoreInProgress.add(rollback);
- }
-
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- if (info.isApex()) {
- apexPackageNames.add(info.getPackageName());
+ synchronized (rollback.getLock()) {
+ if (rollback.isStaged()) {
+ if (rollback.isEnabling()) {
+ enabling.add(rollback);
+ } else if (rollback.isRestoreUserDataInProgress()) {
+ restoreInProgress.add(rollback);
}
+
+ apexPackageNames.addAll(rollback.getApexPackageNames());
}
}
}
@@ -635,30 +650,32 @@
for (Rollback rollback : enabling) {
PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
- PackageInstaller.SessionInfo session =
- installer.getSessionInfo(rollback.getStagedSessionId());
- 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(rollback);
- } else if (session.isStagedSessionApplied()) {
- makeRollbackAvailable(rollback);
+ synchronized (rollback.getLock()) {
+ PackageInstaller.SessionInfo session =
+ installer.getSessionInfo(rollback.getStagedSessionId());
+ 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(rollback);
+ } else if (session.isStagedSessionApplied()) {
+ makeRollbackAvailable(rollback);
+ }
}
}
for (Rollback rollback : restoreInProgress) {
PackageInstaller installer = mContext.getPackageManager().getPackageInstaller();
- PackageInstaller.SessionInfo session =
- installer.getSessionInfo(rollback.getStagedSessionId());
- // TODO: What if session is null?
- if (session != null) {
- if (session.isStagedSessionApplied() || session.isStagedSessionFailed()) {
- synchronized (mLock) {
+ synchronized (rollback.getLock()) {
+ PackageInstaller.SessionInfo session =
+ installer.getSessionInfo(rollback.getStagedSessionId());
+ // TODO: What if session is null?
+ if (session != null) {
+ if (session.isStagedSessionApplied() || session.isStagedSessionFailed()) {
rollback.setRestoreUserDataInProgress(false);
+ saveRollback(rollback);
}
- saveRollback(rollback);
}
}
}
@@ -687,23 +704,19 @@
private void onPackageReplaced(String packageName) {
// TODO: Could this end up incorrectly deleting a rollback for a
// package that is about to be installed?
- VersionedPackage installedVersion = getInstalledPackageVersion(packageName);
+ long installedVersion = getInstalledPackageVersion(packageName);
synchronized (mLock) {
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
- // TODO: Should we remove rollbacks in the ENABLING state here?
- if (rollback.isEnabling() || rollback.isAvailable()) {
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- if (info.getPackageName().equals(packageName)
- && !packageVersionsEqual(
- info.getVersionRolledBackFrom(),
- installedVersion)) {
- iter.remove();
- deleteRollback(rollback);
- break;
- }
+ synchronized (rollback.getLock()) {
+ // TODO: Should we remove rollbacks in the ENABLING state here?
+ if ((rollback.isEnabling() || rollback.isAvailable())
+ && rollback.includesPackageWithDifferentVersion(packageName,
+ installedVersion)) {
+ iter.remove();
+ deleteRollback(rollback);
}
}
}
@@ -760,16 +773,18 @@
Iterator<Rollback> iter = mRollbacks.iterator();
while (iter.hasNext()) {
Rollback rollback = iter.next();
- if (!rollback.isAvailable()) {
- continue;
- }
- if (!now.isBefore(
+ synchronized (rollback.getLock()) {
+ if (!rollback.isAvailable()) {
+ continue;
+ }
+ if (!now.isBefore(
rollback.getTimestamp()
.plusMillis(mRollbackLifetimeDurationInMillis))) {
- iter.remove();
- deleteRollback(rollback);
- } else if (oldest == null || oldest.isAfter(rollback.getTimestamp())) {
- oldest = rollback.getTimestamp();
+ iter.remove();
+ deleteRollback(rollback);
+ } else if (oldest == null || oldest.isAfter(rollback.getTimestamp())) {
+ oldest = rollback.getTimestamp();
+ }
}
}
}
@@ -877,10 +892,12 @@
synchronized (mLock) {
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback rollback = mRollbacks.get(i);
- if (rollback.getApkSessionId() == 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;
+ synchronized (rollback.getLock()) {
+ if (rollback.getApkSessionId() == 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;
+ }
}
}
}
@@ -979,6 +996,7 @@
new IntArray() /* pendingBackups */, new ArrayList<>() /* pendingRestores */,
isApex, new IntArray(), new SparseLongArray() /* ceSnapshotInodes */);
+
try {
ApplicationInfo appInfo = pkgInfo.applicationInfo;
RollbackStore.backupPackageCodePath(rollback, packageName, appInfo.sourceDir);
@@ -992,7 +1010,7 @@
return false;
}
- synchronized (mLock) {
+ synchronized (rollback.getLock()) {
rollback.info.getPackages().add(packageRollbackInfo);
}
return true;
@@ -1020,27 +1038,31 @@
// staged installs
for (int i = 0; i < mRollbacks.size(); i++) {
Rollback rollback = mRollbacks.get(i);
- if (!rollback.isEnabling()) {
- continue;
- }
+ synchronized (rollback.getLock()) {
+ if (!rollback.isEnabling()) {
+ continue;
+ }
- for (PackageRollbackInfo info : rollback.info.getPackages()) {
- if (info.getPackageName().equals(packageName)) {
- mAppDataRollbackHelper.snapshotAppData(
- rollback.info.getRollbackId(), info, userIds);
- saveRollback(rollback);
- break;
+ for (PackageRollbackInfo info : rollback.info.getPackages()) {
+ if (info.getPackageName().equals(packageName)) {
+ mAppDataRollbackHelper.snapshotAppData(
+ rollback.info.getRollbackId(), info, userIds);
+ saveRollback(rollback);
+ break;
+ }
}
}
}
// non-staged installs
PackageRollbackInfo info;
for (NewRollback rollback : mNewRollbacks) {
- info = getPackageRollbackInfo(rollback.rollback, packageName);
- if (info != null) {
- mAppDataRollbackHelper.snapshotAppData(
- rollback.rollback.info.getRollbackId(), info, userIds);
- saveRollback(rollback.rollback);
+ synchronized (rollback.rollback.getLock()) {
+ info = getPackageRollbackInfo(rollback.rollback, packageName);
+ if (info != null) {
+ mAppDataRollbackHelper.snapshotAppData(
+ rollback.rollback.info.getRollbackId(), info, userIds);
+ saveRollback(rollback.rollback);
+ }
}
}
}
@@ -1053,11 +1075,13 @@
synchronized (mLock) {
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback candidate = mRollbacks.get(i);
- if (candidate.isRestoreUserDataInProgress()) {
- info = getPackageRollbackInfo(candidate, packageName);
- if (info != null) {
- rollback = candidate;
- break;
+ synchronized (candidate.getLock()) {
+ if (candidate.isRestoreUserDataInProgress()) {
+ info = getPackageRollbackInfo(candidate, packageName);
+ if (info != null) {
+ rollback = candidate;
+ break;
+ }
}
}
}
@@ -1068,12 +1092,14 @@
}
for (int userId : userIds) {
- final boolean changedRollback = mAppDataRollbackHelper.restoreAppData(
- rollback.info.getRollbackId(), info, userId, appId, seInfo);
+ synchronized (rollback.getLock()) {
+ 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 (changedRollback) {
- saveRollback(rollback);
+ // We've updated metadata about this rollback, so save it to flash.
+ if (changedRollback) {
+ saveRollback(rollback);
+ }
}
}
}
@@ -1147,7 +1173,6 @@
for (int i = 0; i < mRollbacks.size(); ++i) {
Rollback candidate = mRollbacks.get(i);
if (candidate.getStagedSessionId() == originalSessionId) {
- candidate.setApkSessionId(apkSessionId);
rollback = candidate;
break;
}
@@ -1162,7 +1187,10 @@
}
if (rollback != null) {
- saveRollback(rollback);
+ synchronized (rollback.getLock()) {
+ rollback.setApkSessionId(apkSessionId);
+ saveRollback(rollback);
+ }
}
});
}
@@ -1207,18 +1235,18 @@
/**
* Gets the version of the package currently installed.
- * Returns null if the package is not currently installed.
+ * Returns -1 if the package is not currently installed.
*/
- private VersionedPackage getInstalledPackageVersion(String packageName) {
+ private long getInstalledPackageVersion(String packageName) {
PackageManager pm = mContext.getPackageManager();
PackageInfo pkgInfo = null;
try {
pkgInfo = getPackageInfo(packageName);
} catch (PackageManager.NameNotFoundException e) {
- return null;
+ return -1;
}
- return new VersionedPackage(packageName, pkgInfo.getLongVersionCode());
+ return pkgInfo.getLongVersionCode();
}
/**
@@ -1273,44 +1301,49 @@
if (newRollback != null) {
Rollback rollback = completeEnableRollback(newRollback, success);
- if (rollback != null && !rollback.isStaged()) {
- makeRollbackAvailable(rollback);
+ if (rollback != null) {
+ synchronized (rollback.getLock()) {
+ if (!rollback.isStaged()) {
+ makeRollbackAvailable(rollback);
+ }
+ }
}
}
}
}
/**
- * Add a rollback to the list of rollbacks.
- * This should be called after rollback has been enabled for all packages
- * in the rollback. It does not make the rollback available yet.
+ * Add a rollback to the list of rollbacks. This should be called after rollback has been
+ * enabled for all packages in the rollback. It does not make the rollback available yet.
+ *
+ * <p>Note that no rollback-specific locks should be held when this method is called.
*
* @return the Rollback instance for a successfully enable-completed rollback,
* or null on error.
*/
private Rollback completeEnableRollback(NewRollback newRollback, boolean success) {
Rollback rollback = newRollback.rollback;
- if (!success) {
- // The install session was aborted, clean up the pending install.
- deleteRollback(rollback);
- return null;
- }
- if (newRollback.isCancelled) {
- Slog.e(TAG, "Rollback has been cancelled by PackageManager");
- deleteRollback(rollback);
- return null;
- }
+ synchronized (rollback.getLock()) {
+ if (!success) {
+ // The install session was aborted, clean up the pending install.
+ deleteRollback(rollback);
+ return null;
+ }
+ if (newRollback.isCancelled) {
+ Slog.e(TAG, "Rollback has been cancelled by PackageManager");
+ deleteRollback(rollback);
+ return null;
+ }
- // It's safe to access rollback.info outside a synchronized block because
- // this is running on the handler thread and all changes to the
- // 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(rollback);
- return null;
- }
- saveRollback(rollback);
+ if (rollback.info.getPackages().size() != newRollback.packageSessionIds.length) {
+ Slog.e(TAG, "Failed to enable rollback for all packages in session.");
+ deleteRollback(rollback);
+ return null;
+ }
+
+ saveRollback(rollback);
+ }
synchronized (mLock) {
// Note: There is a small window of time between when
// the session has been committed by the package
@@ -1328,14 +1361,13 @@
return rollback;
}
+ @GuardedBy("rollback.getLock")
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) {
- rollback.setAvailable();
- rollback.setTimestamp(Instant.now());
- }
+ rollback.setAvailable();
+ rollback.setTimestamp(Instant.now());
saveRollback(rollback);
// TODO(zezeozue): Provide API to explicitly start observing instead
@@ -1343,11 +1375,7 @@
// should document in PackageInstaller.SessionParams#setEnableRollback
// 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 < rollback.info.getPackages().size(); i++) {
- packages.add(rollback.info.getPackages().get(i).getPackageName());
- }
- mPackageHealthObserver.startObservingHealth(packages,
+ mPackageHealthObserver.startObservingHealth(rollback.getPackageNames(),
mRollbackLifetimeDurationInMillis);
scheduleExpiration(mRollbackLifetimeDurationInMillis);
}
@@ -1372,6 +1400,7 @@
* Returns the {@code PackageRollbackInfo} associated with {@code packageName} from
* a specified {@code Rollback}.
*/
+ @GuardedBy("rollback.getLock")
private static PackageRollbackInfo getPackageRollbackInfo(Rollback rollback,
String packageName) {
for (PackageRollbackInfo info : rollback.info.getPackages()) {
@@ -1398,6 +1427,7 @@
throw new IllegalStateException("Failed to allocate rollback ID");
}
+ @GuardedBy("rollback.getLock")
private void deleteRollback(Rollback rollback) {
for (PackageRollbackInfo info : rollback.info.getPackages()) {
IntArray snapshottedUsers = info.getSnapshottedUsers();
@@ -1416,6 +1446,7 @@
* TODO: Double check we can't do a better job handling the IOException in
* a cases where this method is called.
*/
+ @GuardedBy("rollback.getLock")
private void saveRollback(Rollback rollback) {
try {
mRollbackStore.saveRollback(rollback);
@@ -1430,32 +1461,34 @@
IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
synchronized (mLock) {
for (Rollback rollback : mRollbacks) {
- RollbackInfo info = rollback.info;
- ipw.println(info.getRollbackId() + ":");
- ipw.increaseIndent();
- ipw.println("-state: " + rollback.getStateAsString());
- ipw.println("-timestamp: " + rollback.getTimestamp());
- if (rollback.getStagedSessionId() != -1) {
- ipw.println("-stagedSessionId: " + rollback.getStagedSessionId());
- }
- ipw.println("-packages:");
- ipw.increaseIndent();
- for (PackageRollbackInfo pkg : info.getPackages()) {
- ipw.println(pkg.getPackageName()
- + " " + pkg.getVersionRolledBackFrom().getLongVersionCode()
- + " -> " + pkg.getVersionRolledBackTo().getLongVersionCode());
- }
- ipw.decreaseIndent();
- if (rollback.isCommitted()) {
- ipw.println("-causePackages:");
+ synchronized (rollback.getLock()) {
+ RollbackInfo info = rollback.info;
+ ipw.println(info.getRollbackId() + ":");
ipw.increaseIndent();
- for (VersionedPackage cPkg : info.getCausePackages()) {
- ipw.println(cPkg.getPackageName() + " " + cPkg.getLongVersionCode());
+ ipw.println("-state: " + rollback.getStateAsString());
+ ipw.println("-timestamp: " + rollback.getTimestamp());
+ if (rollback.getStagedSessionId() != -1) {
+ ipw.println("-stagedSessionId: " + rollback.getStagedSessionId());
+ }
+ ipw.println("-packages:");
+ ipw.increaseIndent();
+ for (PackageRollbackInfo pkg : info.getPackages()) {
+ ipw.println(pkg.getPackageName()
+ + " " + pkg.getVersionRolledBackFrom().getLongVersionCode()
+ + " -> " + pkg.getVersionRolledBackTo().getLongVersionCode());
}
ipw.decreaseIndent();
- ipw.println("-committedSessionId: " + info.getCommittedSessionId());
+ if (rollback.isCommitted()) {
+ ipw.println("-causePackages:");
+ ipw.increaseIndent();
+ for (VersionedPackage cPkg : info.getCausePackages()) {
+ ipw.println(cPkg.getPackageName() + " " + cPkg.getLongVersionCode());
+ }
+ ipw.decreaseIndent();
+ ipw.println("-committedSessionId: " + info.getCommittedSessionId());
+ }
+ ipw.decreaseIndent();
}
- ipw.decreaseIndent();
}
}
}
@@ -1516,7 +1549,8 @@
}
}
- NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
+ @GuardedBy("mLock")
+ private NewRollback createNewRollbackLocked(PackageInstaller.SessionInfo parentSession) {
int rollbackId = allocateRollbackIdLocked();
final Rollback rollback;
int parentSessionId = parentSession.getSessionId();
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 772c53f..b6d1f18 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -27,6 +27,8 @@
import android.util.Slog;
import android.util.SparseLongArray;
+import com.android.internal.annotations.GuardedBy;
+
import libcore.io.IoUtils;
import org.json.JSONArray;
@@ -250,6 +252,7 @@
/**
* Saves the given rollback to persistent storage.
*/
+ @GuardedBy("rollback.getLock")
void saveRollback(Rollback rollback) throws IOException {
try {
JSONObject dataJson = new JSONObject();
diff --git a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java b/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
index 3076284..d49b958 100644
--- a/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
+++ b/services/core/java/com/android/server/stats/ProcfsMemoryUtil.java
@@ -34,6 +34,12 @@
private static final Pattern RSS_HIGH_WATER_MARK_IN_KILOBYTES =
Pattern.compile("VmHWM:\\s*(\\d+)\\s*kB");
+ private static final Pattern RSS_IN_KILOBYTES =
+ Pattern.compile("VmRSS:\\s*(\\d+)\\s*kB");
+ private static final Pattern ANON_RSS_IN_KILOBYTES =
+ Pattern.compile("RssAnon:\\s*(\\d+)\\s*kB");
+ private static final Pattern SWAP_IN_KILOBYTES =
+ Pattern.compile("VmSwap:\\s*(\\d+)\\s*kB");
private ProcfsMemoryUtil() {}
@@ -52,16 +58,25 @@
*/
@VisibleForTesting
static int parseVmHWMFromStatus(String contents) {
- if (contents.isEmpty()) {
- return 0;
- }
- final Matcher matcher = RSS_HIGH_WATER_MARK_IN_KILOBYTES.matcher(contents);
- try {
- return matcher.find() ? Integer.parseInt(matcher.group(1)) : 0;
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Failed to parse value", e);
- return 0;
- }
+ return tryParseInt(contents, RSS_HIGH_WATER_MARK_IN_KILOBYTES);
+ }
+
+ /**
+ * Reads memory stat of a process from procfs. Returns values of the VmRss, AnonRSS, VmSwap
+ * fields in /proc/pid/status in kilobytes or 0 if not available.
+ */
+ static MemorySnapshot readMemorySnapshotFromProcfs(int pid) {
+ final String statusPath = String.format(Locale.US, STATUS_FILE_FMT, pid);
+ return parseMemorySnapshotFromStatus(readFile(statusPath));
+ }
+
+ @VisibleForTesting
+ static MemorySnapshot parseMemorySnapshotFromStatus(String contents) {
+ final MemorySnapshot snapshot = new MemorySnapshot();
+ snapshot.rssInKilobytes = tryParseInt(contents, RSS_IN_KILOBYTES);
+ snapshot.anonRssInKilobytes = tryParseInt(contents, ANON_RSS_IN_KILOBYTES);
+ snapshot.swapInKilobytes = tryParseInt(contents, SWAP_IN_KILOBYTES);
+ return snapshot;
}
private static String readFile(String path) {
@@ -72,4 +87,27 @@
return "";
}
}
+
+ private static int tryParseInt(String contents, Pattern pattern) {
+ if (contents.isEmpty()) {
+ return 0;
+ }
+ final Matcher matcher = pattern.matcher(contents);
+ try {
+ return matcher.find() ? Integer.parseInt(matcher.group(1)) : 0;
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Failed to parse value", e);
+ return 0;
+ }
+ }
+
+ static final class MemorySnapshot {
+ public int rssInKilobytes;
+ public int anonRssInKilobytes;
+ public int swapInKilobytes;
+
+ boolean isEmpty() {
+ return (anonRssInKilobytes + swapInKilobytes) == 0;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index 19b8055..e1a48ed 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -29,6 +29,7 @@
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromProcfs;
import static com.android.server.stats.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.stats.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
+import static com.android.server.stats.ProcfsMemoryUtil.readMemorySnapshotFromProcfs;
import static com.android.server.stats.ProcfsMemoryUtil.readRssHighWaterMarkFromProcfs;
import android.annotation.NonNull;
@@ -141,6 +142,7 @@
import com.android.server.am.MemoryStatUtil.MemoryStat;
import com.android.server.role.RoleManagerInternal;
import com.android.server.stats.IonMemoryUtil.IonAllocations;
+import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot;
import com.android.server.storage.DiskStatsFileLogger;
import com.android.server.storage.DiskStatsLoggingService;
@@ -1270,6 +1272,47 @@
SystemProperties.set("sys.rss_hwm_reset.on", "1");
}
+ private void pullProcessMemorySnapshot(
+ int tagId, long elapsedNanos, long wallClockNanos,
+ List<StatsLogEventWrapper> pulledData) {
+ List<ProcessMemoryState> managedProcessList =
+ LocalServices.getService(
+ ActivityManagerInternal.class).getMemoryStateForProcesses();
+ for (ProcessMemoryState managedProcess : managedProcessList) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(managedProcess.uid);
+ e.writeString(managedProcess.processName);
+ e.writeInt(managedProcess.pid);
+ e.writeInt(managedProcess.oomScore);
+ final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(managedProcess.pid);
+ if (snapshot.isEmpty()) {
+ continue;
+ }
+ e.writeInt(snapshot.rssInKilobytes);
+ e.writeInt(snapshot.anonRssInKilobytes);
+ e.writeInt(snapshot.swapInKilobytes);
+ e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes);
+ pulledData.add(e);
+ }
+ int[] pids = getPidsForCommands(MEMORY_INTERESTING_NATIVE_PROCESSES);
+ for (int pid : pids) {
+ StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+ e.writeInt(getUidForPid(pid));
+ e.writeString(readCmdlineFromProcfs(pid));
+ e.writeInt(pid);
+ e.writeInt(-1001); // Placeholder for native processes, OOM_SCORE_ADJ_MIN - 1.
+ final MemorySnapshot snapshot = readMemorySnapshotFromProcfs(pid);
+ if (snapshot.isEmpty()) {
+ continue;
+ }
+ e.writeInt(snapshot.rssInKilobytes);
+ e.writeInt(snapshot.anonRssInKilobytes);
+ e.writeInt(snapshot.swapInKilobytes);
+ e.writeInt(snapshot.anonRssInKilobytes + snapshot.swapInKilobytes);
+ pulledData.add(e);
+ }
+ }
+
private void pullSystemIonHeapSize(
int tagId, long elapsedNanos, long wallClockNanos,
List<StatsLogEventWrapper> pulledData) {
@@ -2352,6 +2395,10 @@
pullProcessMemoryHighWaterMark(tagId, elapsedNanos, wallClockNanos, ret);
break;
}
+ case StatsLog.PROCESS_MEMORY_SNAPSHOT: {
+ pullProcessMemorySnapshot(tagId, elapsedNanos, wallClockNanos, ret);
+ break;
+ }
case StatsLog.SYSTEM_ION_HEAP_SIZE: {
pullSystemIonHeapSize(tagId, elapsedNanos, wallClockNanos, ret);
break;
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index 6830ade..ec36a82 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -181,9 +181,8 @@
*/
@Override
public long interceptKeyBeforeDispatching(
- IBinder focus, KeyEvent event, int policyFlags) {
- WindowState windowState = mService.windowForClientLocked(null, focus, false);
- return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
+ IBinder focusedToken, KeyEvent event, int policyFlags) {
+ return mService.mPolicy.interceptKeyBeforeDispatching(focusedToken, event, policyFlags);
}
/**
@@ -192,9 +191,8 @@
*/
@Override
public KeyEvent dispatchUnhandledKey(
- IBinder focus, KeyEvent event, int policyFlags) {
- WindowState windowState = mService.windowForClientLocked(null, focus, false);
- return mService.mPolicy.dispatchUnhandledKey(windowState, event, policyFlags);
+ IBinder focusedToken, KeyEvent event, int policyFlags) {
+ return mService.mPolicy.dispatchUnhandledKey(focusedToken, event, policyFlags);
}
/** Callback to get pointer layer. */
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index dd9000e..8e0531c 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -528,6 +528,10 @@
populateInputWindowHandle(
inputWindowHandle, w, flags, type, isVisible, hasFocus, hasWallpaper);
+ // register key interception info
+ mService.mKeyInterceptionInfoForToken.put(inputWindowHandle.token,
+ w.getKeyInterceptionInfo());
+
if (w.mWinAnimator.hasSurface()) {
mInputTransaction.setInputWindowInfo(
w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 85ba806..5e14087 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -611,7 +611,7 @@
@Override
public SurfaceControl getAnimationLeashParent() {
- if (!WindowManagerService.sHierarchicalAnimations) {
+ if (WindowManagerService.sHierarchicalAnimations) {
return super.getAnimationLeashParent();
}
// Currently, only the recents animation will create animation leashes for tasks. In this
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 750926f..aa2a964 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -30,6 +30,7 @@
import android.view.MagnificationSpec;
import android.view.WindowInfo;
+import com.android.internal.policy.KeyInterceptionInfo;
import com.android.server.input.InputManagerService;
import com.android.server.policy.WindowManagerPolicy;
@@ -519,4 +520,10 @@
*/
public abstract boolean isTouchableDisplay(int displayId);
+ /**
+ * Returns the info associated with the input token used to determine if a key should be
+ * intercepted. This info can be accessed without holding the global wm lock.
+ */
+ public abstract @Nullable KeyInterceptionInfo
+ getKeyInterceptionInfoFromToken(IBinder inputToken);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 14214b4..d2d6902 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -248,6 +248,7 @@
import com.android.internal.os.IResultReceiver;
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IShortcutService;
+import com.android.internal.policy.KeyInterceptionInfo;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.LatencyTracker;
@@ -284,8 +285,10 @@
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Date;
import java.util.List;
+import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -407,6 +410,14 @@
int mVr2dDisplayId = INVALID_DISPLAY;
boolean mVrModeEnabled = false;
+ /**
+ * Tracks a map of input tokens to info that is used to decide whether to intercept
+ * a key event.
+ */
+ final Map<IBinder, KeyInterceptionInfo> mKeyInterceptionInfoForToken =
+ Collections.synchronizedMap(new ArrayMap<>());
+
+
private final IVrStateCallbacks mVrStateCallbacks = new IVrStateCallbacks.Stub() {
@Override
public void onVrStateChanged(boolean enabled) {
@@ -7360,6 +7371,11 @@
&& configuration.touchscreen == Configuration.TOUCHSCREEN_FINGER;
}
}
+
+ @Override
+ public @Nullable KeyInterceptionInfo getKeyInterceptionInfoFromToken(IBinder inputToken) {
+ return mKeyInterceptionInfoForToken.get(inputToken);
+ }
}
void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 501a93e..0a65e324 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -203,6 +203,7 @@
import android.view.animation.Interpolator;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.policy.KeyInterceptionInfo;
import com.android.internal.util.ToBooleanFunction;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
@@ -633,6 +634,7 @@
private @Nullable InsetsSourceProvider mInsetProvider;
private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
+ private KeyInterceptionInfo mKeyInterceptionInfo;
void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
@Rotation int rotation, boolean requested) {
@@ -2218,6 +2220,7 @@
mClientChannel.dispose();
mClientChannel = null;
}
+ mWmService.mKeyInterceptionInfoForToken.remove(mInputWindowHandle.token);
mInputWindowHandle.token = null;
}
@@ -5327,4 +5330,15 @@
proto.end(token);
}
}
+
+ KeyInterceptionInfo getKeyInterceptionInfo() {
+ if (mKeyInterceptionInfo == null
+ || mKeyInterceptionInfo.layoutParamsPrivateFlags != getAttrs().privateFlags
+ || mKeyInterceptionInfo.layoutParamsType != getAttrs().type
+ || mKeyInterceptionInfo.windowTitle != getWindowTag()) {
+ mKeyInterceptionInfo = new KeyInterceptionInfo(getAttrs().type, getAttrs().privateFlags,
+ getWindowTag().toString());
+ }
+ return mKeyInterceptionInfo;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 704c808..555968a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7389,8 +7389,7 @@
final long callingIdentity = mInjector.binderClearCallingIdentity();
try {
- mInjector.getIActivityManager().requestBugReport(
- ActivityManager.BUGREPORT_OPTION_REMOTE);
+ mInjector.getIActivityManager().requestRemoteBugReport();
mRemoteBugreportServiceIsActive.set(true);
mRemoteBugreportSharingAccepted.set(false);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
index 9926a09..322653b 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyboardInterceptorTest.java
@@ -28,12 +28,12 @@
import static org.mockito.Mockito.when;
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
+import android.os.IBinder;
import android.view.KeyEvent;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.policy.WindowManagerPolicy;
-import com.android.server.policy.WindowManagerPolicy.WindowState;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
@@ -79,7 +79,7 @@
@Test
public void whenVolumeKeyArrives_andPolicySaysUseIt_eventGoesToAms() {
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(0L);
mInterceptor.onKeyEvent(event, 0);
verify(mMockAms).notifyKeyEvent(argThat(matchesKeyEvent(event)), eq(0));
@@ -88,7 +88,7 @@
@Test
public void whenVolumeKeyArrives_andPolicySaysDropIt_eventDropped() {
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(-1L);
mInterceptor.onKeyEvent(event, 0);
verify(mMockAms, times(0)).notifyKeyEvent(anyObject(), anyInt());
@@ -98,14 +98,14 @@
@Test
public void whenVolumeKeyArrives_andPolicySaysDelayThenUse_eventQueuedThenSentToAms() {
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_UP);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(150L);
mInterceptor.onKeyEvent(event, 0);
assertTrue(mHandler.hasMessages());
verify(mMockAms, times(0)).notifyKeyEvent(anyObject(), anyInt());
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(0L);
mHandler.sendAllMessages();
@@ -115,14 +115,14 @@
@Test
public void whenVolumeKeyArrives_andPolicySaysDelayThenDrop_eventQueuedThenDropped() {
KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_VOLUME_DOWN);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(150L);
mInterceptor.onKeyEvent(event, 0);
assertTrue(mHandler.hasMessages());
verify(mMockAms, times(0)).notifyKeyEvent(anyObject(), anyInt());
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(event)), eq(0))).thenReturn(-1L);
mHandler.sendAllMessages();
@@ -137,18 +137,18 @@
new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP),
new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0)};
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[1])), eq(0))).thenReturn(150L);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[3])), eq(0))).thenReturn(75L);
for (KeyEvent event : events) {
mInterceptor.onKeyEvent(event, 0);
}
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[1])), eq(0))).thenReturn(0L);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[3])), eq(0))).thenReturn(0L);
mHandler.sendAllMessages();
@@ -167,18 +167,18 @@
new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_VOLUME_UP),
new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_0)};
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[1])), eq(0))).thenReturn(150L);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[3])), eq(0))).thenReturn(75L);
for (KeyEvent event : events) {
mInterceptor.onKeyEvent(event, 0);
}
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[1])), eq(0))).thenReturn(-1L);
- when(mMockPolicy.interceptKeyBeforeDispatching((WindowState) argThat(nullValue()),
+ when(mMockPolicy.interceptKeyBeforeDispatching((IBinder) argThat(nullValue()),
argThat(matchesKeyEvent(events[3])), eq(0))).thenReturn(-1L);
mHandler.sendAllMessages();
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index 29a8dad..5c2ad94 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -126,22 +126,6 @@
doTestConnectionDisconnectionReconnection(AudioService.BECOMING_NOISY_DELAY_MS / 2);
}
- /**
- * Verify connecting an A2DP sink will call into AudioService to unmute media
- */
- @Test
- public void testA2dpConnectionUnmutesMedia() throws Exception {
- Log.i(TAG, "testA2dpConnectionUnmutesMedia");
- Assert.assertNotNull("invalid null BT device", mFakeBtDevice);
-
- mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice,
- BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1);
- Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
- verify(mMockAudioService, times(1)).postAccessoryPlugMediaUnmute(
- ArgumentMatchers.eq(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
-
- }
-
private void doTestConnectionDisconnectionReconnection(int delayAfterDisconnection)
throws Exception {
when(mMockAudioService.getDeviceForStream(AudioManager.STREAM_MUSIC))
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
index fc7cfec..0a310d1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -118,17 +118,20 @@
String[] partitions = { "system", "vendor", "odm", "oem", "product", "system_ext" };
String[] appdir = { "app", "priv-app" };
for (int i = 0; i < partitions.length; i++) {
+ final PackageManagerService.SystemPartition systemPartition =
+ PackageManagerService.SYSTEM_PARTITIONS.get(i);
for (int j = 0; j < appdir.length; j++) {
String canonical = new File("/" + partitions[i]).getCanonicalPath();
String path = String.format("%s/%s/A.apk", canonical, appdir[j]);
- Assert.assertEquals(j == 1 && i != 3,
- PackageManagerService.locationIsPrivileged(path));
+ Assert.assertEquals(j == 1 && i != 3, systemPartition.containsPrivPath(path));
- Assert.assertEquals(i == 1 || i == 2, PackageManagerService.locationIsVendor(path));
- Assert.assertEquals(i == 3, PackageManagerService.locationIsOem(path));
- Assert.assertEquals(i == 4, PackageManagerService.locationIsProduct(path));
- Assert.assertEquals(i == 5, PackageManagerService.locationIsSystemExt(path));
+ final int scanFlag = systemPartition.scanFlag;
+ Assert.assertEquals(i == 1, scanFlag == PackageManagerService.SCAN_AS_VENDOR);
+ Assert.assertEquals(i == 2, scanFlag == PackageManagerService.SCAN_AS_ODM);
+ Assert.assertEquals(i == 3, scanFlag == PackageManagerService.SCAN_AS_OEM);
+ Assert.assertEquals(i == 4, scanFlag == PackageManagerService.SCAN_AS_PRODUCT);
+ Assert.assertEquals(i == 5, scanFlag == PackageManagerService.SCAN_AS_SYSTEM_EXT);
}
}
}
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 8cb5197..0b8c2a55 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/AppDataRollbackHelperTest.java
@@ -45,8 +45,6 @@
import java.io.File;
import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Set;
@RunWith(JUnit4.class)
public class AppDataRollbackHelperTest {
@@ -250,28 +248,22 @@
dataForRestore.info.getPackages().add(pendingRestore);
dataForRestore.info.getPackages().add(wasRecentlyRestored);
- Set<Rollback> changed = helper.commitPendingBackupAndRestoreForUser(37,
- Arrays.asList(dataWithPendingBackup, dataWithRecentRestore, dataForDifferentUser,
- dataForRestore));
InOrder inOrder = Mockito.inOrder(installer);
// Check that pending backup and restore for the same package mutually destroyed each other.
+ assertTrue(helper.commitPendingBackupAndRestoreForUser(37, dataWithRecentRestore));
assertEquals(-1, wasRecentlyRestored.getPendingBackups().indexOf(37));
assertNull(wasRecentlyRestored.getRestoreInfo(37));
// Check that backup was performed.
+ assertTrue(helper.commitPendingBackupAndRestoreForUser(37, dataWithPendingBackup));
inOrder.verify(installer).snapshotAppData(eq("com.foo"), eq(37), eq(101),
eq(Installer.FLAG_STORAGE_CE));
assertEquals(-1, pendingBackup.getPendingBackups().indexOf(37));
assertEquals(53, pendingBackup.getCeSnapshotInodes().get(37));
- // Check that changed returns correct Rollback.
- assertEquals(3, changed.size());
- assertTrue(changed.contains(dataWithPendingBackup));
- assertTrue(changed.contains(dataWithRecentRestore));
- assertTrue(changed.contains(dataForRestore));
-
// Check that restore was performed.
+ assertTrue(helper.commitPendingBackupAndRestoreForUser(37, dataForRestore));
inOrder.verify(installer).restoreAppDataSnapshot(
eq("com.abc"), eq(57) /* appId */, eq("seInfo"), eq(37) /* userId */,
eq(17239) /* rollbackId */, eq(Installer.FLAG_STORAGE_CE));
diff --git a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
index d27f1c7..b5925a6 100644
--- a/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
+++ b/services/tests/servicestests/src/com/android/server/rollback/RollbackUnitTest.java
@@ -18,11 +18,18 @@
import static com.google.common.truth.Truth.assertThat;
+import android.content.pm.VersionedPackage;
+import android.content.rollback.PackageRollbackInfo;
+import android.util.IntArray;
+import android.util.SparseLongArray;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
@RunWith(JUnit4.class)
public class RollbackUnitTest {
@@ -74,4 +81,62 @@
assertThat(rollback.isCommitted()).isTrue();
}
+ @Test
+ public void getPackageNamesAllAndJustApex() {
+ String pkg1 = "test.testpackage.pkg1";
+ String pkg2 = "test.testpackage.pkg2";
+ String pkg3 = "com.blah.hello.three";
+ String pkg4 = "com.something.4pack";
+
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
+ PackageRollbackInfo pkgInfo1 = pkgInfoFor(pkg1, 12, 10, false);
+ PackageRollbackInfo pkgInfo2 = pkgInfoFor(pkg2, 12, 10, true);
+ PackageRollbackInfo pkgInfo3 = pkgInfoFor(pkg3, 12, 10, false);
+ PackageRollbackInfo pkgInfo4 = pkgInfoFor(pkg4, 12, 10, true);
+
+ rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2, pkgInfo3, pkgInfo4));
+
+ assertThat(rollback.getPackageNames()).containsExactly(pkg1, pkg2, pkg3, pkg4);
+ assertThat(rollback.getApexPackageNames()).containsExactly(pkg2, pkg4);
+ }
+
+ @Test
+ public void includesPackages() {
+ String pkg1 = "test.testpackage.pkg1";
+ String pkg2 = "test.testpackage.pkg2";
+ String pkg3 = "com.blah.hello.three";
+ String pkg4 = "com.something.4pack";
+
+ Rollback rollback = new Rollback(123, new File("/test/testing"), -1);
+ PackageRollbackInfo pkgInfo1 = pkgInfoFor(pkg1, 12, 10, false);
+ PackageRollbackInfo pkgInfo2 = pkgInfoFor(pkg2, 18, 12, true);
+ PackageRollbackInfo pkgInfo3 = pkgInfoFor(pkg3, 157, 156, false);
+ PackageRollbackInfo pkgInfo4 = pkgInfoFor(pkg4, 99, 1, true);
+
+ rollback.info.getPackages().addAll(Arrays.asList(pkgInfo1, pkgInfo2, pkgInfo3, pkgInfo4));
+
+ assertThat(rollback.includesPackage(pkg2)).isTrue();
+ assertThat(rollback.includesPackage(pkg3)).isTrue();
+ assertThat(rollback.includesPackage("com.something.else")).isFalse();
+
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg1, 12)).isFalse();
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg1, 1)).isTrue();
+
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg2, 18)).isFalse();
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg2, 12)).isTrue();
+
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg3, 157)).isFalse();
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg3, 156)).isTrue();
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg3, 15)).isTrue();
+
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg4, 99)).isFalse();
+ assertThat(rollback.includesPackageWithDifferentVersion(pkg4, 100)).isTrue();
+ }
+
+ private static PackageRollbackInfo pkgInfoFor(
+ String packageName, long fromVersion, long toVersion, boolean isApex) {
+ return new PackageRollbackInfo(new VersionedPackage(packageName, fromVersion),
+ new VersionedPackage(packageName, toVersion),
+ new IntArray(), new ArrayList<>(), isApex, new IntArray(), new SparseLongArray());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java b/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java
index 4fb533f..ae57774 100644
--- a/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java
+++ b/services/tests/servicestests/src/com/android/server/stats/ProcfsMemoryUtilTest.java
@@ -15,12 +15,15 @@
*/
package com.android.server.stats;
+import static com.android.server.stats.ProcfsMemoryUtil.parseMemorySnapshotFromStatus;
import static com.android.server.stats.ProcfsMemoryUtil.parseVmHWMFromStatus;
import static com.google.common.truth.Truth.assertThat;
import androidx.test.filters.SmallTest;
+import com.android.server.stats.ProcfsMemoryUtil.MemorySnapshot;
+
import org.junit.Test;
/**
@@ -90,4 +93,32 @@
public void testParseVmHWMFromStatus_emptyContents() {
assertThat(parseVmHWMFromStatus("")).isEqualTo(0);
}
+
+ @Test
+ public void testParseMemorySnapshotFromStatus_parsesCorrectValue() {
+ MemorySnapshot snapshot = parseMemorySnapshotFromStatus(STATUS_CONTENTS);
+ assertThat(snapshot.rssInKilobytes).isEqualTo(126776);
+ assertThat(snapshot.anonRssInKilobytes).isEqualTo(37860);
+ assertThat(snapshot.swapInKilobytes).isEqualTo(22);
+ assertThat(snapshot.isEmpty()).isFalse();
+ }
+
+ @Test
+ public void testParseMemorySnapshotFromStatus_invalidValue() {
+ MemorySnapshot snapshot =
+ parseMemorySnapshotFromStatus("test\nVmRSS:\tx0x0x\nVmSwap:\t1 kB\ntest");
+ assertThat(snapshot.rssInKilobytes).isEqualTo(0);
+ assertThat(snapshot.anonRssInKilobytes).isEqualTo(0);
+ assertThat(snapshot.swapInKilobytes).isEqualTo(1);
+ assertThat(snapshot.isEmpty()).isFalse();
+ }
+
+ @Test
+ public void testParseMemorySnapshotFromStatus_emptyContents() {
+ MemorySnapshot snapshot = parseMemorySnapshotFromStatus("");
+ assertThat(snapshot.rssInKilobytes).isEqualTo(0);
+ assertThat(snapshot.anonRssInKilobytes).isEqualTo(0);
+ assertThat(snapshot.swapInKilobytes).isEqualTo(0);
+ assertThat(snapshot.isEmpty()).isTrue();
+ }
}
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 bb89446..761f73e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -167,12 +167,13 @@
}
@Override
- public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
+ public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
+ int policyFlags) {
return 0;
}
@Override
- public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
+ public KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags) {
return null;
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 090623e..e3183e3 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -340,6 +340,10 @@
mUserUnlockedStates.put(userId, true);
final UserUsageStatsService userService = getUserDataAndInitializeIfNeededLocked(
userId, System.currentTimeMillis());
+ if (userService == null) {
+ Slog.i(TAG, "Attempted to unlock stopped or removed user " + userId);
+ return;
+ }
userService.userUnlocked(System.currentTimeMillis());
// Process all the pending reported events
while (pendingEvents.peek() != null) {
@@ -456,7 +460,17 @@
"usagestats");
service = new UserUsageStatsService(getContext(), userId, usageStatsDir, this);
if (mUserUnlockedStates.get(userId)) {
- service.init(currentTimeMillis);
+ try {
+ service.init(currentTimeMillis);
+ } catch (Exception e) {
+ if (mUserManager.isUserUnlocked(userId)) {
+ throw e; // rethrow exception - user is unlocked
+ } else {
+ Slog.w(TAG, "Attempted to initialize service for "
+ + "stopped or removed user " + userId);
+ return null;
+ }
+ }
}
mUserState.put(userId, service);
}
@@ -779,6 +793,9 @@
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+ if (service == null) {
+ return; // user was stopped or removed
+ }
service.reportEvent(event);
mAppStandby.reportEvent(event, elapsedRealtime, userId);
@@ -841,6 +858,9 @@
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ if (service == null) {
+ return null; // user was stopped or removed
+ }
List<UsageStats> list = service.queryUsageStats(bucketType, beginTime, endTime);
if (list == null) {
return null;
@@ -873,6 +893,9 @@
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ if (service == null) {
+ return null; // user was stopped or removed
+ }
return service.queryConfigurationStats(bucketType, beginTime, endTime);
}
}
@@ -890,6 +913,9 @@
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ if (service == null) {
+ return null; // user was stopped or removed
+ }
return service.queryEventStats(bucketType, beginTime, endTime);
}
}
@@ -907,6 +933,9 @@
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ if (service == null) {
+ return null; // user was stopped or removed
+ }
return service.queryEvents(beginTime, endTime, shouldObfuscateInstantApps);
}
}
@@ -924,6 +953,9 @@
final UserUsageStatsService service =
getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+ if (service == null) {
+ return null; // user was stopped or removed
+ }
return service.queryEventsForPackage(beginTime, endTime, packageName, includeTaskRoot);
}
}
@@ -1113,7 +1145,15 @@
flushToDisk();
break;
case MSG_UNLOCKED_USER:
- onUserUnlocked(msg.arg1);
+ try {
+ onUserUnlocked(msg.arg1);
+ } catch (Exception e) {
+ if (mUserManager.isUserUnlocked(msg.arg1)) {
+ throw e; // rethrow exception - user is unlocked
+ } else {
+ Slog.w(TAG, "Attempted to unlock stopped or removed user " + msg.arg1);
+ }
+ }
break;
case MSG_REMOVE_USER:
onUserRemoved(msg.arg1);
@@ -1986,6 +2026,9 @@
if (user == UserHandle.USER_SYSTEM) {
final UserUsageStatsService userStats = getUserDataAndInitializeIfNeededLocked(
user, System.currentTimeMillis());
+ if (userStats == null) {
+ return null; // user was stopped or removed
+ }
return userStats.getBackupPayload(key);
} else {
return null;
@@ -2004,6 +2047,9 @@
if (user == UserHandle.USER_SYSTEM) {
final UserUsageStatsService userStats = getUserDataAndInitializeIfNeededLocked(
user, System.currentTimeMillis());
+ if (userStats == null) {
+ return; // user was stopped or removed
+ }
userStats.applyRestoredPayload(key, payload);
}
}
diff --git a/services/wifi/Android.bp b/services/wifi/Android.bp
index 3c916a6..608fc2c 100644
--- a/services/wifi/Android.bp
+++ b/services/wifi/Android.bp
@@ -5,6 +5,9 @@
"java/**/*.java",
"java/**/*.aidl",
],
+ aidl: {
+ local_include_dirs: ["java"]
+ },
libs: [
"services.net",
],
diff --git a/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl b/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl
index eadc726..3af4666 100644
--- a/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl
+++ b/services/wifi/java/android/net/wifi/IWifiStackConnector.aidl
@@ -15,8 +15,9 @@
*/
package android.net.wifi;
+import android.net.wifi.WifiApiServiceInfo;
+
/** @hide */
interface IWifiStackConnector {
- IBinder retrieveApiServiceImpl(String serviceName);
- boolean startApiService(String serviceName);
+ List<WifiApiServiceInfo> getWifiApiServiceInfos();
}
diff --git a/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl b/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
new file mode 100644
index 0000000..45e4c69
--- /dev/null
+++ b/services/wifi/java/android/net/wifi/WifiApiServiceInfo.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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.net.wifi;
+
+/** @hide */
+parcelable WifiApiServiceInfo {
+ String name;
+ IBinder binder;
+}
diff --git a/services/wifi/java/android/net/wifi/WifiStackClient.java b/services/wifi/java/android/net/wifi/WifiStackClient.java
index 64af7a8..dcdfbc5 100644
--- a/services/wifi/java/android/net/wifi/WifiStackClient.java
+++ b/services/wifi/java/android/net/wifi/WifiStackClient.java
@@ -21,12 +21,13 @@
import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityModuleConnector;
-import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
+import java.util.List;
+
/**
* Service used to communicate with the wifi stack, which could be running in a separate
* module.
@@ -56,21 +57,23 @@
@Override
public void onModuleServiceConnected(IBinder service) {
Log.i(TAG, "Wifi stack connected");
+ registerWifiStackService(service);
- // spin up a new thread to not block system_server main thread
- HandlerThread thread = new HandlerThread("InitWifiServicesThread");
- thread.start();
- thread.getThreadHandler().post(() -> {
- registerWifiStackService(service);
- IWifiStackConnector connector = IWifiStackConnector.Stub.asInterface(service);
- registerApiServiceAndStart(connector, Context.WIFI_SCANNING_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_P2P_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_AWARE_SERVICE);
- registerApiServiceAndStart(connector, Context.WIFI_RTT_RANGING_SERVICE);
+ IWifiStackConnector connector = IWifiStackConnector.Stub.asInterface(service);
- thread.quitSafely();
- });
+ List<WifiApiServiceInfo> wifiApiServiceInfos;
+ try {
+ wifiApiServiceInfos = connector.getWifiApiServiceInfos();
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed to getWifiApiServiceInfos()", e);
+ }
+
+ for (WifiApiServiceInfo wifiApiServiceInfo : wifiApiServiceInfos) {
+ String serviceName = wifiApiServiceInfo.name;
+ IBinder binder = wifiApiServiceInfo.binder;
+ Log.i(TAG, "Registering " + serviceName);
+ ServiceManager.addService(serviceName, binder);
+ }
}
}
@@ -81,32 +84,6 @@
Log.i(TAG, "Wifi stack service registered");
}
- private void registerApiServiceAndStart(
- IWifiStackConnector stackConnector, String serviceName) {
- IBinder service = null;
- try {
- service = stackConnector.retrieveApiServiceImpl(serviceName);
- } catch (RemoteException e) {
- throw new RuntimeException("Failed to retrieve service impl " + serviceName, e);
- }
- if (service == null) {
- Log.i(TAG, "Service " + serviceName + " not available");
- return;
- }
- Log.i(TAG, "Registering " + serviceName);
- ServiceManager.addService(serviceName, service);
-
- boolean success = false;
- try {
- success = stackConnector.startApiService(serviceName);
- } catch (RemoteException e) {
- throw new RuntimeException("Failed to start service " + serviceName, e);
- }
- if (!success) {
- throw new RuntimeException("Service " + serviceName + " start failed");
- }
- }
-
/**
* Start the wifi stack. Should be called only once on device startup.
*
diff --git a/startop/view_compiler/Android.bp b/startop/view_compiler/Android.bp
index 4f6524e..c380d29 100644
--- a/startop/view_compiler/Android.bp
+++ b/startop/view_compiler/Android.bp
@@ -25,6 +25,7 @@
"slicer",
],
static_libs: [
+ "libcutils",
"libtinyxml2",
"liblog",
"libutils",
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index adc9e22..81b1e49 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -19,10 +19,9 @@
java_sdk_library {
name: "android.test.mock",
- srcs: [
- "src/**/*.java",
- ":framework-all-sources",
- ],
+ srcs: ["src/**/*.java"],
+ api_srcs: [":framework-all-sources"],
+ libs: ["framework-all"],
api_packages: [
"android.test.mock",