Merge "Fixed name of autofill service on save UI." into pi-dev
diff --git a/api/system-current.txt b/api/system-current.txt
index e54d2f6..20f0ba8 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3663,6 +3663,7 @@
}
public final class ConfigUpdate {
+ field public static final java.lang.String ACTION_UPDATE_CARRIER_ID_DB = "android.os.action.UPDATE_CARRIER_ID_DB";
field public static final java.lang.String ACTION_UPDATE_CARRIER_PROVISIONING_URLS = "android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS";
field public static final java.lang.String ACTION_UPDATE_CT_LOGS = "android.intent.action.UPDATE_CT_LOGS";
field public static final java.lang.String ACTION_UPDATE_INTENT_FIREWALL = "android.intent.action.UPDATE_INTENT_FIREWALL";
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 4b84ed4..ca3257f 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -736,7 +736,13 @@
}
if (!libPaths.isEmpty() && SystemProperties.getBoolean(PROPERTY_NAME_APPEND_NATIVE, true)) {
- ApplicationLoaders.getDefault().addNative(mClassLoader, libPaths);
+ // Temporarily disable logging of disk reads on the Looper thread as this is necessary
+ StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
+ try {
+ ApplicationLoaders.getDefault().addNative(mClassLoader, libPaths);
+ } finally {
+ StrictMode.setThreadPolicy(oldPolicy);
+ }
}
if (addedPaths != null && addedPaths.size() > 0) {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 1b80d3d5..9154ce0 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1660,23 +1660,29 @@
* @see ShutterCallback
*/
public final boolean enableShutterSound(boolean enabled) {
- if (!enabled) {
- IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
- IAudioService audioService = IAudioService.Stub.asInterface(b);
- try {
- if (audioService.isCameraSoundForced()) return false;
- } catch (RemoteException e) {
- Log.e(TAG, "Audio service is unavailable for queries");
+ boolean canDisableShutterSound = true;
+ IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+ IAudioService audioService = IAudioService.Stub.asInterface(b);
+ try {
+ if (audioService.isCameraSoundForced()) {
+ canDisableShutterSound = false;
}
+ } catch (RemoteException e) {
+ Log.e(TAG, "Audio service is unavailable for queries");
+ }
+ if (!enabled && !canDisableShutterSound) {
+ return false;
}
synchronized (mShutterSoundLock) {
- if (enabled && mHasAppOpsPlayAudio) {
- Log.i(TAG, "Shutter sound is not allowed by AppOpsManager");
- return false;
- }
+ mShutterSoundEnabledFromApp = enabled;
+ // Return the result of _enableShutterSound(enabled) in all cases.
+ // If the shutter sound can be disabled, disable it when the device is in DnD mode.
boolean ret = _enableShutterSound(enabled);
- if (ret) {
- mShutterSoundEnabledFromApp = enabled;
+ if (enabled && !mHasAppOpsPlayAudio) {
+ Log.i(TAG, "Shutter sound is not allowed by AppOpsManager");
+ if (canDisableShutterSound) {
+ _enableShutterSound(false);
+ }
}
return ret;
}
@@ -1739,9 +1745,18 @@
}
if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio) {
if (!mHasAppOpsPlayAudio) {
+ IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+ IAudioService audioService = IAudioService.Stub.asInterface(b);
+ try {
+ if (audioService.isCameraSoundForced()) {
+ return;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Audio service is unavailable for queries");
+ }
_enableShutterSound(false);
} else {
- _enableShutterSound(mShutterSoundEnabledFromApp);
+ enableShutterSound(mShutterSoundEnabledFromApp);
}
}
}
diff --git a/core/java/android/os/ConfigUpdate.java b/core/java/android/os/ConfigUpdate.java
index dda0ed8..53b1c51 100644
--- a/core/java/android/os/ConfigUpdate.java
+++ b/core/java/android/os/ConfigUpdate.java
@@ -90,6 +90,14 @@
public static final String ACTION_UPDATE_NETWORK_WATCHLIST
= "android.intent.action.UPDATE_NETWORK_WATCHLIST";
+ /**
+ * Update carrier id config file.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_UPDATE_CARRIER_ID_DB
+ = "android.os.action.UPDATE_CARRIER_ID_DB";
+
private ConfigUpdate() {
}
}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index f8dd7ac..9da3b21 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -747,6 +747,12 @@
jittransitionweightOptBuf,
"-Xjittransitionweight:");
+ property_get("dalvik.vm.profilebootimage", propBuf, "");
+ if (strcmp(propBuf, "true") == 0) {
+ addOption("-Xps-profile-boot-class-path");
+ addOption("-Xps-profile-aot-code");
+ }
+
/*
* Madvise related options.
*/
diff --git a/core/jni/android/graphics/ImageDecoder.cpp b/core/jni/android/graphics/ImageDecoder.cpp
index 3ea6049..df735ae 100644
--- a/core/jni/android/graphics/ImageDecoder.cpp
+++ b/core/jni/android/graphics/ImageDecoder.cpp
@@ -139,18 +139,9 @@
return throw_exception(env, ImageDecoder::kSourceMalformedData, "Could not open file",
nullptr, source);
}
+
std::unique_ptr<SkFILEStream> fileStream(new SkFILEStream(file));
-
- if (::lseek(descriptor, 0, SEEK_CUR) == 0) {
- return native_create(env, std::move(fileStream), source);
- }
-
- // FIXME: This allows us to pretend the current location is the beginning,
- // but it would be better if SkFILEStream allowed treating its starting
- // point as the beginning.
- std::unique_ptr<SkStream> stream(SkFrontBufferedStream::Make(std::move(fileStream),
- SkCodec::MinBufferedBytesNeeded()));
- return native_create(env, std::move(stream), source);
+ return native_create(env, std::move(fileStream), source);
}
static jobject ImageDecoder_nCreateInputStream(JNIEnv* env, jobject /*clazz*/,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1f8d43c..87d8915 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4262,7 +4262,7 @@
<receiver android:name="com.android.server.updates.CarrierIdInstallReceiver"
android:permission="android.permission.UPDATE_CONFIG">
<intent-filter>
- <action android:name="com.android.internal.intent.action.UPDATE_CARRIER_ID_DB" />
+ <action android:name="android.os.action.UPDATE_CARRIER_ID_DB" />
<data android:scheme="content" android:host="*" android:mimeType="*/*" />
</intent-filter>
</receiver>
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index a265a5e..0ca0a11 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -74,6 +74,7 @@
private boolean mEnforceBouncer = false;
private boolean mBouncerOn = false;
+ private boolean mBouncerOffOnDown = false;
private boolean mSessionActive = false;
private boolean mIsTouchScreen = true;
private int mState = StatusBarState.SHADE;
@@ -459,10 +460,19 @@
public void onTouchEvent(MotionEvent event, int width, int height) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mIsTouchScreen = event.isFromSource(InputDevice.SOURCE_TOUCHSCREEN);
+ // If the bouncer was not shown during the down event,
+ // we want the entire gesture going to HumanInteractionClassifier
+ mBouncerOffOnDown = !mBouncerOn;
}
- if (mSessionActive && !mBouncerOn) {
- mDataCollector.onTouchEvent(event, width, height);
- mHumanInteractionClassifier.onTouchEvent(event);
+ if (mSessionActive) {
+ if (!mBouncerOn) {
+ // In case bouncer is "visible", but onFullyShown has not yet been called,
+ // avoid adding the event to DataCollector
+ mDataCollector.onTouchEvent(event, width, height);
+ }
+ if (mBouncerOffOnDown) {
+ mHumanInteractionClassifier.onTouchEvent(event);
+ }
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 20b8407..48090f2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -22995,18 +22995,27 @@
}
}
- private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
+ private final boolean computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
boolean doingAll, long now) {
if (mAdjSeq == app.adjSeq) {
- // This adjustment has already been computed.
- return app.curRawAdj;
+ if (app.adjSeq == app.completedAdjSeq) {
+ // This adjustment has already been computed successfully.
+ return false;
+ } else {
+ // The process is being computed, so there is a cycle. We cannot
+ // rely on this process's state.
+ app.containsCycle = true;
+ return false;
+ }
}
if (app.thread == null) {
app.adjSeq = mAdjSeq;
app.curSchedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
- return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ);
+ app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ;
+ app.completedAdjSeq = app.adjSeq;
+ return false;
}
app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN;
@@ -23019,6 +23028,8 @@
final int appUid = app.info.uid;
final int logUid = mCurOomAdjUid;
+ int prevAppAdj = app.curAdj;
+
if (app.maxAdj <= ProcessList.FOREGROUND_APP_ADJ) {
// The max adjustment doesn't allow this app to be anything
// below foreground, so it is not worth doing work for it.
@@ -23063,7 +23074,10 @@
app.curSchedGroup = ProcessList.SCHED_GROUP_RESTRICTED;
}
}
- return (app.curAdj=app.maxAdj);
+ app.curAdj = app.maxAdj;
+ app.completedAdjSeq = app.adjSeq;
+ // if curAdj is less than prevAppAdj, then this process was promoted
+ return app.curAdj < prevAppAdj;
}
app.systemNoUi = false;
@@ -23075,6 +23089,8 @@
int adj;
int schedGroup;
int procState;
+ int cachedAdjSeq;
+
boolean foregroundActivities = false;
mTmpBroadcastQueue.clear();
if (PROCESS_STATE_CUR_TOP == ActivityManager.PROCESS_STATE_TOP && app == TOP_APP) {
@@ -23388,9 +23404,9 @@
// there are applications dependent on our services or providers, but
// this gives us a baseline and makes sure we don't get into an
// infinite recursion.
- app.adjSeq = mAdjSeq;
app.curRawAdj = adj;
app.hasStartedServices = false;
+ app.adjSeq = mAdjSeq;
if (mBackupTarget != null && app == mBackupTarget.app) {
// If possible we want to avoid killing apps while they're being backed up
@@ -23489,8 +23505,15 @@
if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
ProcessRecord client = cr.binding.client;
- int clientAdj = computeOomAdjLocked(client, cachedAdj,
- TOP_APP, doingAll, now);
+ computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
+ if (client.containsCycle) {
+ // We've detected a cycle. We should ignore this connection and allow
+ // this process to retry computeOomAdjLocked later in case a later-checked
+ // connection from a client would raise its priority legitimately.
+ app.containsCycle = true;
+ continue;
+ }
+ int clientAdj = client.curRawAdj;
int clientProcState = client.curProcState;
if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
@@ -23709,7 +23732,15 @@
// Being our own client is not interesting.
continue;
}
- int clientAdj = computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
+ computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
+ if (client.containsCycle) {
+ // We've detected a cycle. We should ignore this connection and allow
+ // this process to retry computeOomAdjLocked later in case a later-checked
+ // connection from a client would raise its priority legitimately.
+ app.containsCycle = true;
+ continue;
+ }
+ int clientAdj = client.curRawAdj;
int clientProcState = client.curProcState;
if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
// If the other app is cached for any reason, for purposes here
@@ -23937,8 +23968,10 @@
app.curSchedGroup = schedGroup;
app.curProcState = procState;
app.foregroundActivities = foregroundActivities;
+ app.completedAdjSeq = mAdjSeq;
- return app.curRawAdj;
+ // if curAdj is less than prevAppAdj, then this process was promoted
+ return app.curAdj < prevAppAdj;
}
/**
@@ -24912,12 +24945,23 @@
int nextCachedAdj = curCachedAdj+1;
int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
int nextEmptyAdj = curEmptyAdj+2;
+
+ boolean retryCycles = false;
+
+ // need to reset cycle state before calling computeOomAdjLocked because of service connections
+ for (int i=N-1; i>=0; i--) {
+ ProcessRecord app = mLruProcesses.get(i);
+ app.containsCycle = false;
+ }
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
if (!app.killedByAm && app.thread != null) {
app.procStateChanged = false;
computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
+ // if any app encountered a cycle, we need to perform an additional loop later
+ retryCycles |= app.containsCycle;
+
// If we haven't yet assigned the final cached adj
// to the process, do that now.
if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
@@ -24971,6 +25015,39 @@
}
}
+
+ }
+ }
+
+ // Cycle strategy:
+ // - Retry computing any process that has encountered a cycle.
+ // - Continue retrying until no process was promoted.
+ // - Iterate from least important to most important.
+ int cycleCount = 0;
+ while (retryCycles) {
+ cycleCount++;
+ retryCycles = false;
+
+ for (int i=0; i<N; i++) {
+ ProcessRecord app = mLruProcesses.get(i);
+ if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
+ app.adjSeq--;
+ app.completedAdjSeq--;
+ }
+ }
+
+ for (int i=0; i<N; i++) {
+ ProcessRecord app = mLruProcesses.get(i);
+ if (!app.killedByAm && app.thread != null && app.containsCycle == true) {
+ if (computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now)) {
+ retryCycles = true;
+ }
+ }
+ }
+ }
+ for (int i=N-1; i>=0; i--) {
+ ProcessRecord app = mLruProcesses.get(i);
+ if (!app.killedByAm && app.thread != null) {
applyOomAdjLocked(app, true, now, nowElapsed);
// Count the number of process types.
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b7fde1d..caf52e3 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -149,6 +149,8 @@
String waitingToKill; // Process is waiting to be killed when in the bg, and reason
Object forcingToImportant; // Token that is forcing this process to be important
int adjSeq; // Sequence id for identifying oom_adj assignment cycles
+ int completedAdjSeq; // Sequence id for identifying oom_adj assignment cycles
+ boolean containsCycle; // Whether this app has encountered a cycle in the most recent update
int lruSeq; // Sequence id for identifying LRU update cycles
CompatibilityInfo compat; // last used compatibility mode
IBinder.DeathRecipient deathRecipient; // Who is watching for the death.
diff --git a/services/core/java/com/android/server/display/ColorDisplayService.java b/services/core/java/com/android/server/display/ColorDisplayService.java
index 030c915..213ec36 100644
--- a/services/core/java/com/android/server/display/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/ColorDisplayService.java
@@ -189,7 +189,7 @@
mController = new ColorDisplayController(getContext(), mCurrentUser);
mController.setListener(this);
- setCoefficientMatrix(getContext(), DisplayTransformManager.isNativeModeEnabled());
+ setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix());
// Prepare color transformation matrix.
setMatrix(mController.getColorTemperature(), mMatrixNight);
@@ -293,7 +293,7 @@
mColorMatrixAnimator.cancel();
}
- setCoefficientMatrix(getContext(), DisplayTransformManager.isColorModeNative(mode));
+ setCoefficientMatrix(getContext(), DisplayTransformManager.needsLinearColorMatrix(mode));
setMatrix(mController.getColorTemperature(), mMatrixNight);
final DisplayTransformManager dtm = getLocalService(DisplayTransformManager.class);
@@ -306,13 +306,12 @@
}
/**
- * Set coefficients based on native mode. Use DisplayTransformManager#isNativeModeEnabled while
- * setting is stable; when setting is changing, pass native mode selection directly.
+ * Set coefficients based on whether the color matrix is linear or not.
*/
- private void setCoefficientMatrix(Context context, boolean isNative) {
- final String[] coefficients = context.getResources().getStringArray(isNative
- ? R.array.config_nightDisplayColorTemperatureCoefficientsNative
- : R.array.config_nightDisplayColorTemperatureCoefficients);
+ private void setCoefficientMatrix(Context context, boolean needsLinear) {
+ final String[] coefficients = context.getResources().getStringArray(needsLinear
+ ? R.array.config_nightDisplayColorTemperatureCoefficients
+ : R.array.config_nightDisplayColorTemperatureCoefficientsNative);
for (int i = 0; i < 9 && i < coefficients.length; i++) {
mColorTempCoefficients[i] = Float.parseFloat(coefficients[i]);
}
diff --git a/services/core/java/com/android/server/display/DisplayTransformManager.java b/services/core/java/com/android/server/display/DisplayTransformManager.java
index 57d88e1..57a4f0d 100644
--- a/services/core/java/com/android/server/display/DisplayTransformManager.java
+++ b/services/core/java/com/android/server/display/DisplayTransformManager.java
@@ -231,21 +231,19 @@
}
/**
- * Return true when colors are stretched from the working color space to the
- * native color space.
+ * Return true when the color matrix works in linear space.
*/
- public static boolean isNativeModeEnabled() {
+ public static boolean needsLinearColorMatrix() {
return SystemProperties.getInt(PERSISTENT_PROPERTY_DISPLAY_COLOR,
- DISPLAY_COLOR_MANAGED) != DISPLAY_COLOR_MANAGED;
+ DISPLAY_COLOR_UNMANAGED) != DISPLAY_COLOR_UNMANAGED;
}
/**
- * Return true when the specified colorMode stretches colors from the
- * working color space to the native color space.
+ * Return true when the specified colorMode requires the color matrix to
+ * work in linear space.
*/
- public static boolean isColorModeNative(int colorMode) {
- return !(colorMode == ColorDisplayController.COLOR_MODE_NATURAL ||
- colorMode == ColorDisplayController.COLOR_MODE_BOOSTED);
+ public static boolean needsLinearColorMatrix(int colorMode) {
+ return colorMode != ColorDisplayController.COLOR_MODE_SATURATED;
}
public boolean setColorMode(int colorMode, float[] nightDisplayMatrix) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index a92fbb6..01f84c4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -25,6 +25,7 @@
import android.accounts.IAccountManager;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -53,17 +54,20 @@
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
+import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.net.Uri;
-import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IUserManager;
import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.AutoCloseInputStream;
+import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
@@ -78,27 +82,41 @@
import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.PrintWriterPrinter;
-
import com.android.internal.content.PackageHelper;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
-
import dalvik.system.DexFile;
-
-import libcore.io.IoUtils;
-
-import java.io.FileDescriptor;
+import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URISyntaxException;
-import java.util.*;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.FileAttribute;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.WeakHashMap;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
+import libcore.io.IoUtils;
+import libcore.io.Streams;
class PackageManagerShellCommand extends ShellCommand {
/** Path for streaming APK content */
private static final String STDIN_PATH = "-";
+ /** Path where ART profiles snapshots are dumped for the shell user */
+ private final static String ART_PROFILE_SNAPSHOT_DEBUG_LOCATION = "/data/misc/profman/";
final IPackageManager mInterface;
final private WeakHashMap<String, Resources> mResourceCache =
@@ -167,6 +185,8 @@
return runDexoptJob();
case "dump-profiles":
return runDumpProfiles();
+ case "snapshot-profile":
+ return runSnapshotProfile();
case "uninstall":
return runUninstall();
case "clear":
@@ -1287,6 +1307,120 @@
return 0;
}
+ private int runSnapshotProfile() throws RemoteException {
+ PrintWriter pw = getOutPrintWriter();
+
+ // Parse the arguments
+ final String packageName = getNextArg();
+ final boolean isBootImage = "android".equals(packageName);
+
+ String codePath = null;
+ String opt;
+ while ((opt = getNextArg()) != null) {
+ switch (opt) {
+ case "--code-path":
+ if (isBootImage) {
+ pw.write("--code-path cannot be used for the boot image.");
+ return -1;
+ }
+ codePath = getNextArg();
+ break;
+ default:
+ pw.write("Unknown arg: " + opt);
+ return -1;
+ }
+ }
+
+ // If no code path was explicitly requested, select the base code path.
+ String baseCodePath = null;
+ if (!isBootImage) {
+ PackageInfo packageInfo = mInterface.getPackageInfo(packageName, /* flags */ 0,
+ /* userId */0);
+ if (packageInfo == null) {
+ pw.write("Package not found " + packageName);
+ return -1;
+ }
+ baseCodePath = packageInfo.applicationInfo.getBaseCodePath();
+ if (codePath == null) {
+ codePath = baseCodePath;
+ }
+ }
+
+ // Create the profile snapshot.
+ final SnapshotRuntimeProfileCallback callback = new SnapshotRuntimeProfileCallback();
+ // The calling package is needed to debug permission access.
+ final String callingPackage = (Binder.getCallingUid() == Process.ROOT_UID)
+ ? "root" : "com.android.shell";
+ final int profileType = isBootImage
+ ? ArtManager.PROFILE_BOOT_IMAGE : ArtManager.PROFILE_APPS;
+ if (!mInterface.getArtManager().isRuntimeProfilingEnabled(profileType, callingPackage)) {
+ pw.println("Error: Runtime profiling is not enabled");
+ return -1;
+ }
+ mInterface.getArtManager().snapshotRuntimeProfile(profileType, packageName,
+ codePath, callback, callingPackage);
+ if (!callback.waitTillDone()) {
+ pw.println("Error: callback not called");
+ return callback.mErrCode;
+ }
+
+ // Copy the snapshot profile to the output profile file.
+ try (InputStream inStream = new AutoCloseInputStream(callback.mProfileReadFd)) {
+ final String outputFileSuffix = isBootImage || Objects.equals(baseCodePath, codePath)
+ ? "" : ("-" + new File(codePath).getName());
+ final String outputProfilePath =
+ ART_PROFILE_SNAPSHOT_DEBUG_LOCATION + packageName + outputFileSuffix + ".prof";
+ try (OutputStream outStream = new FileOutputStream(outputProfilePath)) {
+ Streams.copy(inStream, outStream);
+ }
+ } catch (IOException e) {
+ pw.println("Error when reading the profile fd: " + e.getMessage());
+ e.printStackTrace(pw);
+ return -1;
+ }
+ return 0;
+ }
+
+ private static class SnapshotRuntimeProfileCallback
+ extends ISnapshotRuntimeProfileCallback.Stub {
+ private boolean mSuccess = false;
+ private int mErrCode = -1;
+ private ParcelFileDescriptor mProfileReadFd = null;
+ private CountDownLatch mDoneSignal = new CountDownLatch(1);
+
+ @Override
+ public void onSuccess(ParcelFileDescriptor profileReadFd) {
+ mSuccess = true;
+ try {
+ // We need to dup the descriptor. We are in the same process as system server
+ // and we will be receiving the same object (which will be closed on the
+ // server side).
+ mProfileReadFd = profileReadFd.dup();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ mDoneSignal.countDown();
+ }
+
+ @Override
+ public void onError(int errCode) {
+ mSuccess = false;
+ mErrCode = errCode;
+ mDoneSignal.countDown();
+ }
+
+ boolean waitTillDone() {
+ boolean done = false;
+ try {
+ // The time-out is an arbitrary large value. Since this is a local call the result
+ // will come very fast.
+ done = mDoneSignal.await(10000000, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException ignored) {
+ }
+ return done && mSuccess;
+ }
+ }
+
private int runUninstall() throws RemoteException {
final PrintWriter pw = getOutPrintWriter();
int flags = 0;
@@ -2761,7 +2895,13 @@
pw.println("");
pw.println(" dump-profiles TARGET-PACKAGE");
pw.println(" Dumps method/class profile files to");
- pw.println(" /data/misc/profman/TARGET-PACKAGE.txt");
+ pw.println(" " + ART_PROFILE_SNAPSHOT_DEBUG_LOCATION + "TARGET-PACKAGE.txt");
+ pw.println("");
+ pw.println(" snapshot-profile TARGET-PACKAGE [--code-path path]");
+ pw.println(" Take a snapshot of the package profiles to");
+ pw.println(" " + ART_PROFILE_SNAPSHOT_DEBUG_LOCATION
+ + "TARGET-PACKAGE[-code-path].prof");
+ pw.println(" If TARGET-PACKAGE=android it will take a snapshot of the boot image");
pw.println("");
pw.println(" set-home-activity [--user USER_ID] TARGET-COMPONENT");
pw.println(" Set the default home activity (aka launcher).");
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
index a14b950..b4b34c5 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
@@ -40,6 +40,7 @@
import android.support.test.filters.SmallTest;
import android.testing.DexmakerShareClassLoaderRule;
+import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
import com.android.server.pm.PackageManagerService;
@@ -150,6 +151,28 @@
}
@Test
+ public void testSuspendedPackage() {
+ mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
+ final String suspendingPackage = "com.test.suspending.package";
+ final String dialogMessage = "Test Message";
+ when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
+ .thenReturn(suspendingPackage);
+ when(mPackageManagerInternal.getSuspendedDialogMessage(TEST_PACKAGE_NAME, TEST_USER_ID))
+ .thenReturn(dialogMessage);
+ // THEN calling intercept returns true
+ assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+
+ // Check intent parameters
+ assertEquals(dialogMessage,
+ mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_DIALOG_MESSAGE));
+ assertEquals(suspendingPackage,
+ mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_SUSPENDING_PACKAGE));
+ assertEquals(TEST_PACKAGE_NAME,
+ mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_SUSPENDED_PACKAGE));
+ assertEquals(TEST_USER_ID, mInterceptor.mIntent.getIntExtra(Intent.EXTRA_USER_ID, -1000));
+ }
+
+ @Test
public void testInterceptQuietProfile() {
// GIVEN that the user the activity is starting as is currently in quiet mode
when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);