Merge "Tuner API: modify permission check" into rvc-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index b6f85b2..e14ca99 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -23,8 +23,6 @@
import static android.app.usage.UsageStatsManager.REASON_MAIN_PREDICTED;
import static android.app.usage.UsageStatsManager.REASON_MAIN_TIMEOUT;
import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE;
-import static android.app.usage.UsageStatsManager.REASON_SUB_DEFAULT_APP_UPDATE;
-import static android.app.usage.UsageStatsManager.REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
import static android.app.usage.UsageStatsManager.REASON_SUB_MASK;
import static android.app.usage.UsageStatsManager.REASON_SUB_PREDICTED_RESTORED;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_ACTIVE_TIMEOUT;
@@ -75,6 +73,7 @@
import android.content.pm.ParceledListSlice;
import android.database.ContentObserver;
import android.hardware.display.DisplayManager;
+import android.net.ConnectivityManager;
import android.net.NetworkScoreManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -305,7 +304,10 @@
private final AppStandbyHandler mHandler;
private final Context mContext;
+ // TODO: Provide a mechanism to set an external bucketing service
+
private AppWidgetManager mAppWidgetManager;
+ private ConnectivityManager mConnectivityManager;
private PackageManager mPackageManager;
Injector mInjector;
@@ -409,6 +411,7 @@
settingsObserver.updateSettings();
mAppWidgetManager = mContext.getSystemService(AppWidgetManager.class);
+ mConnectivityManager = mContext.getSystemService(ConnectivityManager.class);
mInjector.registerDisplayListener(mDisplayListener, mHandler);
synchronized (mAppIdleLock) {
@@ -1516,38 +1519,6 @@
}
}
- /**
- * Remove an app from the {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED}
- * bucket if it was forced into the bucket by the system because it was buggy.
- */
- @VisibleForTesting
- void maybeUnrestrictBuggyApp(String packageName, int userId) {
- synchronized (mAppIdleLock) {
- final long elapsedRealtime = mInjector.elapsedRealtime();
- final AppIdleHistory.AppUsageHistory app =
- mAppIdleHistory.getAppUsageHistory(packageName, userId, elapsedRealtime);
- if (app.currentBucket != STANDBY_BUCKET_RESTRICTED
- || (app.bucketingReason & REASON_MAIN_MASK) != REASON_MAIN_FORCED_BY_SYSTEM) {
- return;
- }
-
- final int newBucket;
- final int newReason;
- if ((app.bucketingReason & REASON_SUB_MASK) == REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY) {
- // If bugginess was the only reason the app should be restricted, then lift it out.
- newBucket = STANDBY_BUCKET_RARE;
- newReason = REASON_MAIN_DEFAULT | REASON_SUB_DEFAULT_APP_UPDATE;
- } else {
- // There's another reason the app was restricted. Remove the buggy bit and call
- // it a day.
- newBucket = STANDBY_BUCKET_RESTRICTED;
- newReason = app.bucketingReason & ~REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY;
- }
- mAppIdleHistory.setAppStandbyBucket(
- packageName, userId, elapsedRealtime, newBucket, newReason);
- }
- }
-
private class PackageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -1557,14 +1528,10 @@
clearCarrierPrivilegedApps();
}
if ((Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
- Intent.ACTION_PACKAGE_ADDED.equals(action))) {
- final String pkgName = intent.getData().getSchemeSpecificPart();
- final int userId = getSendingUserId();
- if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- maybeUnrestrictBuggyApp(pkgName, userId);
- } else {
- clearAppIdleForPackage(pkgName, userId);
- }
+ Intent.ACTION_PACKAGE_ADDED.equals(action))
+ && !intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+ clearAppIdleForPackage(intent.getData().getSchemeSpecificPart(),
+ getSendingUserId());
}
}
}
diff --git a/apex/statsd/framework/Android.bp b/apex/statsd/framework/Android.bp
index 986682e..8185bb0 100644
--- a/apex/statsd/framework/Android.bp
+++ b/apex/statsd/framework/Android.bp
@@ -166,8 +166,10 @@
android_test {
name: "FrameworkStatsdTest",
- sdk_version: "module_current",
+ platform_apis: true,
srcs: [
+ // TODO(b/147705194): Use framework-statsd as a lib dependency instead.
+ ":framework-statsd-sources",
"test/**/*.java",
],
manifest: "test/AndroidManifest.xml",
@@ -178,7 +180,6 @@
libs: [
"android.test.runner.stubs",
"android.test.base.stubs",
- "framework-statsd",
],
test_suites: [
"device-tests",
diff --git a/api/current.txt b/api/current.txt
index 61b427c..8152cd6 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -288,9 +288,9 @@
field public static final int alignmentMode = 16843642; // 0x101037a
field public static final int allContactsName = 16843468; // 0x10102cc
field public static final int allowAudioPlaybackCapture = 16844289; // 0x1010601
+ field public static final int allowAutoRevokePermissionsExemption = 16844309; // 0x1010615
field public static final int allowBackup = 16843392; // 0x1010280
field public static final int allowClearUserData = 16842757; // 0x1010005
- field public static final int allowDontAutoRevokePermissions = 16844309; // 0x1010615
field public static final int allowEmbedded = 16843765; // 0x10103f5
field public static final int allowNativeHeapPointerTagging = 16844307; // 0x1010613
field public static final int allowParallelSyncs = 16843570; // 0x1010332
@@ -1140,7 +1140,7 @@
field public static final int reqKeyboardType = 16843304; // 0x1010228
field public static final int reqNavigation = 16843306; // 0x101022a
field public static final int reqTouchScreen = 16843303; // 0x1010227
- field public static final int requestDontAutoRevokePermissions = 16844308; // 0x1010614
+ field public static final int requestAutoRevokePermissionsExemption = 16844308; // 0x1010614
field public static final int requestLegacyExternalStorage = 16844291; // 0x1010603
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
@@ -4049,6 +4049,7 @@
method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int);
method @RequiresPermission(android.Manifest.permission.REORDER_TASKS) public void moveTaskToFront(int, int, android.os.Bundle);
method @Deprecated public void restartPackage(String);
+ method public void setProcessStateSummary(@Nullable byte[]);
method public static void setVrThread(int);
method public void setWatchHeapLimit(long);
field public static final String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT";
@@ -4561,12 +4562,14 @@
method public int getPackageUid();
method public int getPid();
method @NonNull public String getProcessName();
+ method @Nullable public byte[] getProcessStateSummary();
method public long getPss();
method public int getRealUid();
method public int getReason();
method public long getRss();
method public int getStatus();
method public long getTimestamp();
+ method @Nullable public java.io.InputStream getTraceInputStream() throws java.io.IOException;
method @NonNull public android.os.UserHandle getUserHandle();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.ApplicationExitInfo> CREATOR;
@@ -10202,7 +10205,6 @@
field public static final String MEDIA_ROUTER_SERVICE = "media_router";
field public static final String MEDIA_SESSION_SERVICE = "media_session";
field public static final String MIDI_SERVICE = "midi";
- field public static final String MMS_SERVICE = "mms";
field public static final int MODE_APPEND = 32768; // 0x8000
field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
field @Deprecated public static final int MODE_MULTI_PROCESS = 4; // 0x4
@@ -47545,11 +47547,6 @@
method @Nullable public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, @NonNull java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback);
}
- public class MmsManager {
- method public void downloadMultimediaMessage(int, @NonNull String, @NonNull android.net.Uri, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long);
- method public void sendMultimediaMessage(int, @NonNull android.net.Uri, @Nullable String, @Nullable android.os.Bundle, @Nullable android.app.PendingIntent, long);
- }
-
@Deprecated public class NeighboringCellInfo implements android.os.Parcelable {
ctor @Deprecated public NeighboringCellInfo();
ctor @Deprecated public NeighboringCellInfo(int, int);
diff --git a/api/system-current.txt b/api/system-current.txt
index 325a2ee..b0ad484 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1871,7 +1871,6 @@
field public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
field public static final String ACTION_USER_ADDED = "android.intent.action.USER_ADDED";
field public static final String ACTION_USER_REMOVED = "android.intent.action.USER_REMOVED";
- field @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public static final String ACTION_USER_SWITCHED = "android.intent.action.USER_SWITCHED";
field public static final String ACTION_VOICE_ASSIST = "android.intent.action.VOICE_ASSIST";
field public static final String CATEGORY_LEANBACK_SETTINGS = "android.intent.category.LEANBACK_SETTINGS";
field public static final String EXTRA_CALLING_PACKAGE = "android.intent.extra.CALLING_PACKAGE";
@@ -10703,8 +10702,8 @@
method public int getNumRtpPacketsTransmitted();
method public int getNumRtpPacketsTransmittedLost();
method public int getUplinkCallQualityLevel();
- method public boolean isIncomingSilenceDetected();
- method public boolean isOutgoingSilenceDetected();
+ method public boolean isIncomingSilenceDetectedAtCallSetup();
+ method public boolean isOutgoingSilenceDetectedAtCallSetup();
method public boolean isRtpInactivityDetected();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CALL_QUALITY_BAD = 4; // 0x4
@@ -11567,14 +11566,10 @@
field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
field public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = "com.android.omadm.service.CONFIGURATION_UPDATE";
- field public static final String ACTION_SERVICE_PROVIDERS_UPDATED = "android.telephony.action.SERVICE_PROVIDERS_UPDATED";
field public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
- field public static final int CARD_POWER_DOWN = 0; // 0x0
- field public static final int CARD_POWER_UP = 1; // 0x1
- field public static final int CARD_POWER_UP_PASS_THROUGH = 2; // 0x2
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -11585,7 +11580,6 @@
field public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt";
field @Deprecated public static final String EXTRA_APN_TYPE = "apnType";
field public static final String EXTRA_APN_TYPE_INT = "apnTypeInt";
- field public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN";
field public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable";
field public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE = "android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE";
field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4; // 0x4
@@ -11598,16 +11592,12 @@
field public static final String EXTRA_PCO_VALUE = "pcoValue";
field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
field public static final String EXTRA_PHONE_IN_EMERGENCY_CALL = "android.telephony.extra.PHONE_IN_EMERGENCY_CALL";
- field public static final String EXTRA_PLMN = "android.telephony.extra.PLMN";
field public static final String EXTRA_REDIRECTION_URL = "redirectionUrl";
- field public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN";
- field public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN";
field public static final String EXTRA_SIM_COMBINATION_NAMES = "android.telephony.extra.SIM_COMBINATION_NAMES";
field public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE = "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1; // 0x1
field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0; // 0x0
field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
- field public static final String EXTRA_SPN = "android.telephony.extra.SPN";
field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
diff --git a/api/test-current.txt b/api/test-current.txt
index 286408d..2145505 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3625,8 +3625,8 @@
method public int getNumRtpPacketsTransmitted();
method public int getNumRtpPacketsTransmittedLost();
method public int getUplinkCallQualityLevel();
- method public boolean isIncomingSilenceDetected();
- method public boolean isOutgoingSilenceDetected();
+ method public boolean isIncomingSilenceDetectedAtCallSetup();
+ method public boolean isOutgoingSilenceDetectedAtCallSetup();
method public boolean isRtpInactivityDetected();
method public void writeToParcel(android.os.Parcel, int);
field public static final int CALL_QUALITY_BAD = 4; // 0x4
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index d79123b..60d1ac7 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -267,28 +267,29 @@
bool workerDone = false;
FdBuffer buffer;
- // Create shared data and pipe
- WorkerThreadData data(this);
- if (!data.pipe.init()) {
+ // Create shared data and pipe. Don't put data on the stack since this thread may exit early.
+ sp<WorkerThreadData> data = new WorkerThreadData(this);
+ if (!data->pipe.init()) {
return -errno;
}
-
- std::thread([&]() {
+ data->incStrong(this);
+ std::thread([data, this]() {
// Don't crash the service if writing to a closed pipe (may happen if dumping times out)
signal(SIGPIPE, sigpipe_handler);
- status_t err = data.section->BlockingCall(data.pipe.writeFd());
+ status_t err = data->section->BlockingCall(data->pipe.writeFd());
{
- std::unique_lock<std::mutex> lock(data.lock);
- data.workerDone = true;
- data.workerError = err;
+ std::scoped_lock<std::mutex> lock(data->lock);
+ data->workerDone = true;
+ data->workerError = err;
// unique_fd is not thread safe. If we don't lock it, reset() may pause half way while
// the other thread executes to the end, calling ~Fpipe, which is a race condition.
- data.pipe.writeFd().reset();
+ data->pipe.writeFd().reset();
}
+ data->decStrong(this);
}).detach();
// Loop reading until either the timeout or the worker side is done (i.e. eof).
- err = buffer.read(data.pipe.readFd().get(), this->timeoutMs);
+ err = buffer.read(data->pipe.readFd().get(), this->timeoutMs);
if (err != NO_ERROR) {
ALOGE("[%s] reader failed with error '%s'", this->name.string(), strerror(-err));
}
@@ -296,13 +297,13 @@
// If the worker side is finished, then return its error (which may overwrite
// our possible error -- but it's more interesting anyway). If not, then we timed out.
{
- std::unique_lock<std::mutex> lock(data.lock);
- data.pipe.close();
- if (data.workerError != NO_ERROR) {
- err = data.workerError;
+ std::scoped_lock<std::mutex> lock(data->lock);
+ data->pipe.close();
+ if (data->workerError != NO_ERROR) {
+ err = data->workerError;
ALOGE("[%s] worker failed with error '%s'", this->name.string(), strerror(-err));
}
- workerDone = data.workerDone;
+ workerDone = data->workerDone;
}
writer->setSectionStats(buffer);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index f4ee8fa..b3a0be1 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3628,11 +3628,40 @@
}
}
+ /**
+ * Set custom state data for this process. It will be included in the record of
+ * {@link ApplicationExitInfo} on the death of the current calling process; the new process
+ * of the app can retrieve this state data by calling
+ * {@link ApplicationExitInfo#getProcessStateSummary} on the record returned by
+ * {@link #getHistoricalProcessExitReasons}.
+ *
+ * <p> This would be useful for the calling app to save its stateful data: if it's
+ * killed later for any reason, the new process of the app can know what the
+ * previous process of the app was doing. For instance, you could use this to encode
+ * the current level in a game, or a set of features/experiments that were enabled. Later you
+ * could analyze under what circumstances the app tends to crash or use too much memory.
+ * However, it's not suggested to rely on this to restore the applications previous UI state
+ * or so, it's only meant for analyzing application healthy status.</p>
+ *
+ * <p> System might decide to throttle the calls to this API; so call this API in a reasonable
+ * manner, excessive calls to this API could result a {@link java.lang.RuntimeException}.
+ * </p>
+ *
+ * @param state The state data
+ */
+ public void setProcessStateSummary(@Nullable byte[] state) {
+ try {
+ getService().setProcessStateSummary(state);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
/*
* @return Whether or not the low memory kill will be reported in
* {@link #getHistoricalProcessExitReasons}.
*
- * @see {@link ApplicationExitInfo#REASON_LOW_MEMORY}
+ * @see ApplicationExitInfo#REASON_LOW_MEMORY
*/
public static boolean isLowMemoryKillReportSupported() {
return SystemProperties.getBoolean("persist.sys.lmk.reportkills", false);
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index 5df3257..61be01f 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -23,7 +23,9 @@
import android.app.ActivityManager.RunningAppProcessInfo.Importance;
import android.icu.text.SimpleDateFormat;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.DebugUtils;
@@ -31,12 +33,17 @@
import android.util.proto.ProtoOutputStream;
import android.util.proto.WireTypeMismatchException;
+import com.android.internal.util.ArrayUtils;
+
+import java.io.File;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Date;
import java.util.Objects;
+import java.util.zip.GZIPInputStream;
/**
* Describes the information of an application process's death.
@@ -321,85 +328,105 @@
// be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000.
/**
- * @see {@link #getPid}
+ * @see #getPid
*/
private int mPid;
/**
- * @see {@link #getRealUid}
+ * @see #getRealUid
*/
private int mRealUid;
/**
- * @see {@link #getPackageUid}
+ * @see #getPackageUid
*/
private int mPackageUid;
/**
- * @see {@link #getDefiningUid}
+ * @see #getDefiningUid
*/
private int mDefiningUid;
/**
- * @see {@link #getProcessName}
+ * @see #getProcessName
*/
private String mProcessName;
/**
- * @see {@link #getReason}
+ * @see #getReason
*/
private @Reason int mReason;
/**
- * @see {@link #getStatus}
+ * @see #getStatus
*/
private int mStatus;
/**
- * @see {@link #getImportance}
+ * @see #getImportance
*/
private @Importance int mImportance;
/**
- * @see {@link #getPss}
+ * @see #getPss
*/
private long mPss;
/**
- * @see {@link #getRss}
+ * @see #getRss
*/
private long mRss;
/**
- * @see {@link #getTimestamp}
+ * @see #getTimestamp
*/
private @CurrentTimeMillisLong long mTimestamp;
/**
- * @see {@link #getDescription}
+ * @see #getDescription
*/
private @Nullable String mDescription;
/**
- * @see {@link #getSubReason}
+ * @see #getSubReason
*/
private @SubReason int mSubReason;
/**
- * @see {@link #getConnectionGroup}
+ * @see #getConnectionGroup
*/
private int mConnectionGroup;
/**
- * @see {@link #getPackageName}
+ * @see #getPackageName
*/
private String mPackageName;
/**
- * @see {@link #getPackageList}
+ * @see #getPackageList
*/
private String[] mPackageList;
+ /**
+ * @see #getProcessStateSummary
+ */
+ private byte[] mState;
+
+ /**
+ * The file to the trace file in the storage;
+ *
+ * for system internal use only, will not retain across processes.
+ *
+ * @see #getTraceInputStream
+ */
+ private File mTraceFile;
+
+ /**
+ * The Binder interface to retrieve the file descriptor to
+ * the trace file from the system.
+ */
+ private IAppTraceRetriever mAppTraceRetriever;
+
/** @hide */
@IntDef(prefix = { "REASON_" }, value = {
REASON_UNKNOWN,
@@ -557,6 +584,54 @@
}
/**
+ * Return the state data set by calling {@link ActivityManager#setProcessStateSummary}
+ * from the process before its death.
+ *
+ * @return The process-customized data
+ * @see ActivityManager#setProcessStateSummary(byte[])
+ */
+ public @Nullable byte[] getProcessStateSummary() {
+ return mState;
+ }
+
+ /**
+ * Return the InputStream to the traces that was taken by the system
+ * prior to the death of the process; typically it'll be available when
+ * the reason is {@link #REASON_ANR}, though if the process gets an ANR
+ * but recovers, and dies for another reason later, this trace will be included
+ * in the record of {@link ApplicationExitInfo} still.
+ *
+ * @return The input stream to the traces that was taken by the system
+ * prior to the death of the process.
+ */
+ public @Nullable InputStream getTraceInputStream() throws IOException {
+ if (mAppTraceRetriever == null) {
+ return null;
+ }
+ try {
+ final ParcelFileDescriptor fd = mAppTraceRetriever.getTraceFileDescriptor(
+ mPackageName, mPackageUid, mPid);
+ if (fd == null) {
+ return null;
+ }
+ return new GZIPInputStream(new ParcelFileDescriptor.AutoCloseInputStream(fd));
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Similar to {@link #getTraceInputStream} but return the File object.
+ *
+ * For internal use only.
+ *
+ * @hide
+ */
+ public @Nullable File getTraceFile() {
+ return mTraceFile;
+ }
+
+ /**
* A subtype reason in conjunction with {@link #mReason}.
*
* For internal use only.
@@ -569,7 +644,7 @@
/**
* The connection group this process belongs to, if there is any.
- * @see {@link android.content.Context#updateServiceGroup}.
+ * @see android.content.Context#updateServiceGroup
*
* For internal use only.
*
@@ -582,8 +657,6 @@
/**
* Name of first package running in this process;
*
- * For system internal use only, will not retain across processes.
- *
* @hide
*/
public String getPackageName() {
@@ -602,7 +675,7 @@
}
/**
- * @see {@link #getPid}
+ * @see #getPid
*
* @hide
*/
@@ -611,7 +684,7 @@
}
/**
- * @see {@link #getRealUid}
+ * @see #getRealUid
*
* @hide
*/
@@ -620,7 +693,7 @@
}
/**
- * @see {@link #getPackageUid}
+ * @see #getPackageUid
*
* @hide
*/
@@ -629,7 +702,7 @@
}
/**
- * @see {@link #getDefiningUid}
+ * @see #getDefiningUid
*
* @hide
*/
@@ -638,7 +711,7 @@
}
/**
- * @see {@link #getProcessName}
+ * @see #getProcessName
*
* @hide
*/
@@ -647,7 +720,7 @@
}
/**
- * @see {@link #getReason}
+ * @see #getReason
*
* @hide
*/
@@ -656,7 +729,7 @@
}
/**
- * @see {@link #getStatus}
+ * @see #getStatus
*
* @hide
*/
@@ -665,7 +738,7 @@
}
/**
- * @see {@link #getImportance}
+ * @see #getImportance
*
* @hide
*/
@@ -674,7 +747,7 @@
}
/**
- * @see {@link #getPss}
+ * @see #getPss
*
* @hide
*/
@@ -683,7 +756,7 @@
}
/**
- * @see {@link #getRss}
+ * @see #getRss
*
* @hide
*/
@@ -692,7 +765,7 @@
}
/**
- * @see {@link #getTimestamp}
+ * @see #getTimestamp
*
* @hide
*/
@@ -701,7 +774,7 @@
}
/**
- * @see {@link #getDescription}
+ * @see #getDescription
*
* @hide
*/
@@ -710,7 +783,7 @@
}
/**
- * @see {@link #getSubReason}
+ * @see #getSubReason
*
* @hide
*/
@@ -719,7 +792,7 @@
}
/**
- * @see {@link #getConnectionGroup}
+ * @see #getConnectionGroup
*
* @hide
*/
@@ -728,7 +801,7 @@
}
/**
- * @see {@link #getPackageName}
+ * @see #getPackageName
*
* @hide
*/
@@ -737,7 +810,7 @@
}
/**
- * @see {@link #getPackageList}
+ * @see #getPackageList
*
* @hide
*/
@@ -745,6 +818,33 @@
mPackageList = packageList;
}
+ /**
+ * @see #getProcessStateSummary
+ *
+ * @hide
+ */
+ public void setProcessStateSummary(final byte[] state) {
+ mState = state;
+ }
+
+ /**
+ * @see #getTraceFile
+ *
+ * @hide
+ */
+ public void setTraceFile(final File traceFile) {
+ mTraceFile = traceFile;
+ }
+
+ /**
+ * @see #mAppTraceRetriever
+ *
+ * @hide
+ */
+ public void setAppTraceRetriever(final IAppTraceRetriever retriever) {
+ mAppTraceRetriever = retriever;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -757,6 +857,7 @@
dest.writeInt(mPackageUid);
dest.writeInt(mDefiningUid);
dest.writeString(mProcessName);
+ dest.writeString(mPackageName);
dest.writeInt(mConnectionGroup);
dest.writeInt(mReason);
dest.writeInt(mSubReason);
@@ -766,6 +867,13 @@
dest.writeLong(mRss);
dest.writeLong(mTimestamp);
dest.writeString(mDescription);
+ dest.writeByteArray(mState);
+ if (mAppTraceRetriever != null) {
+ dest.writeInt(1);
+ dest.writeStrongBinder(mAppTraceRetriever.asBinder());
+ } else {
+ dest.writeInt(0);
+ }
}
/** @hide */
@@ -779,6 +887,7 @@
mPackageUid = other.mPackageUid;
mDefiningUid = other.mDefiningUid;
mProcessName = other.mProcessName;
+ mPackageName = other.mPackageName;
mConnectionGroup = other.mConnectionGroup;
mReason = other.mReason;
mStatus = other.mStatus;
@@ -790,6 +899,9 @@
mDescription = other.mDescription;
mPackageName = other.mPackageName;
mPackageList = other.mPackageList;
+ mState = other.mState;
+ mTraceFile = other.mTraceFile;
+ mAppTraceRetriever = other.mAppTraceRetriever;
}
private ApplicationExitInfo(@NonNull Parcel in) {
@@ -798,6 +910,7 @@
mPackageUid = in.readInt();
mDefiningUid = in.readInt();
mProcessName = in.readString();
+ mPackageName = in.readString();
mConnectionGroup = in.readInt();
mReason = in.readInt();
mSubReason = in.readInt();
@@ -807,6 +920,10 @@
mRss = in.readLong();
mTimestamp = in.readLong();
mDescription = in.readString();
+ mState = in.createByteArray();
+ if (in.readInt() == 1) {
+ mAppTraceRetriever = IAppTraceRetriever.Stub.asInterface(in.readStrongBinder());
+ }
}
public @NonNull static final Creator<ApplicationExitInfo> CREATOR =
@@ -839,6 +956,9 @@
pw.print(prefix + " pss="); DebugUtils.printSizeValue(pw, mPss << 10); pw.println();
pw.print(prefix + " rss="); DebugUtils.printSizeValue(pw, mRss << 10); pw.println();
pw.println(prefix + " description=" + mDescription);
+ pw.println(prefix + " state=" + (ArrayUtils.isEmpty(mState)
+ ? "empty" : Integer.toString(mState.length) + " bytes"));
+ pw.println(prefix + " trace=" + mTraceFile);
}
@Override
@@ -859,6 +979,9 @@
sb.append(" pss="); DebugUtils.sizeValueToString(mPss << 10, sb);
sb.append(" rss="); DebugUtils.sizeValueToString(mRss << 10, sb);
sb.append(" description=").append(mDescription);
+ sb.append(" state=").append(ArrayUtils.isEmpty(mState)
+ ? "empty" : Integer.toString(mState.length) + " bytes");
+ sb.append(" trace=").append(mTraceFile);
return sb.toString();
}
@@ -961,6 +1084,9 @@
proto.write(ApplicationExitInfoProto.RSS, mRss);
proto.write(ApplicationExitInfoProto.TIMESTAMP, mTimestamp);
proto.write(ApplicationExitInfoProto.DESCRIPTION, mDescription);
+ proto.write(ApplicationExitInfoProto.STATE, mState);
+ proto.write(ApplicationExitInfoProto.TRACE_FILE,
+ mTraceFile == null ? null : mTraceFile.getAbsolutePath());
proto.end(token);
}
@@ -1019,6 +1145,15 @@
case (int) ApplicationExitInfoProto.DESCRIPTION:
mDescription = proto.readString(ApplicationExitInfoProto.DESCRIPTION);
break;
+ case (int) ApplicationExitInfoProto.STATE:
+ mState = proto.readBytes(ApplicationExitInfoProto.STATE);
+ break;
+ case (int) ApplicationExitInfoProto.TRACE_FILE:
+ final String path = proto.readString(ApplicationExitInfoProto.TRACE_FILE);
+ if (!TextUtils.isEmpty(path)) {
+ mTraceFile = new File(path);
+ }
+ break;
}
}
proto.end(token);
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 6f0611e..b8221b4 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -652,4 +652,27 @@
*/
void setActivityLocusContext(in ComponentName activity, in LocusId locusId,
in IBinder appToken);
+
+ /**
+ * Set custom state data for this process. It will be included in the record of
+ * {@link ApplicationExitInfo} on the death of the current calling process; the new process
+ * of the app can retrieve this state data by calling
+ * {@link ApplicationExitInfo#getProcessStateSummary} on the record returned by
+ * {@link #getHistoricalProcessExitReasons}.
+ *
+ * <p> This would be useful for the calling app to save its stateful data: if it's
+ * killed later for any reason, the new process of the app can know what the
+ * previous process of the app was doing. For instance, you could use this to encode
+ * the current level in a game, or a set of features/experiments that were enabled. Later you
+ * could analyze under what circumstances the app tends to crash or use too much memory.
+ * However, it's not suggested to rely on this to restore the applications previous UI state
+ * or so, it's only meant for analyzing application healthy status.</p>
+ *
+ * <p> System might decide to throttle the calls to this API; so call this API in a reasonable
+ * manner, excessive calls to this API could result a {@link java.lang.RuntimeException}.
+ * </p>
+ *
+ * @param state The customized state data
+ */
+ void setProcessStateSummary(in byte[] state);
}
diff --git a/core/java/android/app/IAppTraceRetriever.aidl b/core/java/android/app/IAppTraceRetriever.aidl
new file mode 100644
index 0000000..1463da7
--- /dev/null
+++ b/core/java/android/app/IAppTraceRetriever.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 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.app;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * An interface that's to be used by {@link ApplicationExitInfo#getTraceFile()}
+ * to retrieve the actual file descriptor to its trace file.
+ *
+ * @hide
+ */
+interface IAppTraceRetriever {
+ /**
+ * Retrieve the trace file with given packageName/uid/pid.
+ *
+ * @param packagename The target package name of the trace
+ * @param uid The target UID of the trace
+ * @param pid The target PID of the trace
+ * @return The file descriptor to the trace file, or null if it's not found.
+ */
+ ParcelFileDescriptor getTraceFileDescriptor(in String packageName,
+ int uid, int pid);
+}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 32e815e..37f1a65 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6832,6 +6832,10 @@
* package will no longer be suspended. The admin can block this by using
* {@link #setUninstallBlocked}.
*
+ * <p>Some apps cannot be suspended, such as device admins, the active launcher, the required
+ * package installer, the required package uninstaller, the required package verifier, the
+ * default dialer, and the permission controller.
+ *
* @param admin The name of the admin component to check, or {@code null} if the caller is a
* package access delegate.
* @param packageNames The package names to suspend or unsuspend.
diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java
index 0a67802..0d66198 100644
--- a/core/java/android/app/usage/UsageStatsManager.java
+++ b/core/java/android/app/usage/UsageStatsManager.java
@@ -204,16 +204,6 @@
/** @hide */
public static final int REASON_SUB_MASK = 0x00FF;
/**
- * The reason for using the default main reason is unknown or undefined.
- * @hide
- */
- public static final int REASON_SUB_DEFAULT_UNDEFINED = 0x0000;
- /**
- * The app was updated.
- * @hide
- */
- public static final int REASON_SUB_DEFAULT_APP_UPDATE = 0x0001;
- /**
* The app was interacted with in some way by the system.
* @hide
*/
@@ -1079,14 +1069,6 @@
switch (standbyReason & REASON_MAIN_MASK) {
case REASON_MAIN_DEFAULT:
sb.append("d");
- switch (subReason) {
- case REASON_SUB_DEFAULT_UNDEFINED:
- // Historically, undefined didn't have a string, so don't add anything here.
- break;
- case REASON_SUB_DEFAULT_APP_UPDATE:
- sb.append("-au");
- break;
- }
break;
case REASON_MAIN_FORCED_BY_SYSTEM:
sb.append("s");
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 318ae11..c8c8fa6d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3430,7 +3430,7 @@
TELEPHONY_SUBSCRIPTION_SERVICE,
CARRIER_CONFIG_SERVICE,
EUICC_SERVICE,
- MMS_SERVICE,
+ //@hide: MMS_SERVICE,
TELECOM_SERVICE,
CLIPBOARD_SERVICE,
INPUT_METHOD_SERVICE,
@@ -4344,6 +4344,7 @@
*
* @see #getSystemService(String)
* @see android.telephony.MmsManager
+ * @hide
*/
public static final String MMS_SERVICE = "mms";
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 38c1890..95385ee 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3675,9 +3675,7 @@
* {@link android.Manifest.permission#MANAGE_USERS} to receive this broadcast.
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- @SystemApi
+ @UnsupportedAppUsage
public static final String ACTION_USER_SWITCHED =
"android.intent.action.USER_SWITCHED";
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index e41ed85..6f8acb6 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -1820,8 +1820,8 @@
.setUseEmbeddedDex(bool(false, R.styleable.AndroidManifestApplication_useEmbeddedDex, sa))
.setUsesNonSdkApi(bool(false, R.styleable.AndroidManifestApplication_usesNonSdkApi, sa))
.setVmSafeMode(bool(false, R.styleable.AndroidManifestApplication_vmSafeMode, sa))
- .setDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_requestDontAutoRevokePermissions, sa))
- .setAllowDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_allowDontAutoRevokePermissions, sa))
+ .setDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_requestAutoRevokePermissionsExemption, sa))
+ .setAllowDontAutoRevokePermissions(bool(false, R.styleable.AndroidManifestApplication_allowAutoRevokePermissionsExemption, sa))
// targetSdkVersion gated
.setAllowAudioPlaybackCapture(bool(targetSdk >= Build.VERSION_CODES.Q, R.styleable.AndroidManifestApplication_allowAudioPlaybackCapture, sa))
.setBaseHardwareAccelerated(bool(targetSdk >= Build.VERSION_CODES.ICE_CREAM_SANDWICH, R.styleable.AndroidManifestApplication_hardwareAccelerated, sa))
diff --git a/core/java/android/service/quickaccesswallet/WalletServiceEvent.java b/core/java/android/service/quickaccesswallet/WalletServiceEvent.java
index fb524be..5ee92da 100644
--- a/core/java/android/service/quickaccesswallet/WalletServiceEvent.java
+++ b/core/java/android/service/quickaccesswallet/WalletServiceEvent.java
@@ -40,10 +40,16 @@
public static final int TYPE_NFC_PAYMENT_STARTED = 1;
/**
+ * Indicates that the wallet cards have changed and should be refreshed.
+ * @hide
+ */
+ public static final int TYPE_WALLET_CARDS_UPDATED = 2;
+
+ /**
* @hide
*/
@Retention(RetentionPolicy.SOURCE)
- @IntDef({TYPE_NFC_PAYMENT_STARTED})
+ @IntDef({TYPE_NFC_PAYMENT_STARTED, TYPE_WALLET_CARDS_UPDATED})
public @interface EventType {
}
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index b587fbe..cd22ad6 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -98,7 +98,10 @@
}
/**
- * Release the SurfaceControl associated with the SurfacePackage.
+ * Release the {@link SurfaceControl} associated with this package.
+ * It's not necessary to call this if you pass the package to
+ * {@link SurfaceView#setChildSurfacePackage} as {@link SurfaceView} will
+ * take ownership in that case.
*/
public void release() {
if (mSurfaceControl != null) {
@@ -230,7 +233,7 @@
* and render the object unusable.
*/
public void release() {
- mViewRoot.dispatchDetachedFromWindow();
+ mViewRoot.die(false /* immediate */);
mSurfaceControl.release();
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 1f7c3504..3e1e393 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -496,8 +496,17 @@
updateSurface();
releaseSurfaces();
- mHaveFrame = false;
+ // We don't release this as part of releaseSurfaces as
+ // that is also called on transient visibility changes. We can't
+ // recreate this Surface, so only release it when we are fully
+ // detached.
+ if (mSurfacePackage != null) {
+ mSurfacePackage.release();
+ mSurfacePackage = null;
+ }
+
+ mHaveFrame = false;
super.onDetachedFromWindow();
}
@@ -1546,7 +1555,9 @@
* Display the view-hierarchy embedded within a {@link SurfaceControlViewHost.SurfacePackage}
* within this SurfaceView. If this SurfaceView is above it's host Surface (see
* {@link #setZOrderOnTop} then the embedded Surface hierarchy will be able to receive
- * input.
+ * input. This will take ownership of the SurfaceControl contained inside the SurfacePackage
+ * and free the caller of the obligation to call
+ * {@link SurfaceControlViewHost.SurfacePackage#release}.
*
* @param p The SurfacePackage to embed.
*/
@@ -1556,6 +1567,7 @@
mSurfacePackage.getSurfaceControl() : null;
if (mSurfaceControl != null && lastSc != null) {
mTmpTransaction.reparent(lastSc, null).apply();
+ mSurfacePackage.release();
} else if (mSurfaceControl != null) {
reparentSurfacePackage(mTmpTransaction, p);
mTmpTransaction.apply();
diff --git a/core/java/android/widget/EditText.java b/core/java/android/widget/EditText.java
index 728824c..d661bc6 100644
--- a/core/java/android/widget/EditText.java
+++ b/core/java/android/widget/EditText.java
@@ -24,7 +24,6 @@
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.MovementMethod;
import android.util.AttributeSet;
-import android.view.accessibility.AccessibilityNodeInfo;
/*
* This is supposed to be a *very* thin veneer over TextView.
@@ -179,13 +178,4 @@
protected boolean supportsAutoSizeText() {
return false;
}
-
- /** @hide */
- @Override
- public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
- super.onInitializeAccessibilityNodeInfoInternal(info);
- if (isEnabled()) {
- info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_TEXT);
- }
- }
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 16d9ed3..4aeea10 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -317,6 +317,7 @@
private SelectionActionModeHelper mSelectionActionModeHelper;
boolean mIsBeingLongClicked;
+ boolean mIsBeingLongClickedByAccessibility;
private SuggestionsPopupWindow mSuggestionsPopupWindow;
SuggestionRangeSpan mSuggestionRangeSpan;
@@ -1312,6 +1313,12 @@
if (TextView.DEBUG_CURSOR) {
logCursor("performLongClick", "handled=%s", handled);
}
+ if (mIsBeingLongClickedByAccessibility) {
+ if (!handled) {
+ toggleInsertionActionMode();
+ }
+ return true;
+ }
// Long press in empty space moves cursor and starts the insertion action mode.
if (!handled && !isPositionOnText(mTouchState.getLastDownX(), mTouchState.getLastDownY())
&& !mTouchState.isOnHandle() && mInsertionControllerEnabled) {
@@ -1359,6 +1366,14 @@
return handled;
}
+ private void toggleInsertionActionMode() {
+ if (mTextActionMode != null) {
+ stopTextActionMode();
+ } else {
+ startInsertionActionMode();
+ }
+ }
+
float getLastUpPositionX() {
return mTouchState.getLastUpX();
}
@@ -5436,11 +5451,7 @@
config.getScaledTouchSlop());
if (isWithinTouchSlop) {
// Tapping on the handle toggles the insertion action mode.
- if (mTextActionMode != null) {
- stopTextActionMode();
- } else {
- startInsertionActionMode();
- }
+ toggleInsertionActionMode();
}
} else {
if (mTextActionMode != null) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 2168018..e178318 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -12108,6 +12108,23 @@
onEditorAction(getImeActionId());
}
} return true;
+ case AccessibilityNodeInfo.ACTION_LONG_CLICK: {
+ if (isLongClickable()) {
+ boolean handled;
+ if (isEnabled() && (mBufferType == BufferType.EDITABLE)) {
+ mEditor.mIsBeingLongClickedByAccessibility = true;
+ try {
+ handled = performLongClick();
+ } finally {
+ mEditor.mIsBeingLongClickedByAccessibility = false;
+ }
+ } else {
+ handled = performLongClick();
+ }
+ return handled;
+ }
+ }
+ return false;
default: {
return super.performAccessibilityActionInternal(action, arguments);
}
diff --git a/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java b/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
index 9aee879f..ef8d018 100644
--- a/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
+++ b/core/java/com/android/ims/internal/uce/uceservice/ImsUceManager.java
@@ -50,6 +50,16 @@
public static final String ACTION_UCE_SERVICE_DOWN =
"com.android.ims.internal.uce.UCE_SERVICE_DOWN";
+ /**
+ * Uce Service status received in IUceListener.setStatus() callback
+ */
+ public static final int UCE_SERVICE_STATUS_FAILURE = 0;
+ /** indicate UI to call Presence/Options API. */
+ public static final int UCE_SERVICE_STATUS_ON = 1;
+ /** Indicate UI destroy Presence/Options */
+ public static final int UCE_SERVICE_STATUS_CLOSED = 2;
+ /** Service up and trying to register for network events */
+ public static final int UCE_SERVICE_STATUS_READY = 3;
/**
* Gets the instance of UCE Manager
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
index 9ba0259..ab890d2 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
@@ -93,43 +93,6 @@
dest.writeString(mDescription);
}
- @Override
- public String toString() {
- StringBuilder sb = new StringBuilder("CompatibilityChangeInfo(")
- .append(getId());
- if (getName() != null) {
- sb.append("; name=").append(getName());
- }
- if (getEnableAfterTargetSdk() != -1) {
- sb.append("; enableAfterTargetSdk=").append(getEnableAfterTargetSdk());
- }
- if (getDisabled()) {
- sb.append("; disabled");
- }
- if (getLoggingOnly()) {
- sb.append("; loggingOnly");
- }
- return sb.append(")").toString();
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || !(o instanceof CompatibilityChangeInfo)) {
- return false;
- }
- CompatibilityChangeInfo that = (CompatibilityChangeInfo) o;
- return this.mChangeId == that.mChangeId
- && this.mName.equals(that.mName)
- && this.mEnableAfterTargetSdk == that.mEnableAfterTargetSdk
- && this.mDisabled == that.mDisabled
- && this.mLoggingOnly == that.mLoggingOnly
- && this.mDescription.equals(that.mDescription);
-
- }
-
public static final Parcelable.Creator<CompatibilityChangeInfo> CREATOR =
new Parcelable.Creator<CompatibilityChangeInfo>() {
diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl
index 6408def..523ed6f 100644
--- a/core/java/com/android/internal/compat/IPlatformCompat.aidl
+++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl
@@ -222,14 +222,6 @@
CompatibilityChangeInfo[] listAllChanges();
/**
- * List the compatibility changes that should be present in the UI.
- * Filters out certain changes like e.g. logging only.
- *
- * @return An array of {@link CompatChangeInfo}.
- */
- CompatibilityChangeInfo[] listUIChanges();
-
- /**
* Get an instance that can determine whether a changeid can be overridden for a package name.
*/
IOverrideValidator getOverrideValidator();
diff --git a/core/proto/android/app/appexitinfo.proto b/core/proto/android/app/appexitinfo.proto
index 66173f6..4b9444e 100644
--- a/core/proto/android/app/appexitinfo.proto
+++ b/core/proto/android/app/appexitinfo.proto
@@ -42,4 +42,6 @@
optional int64 rss = 12;
optional int64 timestamp = 13;
optional string description = 14;
+ optional bytes state = 15;
+ optional string trace_file = 16;
}
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 93b2063..2496900 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1832,7 +1832,7 @@
revoked when the app is unused for an extended amount of time.
The default value is {@code false}. -->
- <attr name="requestDontAutoRevokePermissions" format="boolean" />
+ <attr name="requestAutoRevokePermissionsExemption" format="boolean" />
<!-- If {@code true} its permissions shouldn't get automatically
revoked when the app is unused for an extended amount of time.
@@ -1840,7 +1840,7 @@
This implies {@code requestDontAutoRevokePermissions=true}
The default value is {@code false}. -->
- <attr name="allowDontAutoRevokePermissions" format="boolean" />
+ <attr name="allowAutoRevokePermissionsExemption" format="boolean" />
</declare-styleable>
<!-- An attribution is a logical part of an app and is identified by a tag.
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index cf68aff..5306518 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3014,8 +3014,8 @@
<!-- @hide @SystemApi -->
<public name="minExtensionVersion" />
<public name="allowNativeHeapPointerTagging" />
- <public name="requestDontAutoRevokePermissions" />
- <public name="allowDontAutoRevokePermissions" />
+ <public name="requestAutoRevokePermissionsExemption" />
+ <public name="allowAutoRevokePermissionsExemption" />
<public name="preserveLegacyExternalStorage" />
<public name="mimeGroup" />
<public name="enableGwpAsan" />
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index a72be25..45d4b38 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -65,6 +65,7 @@
import android.app.Instrumentation;
import android.content.ClipData;
import android.content.ClipboardManager;
+import android.os.Bundle;
import android.support.test.uiautomator.UiDevice;
import android.text.InputType;
import android.text.Selection;
@@ -75,6 +76,7 @@
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.textclassifier.SelectionEvent;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
@@ -358,6 +360,20 @@
}
@Test
+ public void testToolbarAppearsAccessibilityLongClick() throws Throwable {
+ final String text = "Toolbar appears after performing accessibility's ACTION_LONG_CLICK.";
+ mActivityRule.runOnUiThread(() -> {
+ final TextView textView = mActivity.findViewById(R.id.textview);
+ final Bundle args = new Bundle();
+ textView.performAccessibilityAction(AccessibilityNodeInfo.ACTION_LONG_CLICK, args);
+ });
+ mInstrumentation.waitForIdleSync();
+
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarIsDisplayed();
+ }
+
+ @Test
public void testSelectionRemovedWhenNonselectableTextLosesFocus() throws Throwable {
final TextLinks.TextLink textLink = addLinkifiedTextToTextView(R.id.nonselectable_textview);
final int position = (textLink.getStart() + textLink.getEnd()) / 2;
diff --git a/mms/java/android/telephony/MmsManager.java b/mms/java/android/telephony/MmsManager.java
index f07cd5e..6e47741 100644
--- a/mms/java/android/telephony/MmsManager.java
+++ b/mms/java/android/telephony/MmsManager.java
@@ -32,6 +32,7 @@
/**
* Manages MMS operations such as sending multimedia messages.
* Get this object by calling Context#getSystemService(Context#MMS_SERVICE).
+ * @hide
*/
@SystemService(Context.MMS_SERVICE)
public class MmsManager {
diff --git a/packages/SystemUI/res/layout/controls_no_favorites.xml b/packages/SystemUI/res/layout/controls_no_favorites.xml
index 8074efd..74fc167 100644
--- a/packages/SystemUI/res/layout/controls_no_favorites.xml
+++ b/packages/SystemUI/res/layout/controls_no_favorites.xml
@@ -40,14 +40,20 @@
android:paddingBottom="8dp" />
<TextView
+ style="@style/TextAppearance.ControlSetup.Title"
android:id="@+id/controls_title"
android:text="@string/quick_controls_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:singleLine="true"
- android:layout_gravity="center"
- android:textSize="25sp"
- android:textColor="@*android:color/foreground_material_dark"
- android:fontFamily="@*android:string/config_headlineFontFamily" />
+ android:layout_gravity="center" />
+
+ <TextView
+ style="@style/TextAppearance.ControlSetup.Subtitle"
+ android:id="@+id/controls_subtitle"
+ android:visibility="gone"
+ android:layout_marginTop="12dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center" />
</LinearLayout>
</merge>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
index 44c409e..b5822c8 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_section_header.xml
@@ -35,7 +35,34 @@
android:forceHasOverlappingRendering="false"
android:clipChildren="false"
>
- <include layout="@layout/status_bar_notification_section_header_contents"/>
+ <FrameLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="start|center_vertical"
+ android:layout_weight="1">
+
+ <TextView
+ style="@style/TextAppearance.NotificationSectionHeaderButton"
+ android:id="@+id/header_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:forceHasOverlappingRendering="false"
+ android:text="@string/notification_section_header_gentle"
+ />
+
+ </FrameLayout>
+ <ImageView
+ android:id="@+id/btn_clear_all"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:src="@drawable/status_bar_notification_section_header_clear_btn"
+ android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all"
+ android:scaleType="center"
+ android:tint="?attr/wallpaperTextColor"
+ android:tintMode="src_in"
+ android:visibility="gone"
+ android:forceHasOverlappingRendering="false"
+ />
</LinearLayout>
</com.android.systemui.statusbar.notification.stack.SectionHeaderView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml b/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml
deleted file mode 100644
index 3b9c44d..0000000
--- a/packages/SystemUI/res/layout/status_bar_notification_section_header_contents.xml
+++ /dev/null
@@ -1,47 +0,0 @@
-<!--
- ~ Copyright (C) 2019 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-
-<!-- Used by both status_bar_notification_header and SectionHeaderView -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android" >
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="start|center_vertical"
- android:layout_weight="1">
-
- <TextView
- style="@style/TextAppearance.NotificationSectionHeaderButton"
- android:id="@+id/header_label"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:forceHasOverlappingRendering="false"
- android:text="@string/notification_section_header_gentle"
- />
-
- </FrameLayout>
- <ImageView
- android:id="@+id/btn_clear_all"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:src="@drawable/status_bar_notification_section_header_clear_btn"
- android:contentDescription="@string/accessibility_notification_section_header_gentle_clear_all"
- android:scaleType="center"
- android:tint="?attr/wallpaperTextColor"
- android:tintMode="src_in"
- android:visibility="gone"
- android:forceHasOverlappingRendering="false"
- />
-</merge>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 06e027d..4d6b759 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -532,4 +532,8 @@
<!-- Respect drawable/rounded.xml intrinsic size for multiple radius corner path customization -->
<bool name="config_roundedCornerMultipleRadius">false</bool>
+ <!-- Controls can query a preferred application for limited number of suggested controls.
+ This config value should contain the package name of that preferred application.
+ -->
+ <string translatable="false" name="config_controlsPreferredPackage"></string>
</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 93bafdb..cb08840 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2657,4 +2657,8 @@
<!-- Tooltip to show in management screen when there are multiple structures [CHAR_LIMIT=50] -->
<string name="controls_structure_tooltip">Swipe to see other structures</string>
+
+ <!-- Message to tell the user to wait while systemui attempts to load a set of
+ recommended controls [CHAR_LIMIT=30] -->
+ <string name="controls_seeding_in_progress">Loading recommendations</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 20b88a1..1283fe0 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -699,6 +699,20 @@
<item name="*android:colorPopupBackground">@color/control_list_popup_background</item>
</style>
+ <style name="TextAppearance.ControlSetup">
+ <item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
+ <item name="android:textColor">@color/control_primary_text</item>
+ <item name="android:singleLine">true</item>
+ </style>
+
+ <style name="TextAppearance.ControlSetup.Title">
+ <item name="android:textSize">25sp</item>
+ </style>
+
+ <style name="TextAppearance.ControlSetup.Subtitle">
+ <item name="android:textSize">16sp</item>
+ </style>
+
<style name="Theme.ControlsRequestDialog" parent="@style/Theme.SystemUI.MediaProjectionAlertDialog"/>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index a8a3cae..5f004a6 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -122,7 +122,7 @@
protected int mRoundedDefaultBottom;
@VisibleForTesting
protected View[] mOverlays;
- private DisplayCutoutView[] mCutoutViews;
+ private DisplayCutoutView[] mCutoutViews = new DisplayCutoutView[BOUNDS_POSITION_LENGTH];
private float mDensity;
private WindowManager mWindowManager;
private int mRotation;
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
index c5af436..d4d4d2a 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt
@@ -43,6 +43,14 @@
fun bindAndLoad(component: ComponentName, callback: LoadCallback): Runnable
/**
+ * Request bind to a service and load a limited number of suggested controls.
+ *
+ * @param component The [ComponentName] of the service to bind
+ * @param callback a callback to return the loaded controls to (or an error).
+ */
+ fun bindAndLoadSuggested(component: ComponentName, callback: LoadCallback)
+
+ /**
* Request to bind to the given service.
*
* @param component The [ComponentName] of the service to bind
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
index f8d4a39..5d03fc5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt
@@ -44,6 +44,8 @@
companion object {
private const val TAG = "ControlsBindingControllerImpl"
+ private const val MAX_CONTROLS_REQUEST = 100000L
+ private const val SUGGESTED_CONTROLS_REQUEST = 4L
}
private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
@@ -97,24 +99,37 @@
component: ComponentName,
callback: ControlsBindingController.LoadCallback
): Runnable {
- val subscriber = LoadSubscriber(callback)
+ val subscriber = LoadSubscriber(callback, MAX_CONTROLS_REQUEST)
retrieveLifecycleManager(component).maybeBindAndLoad(subscriber)
return subscriber.loadCancel()
}
+ override fun bindAndLoadSuggested(
+ component: ComponentName,
+ callback: ControlsBindingController.LoadCallback
+ ) {
+ val subscriber = LoadSubscriber(callback, SUGGESTED_CONTROLS_REQUEST)
+ retrieveLifecycleManager(component).maybeBindAndLoadSuggested(subscriber)
+ }
+
override fun subscribe(structureInfo: StructureInfo) {
// make sure this has happened. only allow one active subscription
unsubscribe()
- statefulControlSubscriber = null
val provider = retrieveLifecycleManager(structureInfo.componentName)
- val scs = StatefulControlSubscriber(lazyController.get(), provider, backgroundExecutor)
+ val scs = StatefulControlSubscriber(
+ lazyController.get(),
+ provider,
+ backgroundExecutor,
+ MAX_CONTROLS_REQUEST
+ )
statefulControlSubscriber = scs
provider.maybeBindAndSubscribe(structureInfo.controls.map { it.controlId }, scs)
}
override fun unsubscribe() {
statefulControlSubscriber?.cancel()
+ statefulControlSubscriber = null
}
override fun action(
@@ -201,10 +216,11 @@
private inner class OnSubscribeRunnable(
token: IBinder,
- val subscription: IControlsSubscription
+ val subscription: IControlsSubscription,
+ val requestLimit: Long
) : CallbackRunnable(token) {
override fun doRun() {
- provider?.startSubscription(subscription)
+ provider?.startSubscription(subscription, requestLimit)
}
}
@@ -234,7 +250,8 @@
}
private inner class LoadSubscriber(
- val callback: ControlsBindingController.LoadCallback
+ val callback: ControlsBindingController.LoadCallback,
+ val requestLimit: Long
) : IControlsSubscriber.Stub() {
val loadedControls = ArrayList<Control>()
var hasError = false
@@ -246,7 +263,7 @@
override fun onSubscribe(token: IBinder, subs: IControlsSubscription) {
_loadCancelInternal = subs::cancel
- backgroundExecutor.execute(OnSubscribeRunnable(token, subs))
+ backgroundExecutor.execute(OnSubscribeRunnable(token, subs, requestLimit))
}
override fun onNext(token: IBinder, c: Control) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
index 9e0d26c..ae75dd4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt
@@ -114,6 +114,25 @@
// FAVORITE MANAGEMENT
/**
+ * Send a request to seed favorites into the persisted XML file
+ *
+ * @param componentName the component to seed controls from
+ * @param callback true if the favorites were persisted
+ */
+ fun seedFavoritesForComponent(
+ componentName: ComponentName,
+ callback: Consumer<Boolean>
+ )
+
+ /**
+ * Callback to be informed when the seeding process has finished
+ *
+ * @param callback consumer accepts true if successful
+ * @return true if seeding is in progress and the callback was added
+ */
+ fun addSeedingFavoritesCallback(callback: Consumer<Boolean>): Boolean
+
+ /**
* Get all the favorites.
*
* @return a list of the structures that have at least one favorited control
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 9cb902f..2e34ea5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -31,6 +31,7 @@
import android.provider.Settings
import android.service.controls.Control
import android.service.controls.actions.ControlAction
+import android.util.ArrayMap
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.Dumpable
@@ -74,6 +75,9 @@
private var loadCanceller: Runnable? = null
+ private var seedingInProgress = false
+ private val seedingCallbacks = mutableListOf<Consumer<Boolean>>()
+
private var currentUser = UserHandle.of(ActivityManager.getCurrentUser())
override val currentUserId
get() = currentUser.identifier
@@ -280,6 +284,84 @@
)
}
+ override fun addSeedingFavoritesCallback(callback: Consumer<Boolean>): Boolean {
+ if (!seedingInProgress) return false
+ executor.execute {
+ // status may have changed by this point, so check again and inform the
+ // caller if necessary
+ if (seedingInProgress) seedingCallbacks.add(callback)
+ else callback.accept(false)
+ }
+ return true
+ }
+
+ override fun seedFavoritesForComponent(
+ componentName: ComponentName,
+ callback: Consumer<Boolean>
+ ) {
+ Log.i(TAG, "Beginning request to seed favorites for: $componentName")
+ if (!confirmAvailability()) {
+ if (userChanging) {
+ // Try again later, userChanging should not last forever. If so, we have bigger
+ // problems. This will return a runnable that allows to cancel the delayed version,
+ // it will not be able to cancel the load if
+ executor.executeDelayed(
+ { seedFavoritesForComponent(componentName, callback) },
+ USER_CHANGE_RETRY_DELAY,
+ TimeUnit.MILLISECONDS
+ )
+ } else {
+ callback.accept(false)
+ }
+ return
+ }
+ seedingInProgress = true
+ bindingController.bindAndLoadSuggested(
+ componentName,
+ object : ControlsBindingController.LoadCallback {
+ override fun accept(controls: List<Control>) {
+ executor.execute {
+ val structureToControls =
+ ArrayMap<CharSequence, MutableList<ControlInfo>>()
+
+ controls.forEach {
+ val structure = it.structure ?: ""
+ val list = structureToControls.get(structure)
+ ?: mutableListOf<ControlInfo>()
+ list.add(ControlInfo(it.controlId, it.title, it.deviceType))
+ structureToControls.put(structure, list)
+ }
+
+ structureToControls.forEach {
+ (s, cs) -> Favorites.replaceControls(
+ StructureInfo(componentName, s, cs))
+ }
+
+ persistenceWrapper.storeFavorites(Favorites.getAllStructures())
+ callback.accept(true)
+ endSeedingCall(true)
+ }
+ }
+
+ override fun error(message: String) {
+ Log.e(TAG, "Unable to seed favorites: $message")
+ executor.execute {
+ callback.accept(false)
+ endSeedingCall(false)
+ }
+ }
+ }
+ )
+ }
+
+ private fun endSeedingCall(state: Boolean) {
+ seedingInProgress = false
+ seedingCallbacks.forEach {
+ it.accept(state)
+ }
+ seedingCallbacks.clear()
+ }
+
override fun cancelLoad() {
loadCanceller?.let {
executor.execute(it)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index 4918bd7..209d056 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -66,22 +66,17 @@
@GuardedBy("subscriptions")
private val subscriptions = mutableListOf<IControlsSubscription>()
private var requiresBound = false
- @GuardedBy("queuedMessages")
- private val queuedMessages: MutableSet<Message> = ArraySet()
+ @GuardedBy("queuedServiceMethods")
+ private val queuedServiceMethods: MutableSet<ServiceMethod> = ArraySet()
private var wrapper: ServiceWrapper? = null
private var bindTryCount = 0
private val TAG = javaClass.simpleName
private var onLoadCanceller: Runnable? = null
companion object {
- private const val MSG_LOAD = 0
- private const val MSG_SUBSCRIBE = 1
- private const val MSG_ACTION = 2
- private const val MSG_UNBIND = 3
private const val BIND_RETRY_DELAY = 1000L // ms
private const val LOAD_TIMEOUT_SECONDS = 30L // seconds
private const val MAX_BIND_RETRIES = 5
- private const val MAX_CONTROLS_REQUEST = 100000L
private const val DEBUG = true
private val BIND_FLAGS = Context.BIND_AUTO_CREATE or Context.BIND_FOREGROUND_SERVICE or
Context.BIND_WAIVE_PRIORITY
@@ -130,7 +125,7 @@
try {
service.linkToDeath(this@ControlsProviderLifecycleManager, 0)
} catch (_: RemoteException) {}
- handlePendingMessages()
+ handlePendingServiceMethods()
}
override fun onServiceDisconnected(name: ComponentName?) {
@@ -140,29 +135,14 @@
}
}
- private fun handlePendingMessages() {
- val queue = synchronized(queuedMessages) {
- ArraySet(queuedMessages).also {
- queuedMessages.clear()
+ private fun handlePendingServiceMethods() {
+ val queue = synchronized(queuedServiceMethods) {
+ ArraySet(queuedServiceMethods).also {
+ queuedServiceMethods.clear()
}
}
- if (Message.Unbind in queue) {
- bindService(false)
- return
- }
-
- queue.filter { it is Message.Load }.forEach {
- val msg = it as Message.Load
- load(msg.subscriber)
- }
-
- queue.filter { it is Message.Subscribe }.forEach {
- val msg = it as Message.Subscribe
- subscribe(msg.list, msg.subscriber)
- }
- queue.filter { it is Message.Action }.forEach {
- val msg = it as Message.Action
- action(msg.id, msg.action)
+ queue.forEach {
+ it.run()
}
}
@@ -177,33 +157,17 @@
}
}
- private fun queueMessage(message: Message) {
- synchronized(queuedMessages) {
- queuedMessages.add(message)
+ private fun queueServiceMethod(sm: ServiceMethod) {
+ synchronized(queuedServiceMethods) {
+ queuedServiceMethods.add(sm)
}
}
- private fun unqueueMessageType(type: Int) {
- synchronized(queuedMessages) {
- queuedMessages.removeIf { it.type == type }
- }
- }
-
- private fun load(subscriber: IControlsSubscriber.Stub) {
- if (DEBUG) {
- Log.d(TAG, "load $componentName")
- }
- if (!(wrapper?.load(subscriber) ?: false)) {
- queueMessage(Message.Load(subscriber))
- binderDied()
- }
- }
-
- private inline fun invokeOrQueue(f: () -> Unit, msg: Message) {
+ private fun invokeOrQueue(sm: ServiceMethod) {
wrapper?.run {
- f()
+ sm.run()
} ?: run {
- queueMessage(msg)
+ queueServiceMethod(sm)
bindService(true)
}
}
@@ -217,7 +181,6 @@
* @param subscriber the subscriber that manages coordination for loading controls
*/
fun maybeBindAndLoad(subscriber: IControlsSubscriber.Stub) {
- unqueueMessageType(MSG_UNBIND)
onLoadCanceller = executor.executeDelayed({
// Didn't receive a response in time, log and send back error
Log.d(TAG, "Timeout waiting onLoad for $componentName")
@@ -225,7 +188,26 @@
unbindService()
}, LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
- invokeOrQueue({ load(subscriber) }, Message.Load(subscriber))
+ invokeOrQueue(Load(subscriber))
+ }
+
+ /**
+ * Request a call to [IControlsProvider.loadSuggested].
+ *
+ * If the service is not bound, the call will be queued and the service will be bound first.
+ * The service will be unbound after the controls are returned or the call times out.
+ *
+ * @param subscriber the subscriber that manages coordination for loading controls
+ */
+ fun maybeBindAndLoadSuggested(subscriber: IControlsSubscriber.Stub) {
+ onLoadCanceller = executor.executeDelayed({
+ // Didn't receive a response in time, log and send back error
+ Log.d(TAG, "Timeout waiting onLoadSuggested for $componentName")
+ subscriber.onError(token, "Timeout waiting onLoadSuggested")
+ unbindService()
+ }, LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
+
+ invokeOrQueue(Suggest(subscriber))
}
fun cancelLoadTimeout() {
@@ -240,23 +222,8 @@
*
* @param controlIds a list of the ids of controls to send status back.
*/
- fun maybeBindAndSubscribe(controlIds: List<String>, subscriber: IControlsSubscriber) {
- invokeOrQueue(
- { subscribe(controlIds, subscriber) },
- Message.Subscribe(controlIds, subscriber)
- )
- }
-
- private fun subscribe(controlIds: List<String>, subscriber: IControlsSubscriber) {
- if (DEBUG) {
- Log.d(TAG, "subscribe $componentName - $controlIds")
- }
-
- if (!(wrapper?.subscribe(controlIds, subscriber) ?: false)) {
- queueMessage(Message.Subscribe(controlIds, subscriber))
- binderDied()
- }
- }
+ fun maybeBindAndSubscribe(controlIds: List<String>, subscriber: IControlsSubscriber) =
+ invokeOrQueue(Subscribe(controlIds, subscriber))
/**
* Request a call to [ControlsProviderService.performControlAction].
@@ -266,19 +233,8 @@
* @param controlId the id of the [Control] the action is performed on
* @param action the action performed
*/
- fun maybeBindAndSendAction(controlId: String, action: ControlAction) {
- invokeOrQueue({ action(controlId, action) }, Message.Action(controlId, action))
- }
-
- private fun action(controlId: String, action: ControlAction) {
- if (DEBUG) {
- Log.d(TAG, "onAction $componentName - $controlId")
- }
- if (!(wrapper?.action(controlId, action, actionCallbackService) ?: false)) {
- queueMessage(Message.Action(controlId, action))
- binderDied()
- }
- }
+ fun maybeBindAndSendAction(controlId: String, action: ControlAction) =
+ invokeOrQueue(Action(controlId, action))
/**
* Starts the subscription to the [ControlsProviderService] and requests status of controls.
@@ -286,14 +242,14 @@
* @param subscription the subscription to use to request controls
* @see maybeBindAndLoad
*/
- fun startSubscription(subscription: IControlsSubscription) {
+ fun startSubscription(subscription: IControlsSubscription, requestLimit: Long) {
if (DEBUG) {
Log.d(TAG, "startSubscription: $subscription")
}
synchronized(subscriptions) {
subscriptions.add(subscription)
}
- wrapper?.request(subscription, MAX_CONTROLS_REQUEST)
+ wrapper?.request(subscription, requestLimit)
}
/**
@@ -316,7 +272,6 @@
* Request bind to the service.
*/
fun bindService() {
- unqueueMessageType(MSG_UNBIND)
bindService(true)
}
@@ -350,21 +305,55 @@
}
/**
- * Messages for the internal queue.
+ * Service methods that can be queued or invoked, and are retryable for failure scenarios
*/
- sealed class Message {
- abstract val type: Int
- class Load(val subscriber: IControlsSubscriber.Stub) : Message() {
- override val type = MSG_LOAD
+ abstract inner class ServiceMethod {
+ fun run() {
+ if (!callWrapper()) {
+ queueServiceMethod(this)
+ binderDied()
+ }
}
- object Unbind : Message() {
- override val type = MSG_UNBIND
+
+ internal abstract fun callWrapper(): Boolean
+ }
+
+ inner class Load(val subscriber: IControlsSubscriber.Stub) : ServiceMethod() {
+ override fun callWrapper(): Boolean {
+ if (DEBUG) {
+ Log.d(TAG, "load $componentName")
+ }
+ return wrapper?.load(subscriber) ?: false
}
- class Subscribe(val list: List<String>, val subscriber: IControlsSubscriber) : Message() {
- override val type = MSG_SUBSCRIBE
+ }
+
+ inner class Suggest(val subscriber: IControlsSubscriber.Stub) : ServiceMethod() {
+ override fun callWrapper(): Boolean {
+ if (DEBUG) {
+ Log.d(TAG, "suggest $componentName")
+ }
+ return wrapper?.loadSuggested(subscriber) ?: false
}
- class Action(val id: String, val action: ControlAction) : Message() {
- override val type = MSG_ACTION
+ }
+ inner class Subscribe(
+ val list: List<String>,
+ val subscriber: IControlsSubscriber
+ ) : ServiceMethod() {
+ override fun callWrapper(): Boolean {
+ if (DEBUG) {
+ Log.d(TAG, "subscribe $componentName - $list")
+ }
+
+ return wrapper?.subscribe(list, subscriber) ?: false
+ }
+ }
+
+ inner class Action(val id: String, val action: ControlAction) : ServiceMethod() {
+ override fun callWrapper(): Boolean {
+ if (DEBUG) {
+ Log.d(TAG, "onAction $componentName - $id")
+ }
+ return wrapper?.action(id, action, actionCallbackService) ?: false
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
index b2afd3c..2c717f5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ServiceWrapper.kt
@@ -50,6 +50,12 @@
}
}
+ fun loadSuggested(subscriber: IControlsSubscriber): Boolean {
+ return callThroughService {
+ service.loadSuggested(subscriber)
+ }
+ }
+
fun subscribe(controlIds: List<String>, subscriber: IControlsSubscriber): Boolean {
return callThroughService {
service.subscribe(controlIds, subscriber)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
index a371aa6..e3eabff 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/StatefulControlSubscriber.kt
@@ -31,7 +31,8 @@
class StatefulControlSubscriber(
private val controller: ControlsController,
private val provider: ControlsProviderLifecycleManager,
- private val bgExecutor: DelayableExecutor
+ private val bgExecutor: DelayableExecutor,
+ private val requestLimit: Long
) : IControlsSubscriber.Stub() {
private var subscriptionOpen = false
private var subscription: IControlsSubscription? = null
@@ -50,7 +51,7 @@
run(token) {
subscriptionOpen = true
subscription = subs
- provider.startSubscription(subs)
+ provider.startSubscription(subs, requestLimit)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
index a7fc2ac8..74a6c87 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsRequestDialog.kt
@@ -55,7 +55,7 @@
private lateinit var control: Control
private var dialog: Dialog? = null
private val callback = object : ControlsListingController.ControlsListingCallback {
- override fun onServicesUpdated(candidates: List<ControlsServiceInfo>) {}
+ override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {}
}
private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 138cd47..ffae4653 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -52,6 +52,7 @@
import dagger.Lazy
import java.text.Collator
+import java.util.function.Consumer
import javax.inject.Inject
import javax.inject.Singleton
@@ -89,6 +90,7 @@
private var popup: ListPopupWindow? = null
private var activeDialog: Dialog? = null
private val addControlsItem: SelectionItem
+ private var hidden = true
init {
val addDrawable = context.getDrawable(R.drawable.ic_add).apply {
@@ -134,11 +136,15 @@
override fun show(parent: ViewGroup) {
Log.d(ControlsUiController.TAG, "show()")
this.parent = parent
+ hidden = false
allStructures = controlsController.get().getFavorites()
selectedStructure = loadPreference(allStructures)
- if (selectedStructure.controls.isEmpty() && allStructures.size <= 1) {
+ val cb = Consumer<Boolean> { _ -> reload(parent) }
+ if (controlsController.get().addSeedingFavoritesCallback(cb)) {
+ listingCallback = createCallback(::showSeedingView)
+ } else if (selectedStructure.controls.isEmpty() && allStructures.size <= 1) {
// only show initial view if there are really no favorites across any structure
listingCallback = createCallback(::showInitialSetupView)
} else {
@@ -154,6 +160,20 @@
controlsListingController.get().addCallback(listingCallback)
}
+ private fun reload(parent: ViewGroup) {
+ if (hidden) return
+ show(parent)
+ }
+
+ private fun showSeedingView(items: List<SelectionItem>) {
+ parent.removeAllViews()
+
+ val inflater = LayoutInflater.from(context)
+ inflater.inflate(R.layout.controls_no_favorites, parent, true)
+ val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle)
+ subtitle.setVisibility(View.VISIBLE)
+ }
+
private fun showInitialSetupView(items: List<SelectionItem>) {
parent.removeAllViews()
@@ -320,13 +340,14 @@
selectedStructure = newSelection
updatePreferences(selectedStructure)
controlsListingController.get().removeCallback(listingCallback)
- show(parent)
+ reload(parent)
}
}
}
override fun hide() {
Log.d(ControlsUiController.TAG, "hide()")
+ hidden = true
popup?.dismiss()
activeDialog?.dismiss()
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index b99d765..73539f9 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -31,11 +31,13 @@
import android.app.admin.DevicePolicyManager;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -92,6 +94,8 @@
import com.android.systemui.MultiListLayout.MultiListAdapter;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.controls.ControlsServiceInfo;
+import com.android.systemui.controls.controller.ControlsController;
import com.android.systemui.controls.management.ControlsListingController;
import com.android.systemui.controls.ui.ControlsUiController;
import com.android.systemui.dagger.qualifiers.Background;
@@ -148,6 +152,9 @@
private static final String GLOBAL_ACTION_KEY_EMERGENCY = "emergency";
private static final String GLOBAL_ACTION_KEY_SCREENSHOT = "screenshot";
+ private static final String PREFS_CONTROLS_SEEDING_COMPLETED = "ControlsSeedingCompleted";
+ private static final String PREFS_CONTROLS_FILE = "controls_prefs";
+
private final Context mContext;
private final GlobalActionsManager mWindowManagerFuncs;
private final AudioManager mAudioManager;
@@ -215,7 +222,8 @@
NotificationShadeWindowController notificationShadeWindowController,
ControlsUiController controlsUiController, IWindowManager iWindowManager,
@Background Executor backgroundExecutor,
- ControlsListingController controlsListingController) {
+ ControlsListingController controlsListingController,
+ ControlsController controlsController) {
mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
mWindowManagerFuncs = windowManagerFuncs;
mAudioManager = audioManager;
@@ -279,9 +287,46 @@
}
});
- mControlsListingController.addCallback(list -> mAnyControlsProviders = !list.isEmpty());
+ String preferredControlsPackage = mContext.getResources()
+ .getString(com.android.systemui.R.string.config_controlsPreferredPackage);
+ mControlsListingController.addCallback(list -> {
+ mAnyControlsProviders = !list.isEmpty();
+
+ /*
+ * See if any service providers match the preferred component. If they do,
+ * and there are no current favorites, and we haven't successfully loaded favorites to
+ * date, query the preferred component for a limited number of suggested controls.
+ */
+ ComponentName preferredComponent = null;
+ for (ControlsServiceInfo info : list) {
+ if (info.componentName.getPackageName().equals(preferredControlsPackage)) {
+ preferredComponent = info.componentName;
+ break;
+ }
+ }
+
+ if (preferredComponent == null) return;
+
+ SharedPreferences prefs = context.getSharedPreferences(PREFS_CONTROLS_FILE,
+ Context.MODE_PRIVATE);
+ boolean isSeeded = prefs.getBoolean(PREFS_CONTROLS_SEEDING_COMPLETED, false);
+ boolean hasFavorites = controlsController.getFavorites().size() > 0;
+ if (!isSeeded && !hasFavorites) {
+ controlsController.seedFavoritesForComponent(
+ preferredComponent,
+ (accepted) -> {
+ Log.i(TAG, "Controls seeded: " + accepted);
+ prefs.edit().putBoolean(PREFS_CONTROLS_SEEDING_COMPLETED,
+ accepted).apply();
+ }
+ );
+ }
+ });
}
+
+
+
/**
* Show the global actions dialog (creating if necessary)
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index 42a7c6a..f6f8363 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -447,7 +447,6 @@
}
}
-
@VisibleForTesting
ExpandableView getGentleHeaderView() {
return mGentleHeader;
@@ -471,7 +470,7 @@
private final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
@Override
public void onLocaleListChanged() {
- mGentleHeader.reinflateContents();
+ reinflateViews(LayoutInflater.from(mParent.getContext()));
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
index 1b4f98f..bc25c71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/PeopleHubView.kt
@@ -76,9 +76,7 @@
}
}
- override fun needsClippingToShelf(): Boolean {
- return true
- }
+ override fun needsClippingToShelf(): Boolean = true
override fun applyContentTransformation(contentAlpha: Float, translationY: Float) {
super.applyContentTransformation(contentAlpha, translationY)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
index deb5532..a3d8eec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/SectionHeaderView.java
@@ -20,7 +20,6 @@
import android.annotation.StringRes;
import android.content.Context;
import android.util.AttributeSet;
-import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -30,16 +29,17 @@
import com.android.systemui.R;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
-import java.util.Objects;
-
/**
- * Similar in size and appearance to the NotificationShelf, appears at the beginning of some
- * notification sections. Currently only used for gentle notifications.
+ * Header displayed above a notification section in the shade. Currently used for Alerting and
+ * Silent sections.
*/
public class SectionHeaderView extends StackScrollerDecorView {
+
private ViewGroup mContents;
private TextView mLabelView;
private ImageView mClearAllButton;
+ @StringRes @Nullable private Integer mLabelTextId;
+ @Nullable private View.OnClickListener mLabelClickListener = null;
@Nullable private View.OnClickListener mOnClearClickListener = null;
public SectionHeaderView(Context context, AttributeSet attrs) {
@@ -48,18 +48,24 @@
@Override
protected void onFinishInflate() {
- mContents = Objects.requireNonNull(findViewById(R.id.content));
+ mContents = requireViewById(R.id.content);
bindContents();
super.onFinishInflate();
setVisible(true /* nowVisible */, false /* animate */);
}
private void bindContents() {
- mLabelView = Objects.requireNonNull(findViewById(R.id.header_label));
- mClearAllButton = Objects.requireNonNull(findViewById(R.id.btn_clear_all));
+ mLabelView = requireViewById(R.id.header_label);
+ mClearAllButton = requireViewById(R.id.btn_clear_all);
if (mOnClearClickListener != null) {
mClearAllButton.setOnClickListener(mOnClearClickListener);
}
+ if (mLabelClickListener != null) {
+ mLabelView.setOnClickListener(mLabelClickListener);
+ }
+ if (mLabelTextId != null) {
+ mLabelView.setText(mLabelTextId);
+ }
}
@Override
@@ -72,21 +78,6 @@
return null;
}
- /**
- * Destroys and reinflates the visible contents of the section header. For use on configuration
- * changes or any other time that layout values might need to be re-evaluated.
- *
- * Does not reinflate the base content view itself ({@link #findContentView()} or any of the
- * decorator views, such as the background view or shadow view.
- */
- void reinflateContents() {
- mContents.removeAllViews();
- LayoutInflater.from(getContext()).inflate(
- R.layout.status_bar_notification_section_header_contents,
- mContents);
- bindContents();
- }
-
@Override
public boolean isTransparent() {
return true;
@@ -105,6 +96,7 @@
* Fired whenever the user clicks on the body of the header (e.g. no sub-buttons or anything).
*/
void setOnHeaderClickListener(View.OnClickListener listener) {
+ mLabelClickListener = listener;
mLabelView.setOnClickListener(listener);
}
@@ -129,6 +121,7 @@
}
void setHeaderText(@StringRes int resId) {
+ mLabelTextId = resId;
mLabelView.setText(resId);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
index c25d4e2..2ff2b2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsBindingControllerImplTest.kt
@@ -207,6 +207,56 @@
}
@Test
+ fun testBindAndLoadSuggested() {
+ val callback = object : ControlsBindingController.LoadCallback {
+ override fun error(message: String) {}
+
+ override fun accept(t: List<Control>) {}
+ }
+ controller.bindAndLoadSuggested(TEST_COMPONENT_NAME_1, callback)
+
+ verify(providers[0]).maybeBindAndLoadSuggested(any())
+ }
+
+ @Test
+ fun testLoadSuggested_onCompleteRemovesTimeout() {
+ val callback = object : ControlsBindingController.LoadCallback {
+ override fun error(message: String) {}
+
+ override fun accept(t: List<Control>) {}
+ }
+ val subscription = mock(IControlsSubscription::class.java)
+
+ controller.bindAndLoadSuggested(TEST_COMPONENT_NAME_1, callback)
+
+ verify(providers[0]).maybeBindAndLoadSuggested(capture(subscriberCaptor))
+ val b = Binder()
+ subscriberCaptor.value.onSubscribe(b, subscription)
+
+ subscriberCaptor.value.onComplete(b)
+ verify(providers[0]).cancelLoadTimeout()
+ }
+
+ @Test
+ fun testLoadSuggested_onErrorRemovesTimeout() {
+ val callback = object : ControlsBindingController.LoadCallback {
+ override fun error(message: String) {}
+
+ override fun accept(t: List<Control>) {}
+ }
+ val subscription = mock(IControlsSubscription::class.java)
+
+ controller.bindAndLoadSuggested(TEST_COMPONENT_NAME_1, callback)
+
+ verify(providers[0]).maybeBindAndLoadSuggested(capture(subscriberCaptor))
+ val b = Binder()
+ subscriberCaptor.value.onSubscribe(b, subscription)
+
+ subscriberCaptor.value.onError(b, "")
+ verify(providers[0]).cancelLoadTimeout()
+ }
+
+ @Test
fun testBindService() {
controller.bindService(TEST_COMPONENT_NAME_1)
executor.runAllReady()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
index f9c9815..971d14c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt
@@ -80,6 +80,10 @@
@Captor
private lateinit var structureInfoCaptor: ArgumentCaptor<StructureInfo>
+
+ @Captor
+ private lateinit var booleanConsumer: ArgumentCaptor<Consumer<Boolean>>
+
@Captor
private lateinit var controlLoadCallbackCaptor:
ArgumentCaptor<ControlsBindingController.LoadCallback>
@@ -155,9 +159,13 @@
verify(listingController).addCallback(capture(listingCallbackCaptor))
}
- private fun builderFromInfo(controlInfo: ControlInfo): Control.StatelessBuilder {
+ private fun builderFromInfo(
+ controlInfo: ControlInfo,
+ structure: CharSequence = ""
+ ): Control.StatelessBuilder {
return Control.StatelessBuilder(controlInfo.controlId, pendingIntent)
.setDeviceType(controlInfo.deviceType).setTitle(controlInfo.controlTitle)
+ .setStructure(structure)
}
@Test
@@ -746,4 +754,70 @@
inOrder.verify(persistenceWrapper).readFavorites()
inOrder.verify(listingController).addCallback(listingCallbackCaptor.value)
}
+
+ @Test
+ fun testSeedFavoritesForComponent() {
+ var succeeded = false
+ val control = builderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure).build()
+
+ controller.seedFavoritesForComponent(TEST_COMPONENT, Consumer { accepted ->
+ succeeded = accepted
+ })
+
+ verify(bindingController).bindAndLoadSuggested(eq(TEST_COMPONENT),
+ capture(controlLoadCallbackCaptor))
+
+ controlLoadCallbackCaptor.value.accept(listOf(control))
+
+ delayableExecutor.runAllReady()
+
+ assertEquals(listOf(TEST_STRUCTURE_INFO),
+ controller.getFavoritesForComponent(TEST_COMPONENT))
+ assertTrue(succeeded)
+ }
+
+ @Test
+ fun testSeedFavoritesForComponent_error() {
+ var succeeded = false
+
+ controller.seedFavoritesForComponent(TEST_COMPONENT, Consumer { accepted ->
+ succeeded = accepted
+ })
+
+ verify(bindingController).bindAndLoadSuggested(eq(TEST_COMPONENT),
+ capture(controlLoadCallbackCaptor))
+
+ controlLoadCallbackCaptor.value.error("Error loading")
+
+ delayableExecutor.runAllReady()
+
+ assertEquals(listOf<StructureInfo>(), controller.getFavoritesForComponent(TEST_COMPONENT))
+ assertFalse(succeeded)
+ }
+
+ @Test
+ fun testSeedFavoritesForComponent_inProgressCallback() {
+ var succeeded = false
+ var seeded = false
+ val control = builderFromInfo(TEST_CONTROL_INFO, TEST_STRUCTURE_INFO.structure).build()
+
+ controller.seedFavoritesForComponent(TEST_COMPONENT, Consumer { accepted ->
+ succeeded = accepted
+ })
+
+ verify(bindingController).bindAndLoadSuggested(eq(TEST_COMPONENT),
+ capture(controlLoadCallbackCaptor))
+
+ controller.addSeedingFavoritesCallback(Consumer { accepted ->
+ seeded = accepted
+ })
+ controlLoadCallbackCaptor.value.accept(listOf(control))
+
+ delayableExecutor.runAllReady()
+
+ assertEquals(listOf(TEST_STRUCTURE_INFO),
+ controller.getFavoritesForComponent(TEST_COMPONENT))
+ assertTrue(succeeded)
+ assertTrue(seeded)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
index cd82844..789d6df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt
@@ -92,6 +92,22 @@
}
@Test
+ fun testLoadSuggested_happyPath() {
+ val result = wrapper.loadSuggested(subscriber)
+
+ assertTrue(result)
+ verify(service).loadSuggested(subscriber)
+ }
+
+ @Test
+ fun testLoadSuggested_error() {
+ `when`(service.loadSuggested(any())).thenThrow(exception)
+ val result = wrapper.loadSuggested(subscriber)
+
+ assertFalse(result)
+ }
+
+ @Test
fun testSubscribe_happyPath() {
val list = listOf("TEST_ID")
val result = wrapper.subscribe(list, subscriber)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
index ff5c8d4..267520e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt
@@ -59,13 +59,15 @@
private lateinit var scs: StatefulControlSubscriber
+ private val REQUEST_LIMIT = 5L
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
`when`(provider.componentName).thenReturn(TEST_COMPONENT)
`when`(provider.token).thenReturn(token)
- scs = StatefulControlSubscriber(controller, provider, executor)
+ scs = StatefulControlSubscriber(controller, provider, executor, REQUEST_LIMIT)
}
@Test
@@ -73,7 +75,7 @@
scs.onSubscribe(token, subscription)
executor.runAllReady()
- verify(provider).startSubscription(subscription)
+ verify(provider).startSubscription(subscription, REQUEST_LIMIT)
}
@Test
@@ -81,7 +83,7 @@
scs.onSubscribe(badToken, subscription)
executor.runAllReady()
- verify(provider, never()).startSubscription(subscription)
+ verify(provider, never()).startSubscription(subscription, REQUEST_LIMIT)
}
@Test
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ebca1f7..a458498 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -579,6 +579,13 @@
static final String EXTRA_DESCRIPTION = "android.intent.extra.DESCRIPTION";
static final String EXTRA_BUGREPORT_TYPE = "android.intent.extra.BUGREPORT_TYPE";
+ /**
+ * The maximum number of bytes that {@link #setProcessStateSummary} accepts.
+ *
+ * @see {@link android.app.ActivityManager#setProcessStateSummary(byte[])}
+ */
+ static final int MAX_STATE_DATA_SIZE = 128;
+
/** All system services */
SystemServiceManager mSystemServiceManager;
@@ -3202,7 +3209,7 @@
return mAtmInternal.compatibilityInfoForPackage(ai);
}
- private void enforceNotIsolatedCaller(String caller) {
+ /* package */ void enforceNotIsolatedCaller(String caller) {
if (UserHandle.isIsolated(Binder.getCallingUid())) {
throw new SecurityException("Isolated process not allowed to call " + caller);
}
@@ -3887,6 +3894,18 @@
public static File dumpStackTraces(ArrayList<Integer> firstPids,
ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile) {
+ return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePids,
+ logExceptionCreatingFile, null);
+ }
+
+ /**
+ * @param firstPidOffsets Optional, when it's set, it receives the start/end offset
+ * of the very first pid to be dumped.
+ */
+ /* package */ static File dumpStackTraces(ArrayList<Integer> firstPids,
+ ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
+ ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile,
+ long[] firstPidOffsets) {
ArrayList<Integer> extraPids = null;
Slog.i(TAG, "dumpStackTraces pids=" + lastPids + " nativepids=" + nativePids);
@@ -3938,12 +3957,22 @@
return null;
}
- dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids);
+ Pair<Long, Long> offsets = dumpStackTraces(
+ tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids);
+ if (firstPidOffsets != null) {
+ if (offsets == null) {
+ firstPidOffsets[0] = firstPidOffsets[1] = -1;
+ } else {
+ firstPidOffsets[0] = offsets.first; // Start offset to the ANR trace file
+ firstPidOffsets[1] = offsets.second; // End offset to the ANR trace file
+ }
+ }
return tracesFile;
}
@GuardedBy("ActivityManagerService.class")
private static SimpleDateFormat sAnrFileDateFormat;
+ static final String ANR_FILE_PREFIX = "anr_";
private static synchronized File createAnrDumpFile(File tracesDir) throws IOException {
if (sAnrFileDateFormat == null) {
@@ -3951,7 +3980,7 @@
}
final String formattedDate = sAnrFileDateFormat.format(new Date());
- final File anrFile = new File(tracesDir, "anr_" + formattedDate);
+ final File anrFile = new File(tracesDir, ANR_FILE_PREFIX + formattedDate);
if (anrFile.createNewFile()) {
FileUtils.setPermissions(anrFile.getAbsolutePath(), 0600, -1, -1); // -rw-------
@@ -4020,7 +4049,10 @@
return SystemClock.elapsedRealtime() - timeStart;
}
- public static void dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
+ /**
+ * @return The start/end offset of the trace of the very first PID
+ */
+ public static Pair<Long, Long> dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) {
Slog.i(TAG, "Dumping to " + tracesFile);
@@ -4032,21 +4064,39 @@
// We must complete all stack dumps within 20 seconds.
long remainingTime = 20 * 1000;
+ // As applications are usually interested with the ANR stack traces, but we can't share with
+ // them the stack traces other than their own stacks. So after the very first PID is
+ // dumped, remember the current file size.
+ long firstPidStart = -1;
+ long firstPidEnd = -1;
+
// First collect all of the stacks of the most important pids.
if (firstPids != null) {
int num = firstPids.size();
for (int i = 0; i < num; i++) {
- Slog.i(TAG, "Collecting stacks for pid " + firstPids.get(i));
- final long timeTaken = dumpJavaTracesTombstoned(firstPids.get(i), tracesFile,
+ final int pid = firstPids.get(i);
+ // We don't copy ANR traces from the system_server intentionally.
+ final boolean firstPid = i == 0 && MY_PID != pid;
+ File tf = null;
+ if (firstPid) {
+ tf = new File(tracesFile);
+ firstPidStart = tf.exists() ? tf.length() : 0;
+ }
+
+ Slog.i(TAG, "Collecting stacks for pid " + pid);
+ final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile,
remainingTime);
remainingTime -= timeTaken;
if (remainingTime <= 0) {
- Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + firstPids.get(i) +
- "); deadline exceeded.");
- return;
+ Slog.e(TAG, "Aborting stack trace dump (current firstPid=" + pid
+ + "); deadline exceeded.");
+ return firstPidStart >= 0 ? new Pair<>(firstPidStart, firstPidEnd) : null;
}
+ if (firstPid) {
+ firstPidEnd = tf.length();
+ }
if (DEBUG_ANR) {
Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
}
@@ -4068,7 +4118,7 @@
if (remainingTime <= 0) {
Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +
"); deadline exceeded.");
- return;
+ return firstPidStart >= 0 ? new Pair<>(firstPidStart, firstPidEnd) : null;
}
if (DEBUG_ANR) {
@@ -4088,7 +4138,7 @@
if (remainingTime <= 0) {
Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid +
"); deadline exceeded.");
- return;
+ return firstPidStart >= 0 ? new Pair<>(firstPidStart, firstPidEnd) : null;
}
if (DEBUG_ANR) {
@@ -4097,6 +4147,7 @@
}
}
Slog.i(TAG, "Done dumping");
+ return firstPidStart >= 0 ? new Pair<>(firstPidStart, firstPidEnd) : null;
}
@Override
@@ -10280,6 +10331,15 @@
return new ParceledListSlice<ApplicationExitInfo>(results);
}
+ @Override
+ public void setProcessStateSummary(@Nullable byte[] state) {
+ if (state != null && state.length > MAX_STATE_DATA_SIZE) {
+ throw new IllegalArgumentException("Data size is too large");
+ }
+ mProcessList.mAppExitInfoTracker.setProcessStateSummary(Binder.getCallingUid(),
+ Binder.getCallingPid(), state);
+ }
+
/**
* Check if the calling process has the permission to dump given package,
* throw SecurityException if it doesn't have the permission.
@@ -10287,7 +10347,7 @@
* @return The UID of the given package, or {@link android.os.Process#INVALID_UID}
* if the package is not found.
*/
- private int enforceDumpPermissionForPackage(String packageName, int userId, int callingUid,
+ int enforceDumpPermissionForPackage(String packageName, int userId, int callingUid,
String function) {
long identity = Binder.clearCallingIdentity();
int uid = Process.INVALID_UID;
diff --git a/services/core/java/com/android/server/am/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index 028a059..0c3d02d 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -17,23 +17,31 @@
package com.android.server.am;
import static android.app.ActivityManager.RunningAppProcessInfo.procStateToImportance;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import android.annotation.Nullable;
import android.app.ApplicationExitInfo;
import android.app.ApplicationExitInfo.Reason;
import android.app.ApplicationExitInfo.SubReason;
+import android.app.IAppTraceRetriever;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.icu.text.SimpleDateFormat;
+import android.os.Binder;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.system.OsConstants;
@@ -52,12 +60,17 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ProcessMap;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.IoThread;
import com.android.server.ServiceThread;
import com.android.server.SystemServiceManager;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -68,6 +81,10 @@
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.zip.GZIPOutputStream;
/**
* A class to manage all the {@link android.app.ApplicationExitInfo} records.
@@ -80,16 +97,21 @@
*/
private static final long APP_EXIT_INFO_PERSIST_INTERVAL = TimeUnit.MINUTES.toMillis(30);
- /** These are actions that the forEachPackage should take after each iteration */
+ /** These are actions that the forEach* should take after each iteration */
private static final int FOREACH_ACTION_NONE = 0;
- private static final int FOREACH_ACTION_REMOVE_PACKAGE = 1;
+ private static final int FOREACH_ACTION_REMOVE_ITEM = 1;
private static final int FOREACH_ACTION_STOP_ITERATION = 2;
private static final int APP_EXIT_RAW_INFO_POOL_SIZE = 8;
@VisibleForTesting
+ static final String APP_EXIT_STORE_DIR = "procexitstore";
+
+ @VisibleForTesting
static final String APP_EXIT_INFO_FILE = "procexitinfo";
+ private static final String APP_TRACE_FILE_SUFFIX = ".gz";
+
private final Object mLock = new Object();
/**
@@ -153,6 +175,13 @@
final ArrayList<ApplicationExitInfo> mTmpInfoList2 = new ArrayList<ApplicationExitInfo>();
/**
+ * The path to the directory which includes the historical proc exit info file
+ * as specified in {@link #mProcExitInfoFile}, as well as the associated trace files.
+ */
+ @VisibleForTesting
+ File mProcExitStoreDir;
+
+ /**
* The path to the historical proc exit info file, persisted in the storage.
*/
@VisibleForTesting
@@ -176,6 +205,35 @@
final AppExitInfoExternalSource mAppExitInfoSourceLmkd =
new AppExitInfoExternalSource("lmkd", ApplicationExitInfo.REASON_LOW_MEMORY);
+ /**
+ * The active per-UID/PID state data set by
+ * {@link android.app.ActivityManager#setProcessStateSummary};
+ * these state data are to be "claimed" when its process dies, by then the data will be moved
+ * from this list to the new instance of ApplicationExitInfo.
+ *
+ * <p> The mapping here is UID -> PID -> state </p>
+ *
+ * @see android.app.ActivityManager#setProcessStateSummary(byte[])
+ */
+ @GuardedBy("mLock")
+ final SparseArray<SparseArray<byte[]>> mActiveAppStateSummary = new SparseArray<>();
+
+ /**
+ * The active per-UID/PID trace file when an ANR occurs but the process hasn't been killed yet,
+ * each record is a path to the actual trace file; these files are to be "claimed"
+ * when its process dies, by then the "ownership" of the files will be transferred
+ * from this list to the new instance of ApplicationExitInfo.
+ *
+ * <p> The mapping here is UID -> PID -> file </p>
+ */
+ @GuardedBy("mLock")
+ final SparseArray<SparseArray<File>> mActiveAppTraces = new SparseArray<>();
+
+ /**
+ * The implementation of the interface IAppTraceRetriever.
+ */
+ final AppTraceRetriever mAppTraceRetriever = new AppTraceRetriever();
+
AppExitInfoTracker() {
mData = new ProcessMap<AppExitInfoContainer>();
mRawRecordsPool = new SynchronizedPool<ApplicationExitInfo>(APP_EXIT_RAW_INFO_POOL_SIZE);
@@ -187,7 +245,13 @@
THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
thread.start();
mKillHandler = new KillHandler(thread.getLooper());
- mProcExitInfoFile = new File(SystemServiceManager.ensureSystemDir(), APP_EXIT_INFO_FILE);
+
+ mProcExitStoreDir = new File(SystemServiceManager.ensureSystemDir(), APP_EXIT_STORE_DIR);
+ if (!FileUtils.createDir(mProcExitStoreDir)) {
+ Slog.e(TAG, "Unable to create " + mProcExitStoreDir);
+ return;
+ }
+ mProcExitInfoFile = new File(mProcExitStoreDir, APP_EXIT_INFO_FILE);
mAppExitInfoHistoryListSize = service.mContext.getResources().getInteger(
com.android.internal.R.integer.config_app_exit_info_history_list_size);
@@ -304,7 +368,7 @@
+ "(" + raw.getPid() + "/u" + raw.getRealUid() + ")");
}
- ApplicationExitInfo info = getExitInfo(raw.getPackageName(),
+ ApplicationExitInfo info = getExitInfoLocked(raw.getPackageName(),
raw.getPackageUid(), raw.getPid());
// query zygote and lmkd to get the exit info, and clear the saved info
@@ -312,7 +376,7 @@
raw.getPid(), raw.getRealUid());
Pair<Long, Object> lmkd = mAppExitInfoSourceLmkd.remove(
raw.getPid(), raw.getRealUid());
- mIsolatedUidRecords.removeIsolatedUid(raw.getRealUid());
+ mIsolatedUidRecords.removeIsolatedUidLocked(raw.getRealUid());
if (info == null) {
info = addExitInfoLocked(raw);
@@ -333,7 +397,7 @@
@VisibleForTesting
@GuardedBy("mLock")
void handleNoteAppKillLocked(final ApplicationExitInfo raw) {
- ApplicationExitInfo info = getExitInfo(
+ ApplicationExitInfo info = getExitInfoLocked(
raw.getPackageName(), raw.getPackageUid(), raw.getPid());
if (info == null) {
@@ -359,7 +423,7 @@
final String[] packages = raw.getPackageList();
final int uid = raw.getPackageUid();
for (int i = 0; i < packages.length; i++) {
- addExitInfoInner(packages[i], uid, info);
+ addExitInfoInnerLocked(packages[i], uid, info);
}
schedulePersistProcessExitInfo(false);
@@ -400,39 +464,37 @@
*
* @return true if a recond is updated
*/
- private boolean updateExitInfoIfNecessary(int pid, int uid, Integer status, Integer reason) {
- synchronized (mLock) {
- if (UserHandle.isIsolated(uid)) {
- Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
- if (k != null) {
- uid = k;
- }
- }
- ArrayList<ApplicationExitInfo> tlist = mTmpInfoList;
- tlist.clear();
- final int targetUid = uid;
- forEachPackage((packageName, records) -> {
- AppExitInfoContainer container = records.get(targetUid);
- if (container == null) {
- return FOREACH_ACTION_NONE;
- }
- tlist.clear();
- container.getExitInfoLocked(pid, 1, tlist);
- if (tlist.size() == 0) {
- return FOREACH_ACTION_NONE;
- }
- ApplicationExitInfo info = tlist.get(0);
- if (info.getRealUid() != targetUid) {
- tlist.clear();
- return FOREACH_ACTION_NONE;
- }
- // Okay found it, update its reason.
- updateExistingExitInfoRecordLocked(info, status, reason);
-
- return FOREACH_ACTION_STOP_ITERATION;
- });
- return tlist.size() > 0;
+ @GuardedBy("mLock")
+ private boolean updateExitInfoIfNecessaryLocked(
+ int pid, int uid, Integer status, Integer reason) {
+ Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
+ if (k != null) {
+ uid = k;
}
+ ArrayList<ApplicationExitInfo> tlist = mTmpInfoList;
+ tlist.clear();
+ final int targetUid = uid;
+ forEachPackageLocked((packageName, records) -> {
+ AppExitInfoContainer container = records.get(targetUid);
+ if (container == null) {
+ return FOREACH_ACTION_NONE;
+ }
+ tlist.clear();
+ container.getExitInfoLocked(pid, 1, tlist);
+ if (tlist.size() == 0) {
+ return FOREACH_ACTION_NONE;
+ }
+ ApplicationExitInfo info = tlist.get(0);
+ if (info.getRealUid() != targetUid) {
+ tlist.clear();
+ return FOREACH_ACTION_NONE;
+ }
+ // Okay found it, update its reason.
+ updateExistingExitInfoRecordLocked(info, status, reason);
+
+ return FOREACH_ACTION_STOP_ITERATION;
+ });
+ return tlist.size() > 0;
}
/**
@@ -441,38 +503,43 @@
@VisibleForTesting
void getExitInfo(final String packageName, final int filterUid,
final int filterPid, final int maxNum, final ArrayList<ApplicationExitInfo> results) {
- synchronized (mLock) {
- boolean emptyPackageName = TextUtils.isEmpty(packageName);
- if (!emptyPackageName) {
- // fast path
- AppExitInfoContainer container = mData.get(packageName, filterUid);
- if (container != null) {
- container.getExitInfoLocked(filterPid, maxNum, results);
- }
- } else {
- // slow path
- final ArrayList<ApplicationExitInfo> list = mTmpInfoList2;
- list.clear();
- // get all packages
- forEachPackage((name, records) -> {
- AppExitInfoContainer container = records.get(filterUid);
+ long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ boolean emptyPackageName = TextUtils.isEmpty(packageName);
+ if (!emptyPackageName) {
+ // fast path
+ AppExitInfoContainer container = mData.get(packageName, filterUid);
if (container != null) {
- mTmpInfoList.clear();
- results.addAll(container.toListLocked(mTmpInfoList, filterPid));
+ container.getExitInfoLocked(filterPid, maxNum, results);
}
- return AppExitInfoTracker.FOREACH_ACTION_NONE;
- });
+ } else {
+ // slow path
+ final ArrayList<ApplicationExitInfo> list = mTmpInfoList2;
+ list.clear();
+ // get all packages
+ forEachPackageLocked((name, records) -> {
+ AppExitInfoContainer container = records.get(filterUid);
+ if (container != null) {
+ mTmpInfoList.clear();
+ results.addAll(container.toListLocked(mTmpInfoList, filterPid));
+ }
+ return AppExitInfoTracker.FOREACH_ACTION_NONE;
+ });
- Collections.sort(list, (a, b) -> (int) (b.getTimestamp() - a.getTimestamp()));
- int size = list.size();
- if (maxNum > 0) {
- size = Math.min(size, maxNum);
+ Collections.sort(list, (a, b) -> (int) (b.getTimestamp() - a.getTimestamp()));
+ int size = list.size();
+ if (maxNum > 0) {
+ size = Math.min(size, maxNum);
+ }
+ for (int i = 0; i < size; i++) {
+ results.add(list.get(i));
+ }
+ list.clear();
}
- for (int i = 0; i < size; i++) {
- results.add(list.get(i));
- }
- list.clear();
}
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
@@ -480,17 +547,16 @@
* Return the first matching exit info record, for internal use, the parameters are not supposed
* to be empty.
*/
- private ApplicationExitInfo getExitInfo(final String packageName,
+ @GuardedBy("mLock")
+ private ApplicationExitInfo getExitInfoLocked(final String packageName,
final int filterUid, final int filterPid) {
- synchronized (mLock) {
- ArrayList<ApplicationExitInfo> list = mTmpInfoList;
- list.clear();
- getExitInfo(packageName, filterUid, filterPid, 1, list);
+ ArrayList<ApplicationExitInfo> list = mTmpInfoList;
+ list.clear();
+ getExitInfo(packageName, filterUid, filterPid, 1, list);
- ApplicationExitInfo info = list.size() > 0 ? list.get(0) : null;
- list.clear();
- return info;
- }
+ ApplicationExitInfo info = list.size() > 0 ? list.get(0) : null;
+ list.clear();
+ return info;
}
@VisibleForTesting
@@ -498,8 +564,10 @@
mAppExitInfoSourceZygote.removeByUserId(userId);
mAppExitInfoSourceLmkd.removeByUserId(userId);
mIsolatedUidRecords.removeByUserId(userId);
- removeByUserId(userId);
- schedulePersistProcessExitInfo(true);
+ synchronized (mLock) {
+ removeByUserIdLocked(userId);
+ schedulePersistProcessExitInfo(true);
+ }
}
@VisibleForTesting
@@ -507,13 +575,16 @@
if (packageName != null) {
final boolean removeUid = TextUtils.isEmpty(
mService.mPackageManagerInt.getNameForUid(uid));
- if (removeUid) {
- mAppExitInfoSourceZygote.removeByUid(uid, allUsers);
- mAppExitInfoSourceLmkd.removeByUid(uid, allUsers);
- mIsolatedUidRecords.removeAppUid(uid, allUsers);
+ synchronized (mLock) {
+ if (removeUid) {
+ mAppExitInfoSourceZygote.removeByUidLocked(uid, allUsers);
+ mAppExitInfoSourceLmkd.removeByUidLocked(uid, allUsers);
+ mIsolatedUidRecords.removeAppUid(uid, allUsers);
+ }
+ removePackageLocked(packageName, uid, removeUid,
+ allUsers ? UserHandle.USER_ALL : UserHandle.getUserId(uid));
+ schedulePersistProcessExitInfo(true);
}
- removePackage(packageName, allUsers ? UserHandle.USER_ALL : UserHandle.getUserId(uid));
- schedulePersistProcessExitInfo(true);
}
}
@@ -593,6 +664,7 @@
}
}
synchronized (mLock) {
+ pruneAnrTracesIfNecessaryLocked();
mAppExitInfoLoaded = true;
}
}
@@ -634,7 +706,7 @@
ProtoOutputStream proto = new ProtoOutputStream(out);
proto.write(AppsExitInfoProto.LAST_UPDATE_TIMESTAMP, now);
synchronized (mLock) {
- forEachPackage((packageName, records) -> {
+ forEachPackageLocked((packageName, records) -> {
long token = proto.start(AppsExitInfoProto.PACKAGES);
proto.write(AppsExitInfoProto.Package.PACKAGE_NAME, packageName);
int uidArraySize = records.size();
@@ -688,6 +760,9 @@
mProcExitInfoFile.delete();
}
mData.getMap().clear();
+ mActiveAppStateSummary.clear();
+ mActiveAppTraces.clear();
+ pruneAnrTracesIfNecessaryLocked();
}
}
@@ -695,15 +770,15 @@
* Helper function for shell command
*/
void clearHistoryProcessExitInfo(String packageName, int userId) {
- synchronized (mLock) {
- if (TextUtils.isEmpty(packageName)) {
- if (userId == UserHandle.USER_ALL) {
- mData.getMap().clear();
- } else {
- removeByUserId(userId);
- }
- } else {
- removePackage(packageName, userId);
+ if (TextUtils.isEmpty(packageName)) {
+ synchronized (mLock) {
+ removeByUserIdLocked(userId);
+ }
+ } else {
+ final int uid = mService.mPackageManagerInt.getPackageUid(packageName,
+ PackageManager.MATCH_ALL, userId);
+ synchronized (mLock) {
+ removePackageLocked(packageName, uid, true, userId);
}
}
schedulePersistProcessExitInfo(true);
@@ -716,7 +791,7 @@
pw.println("Last Timestamp of Persistence Into Persistent Storage: "
+ sdf.format(new Date(mLastAppExitInfoPersistTimestamp)));
if (TextUtils.isEmpty(packageName)) {
- forEachPackage((name, records) -> {
+ forEachPackageLocked((name, records) -> {
dumpHistoryProcessExitInfoLocked(pw, " ", name, records, sdf);
return AppExitInfoTracker.FOREACH_ACTION_NONE;
});
@@ -741,86 +816,108 @@
}
}
- private void addExitInfoInner(String packageName, int userId, ApplicationExitInfo info) {
- synchronized (mLock) {
- AppExitInfoContainer container = mData.get(packageName, userId);
- if (container == null) {
- container = new AppExitInfoContainer(mAppExitInfoHistoryListSize);
- if (UserHandle.isIsolated(info.getRealUid())) {
- Integer k = mIsolatedUidRecords.getUidByIsolatedUid(info.getRealUid());
- if (k != null) {
- container.mUid = k;
- }
- } else {
- container.mUid = info.getRealUid();
+ @GuardedBy("mLock")
+ private void addExitInfoInnerLocked(String packageName, int userId, ApplicationExitInfo info) {
+ AppExitInfoContainer container = mData.get(packageName, userId);
+ if (container == null) {
+ container = new AppExitInfoContainer(mAppExitInfoHistoryListSize);
+ if (UserHandle.isIsolated(info.getRealUid())) {
+ Integer k = mIsolatedUidRecords.getUidByIsolatedUid(info.getRealUid());
+ if (k != null) {
+ container.mUid = k;
}
- mData.put(packageName, userId, container);
+ } else {
+ container.mUid = info.getRealUid();
}
- container.addExitInfoLocked(info);
+ mData.put(packageName, userId, container);
}
+ container.addExitInfoLocked(info);
}
- private void forEachPackage(
+ @GuardedBy("mLocked")
+ private void forEachPackageLocked(
BiFunction<String, SparseArray<AppExitInfoContainer>, Integer> callback) {
if (callback != null) {
- synchronized (mLock) {
- ArrayMap<String, SparseArray<AppExitInfoContainer>> map = mData.getMap();
- for (int i = map.size() - 1; i >= 0; i--) {
- switch (callback.apply(map.keyAt(i), map.valueAt(i))) {
- case FOREACH_ACTION_REMOVE_PACKAGE:
- map.removeAt(i);
- break;
- case FOREACH_ACTION_STOP_ITERATION:
- i = 0;
- break;
- case FOREACH_ACTION_NONE:
- default:
- break;
- }
- }
- }
- }
- }
-
- private void removePackage(String packageName, int userId) {
- synchronized (mLock) {
- if (userId == UserHandle.USER_ALL) {
- mData.getMap().remove(packageName);
- } else {
- ArrayMap<String, SparseArray<AppExitInfoContainer>> map =
- mData.getMap();
- SparseArray<AppExitInfoContainer> array = map.get(packageName);
- if (array == null) {
- return;
- }
- for (int i = array.size() - 1; i >= 0; i--) {
- if (UserHandle.getUserId(array.keyAt(i)) == userId) {
- array.removeAt(i);
+ ArrayMap<String, SparseArray<AppExitInfoContainer>> map = mData.getMap();
+ for (int i = map.size() - 1; i >= 0; i--) {
+ switch (callback.apply(map.keyAt(i), map.valueAt(i))) {
+ case FOREACH_ACTION_REMOVE_ITEM:
+ final SparseArray<AppExitInfoContainer> records = map.valueAt(i);
+ for (int j = records.size() - 1; j >= 0; j--) {
+ records.valueAt(j).destroyLocked();
+ }
+ map.removeAt(i);
break;
- }
- }
- if (array.size() == 0) {
- map.remove(packageName);
+ case FOREACH_ACTION_STOP_ITERATION:
+ i = 0;
+ break;
+ case FOREACH_ACTION_NONE:
+ default:
+ break;
}
}
}
}
- private void removeByUserId(final int userId) {
- if (userId == UserHandle.USER_ALL) {
- synchronized (mLock) {
- mData.getMap().clear();
+ @GuardedBy("mLocked")
+ private void removePackageLocked(String packageName, int uid, boolean removeUid, int userId) {
+ if (removeUid) {
+ mActiveAppStateSummary.remove(uid);
+ final int idx = mActiveAppTraces.indexOfKey(uid);
+ if (idx >= 0) {
+ final SparseArray<File> array = mActiveAppTraces.valueAt(idx);
+ for (int i = array.size() - 1; i >= 0; i--) {
+ array.valueAt(i).delete();
+ }
+ mActiveAppTraces.removeAt(idx);
}
+ }
+ ArrayMap<String, SparseArray<AppExitInfoContainer>> map = mData.getMap();
+ SparseArray<AppExitInfoContainer> array = map.get(packageName);
+ if (array == null) {
return;
}
- forEachPackage((packageName, records) -> {
+ if (userId == UserHandle.USER_ALL) {
+ for (int i = array.size() - 1; i >= 0; i--) {
+ array.valueAt(i).destroyLocked();
+ }
+ mData.getMap().remove(packageName);
+ } else {
+ for (int i = array.size() - 1; i >= 0; i--) {
+ if (UserHandle.getUserId(array.keyAt(i)) == userId) {
+ array.valueAt(i).destroyLocked();
+ array.removeAt(i);
+ break;
+ }
+ }
+ if (array.size() == 0) {
+ map.remove(packageName);
+ }
+ }
+ }
+
+ @GuardedBy("mLocked")
+ private void removeByUserIdLocked(final int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ mData.getMap().clear();
+ mActiveAppStateSummary.clear();
+ mActiveAppTraces.clear();
+ pruneAnrTracesIfNecessaryLocked();
+ return;
+ }
+ removeFromSparse2dArray(mActiveAppStateSummary,
+ (v) -> UserHandle.getUserId(v) == userId, null, null);
+ removeFromSparse2dArray(mActiveAppTraces,
+ (v) -> UserHandle.getUserId(v) == userId, null, (v) -> v.delete());
+ forEachPackageLocked((packageName, records) -> {
for (int i = records.size() - 1; i >= 0; i--) {
if (UserHandle.getUserId(records.keyAt(i)) == userId) {
+ records.valueAt(i).destroyLocked();
records.removeAt(i);
break;
}
}
- return records.size() == 0 ? FOREACH_ACTION_REMOVE_PACKAGE : FOREACH_ACTION_NONE;
+ return records.size() == 0 ? FOREACH_ACTION_REMOVE_ITEM : FOREACH_ACTION_NONE;
});
}
@@ -862,6 +959,262 @@
}
/**
+ * Called from {@link ActivityManagerService#setProcessStateSummary}.
+ */
+ @VisibleForTesting
+ void setProcessStateSummary(int uid, final int pid, final byte[] data) {
+ synchronized (mLock) {
+ Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
+ if (k != null) {
+ uid = k;
+ }
+ putToSparse2dArray(mActiveAppStateSummary, uid, pid, data, SparseArray::new, null);
+ }
+ }
+
+ @VisibleForTesting
+ @Nullable byte[] getProcessStateSummary(int uid, final int pid) {
+ synchronized (mLock) {
+ Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
+ if (k != null) {
+ uid = k;
+ }
+ int index = mActiveAppStateSummary.indexOfKey(uid);
+ if (index < 0) {
+ return null;
+ }
+ return mActiveAppStateSummary.valueAt(index).get(pid);
+ }
+ }
+
+ /**
+ * Called from ProcessRecord when an ANR occurred and the ANR trace is taken.
+ */
+ void scheduleLogAnrTrace(final int pid, final int uid, final String[] packageList,
+ final File traceFile, final long startOff, final long endOff) {
+ mKillHandler.sendMessage(PooledLambda.obtainMessage(
+ this::handleLogAnrTrace, pid, uid, packageList,
+ traceFile, startOff, endOff));
+ }
+
+ /**
+ * Copy and compress the given ANR trace file
+ */
+ @VisibleForTesting
+ void handleLogAnrTrace(final int pid, int uid, final String[] packageList,
+ final File traceFile, final long startOff, final long endOff) {
+ if (!traceFile.exists() || ArrayUtils.isEmpty(packageList)) {
+ return;
+ }
+ final long size = traceFile.length();
+ final long length = endOff - startOff;
+ if (startOff >= size || endOff > size || length <= 0) {
+ return;
+ }
+
+ final File outFile = new File(mProcExitStoreDir, traceFile.getName()
+ + APP_TRACE_FILE_SUFFIX);
+ // Copy & compress
+ if (copyToGzFile(traceFile, outFile, startOff, length)) {
+ // Wrote successfully.
+ synchronized (mLock) {
+ Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
+ if (k != null) {
+ uid = k;
+ }
+ if (DEBUG_PROCESSES) {
+ Slog.i(TAG, "Stored ANR traces of " + pid + "/u" + uid + " in " + outFile);
+ }
+ boolean pending = true;
+ // Unlikely but possible: the app has died
+ for (int i = 0; i < packageList.length; i++) {
+ final AppExitInfoContainer container = mData.get(packageList[i], uid);
+ // Try to see if we could append this trace to an existing record
+ if (container != null && container.appendTraceIfNecessaryLocked(pid, outFile)) {
+ // Okay someone took it
+ pending = false;
+ }
+ }
+ if (pending) {
+ // Save it into a temporary list for later use (when the app dies).
+ putToSparse2dArray(mActiveAppTraces, uid, pid, outFile,
+ SparseArray::new, (v) -> v.delete());
+ }
+ }
+ }
+ }
+
+ /**
+ * Copy the given portion of the file into a gz file.
+ *
+ * @param inFile The source file.
+ * @param outFile The destination file, which will be compressed in gzip format.
+ * @param start The start offset where the copy should start from.
+ * @param length The number of bytes that should be copied.
+ * @return If the copy was successful or not.
+ */
+ private static boolean copyToGzFile(final File inFile, final File outFile,
+ final long start, final long length) {
+ long remaining = length;
+ try (
+ BufferedInputStream in = new BufferedInputStream(new FileInputStream(inFile));
+ GZIPOutputStream out = new GZIPOutputStream(new BufferedOutputStream(
+ new FileOutputStream(outFile)))) {
+ final byte[] buffer = new byte[8192];
+ in.skip(start);
+ while (remaining > 0) {
+ int t = in.read(buffer, 0, (int) Math.min(buffer.length, remaining));
+ if (t < 0) {
+ break;
+ }
+ out.write(buffer, 0, t);
+ remaining -= t;
+ }
+ } catch (IOException e) {
+ if (DEBUG_PROCESSES) {
+ Slog.e(TAG, "Error in copying ANR trace from " + inFile + " to " + outFile, e);
+ }
+ return false;
+ }
+ return remaining == 0 && outFile.exists();
+ }
+
+ /**
+ * In case there is any orphan ANR trace file, remove it.
+ */
+ @GuardedBy("mLock")
+ private void pruneAnrTracesIfNecessaryLocked() {
+ final ArraySet<String> allFiles = new ArraySet();
+ final File[] files = mProcExitStoreDir.listFiles((f) -> {
+ final String name = f.getName();
+ boolean trace = name.startsWith(ActivityManagerService.ANR_FILE_PREFIX)
+ && name.endsWith(APP_TRACE_FILE_SUFFIX);
+ if (trace) {
+ allFiles.add(name);
+ }
+ return trace;
+ });
+ if (ArrayUtils.isEmpty(files)) {
+ return;
+ }
+ // Find out the owners from the existing records
+ forEachPackageLocked((name, records) -> {
+ for (int i = records.size() - 1; i >= 0; i--) {
+ final AppExitInfoContainer container = records.valueAt(i);
+ container.forEachRecordLocked((pid, info) -> {
+ final File traceFile = info.getTraceFile();
+ if (traceFile != null) {
+ allFiles.remove(traceFile.getName());
+ }
+ return FOREACH_ACTION_NONE;
+ });
+ }
+ return AppExitInfoTracker.FOREACH_ACTION_NONE;
+ });
+ // See if there is any active process owns it.
+ forEachSparse2dArray(mActiveAppTraces, (v) -> allFiles.remove(v.getName()));
+
+ // Remove orphan traces if nobody claims it.
+ for (int i = allFiles.size() - 1; i >= 0; i--) {
+ (new File(mProcExitStoreDir, allFiles.valueAt(i))).delete();
+ }
+ }
+
+ /**
+ * A utility function to add the given value to the given 2d SparseArray
+ */
+ private static <T extends SparseArray<U>, U> void putToSparse2dArray(final SparseArray<T> array,
+ final int outerKey, final int innerKey, final U value, final Supplier<T> newInstance,
+ final Consumer<U> actionToOldValue) {
+ int idx = array.indexOfKey(outerKey);
+ T innerArray = null;
+ if (idx < 0) {
+ innerArray = newInstance.get();
+ array.put(outerKey, innerArray);
+ } else {
+ innerArray = array.valueAt(idx);
+ }
+ idx = innerArray.indexOfKey(innerKey);
+ if (idx >= 0) {
+ if (actionToOldValue != null) {
+ actionToOldValue.accept(innerArray.valueAt(idx));
+ }
+ innerArray.setValueAt(idx, value);
+ } else {
+ innerArray.put(innerKey, value);
+ }
+ }
+
+ /**
+ * A utility function to iterate through the given 2d SparseArray
+ */
+ private static <T extends SparseArray<U>, U> void forEachSparse2dArray(
+ final SparseArray<T> array, final Consumer<U> action) {
+ if (action != null) {
+ for (int i = array.size() - 1; i >= 0; i--) {
+ T innerArray = array.valueAt(i);
+ if (innerArray == null) {
+ continue;
+ }
+ for (int j = innerArray.size() - 1; j >= 0; j--) {
+ action.accept(innerArray.valueAt(j));
+ }
+ }
+ }
+ }
+
+ /**
+ * A utility function to remove elements from the given 2d SparseArray
+ */
+ private static <T extends SparseArray<U>, U> void removeFromSparse2dArray(
+ final SparseArray<T> array, final Predicate<Integer> outerPredicate,
+ final Predicate<Integer> innerPredicate, final Consumer<U> action) {
+ for (int i = array.size() - 1; i >= 0; i--) {
+ if (outerPredicate == null || outerPredicate.test(array.keyAt(i))) {
+ final T innerArray = array.valueAt(i);
+ if (innerArray == null) {
+ continue;
+ }
+ for (int j = innerArray.size() - 1; j >= 0; j--) {
+ if (innerPredicate == null || innerPredicate.test(innerArray.keyAt(j))) {
+ if (action != null) {
+ action.accept(innerArray.valueAt(j));
+ }
+ innerArray.removeAt(j);
+ }
+ }
+ if (innerArray.size() == 0) {
+ array.removeAt(i);
+ }
+ }
+ }
+ }
+
+ /**
+ * A utility function to find and remove elements from the given 2d SparseArray.
+ */
+ private static <T extends SparseArray<U>, U> U findAndRemoveFromSparse2dArray(
+ final SparseArray<T> array, final int outerKey, final int innerKey) {
+ final int idx = array.indexOfKey(outerKey);
+ if (idx >= 0) {
+ T p = array.valueAt(idx);
+ if (p == null) {
+ return null;
+ }
+ final int innerIdx = p.indexOfKey(innerKey);
+ if (innerIdx >= 0) {
+ final U ret = p.valueAt(innerIdx);
+ p.removeAt(innerIdx);
+ if (p.size() == 0) {
+ array.removeAt(idx);
+ }
+ return ret;
+ }
+ }
+ return null;
+ }
+
+ /**
* A container class of {@link android.app.ApplicationExitInfo}
*/
final class AppExitInfoContainer {
@@ -934,10 +1287,68 @@
}
}
if (oldestIndex >= 0) {
+ final File traceFile = mInfos.valueAt(oldestIndex).getTraceFile();
+ if (traceFile != null) {
+ traceFile.delete();
+ }
mInfos.removeAt(oldestIndex);
}
}
- mInfos.append(info.getPid(), info);
+ // Claim the state information if there is any
+ final int uid = info.getPackageUid();
+ final int pid = info.getPid();
+ info.setProcessStateSummary(findAndRemoveFromSparse2dArray(
+ mActiveAppStateSummary, uid, pid));
+ info.setTraceFile(findAndRemoveFromSparse2dArray(mActiveAppTraces, uid, pid));
+ info.setAppTraceRetriever(mAppTraceRetriever);
+ mInfos.append(pid, info);
+ }
+
+ @GuardedBy("mLock")
+ boolean appendTraceIfNecessaryLocked(final int pid, final File traceFile) {
+ final ApplicationExitInfo r = mInfos.get(pid);
+ if (r != null) {
+ r.setTraceFile(traceFile);
+ r.setAppTraceRetriever(mAppTraceRetriever);
+ return true;
+ }
+ return false;
+ }
+
+ @GuardedBy("mLock")
+ void destroyLocked() {
+ for (int i = mInfos.size() - 1; i >= 0; i--) {
+ ApplicationExitInfo ai = mInfos.valueAt(i);
+ final File traceFile = ai.getTraceFile();
+ if (traceFile != null) {
+ traceFile.delete();
+ }
+ ai.setTraceFile(null);
+ ai.setAppTraceRetriever(null);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void forEachRecordLocked(final BiFunction<Integer, ApplicationExitInfo, Integer> callback) {
+ if (callback != null) {
+ for (int i = mInfos.size() - 1; i >= 0; i--) {
+ switch (callback.apply(mInfos.keyAt(i), mInfos.valueAt(i))) {
+ case FOREACH_ACTION_REMOVE_ITEM:
+ final File traceFile = mInfos.valueAt(i).getTraceFile();
+ if (traceFile != null) {
+ traceFile.delete();
+ }
+ mInfos.removeAt(i);
+ break;
+ case FOREACH_ACTION_STOP_ITERATION:
+ i = 0;
+ break;
+ case FOREACH_ACTION_NONE:
+ default:
+ break;
+ }
+ }
+ }
}
@GuardedBy("mLock")
@@ -1033,6 +1444,7 @@
}
}
+ @GuardedBy("mLock")
Integer getUidByIsolatedUid(int isolatedUid) {
if (UserHandle.isIsolated(isolatedUid)) {
synchronized (mLock) {
@@ -1053,6 +1465,7 @@
}
}
+ @VisibleForTesting
void removeAppUid(int uid, boolean allUsers) {
synchronized (mLock) {
if (allUsers) {
@@ -1071,23 +1484,22 @@
}
}
- int removeIsolatedUid(int isolatedUid) {
+ @GuardedBy("mLock")
+ int removeIsolatedUidLocked(int isolatedUid) {
if (!UserHandle.isIsolated(isolatedUid)) {
return isolatedUid;
}
- synchronized (mLock) {
- int uid = mIsolatedUidToUidMap.get(isolatedUid, -1);
- if (uid == -1) {
- return isolatedUid;
- }
- mIsolatedUidToUidMap.remove(isolatedUid);
- ArraySet<Integer> set = mUidToIsolatedUidMap.get(uid);
- if (set != null) {
- set.remove(isolatedUid);
- }
- // let the ArraySet stay in the mUidToIsolatedUidMap even if it's empty
- return uid;
+ int uid = mIsolatedUidToUidMap.get(isolatedUid, -1);
+ if (uid == -1) {
+ return isolatedUid;
}
+ mIsolatedUidToUidMap.remove(isolatedUid);
+ ArraySet<Integer> set = mUidToIsolatedUidMap.get(uid);
+ if (set != null) {
+ set.remove(isolatedUid);
+ }
+ // let the ArraySet stay in the mUidToIsolatedUidMap even if it's empty
+ return uid;
}
void removeByUserId(int userId) {
@@ -1193,33 +1605,29 @@
mPresetReason = reason;
}
- void add(int pid, int uid, Object extra) {
- if (UserHandle.isIsolated(uid)) {
- Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
- if (k != null) {
- uid = k;
- }
+ @GuardedBy("mLock")
+ private void addLocked(int pid, int uid, Object extra) {
+ Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
+ if (k != null) {
+ uid = k;
}
- synchronized (mLock) {
- SparseArray<Pair<Long, Object>> array = mData.get(uid);
- if (array == null) {
- array = new SparseArray<Pair<Long, Object>>();
- mData.put(uid, array);
- }
- array.put(pid, new Pair<Long, Object>(System.currentTimeMillis(), extra));
+ SparseArray<Pair<Long, Object>> array = mData.get(uid);
+ if (array == null) {
+ array = new SparseArray<Pair<Long, Object>>();
+ mData.put(uid, array);
}
+ array.put(pid, new Pair<Long, Object>(System.currentTimeMillis(), extra));
}
+ @VisibleForTesting
Pair<Long, Object> remove(int pid, int uid) {
- if (UserHandle.isIsolated(uid)) {
+ synchronized (mLock) {
Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
if (k != null) {
uid = k;
}
- }
- synchronized (mLock) {
SparseArray<Pair<Long, Object>> array = mData.get(uid);
if (array != null) {
Pair<Long, Object> p = array.get(pid);
@@ -1228,8 +1636,8 @@
return isFresh(p.first) ? p : null;
}
}
+ return null;
}
- return null;
}
void removeByUserId(int userId) {
@@ -1250,7 +1658,8 @@
}
}
- void removeByUid(int uid, boolean allUsers) {
+ @GuardedBy("mLock")
+ void removeByUidLocked(int uid, boolean allUsers) {
if (UserHandle.isIsolated(uid)) {
Integer k = mIsolatedUidRecords.getUidByIsolatedUid(uid);
if (k != null) {
@@ -1260,17 +1669,13 @@
if (allUsers) {
uid = UserHandle.getAppId(uid);
- synchronized (mLock) {
- for (int i = mData.size() - 1; i >= 0; i--) {
- if (UserHandle.getAppId(mData.keyAt(i)) == uid) {
- mData.removeAt(i);
- }
+ for (int i = mData.size() - 1; i >= 0; i--) {
+ if (UserHandle.getAppId(mData.keyAt(i)) == uid) {
+ mData.removeAt(i);
}
}
} else {
- synchronized (mLock) {
- mData.remove(uid);
- }
+ mData.remove(uid);
}
}
@@ -1292,12 +1697,12 @@
// Unlikely but possible: the record has been created
// Let's update it if we could find a ApplicationExitInfo record
- if (!updateExitInfoIfNecessary(pid, uid, status, mPresetReason)) {
- add(pid, uid, status);
- }
-
- // Notify any interesed party regarding the lmkd kills
synchronized (mLock) {
+ if (!updateExitInfoIfNecessaryLocked(pid, uid, status, mPresetReason)) {
+ addLocked(pid, uid, status);
+ }
+
+ // Notify any interesed party regarding the lmkd kills
final BiConsumer<Integer, Integer> listener = mProcDiedListener;
if (listener != null) {
mService.mHandler.post(()-> listener.accept(pid, uid));
@@ -1305,4 +1710,51 @@
}
}
}
+
+ /**
+ * The implementation to the IAppTraceRetriever interface.
+ */
+ @VisibleForTesting
+ class AppTraceRetriever extends IAppTraceRetriever.Stub {
+ @Override
+ public ParcelFileDescriptor getTraceFileDescriptor(final String packageName,
+ final int uid, final int pid) {
+ mService.enforceNotIsolatedCaller("getTraceFileDescriptor");
+
+ if (TextUtils.isEmpty(packageName)) {
+ throw new IllegalArgumentException("Invalid package name");
+ }
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+ final int callingUserId = UserHandle.getCallingUserId();
+ final int userId = UserHandle.getUserId(uid);
+
+ mService.mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
+ ALLOW_NON_FULL, "getTraceFileDescriptor", null);
+ if (mService.enforceDumpPermissionForPackage(packageName, userId,
+ callingUid, "getTraceFileDescriptor") != Process.INVALID_UID) {
+ synchronized (mLock) {
+ final ApplicationExitInfo info = getExitInfoLocked(packageName, uid, pid);
+ if (info == null) {
+ return null;
+ }
+ final File traceFile = info.getTraceFile();
+ if (traceFile == null) {
+ return null;
+ }
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ // The fd will be closed after being written into Parcel
+ return ParcelFileDescriptor.open(traceFile,
+ ParcelFileDescriptor.MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ return null;
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+ return null;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 16a7c6b..0f9d61f 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -71,7 +71,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.ProcessInfo;
import android.content.res.Resources;
import android.graphics.Point;
import android.net.LocalSocket;
@@ -99,7 +98,6 @@
import android.system.Os;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.ArraySet;
import android.util.EventLog;
import android.util.LongSparseArray;
import android.util.Pair;
@@ -138,10 +136,8 @@
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Set;
/**
* Activity manager code dealing with processes.
@@ -512,13 +508,6 @@
*/
private final int[] mZygoteSigChldMessage = new int[3];
- interface LmkdKillListener {
- /**
- * Called when there is a process kill by lmkd.
- */
- void onLmkdKillOccurred(int pid, int uid);
- }
-
final class IsolatedUidRange {
@VisibleForTesting
public final int mFirstUid;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index e7f66bb..eec7519 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1621,9 +1621,11 @@
// For background ANRs, don't pass the ProcessCpuTracker to
// avoid spending 1/2 second collecting stats to rank lastPids.
StringWriter tracesFileException = new StringWriter();
+ // To hold the start and end offset to the ANR trace file respectively.
+ final long[] offsets = new long[2];
File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
(isSilentAnr()) ? null : processCpuTracker, (isSilentAnr()) ? null : lastPids,
- nativePids, tracesFileException);
+ nativePids, tracesFileException, offsets);
if (isMonitorCpuUsage()) {
mService.updateCpuStatsNow();
@@ -1641,6 +1643,10 @@
if (tracesFile == null) {
// There is no trace file, so dump (only) the alleged culprit's threads to the log
Process.sendSignal(pid, Process.SIGNAL_QUIT);
+ } else if (offsets[1] > 0) {
+ // We've dumped into the trace file successfully
+ mService.mProcessList.mAppExitInfoTracker.scheduleLogAnrTrace(
+ pid, uid, getPackageList(), tracesFile, offsets[0], offsets[1]);
}
FrameworkStatsLog.write(FrameworkStatsLog.ANR_OCCURRED, uid, processName,
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 44f571a..8519b00 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -28,7 +28,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
import android.os.Binder;
-import android.os.Build;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Slog;
@@ -45,7 +44,6 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.util.Arrays;
/**
* System server internal API for gating and reporting compatibility changes.
@@ -58,9 +56,6 @@
private final ChangeReporter mChangeReporter;
private final CompatConfig mCompatConfig;
- private static int sMinTargetSdk = Build.VERSION_CODES.P;
- private static int sMaxTargetSdk = Build.VERSION_CODES.Q;
-
public PlatformCompat(Context context) {
mContext = context;
mChangeReporter = new ChangeReporter(
@@ -225,12 +220,6 @@
return mCompatConfig.dumpChanges();
}
- @Override
- public CompatibilityChangeInfo[] listUIChanges() {
- return Arrays.stream(listAllChanges()).filter(
- x -> isShownInUI(x)).toArray(CompatibilityChangeInfo[]::new);
- }
-
/**
* Check whether the change is known to the compat config.
*
@@ -350,17 +339,4 @@
checkCompatChangeReadPermission();
checkCompatChangeLogPermission();
}
-
- private boolean isShownInUI(CompatibilityChangeInfo change) {
- if (change.getLoggingOnly()) {
- return false;
- }
- if (change.getEnableAfterTargetSdk() != 0) {
- if (change.getEnableAfterTargetSdk() < sMinTargetSdk
- || change.getEnableAfterTargetSdk() > sMaxTargetSdk) {
- return false;
- }
- }
- return true;
- }
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 1292f6c..63048f6 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -252,8 +252,21 @@
* @param permission The permission to check.
*/
void enforcePermission(String permission) {
- mContext.enforceCallingOrSelfPermission(permission,
- String.format("Caller must have the %s permission.", permission));
+ final int status = PermissionChecker.checkCallingOrSelfPermissionForPreflight(mContext,
+ permission);
+ switch (status) {
+ case PermissionChecker.PERMISSION_GRANTED:
+ return;
+ case PermissionChecker.PERMISSION_HARD_DENIED:
+ throw new SecurityException(
+ String.format("Caller must have the %s permission.", permission));
+ case PermissionChecker.PERMISSION_SOFT_DENIED:
+ throw new ServiceSpecificException(Status.TEMPORARY_PERMISSION_DENIED,
+ String.format("Caller must have the %s permission.", permission));
+ default:
+ throw new InternalServerError(
+ new RuntimeException("Unexpected perimission check result."));
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index eb18678..83fff28 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -186,6 +186,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
@@ -1167,7 +1168,9 @@
mAnimator = new WindowAnimator(this);
mRoot = new RootWindowContainer(this);
- mUseBLAST = true;
+ mUseBLAST = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT,
+ WM_USE_BLAST_ADAPTER_FLAG, false);
mWindowPlacerLocked = new WindowSurfacePlacer(this);
mTaskSnapshotController = new TaskSnapshotController(this);
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
index efe8119..23381ff 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java
@@ -46,6 +46,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
import android.os.Debug;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
@@ -55,6 +56,7 @@
import android.text.TextUtils;
import android.util.Pair;
+import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.appop.AppOpsService;
@@ -71,10 +73,17 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
+import java.util.Random;
+import java.util.zip.GZIPInputStream;
/**
* Test class for {@link android.app.ApplicationExitInfo}.
@@ -119,6 +128,8 @@
setFieldValue(AppExitInfoTracker.class, mAppExitInfoTracker, "mAppExitInfoSourceLmkd",
spy(mAppExitInfoTracker.new AppExitInfoExternalSource("lmkd",
ApplicationExitInfo.REASON_LOW_MEMORY)));
+ setFieldValue(AppExitInfoTracker.class, mAppExitInfoTracker, "mAppTraceRetriever",
+ spy(mAppExitInfoTracker.new AppTraceRetriever()));
setFieldValue(ProcessList.class, mProcessList, "mAppExitInfoTracker", mAppExitInfoTracker);
mInjector = new TestInjector(mContext);
mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread());
@@ -169,6 +180,11 @@
public void testApplicationExitInfo() throws Exception {
mAppExitInfoTracker.clearProcessExitInfo(true);
mAppExitInfoTracker.mAppExitInfoLoaded = true;
+ mAppExitInfoTracker.mProcExitStoreDir = new File(mContext.getFilesDir(),
+ AppExitInfoTracker.APP_EXIT_STORE_DIR);
+ assertTrue(FileUtils.createDir(mAppExitInfoTracker.mProcExitStoreDir));
+ mAppExitInfoTracker.mProcExitInfoFile = new File(mAppExitInfoTracker.mProcExitStoreDir,
+ AppExitInfoTracker.APP_EXIT_INFO_FILE);
// Test application calls System.exit()
doNothing().when(mAppExitInfoTracker).schedulePersistProcessExitInfo(anyBoolean());
@@ -188,6 +204,10 @@
final long app1Rss3 = 45680;
final String app1ProcessName = "com.android.test.stub1:process";
final String app1PackageName = "com.android.test.stub1";
+ final byte[] app1Cookie1 = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04,
+ (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08};
+ final byte[] app1Cookie2 = {(byte) 0x08, (byte) 0x07, (byte) 0x06, (byte) 0x05,
+ (byte) 0x04, (byte) 0x03, (byte) 0x02, (byte) 0x01};
final long now1 = System.currentTimeMillis();
ProcessRecord app = makeProcessRecord(
@@ -204,6 +224,9 @@
// Case 1: basic System.exit() test
int exitCode = 5;
+ mAppExitInfoTracker.setProcessStateSummary(app1Uid, app1Pid1, app1Cookie1);
+ assertTrue(ArrayUtils.equals(mAppExitInfoTracker.getProcessStateSummary(app1Uid,
+ app1Pid1), app1Cookie1, app1Cookie1.length));
doReturn(new Pair<Long, Object>(now1, Integer.valueOf(makeExitStatus(exitCode))))
.when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
.remove(anyInt(), anyInt());
@@ -235,6 +258,10 @@
IMPORTANCE_CACHED, // importance
null); // description
+ assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie1,
+ app1Cookie1.length));
+ assertEquals(info.getTraceInputStream(), null);
+
// Case 2: create another app1 process record with a different pid
sleep(1);
final long now2 = System.currentTimeMillis();
@@ -250,6 +277,12 @@
app1ProcessName, // processName
app1PackageName); // packageName
exitCode = 6;
+
+ mAppExitInfoTracker.setProcessStateSummary(app1Uid, app1Pid2, app1Cookie1);
+ // Override with a different cookie
+ mAppExitInfoTracker.setProcessStateSummary(app1Uid, app1Pid2, app1Cookie2);
+ assertTrue(ArrayUtils.equals(mAppExitInfoTracker.getProcessStateSummary(app1Uid,
+ app1Pid2), app1Cookie2, app1Cookie2.length));
doReturn(new Pair<Long, Object>(now2, Integer.valueOf(makeExitStatus(exitCode))))
.when(mAppExitInfoTracker.mAppExitInfoSourceZygote)
.remove(anyInt(), anyInt());
@@ -280,6 +313,12 @@
IMPORTANCE_SERVICE, // importance
null); // description
+ assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie2,
+ app1Cookie2.length));
+ info = list.get(1);
+ assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie1,
+ app1Cookie1.length));
+
// Case 3: Create an instance of app1 with different user, and died because of SIGKILL
sleep(1);
final long now3 = System.currentTimeMillis();
@@ -702,9 +741,19 @@
app1PackageName); // packageName
mAppExitInfoTracker.mIsolatedUidRecords.addIsolatedUid(app1IsolatedUid2User2, app1UidUser2);
+
+ // Pretent it gets an ANR trace too (although the reason here should be REASON_ANR)
+ final File traceFile = new File(mContext.getFilesDir(), "anr_original.txt");
+ final int traceSize = 10240;
+ final int traceStart = 1024;
+ final int traceEnd = 8192;
+ createRandomFile(traceFile, traceSize);
+ assertEquals(traceSize, traceFile.length());
+ mAppExitInfoTracker.handleLogAnrTrace(app.pid, app.uid, app.getPackageList(),
+ traceFile, traceStart, traceEnd);
+
noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
ApplicationExitInfo.SUBREASON_TOO_MANY_EMPTY, app1Description2);
-
updateExitInfo(app);
list.clear();
mAppExitInfoTracker.getExitInfo(app1PackageName, app1UidUser2, app1Pid2User2, 1, list);
@@ -729,6 +778,10 @@
IMPORTANCE_CACHED, // importance
app1Description2); // description
+ // Verify if the traceFile get copied into the records correctly.
+ verifyTraceFile(traceFile, traceStart, info.getTraceFile(), 0, traceEnd - traceStart);
+ traceFile.delete();
+ info.getTraceFile().delete();
// Case 9: User2 gets removed
sleep(1);
@@ -801,8 +854,6 @@
mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, original);
assertTrue(original.size() > 0);
- mAppExitInfoTracker.mProcExitInfoFile = new File(mContext.getFilesDir(),
- AppExitInfoTracker.APP_EXIT_INFO_FILE);
mAppExitInfoTracker.persistProcessExitInfo();
assertTrue(mAppExitInfoTracker.mProcExitInfoFile.exists());
@@ -836,6 +887,37 @@
}
}
+ private static void createRandomFile(File file, int size) throws IOException {
+ try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
+ Random random = new Random();
+ byte[] buf = random.ints('a', 'z').limit(size).collect(
+ StringBuilder::new, StringBuilder::appendCodePoint, StringBuilder::append)
+ .toString().getBytes();
+ out.write(buf);
+ }
+ }
+
+ private static void verifyTraceFile(File originFile, int originStart, File traceFile,
+ int traceStart, int length) throws IOException {
+ assertTrue(originFile.exists());
+ assertTrue(traceFile.exists());
+ assertTrue(originStart < originFile.length());
+ try (GZIPInputStream traceIn = new GZIPInputStream(new FileInputStream(traceFile));
+ BufferedInputStream originIn = new BufferedInputStream(
+ new FileInputStream(originFile))) {
+ assertEquals(traceStart, traceIn.skip(traceStart));
+ assertEquals(originStart, originIn.skip(originStart));
+ byte[] buf1 = new byte[8192];
+ byte[] buf2 = new byte[8192];
+ while (length > 0) {
+ int len = traceIn.read(buf1, 0, Math.min(buf1.length, length));
+ assertEquals(len, originIn.read(buf2, 0, len));
+ assertTrue(ArrayUtils.equals(buf1, buf2, len));
+ length -= len;
+ }
+ }
+ }
+
private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, Integer definingUid,
int connectionGroup, int procState, long pss, long rss,
String processName, String packageName) {
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 72d8525..53b90f2 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -30,12 +30,10 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.os.Build;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.compat.AndroidBuildClassifier;
-import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.server.LocalServices;
import org.junit.Before;
@@ -80,47 +78,6 @@
}
@Test
- public void testListAllChanges() {
- mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
- .addEnabledChangeWithId(1L)
- .addDisabledChangeWithIdAndName(2L, "change2")
- .addTargetSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "description")
- .addTargetSdkChangeWithId(Build.VERSION_CODES.P, 4L)
- .addTargetSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
- .addTargetSdkChangeWithId(Build.VERSION_CODES.R, 6L)
- .addLoggingOnlyChangeWithId(7L)
- .build();
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
- assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly(
- new CompatibilityChangeInfo(1L, "", -1, false, false, ""),
- new CompatibilityChangeInfo(2L, "change2", -1, true, false, ""),
- new CompatibilityChangeInfo(3L, "", Build.VERSION_CODES.O, false, false,
- "description"),
- new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, false, false, ""),
- new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, false, false, ""),
- new CompatibilityChangeInfo(6L, "", Build.VERSION_CODES.R, false, false, ""),
- new CompatibilityChangeInfo(7L, "", -1, false, true, ""));
- }
-
- public void testListUIChanges() {
- mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
- .addEnabledChangeWithId(1L)
- .addDisabledChangeWithIdAndName(2L, "change2")
- .addTargetSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "description")
- .addTargetSdkChangeWithId(Build.VERSION_CODES.P, 4L)
- .addTargetSdkChangeWithId(Build.VERSION_CODES.Q, 5L)
- .addTargetSdkChangeWithId(Build.VERSION_CODES.R, 6L)
- .addLoggingOnlyChangeWithId(7L)
- .build();
- mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
- assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly(
- new CompatibilityChangeInfo(1L, "", -1, false, false, ""),
- new CompatibilityChangeInfo(2L, "change2", -1, true, false, ""),
- new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, false, false, ""),
- new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, false, false, ""));
- }
-
- @Test
public void testRegisterListenerToSameIdThrows() throws Exception {
// Registering a listener to change 1 is successful.
mPlatformCompat.registerListener(1, mListener1);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 05cd26df..f070bff 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -1208,80 +1208,6 @@
STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController, PACKAGE_1));
}
- public void testAppUpdateOnRestrictedBucketStatus() {
- // Updates shouldn't change bucket if the app timed out.
- // Way past all timeouts. App times out into RESTRICTED bucket.
- reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
- mController.checkIdleStates(USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
-
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
-
- // Updates shouldn't change bucket if the app was forced by the system for a non-buggy
- // reason.
- reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
- mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_FORCED_BY_SYSTEM
- | REASON_SUB_FORCED_SYSTEM_FLAG_BACKGROUND_RESOURCE_USAGE);
-
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
-
- // Updates should change bucket if the app was forced by the system for a buggy reason.
- reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
- mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
-
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertNotEquals(STANDBY_BUCKET_RESTRICTED, getStandbyBucket(mController, PACKAGE_1));
-
- // Updates shouldn't change bucket if the app was forced by the system for more than just
- // a buggy reason.
- reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
- mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE
- | REASON_SUB_FORCED_SYSTEM_FLAG_BUGGY);
-
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- assertEquals(REASON_MAIN_FORCED_BY_SYSTEM | REASON_SUB_FORCED_SYSTEM_FLAG_ABUSE,
- getStandbyBucketReason(PACKAGE_1));
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
-
- // Updates shouldn't change bucket if the app was forced by the user.
- reportEvent(mController, USER_INTERACTION, mInjector.mElapsedRealtime, PACKAGE_1);
- mInjector.mElapsedRealtime += RESTRICTED_THRESHOLD * 4;
- mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RESTRICTED,
- REASON_MAIN_FORCED_BY_USER);
-
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp(PACKAGE_1, USER_ID2);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- mController.maybeUnrestrictBuggyApp("com.random.package", USER_ID);
- assertBucket(STANDBY_BUCKET_RESTRICTED);
- }
-
private String getAdminAppsStr(int userId) {
return getAdminAppsStr(userId, mController.getActiveAdminAppsForTest(userId));
}
diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt
index a49d3d53..11568f1 100644
--- a/telephony/api/system-current.txt
+++ b/telephony/api/system-current.txt
@@ -878,14 +878,10 @@
field public static final String ACTION_EMERGENCY_CALLBACK_MODE_CHANGED = "android.intent.action.EMERGENCY_CALLBACK_MODE_CHANGED";
field public static final String ACTION_EMERGENCY_CALL_STATE_CHANGED = "android.intent.action.EMERGENCY_CALL_STATE_CHANGED";
field public static final String ACTION_REQUEST_OMADM_CONFIGURATION_UPDATE = "com.android.omadm.service.CONFIGURATION_UPDATE";
- field public static final String ACTION_SERVICE_PROVIDERS_UPDATED = "android.telephony.action.SERVICE_PROVIDERS_UPDATED";
field public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS = "android.telephony.action.SHOW_NOTICE_ECM_BLOCK_OTHERS";
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
- field public static final int CARD_POWER_DOWN = 0; // 0x0
- field public static final int CARD_POWER_UP = 1; // 0x1
- field public static final int CARD_POWER_UP_PASS_THROUGH = 2; // 0x2
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
@@ -896,7 +892,6 @@
field public static final String EXTRA_APN_PROTOCOL_INT = "apnProtoInt";
field @Deprecated public static final String EXTRA_APN_TYPE = "apnType";
field public static final String EXTRA_APN_TYPE_INT = "apnTypeInt";
- field public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN";
field public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE = "defaultNetworkAvailable";
field public static final String EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE = "android.telephony.extra.DEFAULT_SUBSCRIPTION_SELECT_TYPE";
field public static final int EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_ALL = 4; // 0x4
@@ -909,16 +904,12 @@
field public static final String EXTRA_PCO_VALUE = "pcoValue";
field public static final String EXTRA_PHONE_IN_ECM_STATE = "android.telephony.extra.PHONE_IN_ECM_STATE";
field public static final String EXTRA_PHONE_IN_EMERGENCY_CALL = "android.telephony.extra.PHONE_IN_EMERGENCY_CALL";
- field public static final String EXTRA_PLMN = "android.telephony.extra.PLMN";
field public static final String EXTRA_REDIRECTION_URL = "redirectionUrl";
- field public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN";
- field public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN";
field public static final String EXTRA_SIM_COMBINATION_NAMES = "android.telephony.extra.SIM_COMBINATION_NAMES";
field public static final String EXTRA_SIM_COMBINATION_WARNING_TYPE = "android.telephony.extra.SIM_COMBINATION_WARNING_TYPE";
field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_DUAL_CDMA = 1; // 0x1
field public static final int EXTRA_SIM_COMBINATION_WARNING_TYPE_NONE = 0; // 0x0
field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
- field public static final String EXTRA_SPN = "android.telephony.extra.SPN";
field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff
diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java
index 428a515..1c82e96 100644
--- a/telephony/java/android/telephony/CallQuality.java
+++ b/telephony/java/android/telephony/CallQuality.java
@@ -287,7 +287,7 @@
* Returns true if only silence rtp packets are received for a duration of 20 seconds starting
* at call setup
*/
- public boolean isIncomingSilenceDetected() {
+ public boolean isIncomingSilenceDetectedAtCallSetup() {
return mRxSilenceDetected;
}
@@ -295,7 +295,7 @@
* Returns true if only silence rtp packets are sent for a duration of 20 seconds starting at
* call setup
*/
- public boolean isOutgoingSilenceDetected() {
+ public boolean isOutgoingSilenceDetectedAtCallSetup() {
return mTxSilenceDetected;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fbd69da8..d6f5bb2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1036,7 +1036,9 @@
"android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
/**
- * Broadcast intent that indicates multi-SIM configuration is changed. For example, it changed
+ * Broadcast action to be received by Broadcast receivers.
+ *
+ * Indicates multi-SIM configuration is changed. For example, it changed
* from single SIM capable to dual-SIM capable (DSDS or DSDA) or triple-SIM mode.
*
* It doesn't indicate how many subscriptions are actually active, or which states SIMs are,
@@ -1279,7 +1281,6 @@
* <p>Note: this is a protected intent that can only be sent by the system.
* @hide
*/
- @SystemApi
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_SERVICE_PROVIDERS_UPDATED =
"android.telephony.action.SERVICE_PROVIDERS_UPDATED";
@@ -1289,7 +1290,6 @@
* whether the PLMN should be shown.
* @hide
*/
- @SystemApi
public static final String EXTRA_SHOW_PLMN = "android.telephony.extra.SHOW_PLMN";
/**
@@ -1297,7 +1297,6 @@
* the operator name of the registered network.
* @hide
*/
- @SystemApi
public static final String EXTRA_PLMN = "android.telephony.extra.PLMN";
/**
@@ -1305,7 +1304,6 @@
* whether the PLMN should be shown.
* @hide
*/
- @SystemApi
public static final String EXTRA_SHOW_SPN = "android.telephony.extra.SHOW_SPN";
/**
@@ -1313,7 +1311,6 @@
* the service provider name.
* @hide
*/
- @SystemApi
public static final String EXTRA_SPN = "android.telephony.extra.SPN";
/**
@@ -1321,7 +1318,6 @@
* the service provider name for data service.
* @hide
*/
- @SystemApi
public static final String EXTRA_DATA_SPN = "android.telephony.extra.DATA_SPN";
/**
@@ -9747,14 +9743,12 @@
* Powers down the SIM. SIM must be up prior.
* @hide
*/
- @SystemApi
public static final int CARD_POWER_DOWN = 0;
/**
* Powers up the SIM normally. SIM must be down prior.
* @hide
*/
- @SystemApi
public static final int CARD_POWER_UP = 1;
/**
@@ -9772,7 +9766,6 @@
* is NOT persistent across boots. On reboot, SIM will power up normally.
* @hide
*/
- @SystemApi
public static final int CARD_POWER_UP_PASS_THROUGH = 2;
/**