Merge "Fixed scenario where an authentication response has no dataset." into oc-dev
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index deb5b31..588a1bf 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1441,10 +1441,10 @@
System.err.println("Error: no size specified");
return showUsage();
}
- int len = size.length();
long multiplier = 1;
- if (len > 1) {
- char c = size.charAt(len-1);
+ int len = size.length();
+ char c = size.charAt(len - 1);
+ if (c < '0' || c > '9') {
if (c == 'K' || c == 'k') {
multiplier = 1024L;
} else if (c == 'M' || c == 'm') {
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index dd9db8a..928ef7e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4275,9 +4275,19 @@
View.mDebugViewAttributes = debugViewAttributes;
// request all activities to relaunch for the changes to take place
- for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
- requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false,
- false /* preserveWindow */);
+ requestRelaunchAllActivities();
+ }
+ }
+
+ private void requestRelaunchAllActivities() {
+ for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
+ final Activity activity = entry.getValue().activity;
+ if (!activity.mFinished) {
+ try {
+ ActivityManager.getService().requestActivityRelaunch(entry.getKey());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
}
}
@@ -5084,24 +5094,26 @@
// caused by other sources, such as overlays. That means we want to be as conservative
// about code changes as possible. Take the diff of the old ApplicationInfo and the new
// to see if anything needs to change.
+ LoadedApk apk;
+ LoadedApk resApk;
+ // Update all affected loaded packages with new package information
synchronized (mResourcesManager) {
- // Update all affected loaded packages with new package information
WeakReference<LoadedApk> ref = mPackages.get(ai.packageName);
- LoadedApk apk = ref != null ? ref.get() : null;
- if (apk != null) {
- final ArrayList<String> oldPaths = new ArrayList<>();
- LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths);
- apk.updateApplicationInfo(ai, oldPaths);
- }
-
- ref = mResourcePackages.get(ai.packageName);
apk = ref != null ? ref.get() : null;
- if (apk != null) {
- final ArrayList<String> oldPaths = new ArrayList<>();
- LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths);
- apk.updateApplicationInfo(ai, oldPaths);
- }
-
+ ref = mResourcePackages.get(ai.packageName);
+ resApk = ref != null ? ref.get() : null;
+ }
+ if (apk != null) {
+ final ArrayList<String> oldPaths = new ArrayList<>();
+ LoadedApk.makePaths(this, apk.getApplicationInfo(), oldPaths);
+ apk.updateApplicationInfo(ai, oldPaths);
+ }
+ if (resApk != null) {
+ final ArrayList<String> oldPaths = new ArrayList<>();
+ LoadedApk.makePaths(this, resApk.getApplicationInfo(), oldPaths);
+ resApk.updateApplicationInfo(ai, oldPaths);
+ }
+ synchronized (mResourcesManager) {
// Update all affected Resources objects to use new ResourcesImpl
mResourcesManager.applyNewResourceDirsLocked(ai.sourceDir, ai.resourceDirs);
}
@@ -5116,14 +5128,7 @@
newConfig.assetsSeq = (mConfiguration != null ? mConfiguration.assetsSeq : 0) + 1;
handleConfigurationChanged(newConfig, null);
- // Schedule all activities to reload
- for (final Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
- final Activity activity = entry.getValue().activity;
- if (!activity.mFinished) {
- requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false,
- false);
- }
- }
+ requestRelaunchAllActivities();
}
static void freeTextLayoutCachesIfNeeded(int configDiff) {
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 63c0ef3..1b2543c 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -2497,7 +2497,6 @@
mBackStack = new ArrayList<BackStackRecord>();
}
mBackStack.add(state);
- reportBackStackChanged();
}
boolean popBackStackState(ArrayList<BackStackRecord> records, ArrayList<Boolean> isRecordPop,
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index aeccf56..68fce75 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -200,7 +200,7 @@
void setRequestedOrientation(in IBinder token, int requestedOrientation);
int getRequestedOrientation(in IBinder token);
void unbindFinished(in IBinder token, in Intent service, boolean doRebind);
- void setProcessForeground(in IBinder token, int pid, boolean isForeground);
+ void setProcessImportant(in IBinder token, int pid, boolean isForeground, String reason);
void setServiceForeground(in ComponentName className, in IBinder token,
int id, in Notification notification, int flags);
boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot);
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
index 040b330..8014eca 100644
--- a/core/java/android/app/VrManager.java
+++ b/core/java/android/app/VrManager.java
@@ -6,8 +6,6 @@
import android.os.RemoteException;
import android.service.vr.IVrManager;
-import java.io.FileDescriptor;
-
/**
* Used to control aspects of a devices Virtual Reality (VR) capabilities.
* <p>
@@ -63,32 +61,4 @@
e.rethrowFromSystemServer();
}
}
-
- /**
- * Initiate connection for system controller data.
- *
- * @param fd Controller data file descriptor.
- *
- * {@hide}
- */
- public void connectController(FileDescriptor fd) {
- try {
- mService.connectController(fd);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Sever connection for system controller data.
- *
- * {@hide}
- */
- public void disconnectController() {
- try {
- mService.disconnectController();
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- }
}
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 7261dfa..1994206 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -18,6 +18,7 @@
import android.os.PooledStringWriter;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.service.autofill.FillContext;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -1103,6 +1104,9 @@
* Returns the transformation that has been applied to this view, such as a translation
* or scaling. The returned Matrix object is owned by ViewNode; do not modify it.
* Returns null if there is no transformation applied to the view.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public Matrix getTransformation() {
return mMatrix;
@@ -1112,6 +1116,9 @@
* Returns the visual elevation of the view, used for shadowing and other visual
* characterstics, as set by {@link ViewStructure#setElevation
* ViewStructure.setElevation(float)}.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public float getElevation() {
return mElevation;
@@ -1121,6 +1128,9 @@
* Returns the alpha transformation of the view, used to reduce the overall opacity
* of the view's contents, as set by {@link ViewStructure#setAlpha
* ViewStructure.setAlpha(float)}.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public float getAlpha() {
return mAlpha;
@@ -1289,6 +1299,9 @@
/**
* If {@link #getText()} is non-null, this is where the current selection starts.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public int getTextSelectionStart() {
return mText != null ? mText.mTextSelectionStart : -1;
@@ -1298,6 +1311,9 @@
* If {@link #getText()} is non-null, this is where the current selection starts.
* If there is no selection, returns the same value as {@link #getTextSelectionStart()},
* indicating the cursor position.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public int getTextSelectionEnd() {
return mText != null ? mText.mTextSelectionEnd : -1;
@@ -1319,6 +1335,9 @@
* If there is no text background color, {@link #TEXT_COLOR_UNDEFINED} is returned.
* Note that the text may also contain style spans that modify the color of specific
* parts of the text.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public int getTextBackgroundColor() {
return mText != null ? mText.mTextBackgroundColor : TEXT_COLOR_UNDEFINED;
@@ -1329,6 +1348,9 @@
* with it.
* Note that the text may also contain style spans that modify the size of specific
* parts of the text.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public float getTextSize() {
return mText != null ? mText.mTextSize : 0;
@@ -1341,6 +1363,9 @@
* {@link #TEXT_STYLE_UNDERLINE}.
* Note that the text may also contain style spans that modify the style of specific
* parts of the text.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public int getTextStyle() {
return mText != null ? mText.mTextStyle : 0;
@@ -1351,6 +1376,9 @@
* in the array is a formatted line of text, and the value it contains is the offset
* into the text string where that line starts. May return null if there is no line
* information.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public int[] getTextLineCharOffsets() {
return mText != null ? mText.mLineCharOffsets : null;
@@ -1361,6 +1389,9 @@
* in the array is a formatted line of text, and the value it contains is the baseline
* where that text appears in the view. May return null if there is no line
* information.
+ *
+ * <p>It's only relevant when the {@link AssistStructure} is used for Assist purposes,
+ * not for Autofill purposes.
*/
public int[] getTextLineBaselines() {
return mText != null ? mText.mLineBaselines : null;
diff --git a/core/java/android/app/usage/ExternalStorageStats.java b/core/java/android/app/usage/ExternalStorageStats.java
index 83ac779e..d7e570f 100644
--- a/core/java/android/app/usage/ExternalStorageStats.java
+++ b/core/java/android/app/usage/ExternalStorageStats.java
@@ -37,30 +37,46 @@
/**
* Return the total bytes used by all files in the shared/external storage
* hosted on this volume.
+ * <p>
+ * This value only includes data which is isolated for each user on a
+ * multiuser device. Any OBB data shared between users is not accounted in
+ * this value.
*/
public @BytesLong long getTotalBytes() {
return totalBytes;
}
/**
- * Return the total bytes used by audio files in the shared/external storage
- * hosted on this volume.
+ * Return the total bytes used by all audio files in the shared/external
+ * storage hosted on this volume.
+ * <p>
+ * This value only includes data which is isolated for each user on a
+ * multiuser device. This value does not include any app files which are all
+ * accounted under {@link #getAppBytes()}.
*/
public @BytesLong long getAudioBytes() {
return audioBytes;
}
/**
- * Return the total bytes used by video files in the shared/external storage
- * hosted on this volume.
+ * Return the total bytes used by all video files in the shared/external
+ * storage hosted on this volume.
+ * <p>
+ * This value only includes data which is isolated for each user on a
+ * multiuser device. This value does not include any app files which are all
+ * accounted under {@link #getAppBytes()}.
*/
public @BytesLong long getVideoBytes() {
return videoBytes;
}
/**
- * Return the total bytes used by image files in the shared/external storage
- * hosted on this volume.
+ * Return the total bytes used by all image files in the shared/external
+ * storage hosted on this volume.
+ * <p>
+ * This value only includes data which is isolated for each user on a
+ * multiuser device. This value does not include any app files which are all
+ * accounted under {@link #getAppBytes()}.
*/
public @BytesLong long getImageBytes() {
return imageBytes;
@@ -72,6 +88,9 @@
* <p>
* This data is already accounted against individual apps as returned
* through {@link StorageStats}.
+ * <p>
+ * This value only includes data which is isolated for each user on a
+ * multiuser device.
*/
public @BytesLong long getAppBytes() {
return appBytes;
diff --git a/core/java/android/content/pm/InstantAppResolveInfo.java b/core/java/android/content/pm/InstantAppResolveInfo.java
index 603192a..dcaf66e 100644
--- a/core/java/android/content/pm/InstantAppResolveInfo.java
+++ b/core/java/android/content/pm/InstantAppResolveInfo.java
@@ -47,7 +47,7 @@
private final int mVersionCode;
public InstantAppResolveInfo(@NonNull InstantAppDigest digest, @Nullable String packageName,
- @Nullable List<InstantAppIntentFilter> filters, int versionConde) {
+ @Nullable List<InstantAppIntentFilter> filters, int versionCode) {
// validate arguments
if ((packageName == null && (filters != null && filters.size() != 0))
|| (packageName != null && (filters == null || filters.size() == 0))) {
@@ -61,7 +61,7 @@
mFilters = null;
}
mPackageName = packageName;
- mVersionCode = versionConde;
+ mVersionCode = versionCode;
}
public InstantAppResolveInfo(@NonNull String hostName, @Nullable String packageName,
diff --git a/core/java/android/content/pm/ShortcutInfo.java b/core/java/android/content/pm/ShortcutInfo.java
index 2b76ae2..d0c6397 100644
--- a/core/java/android/content/pm/ShortcutInfo.java
+++ b/core/java/android/content/pm/ShortcutInfo.java
@@ -23,7 +23,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
@@ -96,6 +95,9 @@
public static final int FLAG_ADAPTIVE_BITMAP = 1 << 9;
/** @hide */
+ public static final int FLAG_RETURNED_BY_SERVICE = 1 << 10;
+
+ /** @hide */
@IntDef(flag = true,
value = {
FLAG_DYNAMIC,
@@ -108,6 +110,7 @@
FLAG_STRINGS_RESOLVED,
FLAG_IMMUTABLE,
FLAG_ADAPTIVE_BITMAP,
+ FLAG_RETURNED_BY_SERVICE
})
@Retention(RetentionPolicy.SOURCE)
public @interface ShortcutFlags {}
@@ -1343,6 +1346,16 @@
return (mFlags & flags) == flags;
}
+ /** @hide */
+ public boolean isReturnedByServer() {
+ return hasFlags(FLAG_RETURNED_BY_SERVICE);
+ }
+
+ /** @hide */
+ public void setReturnedByServer() {
+ addFlags(FLAG_RETURNED_BY_SERVICE);
+ }
+
/** Return whether a shortcut is dynamic. */
public boolean isDynamic() {
return hasFlags(FLAG_DYNAMIC);
@@ -1450,7 +1463,7 @@
/**
* Return whether a shortcut's icon is adaptive bitmap following design guideline
- * defined in {@link AdaptiveIconDrawable}.
+ * defined in {@link android.graphics.drawable.AdaptiveIconDrawable}.
*
* @hide internal/unit tests only
*/
@@ -1776,6 +1789,9 @@
if (hasStringResourcesResolved()) {
sb.append("Sr");
}
+ if (isReturnedByServer()) {
+ sb.append("V");
+ }
sb.append("]");
sb.append(", packageName=");
diff --git a/core/java/android/content/pm/ShortcutManager.java b/core/java/android/content/pm/ShortcutManager.java
index a7c09b5..e3e0cc5 100644
--- a/core/java/android/content/pm/ShortcutManager.java
+++ b/core/java/android/content/pm/ShortcutManager.java
@@ -618,6 +618,10 @@
/**
* Return all dynamic shortcuts from the caller app.
*
+ * <p>This API is intended to be used for examining what shortcuts are currently published.
+ * Re-publishing returned {@link ShortcutInfo}s via APIs such as
+ * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons.
+ *
* @throws IllegalStateException when the user is locked.
*/
@NonNull
@@ -633,6 +637,10 @@
/**
* Return all static (manifest) shortcuts from the caller app.
*
+ * <p>This API is intended to be used for examining what shortcuts are currently published.
+ * Re-publishing returned {@link ShortcutInfo}s via APIs such as
+ * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons.
+ *
* @throws IllegalStateException when the user is locked.
*/
@NonNull
@@ -697,6 +705,10 @@
/**
* Return all pinned shortcuts from the caller app.
*
+ * <p>This API is intended to be used for examining what shortcuts are currently published.
+ * Re-publishing returned {@link ShortcutInfo}s via APIs such as
+ * {@link #setDynamicShortcuts(List)} may cause loss of information such as icons.
+ *
* @throws IllegalStateException when the user is locked.
*/
@NonNull
diff --git a/core/java/android/content/res/FontResourcesParser.java b/core/java/android/content/res/FontResourcesParser.java
index b21ccf1..042eb87 100644
--- a/core/java/android/content/res/FontResourcesParser.java
+++ b/core/java/android/content/res/FontResourcesParser.java
@@ -18,6 +18,7 @@
import com.android.internal.R;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Xml;
@@ -36,8 +37,6 @@
*/
public class FontResourcesParser {
private static final String TAG = "FontResourcesParser";
- private static final int NORMAL_WEIGHT = 400;
- private static final int ITALIC = 1;
// A class represents single entry of font-family in xml file.
public interface FamilyResourceEntry {}
@@ -78,10 +77,10 @@
public static final class FontFileResourceEntry {
private final @NonNull String mFileName;
private int mWeight;
- private boolean mItalic;
+ private int mItalic;
private int mResourceId;
- public FontFileResourceEntry(@NonNull String fileName, int weight, boolean italic) {
+ public FontFileResourceEntry(@NonNull String fileName, int weight, int italic) {
mFileName = fileName;
mWeight = weight;
mItalic = italic;
@@ -95,7 +94,7 @@
return mWeight;
}
- public boolean isItalic() {
+ public int getItalic() {
return mItalic;
}
}
@@ -200,8 +199,10 @@
throws XmlPullParserException, IOException {
AttributeSet attrs = Xml.asAttributeSet(parser);
TypedArray array = resources.obtainAttributes(attrs, R.styleable.FontFamilyFont);
- int weight = array.getInt(R.styleable.FontFamilyFont_fontWeight, NORMAL_WEIGHT);
- boolean isItalic = ITALIC == array.getInt(R.styleable.FontFamilyFont_fontStyle, 0);
+ int weight = array.getInt(R.styleable.FontFamilyFont_fontWeight,
+ Typeface.RESOLVE_BY_FONT_TABLE);
+ int italic = array.getInt(R.styleable.FontFamilyFont_fontStyle,
+ Typeface.RESOLVE_BY_FONT_TABLE);
String filename = array.getString(R.styleable.FontFamilyFont_font);
array.recycle();
while (parser.next() != XmlPullParser.END_TAG) {
@@ -210,7 +211,7 @@
if (filename == null) {
return null;
}
- return new FontFileResourceEntry(filename, weight, isItalic);
+ return new FontFileResourceEntry(filename, weight, italic);
}
private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException {
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 235f24c..37c153f 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -183,8 +183,10 @@
* - Wakelock data (wl) gets current and max times.
* New in version 20:
* - Background timers and counters for: Sensor, BluetoothScan, WifiScan, Jobs, Syncs.
+ * New in version 21:
+ * - Actual (not just apportioned) Wakelock time is also recorded.
*/
- static final String CHECKIN_VERSION = "20";
+ static final String CHECKIN_VERSION = "21";
/**
* Old version, we hit 9 and ran out of room, need to remove.
@@ -194,7 +196,7 @@
private static final long BYTES_PER_KB = 1024;
private static final long BYTES_PER_MB = 1048576; // 1024^2
private static final long BYTES_PER_GB = 1073741824; //1024^3
-
+
private static final String VERSION_DATA = "vers";
private static final String UID_DATA = "uid";
private static final String WAKEUP_ALARM_DATA = "wua";
@@ -205,6 +207,12 @@
private static final String VIBRATOR_DATA = "vib";
private static final String FOREGROUND_DATA = "fg";
private static final String STATE_TIME_DATA = "st";
+ // wl line is:
+ // BATTERY_STATS_CHECKIN_VERSION, uid, which, "wl", name,
+ // full totalTime, 'f', count, current duration, max duration, total duration,
+ // partial totalTime, 'p', count, current duration, max duration, total duration,
+ // window totalTime, 'w', count, current duration, max duration, total duration
+ // [Currently, full and window wakelocks have durations current = max = total = -1]
private static final String WAKELOCK_DATA = "wl";
private static final String SYNC_DATA = "sy";
private static final String JOB_DATA = "jb";
@@ -2659,6 +2667,12 @@
sb.append(" max=");
sb.append(maxDurationMs);
}
+ // Put actual time if it is available and different from totalTimeMillis.
+ final long totalDurMs = timer.getTotalDurationMsLocked(elapsedRealtimeUs/1000);
+ if (totalDurMs > totalTimeMillis) {
+ sb.append(" actual=");
+ sb.append(totalDurMs);
+ }
if (timer.isRunningLocked()) {
final long currentMs = timer.getCurrentDurationMsLocked(elapsedRealtimeUs/1000);
if (currentMs >= 0) {
@@ -2742,13 +2756,15 @@
long elapsedRealtimeUs, String name, int which, String linePrefix) {
long totalTimeMicros = 0;
int count = 0;
- long max = -1;
- long current = -1;
+ long max = 0;
+ long current = 0;
+ long totalDuration = 0;
if (timer != null) {
totalTimeMicros = timer.getTotalTimeLocked(elapsedRealtimeUs, which);
- count = timer.getCountLocked(which);
+ count = timer.getCountLocked(which);
current = timer.getCurrentDurationMsLocked(elapsedRealtimeUs/1000);
max = timer.getMaxDurationMsLocked(elapsedRealtimeUs/1000);
+ totalDuration = timer.getTotalDurationMsLocked(elapsedRealtimeUs/1000);
}
sb.append(linePrefix);
sb.append((totalTimeMicros + 500) / 1000); // microseconds to milliseconds with rounding
@@ -2759,9 +2775,16 @@
sb.append(current);
sb.append(',');
sb.append(max);
+ // Partial, full, and window wakelocks are pooled, so totalDuration is meaningful (albeit
+ // not always tracked). Kernel wakelocks (which have name == null) have no notion of
+ // totalDuration independent of totalTimeMicros (since they are not pooled).
+ if (name != null) {
+ sb.append(',');
+ sb.append(totalDuration);
+ }
return ",";
}
-
+
private static final void dumpLineHeader(PrintWriter pw, int uid, String category,
String type) {
pw.print(BATTERY_STATS_CHECKIN_VERSION);
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index eceaa31..6aa601a 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -189,10 +189,11 @@
if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) {
throw new IllegalArgumentException(
"amplitude must either be DEFAULT_AMPLITUDE, " +
- "or between 1 and 255 inclusive");
+ "or between 1 and 255 inclusive (amplitude=" + mAmplitude + ")");
}
if (mTiming <= 0) {
- throw new IllegalArgumentException("timing must be positive");
+ throw new IllegalArgumentException(
+ "timing must be positive (timing=" + mTiming + ")");
}
}
@@ -274,24 +275,31 @@
public void validate() {
if (mTimings.length != mAmplitudes.length) {
throw new IllegalArgumentException(
- "timing and amplitude arrays must be of equal length");
+ "timing and amplitude arrays must be of equal length" +
+ " (timings.length=" + mTimings.length +
+ ", amplitudes.length=" + mAmplitudes.length + ")");
}
if (!hasNonZeroEntry(mTimings)) {
- throw new IllegalArgumentException("at least one timing must be non-zero");
+ throw new IllegalArgumentException("at least one timing must be non-zero" +
+ " (timings=" + Arrays.toString(mTimings) + ")");
}
for (long timing : mTimings) {
if (timing < 0) {
- throw new IllegalArgumentException("timings must all be >= 0");
+ throw new IllegalArgumentException("timings must all be >= 0" +
+ " (timings=" + Arrays.toString(mTimings) + ")");
}
}
for (int amplitude : mAmplitudes) {
if (amplitude < -1 || amplitude > 255) {
throw new IllegalArgumentException(
- "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255");
+ "amplitudes must all be DEFAULT_AMPLITUDE or between 0 and 255" +
+ " (amplitudes=" + Arrays.toString(mAmplitudes) + ")");
}
}
if (mRepeat < -1 || mRepeat >= mTimings.length) {
- throw new IllegalArgumentException("repeat index must be >= -1");
+ throw new IllegalArgumentException(
+ "repeat index must be within the bounds of the timings array" +
+ " (timings.length=" + mTimings.length + ", index=" + mRepeat +")");
}
}
@@ -375,7 +383,8 @@
@Override
public void validate() {
if (mEffectId != EFFECT_CLICK) {
- throw new IllegalArgumentException("Unknown prebaked effect type");
+ throw new IllegalArgumentException(
+ "Unknown prebaked effect type (value=" + mEffectId + ")");
}
}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 1e55c78..2f0eeca 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -157,6 +157,8 @@
// This call needs to continue throwing ArrayIndexOutOfBoundsException but ignore all other
// exceptions for compatibility purposes
if (repeat < -1 || repeat >= pattern.length) {
+ Log.e(TAG, "vibrate called with repeat index out of bounds" +
+ " (pattern.length=" + pattern.length + ", index=" + repeat + ")");
throw new ArrayIndexOutOfBoundsException();
}
diff --git a/core/java/android/os/VintfObject.java b/core/java/android/os/VintfObject.java
index 8302ece..65b33e5 100644
--- a/core/java/android/os/VintfObject.java
+++ b/core/java/android/os/VintfObject.java
@@ -16,14 +16,17 @@
package android.os;
-import java.util.ArrayList;
+import java.util.Map;
import android.util.Log;
-/** @hide */
+/**
+ * Java API for libvintf.
+ * @hide
+ */
public class VintfObject {
- private static final String LOG_TAG = "VintfObject";
+ /// ---------- OTA
/**
* Slurps all device information (both manifests and both matrices)
@@ -45,4 +48,26 @@
*/
public static native int verify(String[] packageInfo);
+ /// ---------- CTS Device Info
+
+ /**
+ * @return a list of HAL names and versions that is supported by this
+ * device as stated in device and framework manifests. For example,
+ * ["android.hidl.manager@1.0", "android.hardware.camera.device@1.0",
+ * "android.hardware.camera.device@3.2"]. There are no duplicates.
+ */
+ public static native String[] getHalNamesAndVersions();
+
+ /**
+ * @return the BOARD_SEPOLICY_VERS build flag available in device manifest.
+ */
+ public static native String getSepolicyVersion();
+
+ /**
+ * @return a list of VNDK snapshots supported by the framework, as
+ * specified in framework manifest. For example,
+ * [("25.0.5", ["libjpeg.so", "libbase.so"]),
+ * ("25.1.3", ["libjpeg.so", "libbase.so"])]
+ */
+ public static native Map<String, String[]> getVndkSnapshots();
}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 88d17ef..5fd9458 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -15,6 +15,7 @@
*/
package android.service.autofill;
+import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.RemoteException;
@@ -156,6 +157,7 @@
*
* <strong>NOTE: </strong>if overridden, it must call {@code super.onCreate()}.
*/
+ @CallSuper
@Override
public void onCreate() {
super.onCreate();
diff --git a/core/java/android/service/vr/IVrManager.aidl b/core/java/android/service/vr/IVrManager.aidl
index fc8afe9..9b37a65 100644
--- a/core/java/android/service/vr/IVrManager.aidl
+++ b/core/java/android/service/vr/IVrManager.aidl
@@ -86,17 +86,5 @@
* currently, else return the display id of the virtual display
*/
int getVr2dDisplayId();
-
- /**
- * Initiate connection for system controller data.
- *
- * @param fd Controller data file descriptor.
- */
- void connectController(in FileDescriptor fd);
-
- /**
- * Sever connection for system controller data.
- */
- void disconnectController();
}
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 91f43d6..585f882 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -1520,6 +1520,18 @@
/**
* Returns a CharSequence concatenating the specified CharSequences,
* retaining their spans if any.
+ *
+ * If there are no parameters, an empty string will be returned.
+ *
+ * If the number of parameters is exactly one, that parameter is returned as output, even if it
+ * is null.
+ *
+ * If the number of parameters is at least two, any null CharSequence among the parameters is
+ * treated as if it was the string <code>"null"</code>.
+ *
+ * If there are paragraph spans in the source CharSequences that satisfy paragraph boundary
+ * requirements in the sources but would no longer satisfy them in the concatenated
+ * CharSequence, they may get extended in the resulting CharSequence or not retained.
*/
public static CharSequence concat(CharSequence... text) {
if (text.length == 0) {
@@ -1531,35 +1543,29 @@
}
boolean spanned = false;
- for (int i = 0; i < text.length; i++) {
- if (text[i] instanceof Spanned) {
+ for (CharSequence piece : text) {
+ if (piece instanceof Spanned) {
spanned = true;
break;
}
}
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < text.length; i++) {
- sb.append(text[i]);
- }
-
- if (!spanned) {
+ if (spanned) {
+ final SpannableStringBuilder ssb = new SpannableStringBuilder();
+ for (CharSequence piece : text) {
+ // If a piece is null, we append the string "null" for compatibility with the
+ // behavior of StringBuilder and the behavior of the concat() method in earlier
+ // versions of Android.
+ ssb.append(piece == null ? "null" : piece);
+ }
+ return new SpannedString(ssb);
+ } else {
+ final StringBuilder sb = new StringBuilder();
+ for (CharSequence piece : text) {
+ sb.append(piece);
+ }
return sb.toString();
}
-
- SpannableString ss = new SpannableString(sb);
- int off = 0;
- for (int i = 0; i < text.length; i++) {
- int len = text[i].length();
-
- if (text[i] instanceof Spanned) {
- copySpansFrom((Spanned) text[i], 0, len, Object.class, ss, off);
- }
-
- off += len;
- }
-
- return new SpannedString(ss);
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 39f1170..c250ca0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7237,44 +7237,53 @@
RectF position = mAttachInfo.mTmpTransformRect;
position.set(0, 0, mRight - mLeft, mBottom - mTop);
+ mapRectFromViewToScreenCoords(position, clipToParent);
+ outRect.set(Math.round(position.left), Math.round(position.top),
+ Math.round(position.right), Math.round(position.bottom));
+ }
+ /**
+ * Map a rectangle from view-relative coordinates to screen-relative coordinates
+ *
+ * @param rect The rectangle to be mapped
+ * @param clipToParent Whether to clip child bounds to the parent ones.
+ * @hide
+ */
+ public void mapRectFromViewToScreenCoords(RectF rect, boolean clipToParent) {
if (!hasIdentityMatrix()) {
- getMatrix().mapRect(position);
+ getMatrix().mapRect(rect);
}
- position.offset(mLeft, mTop);
+ rect.offset(mLeft, mTop);
ViewParent parent = mParent;
while (parent instanceof View) {
View parentView = (View) parent;
- position.offset(-parentView.mScrollX, -parentView.mScrollY);
+ rect.offset(-parentView.mScrollX, -parentView.mScrollY);
if (clipToParent) {
- position.left = Math.max(position.left, 0);
- position.top = Math.max(position.top, 0);
- position.right = Math.min(position.right, parentView.getWidth());
- position.bottom = Math.min(position.bottom, parentView.getHeight());
+ rect.left = Math.max(rect.left, 0);
+ rect.top = Math.max(rect.top, 0);
+ rect.right = Math.min(rect.right, parentView.getWidth());
+ rect.bottom = Math.min(rect.bottom, parentView.getHeight());
}
if (!parentView.hasIdentityMatrix()) {
- parentView.getMatrix().mapRect(position);
+ parentView.getMatrix().mapRect(rect);
}
- position.offset(parentView.mLeft, parentView.mTop);
+ rect.offset(parentView.mLeft, parentView.mTop);
parent = parentView.mParent;
}
if (parent instanceof ViewRootImpl) {
ViewRootImpl viewRootImpl = (ViewRootImpl) parent;
- position.offset(0, -viewRootImpl.mCurScrollY);
+ rect.offset(0, -viewRootImpl.mCurScrollY);
}
- position.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
-
- outRect.set(Math.round(position.left), Math.round(position.top),
- Math.round(position.right), Math.round(position.bottom));
+ rect.offset(mAttachInfo.mWindowLeft, mAttachInfo.mWindowTop);
}
/**
@@ -7351,10 +7360,12 @@
}
structure.setDimens(mLeft, mTop, mScrollX, mScrollY, mRight - mLeft, mBottom - mTop);
- if (!hasIdentityMatrix()) {
- structure.setTransformation(getMatrix());
+ if (!forAutofill) {
+ if (!hasIdentityMatrix()) {
+ structure.setTransformation(getMatrix());
+ }
+ structure.setElevation(getZ());
}
- structure.setElevation(getZ());
structure.setVisibility(getVisibility());
structure.setEnabled(isEnabled());
if (isClickable()) {
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index a081f0b..7362c70 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -102,7 +102,9 @@
string, selectionStartIndex, selectionEndIndex);
final int start = startEnd[0];
final int end = startEnd[1];
- if (start >= 0 && end <= string.length() && start <= end) {
+ if (start <= end
+ && start >= 0 && end <= string.length()
+ && start <= selectionStartIndex && end >= selectionEndIndex) {
final TextSelection.Builder tsBuilder = new TextSelection.Builder(start, end);
final SmartSelection.ClassificationResult[] results =
smartSelection.classifyText(
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index eaf1115..bf44f62 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10136,7 +10136,11 @@
if (lineCount <= 1) {
// Simple case: this is a single line.
final CharSequence text = getText();
- structure.setText(text, getSelectionStart(), getSelectionEnd());
+ if (forAutofill) {
+ structure.setText(text);
+ } else {
+ structure.setText(text, getSelectionStart(), getSelectionEnd());
+ }
} else {
// Complex case: multi-line, could be scrolled or within a scroll container
// so some lines are not visible.
@@ -10172,9 +10176,11 @@
if (expandedBottomLine >= lineCount) {
expandedBottomLine = lineCount - 1;
}
+
// Convert lines into character offsets.
int expandedTopChar = layout.getLineStart(expandedTopLine);
int expandedBottomChar = layout.getLineEnd(expandedBottomLine);
+
// Take into account selection -- if there is a selection, we need to expand
// the text we are returning to include that selection.
final int selStart = getSelectionStart();
@@ -10187,48 +10193,57 @@
expandedBottomChar = selEnd;
}
}
+
// Get the text and trim it to the range we are reporting.
CharSequence text = getText();
if (expandedTopChar > 0 || expandedBottomChar < text.length()) {
text = text.subSequence(expandedTopChar, expandedBottomChar);
}
- structure.setText(text, selStart - expandedTopChar, selEnd - expandedTopChar);
- final int[] lineOffsets = new int[bottomLine - topLine + 1];
- final int[] lineBaselines = new int[bottomLine - topLine + 1];
- final int baselineOffset = getBaselineOffset();
- for (int i = topLine; i <= bottomLine; i++) {
- lineOffsets[i - topLine] = layout.getLineStart(i);
- lineBaselines[i - topLine] = layout.getLineBaseline(i) + baselineOffset;
+
+ if (forAutofill) {
+ structure.setText(text);
+ } else {
+ structure.setText(text, selStart - expandedTopChar, selEnd - expandedTopChar);
+
+ final int[] lineOffsets = new int[bottomLine - topLine + 1];
+ final int[] lineBaselines = new int[bottomLine - topLine + 1];
+ final int baselineOffset = getBaselineOffset();
+ for (int i = topLine; i <= bottomLine; i++) {
+ lineOffsets[i - topLine] = layout.getLineStart(i);
+ lineBaselines[i - topLine] = layout.getLineBaseline(i) + baselineOffset;
+ }
+ structure.setTextLines(lineOffsets, lineBaselines);
}
- structure.setTextLines(lineOffsets, lineBaselines);
}
- // Extract style information that applies to the TextView as a whole.
- int style = 0;
- int typefaceStyle = getTypefaceStyle();
- if ((typefaceStyle & Typeface.BOLD) != 0) {
- style |= AssistStructure.ViewNode.TEXT_STYLE_BOLD;
- }
- if ((typefaceStyle & Typeface.ITALIC) != 0) {
- style |= AssistStructure.ViewNode.TEXT_STYLE_ITALIC;
- }
+ if (!forAutofill) {
+ // Extract style information that applies to the TextView as a whole.
+ int style = 0;
+ int typefaceStyle = getTypefaceStyle();
+ if ((typefaceStyle & Typeface.BOLD) != 0) {
+ style |= AssistStructure.ViewNode.TEXT_STYLE_BOLD;
+ }
+ if ((typefaceStyle & Typeface.ITALIC) != 0) {
+ style |= AssistStructure.ViewNode.TEXT_STYLE_ITALIC;
+ }
- // Global styles can also be set via TextView.setPaintFlags().
- int paintFlags = mTextPaint.getFlags();
- if ((paintFlags & Paint.FAKE_BOLD_TEXT_FLAG) != 0) {
- style |= AssistStructure.ViewNode.TEXT_STYLE_BOLD;
- }
- if ((paintFlags & Paint.UNDERLINE_TEXT_FLAG) != 0) {
- style |= AssistStructure.ViewNode.TEXT_STYLE_UNDERLINE;
- }
- if ((paintFlags & Paint.STRIKE_THRU_TEXT_FLAG) != 0) {
- style |= AssistStructure.ViewNode.TEXT_STYLE_STRIKE_THRU;
- }
+ // Global styles can also be set via TextView.setPaintFlags().
+ int paintFlags = mTextPaint.getFlags();
+ if ((paintFlags & Paint.FAKE_BOLD_TEXT_FLAG) != 0) {
+ style |= AssistStructure.ViewNode.TEXT_STYLE_BOLD;
+ }
+ if ((paintFlags & Paint.UNDERLINE_TEXT_FLAG) != 0) {
+ style |= AssistStructure.ViewNode.TEXT_STYLE_UNDERLINE;
+ }
+ if ((paintFlags & Paint.STRIKE_THRU_TEXT_FLAG) != 0) {
+ style |= AssistStructure.ViewNode.TEXT_STYLE_STRIKE_THRU;
+ }
- // TextView does not have its own text background color. A background is either part
- // of the View (and can be any drawable) or a BackgroundColorSpan inside the text.
- structure.setTextStyle(getTextSize(), getCurrentTextColor(),
- AssistStructure.ViewNode.TEXT_COLOR_UNDEFINED /* bgColor */, style);
+ // TextView does not have its own text background color. A background is either part
+ // of the View (and can be any drawable) or a BackgroundColorSpan inside the text.
+ structure.setTextStyle(getTextSize(), getCurrentTextColor(),
+ AssistStructure.ViewNode.TEXT_COLOR_UNDEFINED /* bgColor */, style);
+ }
}
structure.setHint(getHint());
structure.setInputType(getInputType());
@@ -10385,14 +10400,13 @@
positionInfoStartIndex + positionInfoLength,
viewportToContentHorizontalOffset(), viewportToContentVerticalOffset());
CursorAnchorInfo cursorAnchorInfo = builder.setMatrix(null).build();
- int[] locationOnScreen = getLocationOnScreen();
for (int i = 0; i < positionInfoLength; i++) {
int flags = cursorAnchorInfo.getCharacterBoundsFlags(positionInfoStartIndex + i);
if ((flags & FLAG_HAS_VISIBLE_REGION) == FLAG_HAS_VISIBLE_REGION) {
RectF bounds = cursorAnchorInfo
.getCharacterBounds(positionInfoStartIndex + i);
if (bounds != null) {
- bounds.offset(locationOnScreen[0], locationOnScreen[1]);
+ mapRectFromViewToScreenCoords(bounds, true);
boundingRects[i] = bounds;
}
}
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index 73886a7..91bc681 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -583,4 +583,8 @@
}
return size - leftIdx;
}
+
+ public static @NonNull String[] defeatNullable(@Nullable String[] val) {
+ return (val != null) ? val : EmptyArray.STRING;
+ }
}
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 033f2df..fa9379e 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -23,23 +23,34 @@
#include <JNIHelp.h>
#include <vintf/VintfObject.h>
+#include <vintf/parse_string.h>
#include <vintf/parse_xml.h>
#include "core_jni_helpers.h"
static jclass gString;
+static jclass gHashMapClazz;
+static jmethodID gHashMapInit;
+static jmethodID gHashMapPut;
namespace android {
+using vintf::HalManifest;
+using vintf::SchemaType;
using vintf::VintfObject;
+using vintf::XmlConverter;
+using vintf::Vndk;
using vintf::gHalManifestConverter;
using vintf::gCompatibilityMatrixConverter;
-using vintf::XmlConverter;
+using vintf::to_string;
-static inline jobjectArray toJavaStringArray(JNIEnv* env, const std::vector<std::string>& v) {
+template<typename V>
+static inline jobjectArray toJavaStringArray(JNIEnv* env, const V& v) {
+ size_t i;
+ typename V::const_iterator it;
jobjectArray ret = env->NewObjectArray(v.size(), gString, NULL /* init element */);
- for (size_t i = 0; i < v.size(); ++i) {
- env->SetObjectArrayElement(ret, i, env->NewStringUTF(v[i].c_str()));
+ for (i = 0, it = v.begin(); it != v.end(); ++i, ++it) {
+ env->SetObjectArrayElement(ret, i, env->NewStringUTF(it->c_str()));
}
return ret;
}
@@ -55,7 +66,18 @@
}
}
-static jobjectArray android_os_VintfObject_report(JNIEnv* env, jclass clazz)
+static void tryAddHalNamesAndVersions(const HalManifest *manifest,
+ const std::string& description,
+ std::set<std::string> *output) {
+ if (manifest == nullptr) {
+ LOG(WARNING) << __FUNCTION__ << "Cannot get " << description;
+ } else {
+ auto names = manifest->getHalNamesAndVersions();
+ output->insert(names.begin(), names.end());
+ }
+}
+
+static jobjectArray android_os_VintfObject_report(JNIEnv* env, jclass)
{
std::vector<std::string> cStrings;
@@ -71,7 +93,7 @@
return toJavaStringArray(env, cStrings);
}
-static jint android_os_VintfObject_verify(JNIEnv *env, jclass clazz, jobjectArray packageInfo) {
+static jint android_os_VintfObject_verify(JNIEnv* env, jclass, jobjectArray packageInfo) {
size_t count = env->GetArrayLength(packageInfo);
std::vector<std::string> cPackageInfo{count};
for (size_t i = 0; i < count; ++i) {
@@ -84,20 +106,60 @@
return status;
}
+static jobjectArray android_os_VintfObject_getHalNamesAndVersions(JNIEnv* env, jclass) {
+ std::set<std::string> halNames;
+ tryAddHalNamesAndVersions(VintfObject::GetDeviceHalManifest(),
+ "device manifest", &halNames);
+ tryAddHalNamesAndVersions(VintfObject::GetFrameworkHalManifest(),
+ "framework manifest", &halNames);
+ return toJavaStringArray(env, halNames);
+}
+
+static jstring android_os_VintfObject_getSepolicyVersion(JNIEnv* env, jclass) {
+ const HalManifest *manifest = VintfObject::GetDeviceHalManifest();
+ if (manifest == nullptr || manifest->type() != SchemaType::DEVICE) {
+ LOG(WARNING) << __FUNCTION__ << "Cannot get device manifest";
+ return nullptr;
+ }
+ std::string cString = to_string(manifest->sepolicyVersion());
+ return env->NewStringUTF(cString.c_str());
+}
+
+static jobject android_os_VintfObject_getVndkSnapshots(JNIEnv* env, jclass) {
+ const HalManifest *manifest = VintfObject::GetFrameworkHalManifest();
+ if (manifest == nullptr || manifest->type() != SchemaType::FRAMEWORK) {
+ LOG(WARNING) << __FUNCTION__ << "Cannot get framework manifest";
+ return nullptr;
+ }
+ jobject jMap = env->NewObject(gHashMapClazz, gHashMapInit);
+ for (const Vndk &vndk : manifest->vndks()) {
+ std::string key = to_string(vndk.versionRange());
+ env->CallObjectMethod(jMap, gHashMapPut,
+ env->NewStringUTF(key.c_str()), toJavaStringArray(env, vndk.libraries()));
+ }
+ return jMap;
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod gVintfObjectMethods[] = {
{"report", "()[Ljava/lang/String;", (void*)android_os_VintfObject_report},
{"verify", "([Ljava/lang/String;)I", (void*)android_os_VintfObject_verify},
+ {"getHalNamesAndVersions", "()[Ljava/lang/String;", (void*)android_os_VintfObject_getHalNamesAndVersions},
+ {"getSepolicyVersion", "()Ljava/lang/String;", (void*)android_os_VintfObject_getSepolicyVersion},
+ {"getVndkSnapshots", "()Ljava/util/Map;", (void*)android_os_VintfObject_getVndkSnapshots},
};
-
const char* const kVintfObjectPathName = "android/os/VintfObject";
int register_android_os_VintfObject(JNIEnv* env)
{
gString = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/lang/String"));
+ gHashMapClazz = MakeGlobalRefOrDie(env, FindClassOrDie(env, "java/util/HashMap"));
+ gHashMapInit = GetMethodIDOrDie(env, gHashMapClazz, "<init>", "()V");
+ gHashMapPut = GetMethodIDOrDie(env, gHashMapClazz,
+ "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
return RegisterMethodsOrDie(env, kVintfObjectPathName, gVintfObjectMethods,
NELEM(gVintfObjectMethods));
diff --git a/core/res/res/drawable/sym_def_app_icon.xml b/core/res/res/drawable/sym_def_app_icon.xml
index 0fdb0dd..9c02402 100644
--- a/core/res/res/drawable/sym_def_app_icon.xml
+++ b/core/res/res/drawable/sym_def_app_icon.xml
@@ -2,10 +2,7 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@android:color/white" />
<foreground>
- <inset android:insetLeft="27.7%"
- android:insetTop="27.7%"
- android:insetRight="27.7%"
- android:insetBottom="27.7%">
+ <inset android:inset="27.7%">
<bitmap android:src="@mipmap/sym_def_app_icon"/>
</inset>
</foreground>
diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
index 5e426e8..9c904d1 100644
--- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
+++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
@@ -68,19 +68,19 @@
assertEquals(4, fileEntries.length);
FontFileResourceEntry font1 = fileEntries[0];
assertEquals(400, font1.getWeight());
- assertEquals(false, font1.isItalic());
+ assertEquals(0, font1.getItalic());
assertEquals("res/font/samplefont.ttf", font1.getFileName());
FontFileResourceEntry font2 = fileEntries[1];
assertEquals(400, font2.getWeight());
- assertEquals(true, font2.isItalic());
+ assertEquals(1, font2.getItalic());
assertEquals("res/font/samplefont2.ttf", font2.getFileName());
FontFileResourceEntry font3 = fileEntries[2];
assertEquals(800, font3.getWeight());
- assertEquals(false, font3.isItalic());
+ assertEquals(0, font3.getItalic());
assertEquals("res/font/samplefont3.ttf", font3.getFileName());
FontFileResourceEntry font4 = fileEntries[3];
assertEquals(800, font4.getWeight());
- assertEquals(true, font4.isItalic());
+ assertEquals(1, font4.getItalic());
assertEquals("res/font/samplefont4.ttf", font4.getFileName());
}
diff --git a/core/tests/coretests/src/android/text/method/BackspaceTest.java b/core/tests/coretests/src/android/text/method/BackspaceTest.java
index 6e41831..8b5cc60 100644
--- a/core/tests/coretests/src/android/text/method/BackspaceTest.java
+++ b/core/tests/coretests/src/android/text/method/BackspaceTest.java
@@ -16,10 +16,13 @@
package android.text.method;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.InputType;
+import android.util.KeyUtils;
import android.view.KeyEvent;
+import android.widget.EditText;
import android.widget.TextView.BufferType;
import org.junit.Before;
import org.junit.Test;
@@ -33,13 +36,21 @@
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class BackspaceTest extends KeyListenerTestCase {
+public class BackspaceTest {
+ private EditText mTextView;
+
private static final BaseKeyListener mKeyListener = new BaseKeyListener() {
public int getInputType() {
return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
}
};
+ @Before
+ public void setup() {
+ mTextView = new EditText(InstrumentationRegistry.getInstrumentation().getContext());
+ }
+
+
// Sync the state to the TextView and call onKeyDown with KEYCODE_DEL key event.
// Then update the state to the result of TextView.
private void backspace(final EditorState state, int modifiers) {
@@ -47,7 +58,8 @@
mTextView.setKeyListener(mKeyListener);
mTextView.setSelection(state.mSelectionStart, state.mSelectionEnd);
- final KeyEvent keyEvent = getKey(KeyEvent.KEYCODE_DEL, modifiers);
+ final KeyEvent keyEvent = KeyUtils.generateKeyEvent(
+ KeyEvent.KEYCODE_DEL, KeyEvent.ACTION_DOWN, modifiers);
mTextView.onKeyDown(keyEvent.getKeyCode(), keyEvent);
state.mText = mTextView.getText();
diff --git a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
index 6914e21..c3a5f80 100644
--- a/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
+++ b/core/tests/coretests/src/android/text/method/ForwardDeleteTest.java
@@ -16,10 +16,13 @@
package android.text.method;
+import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.InputType;
+import android.util.KeyUtils;
import android.view.KeyEvent;
+import android.widget.EditText;
import android.widget.TextView.BufferType;
import org.junit.Before;
@@ -34,13 +37,20 @@
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class ForwardDeleteTest extends KeyListenerTestCase {
+public class ForwardDeleteTest {
+ private EditText mTextView;
+
private static final BaseKeyListener mKeyListener = new BaseKeyListener() {
public int getInputType() {
return InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_NORMAL;
}
};
+ @Before
+ public void setup() {
+ mTextView = new EditText(InstrumentationRegistry.getInstrumentation().getContext());
+ }
+
// Sync the state to the TextView and call onKeyDown with KEYCODE_FORWARD_DEL key event.
// Then update the state to the result of TextView.
private void forwardDelete(final EditorState state, int modifiers) {
@@ -48,7 +58,8 @@
mTextView.setKeyListener(mKeyListener);
mTextView.setSelection(state.mSelectionStart, state.mSelectionEnd);
- final KeyEvent keyEvent = getKey(KeyEvent.KEYCODE_FORWARD_DEL, modifiers);
+ final KeyEvent keyEvent = KeyUtils.generateKeyEvent(
+ KeyEvent.KEYCODE_FORWARD_DEL, KeyEvent.ACTION_DOWN, modifiers);
mTextView.onKeyDown(keyEvent.getKeyCode(), keyEvent);
state.mText = mTextView.getText();
diff --git a/core/tests/coretests/src/android/text/method/KeyListenerTestCase.java b/core/tests/coretests/src/android/text/method/KeyListenerTestCase.java
deleted file mode 100644
index 99a0091..0000000
--- a/core/tests/coretests/src/android/text/method/KeyListenerTestCase.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2016 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.text.method;
-
-import android.app.Instrumentation;
-import android.support.test.InstrumentationRegistry;
-import android.view.KeyEvent;
-import android.widget.EditText;
-
-public abstract class KeyListenerTestCase {
- protected Instrumentation mInstrumentation;
- protected EditText mTextView;
-
- public KeyListenerTestCase() {
- }
-
- protected void setup() {
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mTextView = new EditText(mInstrumentation.getContext());
- }
-
- protected static KeyEvent getKey(int keycode, int metaState) {
- long currentTime = System.currentTimeMillis();
- return new KeyEvent(currentTime, currentTime, KeyEvent.ACTION_DOWN, keycode,
- 0 /* repeat */, metaState);
- }
-}
diff --git a/core/tests/coretests/src/android/util/KeyUtils.java b/core/tests/coretests/src/android/util/KeyUtils.java
index b58fda3..593f727 100644
--- a/core/tests/coretests/src/android/util/KeyUtils.java
+++ b/core/tests/coretests/src/android/util/KeyUtils.java
@@ -85,4 +85,19 @@
}
inst.sendKeySync(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER));
}
+
+ /**
+ * Generates a {@link KeyEvent}.
+ *
+ * @param keycode The integer keycode for the event to be generated.
+ * @param keyEventAction The integer {@link KeyEvent} action code.
+ * @param metaState Flags indicating which meta keys are currently pressed.
+ *
+ * @return a new {@link KeyEvent} for current time.
+ */
+ public static KeyEvent generateKeyEvent(int keycode, int keyEventAction, int metaState) {
+ long currentTime = System.currentTimeMillis();
+ return new KeyEvent(currentTime, currentTime, keyEventAction, keycode,
+ 0 /* repeat */, metaState);
+ }
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 742fd60..7b7031b 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -40,6 +40,7 @@
public class TextClassificationManagerTest {
private static final LocaleList LOCALES = LocaleList.forLanguageTags("en");
+ private static final String NO_TYPE = null;
private TextClassificationManager mTcm;
private TextClassifier mClassifier;
@@ -102,6 +103,19 @@
}
@Test
+ public void testSmartSelection_withEmoji() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "\uD83D\uDE02 Hello.";
+ String selected = "Hello";
+ int startIndex = text.indexOf(selected);
+ int endIndex = startIndex + selected.length();
+
+ assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES),
+ isTextSelection(startIndex, endIndex, NO_TYPE));
+ }
+
+ @Test
public void testClassifyText() {
if (isTextClassifierDisabled()) return;
@@ -172,12 +186,17 @@
TextSelection selection = (TextSelection) o;
return startIndex == selection.getSelectionStartIndex()
&& endIndex == selection.getSelectionEndIndex()
- && selection.getEntityCount() > 0
- && type.equals(selection.getEntity(0));
+ && typeMatches(selection, type);
}
return false;
}
+ private boolean typeMatches(TextSelection selection, String type) {
+ return type == null
+ || (selection.getEntityCount() > 0
+ && type.trim().equalsIgnoreCase(selection.getEntity(0)));
+ }
+
@Override
public void describeTo(Description description) {
description.appendValue(
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index abdab39..8a36120 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -23,6 +23,7 @@
import android.annotation.Size;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.StrictMode;
import android.os.Trace;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -600,6 +601,13 @@
src.position(position);
}
+ private void noteHardwareBitmapSlowCall() {
+ if (getConfig() == Config.HARDWARE) {
+ StrictMode.noteSlowCall("Warning: attempt to read pixels from hardware "
+ + "bitmap, which is very slow operation");
+ }
+ }
+
/**
* Tries to make a new bitmap based on the dimensions of this bitmap,
* setting the new bitmap's config to the one specified, and then copying
@@ -618,6 +626,7 @@
if (config == Config.HARDWARE && isMutable) {
throw new IllegalArgumentException("Hardware bitmaps are always immutable");
}
+ noteHardwareBitmapSlowCall();
Bitmap b = nativeCopy(mNativePtr, config.nativeInt, isMutable);
if (b != null) {
b.setPremultiplied(mRequestPremultiplied);
@@ -635,6 +644,7 @@
*/
public Bitmap createAshmemBitmap() {
checkRecycled("Can't copy a recycled bitmap");
+ noteHardwareBitmapSlowCall();
Bitmap b = nativeCopyAshmem(mNativePtr);
if (b != null) {
b.setPremultiplied(mRequestPremultiplied);
@@ -652,6 +662,7 @@
*/
public Bitmap createAshmemBitmap(Config config) {
checkRecycled("Can't copy a recycled bitmap");
+ noteHardwareBitmapSlowCall();
Bitmap b = nativeCopyAshmemConfig(mNativePtr, config.nativeInt);
if (b != null) {
b.setPremultiplied(mRequestPremultiplied);
@@ -772,6 +783,7 @@
boolean isHardware = source.getConfig() == Config.HARDWARE;
if (isHardware) {
+ source.noteHardwareBitmapSlowCall();
source = nativeCopyPreserveInternalConfig(source.mNativePtr);
}
@@ -1218,6 +1230,7 @@
if (quality < 0 || quality > 100) {
throw new IllegalArgumentException("quality must be 0..100");
}
+ StrictMode.noteSlowCall("Compression of a bitmap is slow");
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "Bitmap.compress");
boolean result = nativeCompress(mNativePtr, format.nativeInt,
quality, stream, new byte[WORKING_COMPRESS_STORAGE]);
@@ -1792,6 +1805,7 @@
*/
public void writeToParcel(Parcel p, int flags) {
checkRecycled("Can't parcel a recycled bitmap");
+ noteHardwareBitmapSlowCall();
if (!nativeWriteToParcel(mNativePtr, mIsMutable, mDensity, p)) {
throw new RuntimeException("native writeToParcel failed");
}
@@ -1838,6 +1852,7 @@
public Bitmap extractAlpha(Paint paint, int[] offsetXY) {
checkRecycled("Can't extractAlpha on a recycled bitmap");
long nativePaint = paint != null ? paint.getNativeInstance() : 0;
+ noteHardwareBitmapSlowCall();
Bitmap bm = nativeExtractAlpha(mNativePtr, nativePaint, offsetXY);
if (bm == null) {
throw new RuntimeException("Failed to extractAlpha on Bitmap");
@@ -1853,6 +1868,8 @@
*/
public boolean sameAs(Bitmap other) {
checkRecycled("Can't call sameAs on a recycled bitmap!");
+ noteHardwareBitmapSlowCall();
+ other.noteHardwareBitmapSlowCall();
if (this == other) return true;
if (other == null) return false;
if (other.isRecycled()) {
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index f38d8d2..79898bc 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -233,8 +233,7 @@
// TODO: Add ttc and variation font support. (b/37853920)
if (!fontFamily.addFontFromAssetManager(mgr, fontFile.getFileName(),
0 /* resourceCookie */, false /* isAsset */, 0 /* ttcIndex */,
- fontFile.getWeight(), fontFile.isItalic() ? STYLE_ITALIC : STYLE_NORMAL,
- null /* axes */)) {
+ fontFile.getWeight(), fontFile.getItalic(), null /* axes */)) {
return null;
}
}
diff --git a/graphics/java/android/graphics/drawable/InsetDrawable.java b/graphics/java/android/graphics/drawable/InsetDrawable.java
index bc40191..443aa49 100644
--- a/graphics/java/android/graphics/drawable/InsetDrawable.java
+++ b/graphics/java/android/graphics/drawable/InsetDrawable.java
@@ -188,19 +188,19 @@
// Inset attribute may be overridden by more specific attributes.
if (a.hasValue(R.styleable.InsetDrawable_inset)) {
- final InsetValue inset = getInset(a, R.styleable.InsetDrawable_inset, 0);
+ final InsetValue inset = getInset(a, R.styleable.InsetDrawable_inset, new InsetValue());
state.mInsetLeft = inset;
state.mInsetTop = inset;
state.mInsetRight = inset;
state.mInsetBottom = inset;
}
- state.mInsetLeft = getInset(a, R.styleable.InsetDrawable_insetLeft, 0);
- state.mInsetTop = getInset(a, R.styleable.InsetDrawable_insetTop, 0);
- state.mInsetRight = getInset(a, R.styleable.InsetDrawable_insetRight, 0);
- state.mInsetBottom = getInset(a, R.styleable.InsetDrawable_insetBottom, 0);
+ state.mInsetLeft = getInset(a, R.styleable.InsetDrawable_insetLeft, state.mInsetLeft);
+ state.mInsetTop = getInset(a, R.styleable.InsetDrawable_insetTop, state.mInsetTop);
+ state.mInsetRight = getInset(a, R.styleable.InsetDrawable_insetRight, state.mInsetRight);
+ state.mInsetBottom = getInset(a, R.styleable.InsetDrawable_insetBottom, state.mInsetBottom);
}
- private InsetValue getInset(@NonNull TypedArray a, int index, int defaultValue) {
+ private InsetValue getInset(@NonNull TypedArray a, int index, InsetValue defaultValue) {
if (a.hasValue(index)) {
TypedValue tv = a.peekValue(index);
if (tv.type == TypedValue.TYPE_FRACTION) {
@@ -210,10 +210,13 @@
}
return new InsetValue(f, 0);
} else {
- return new InsetValue(0f, a.getDimensionPixelOffset(index, defaultValue));
+ int dimension = a.getDimensionPixelOffset(index, 0);
+ if (dimension != 0) {
+ return new InsetValue(0, dimension);
+ }
}
}
- return new InsetValue();
+ return defaultValue;
}
private void getInsets(Rect out) {
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index d7f75fc..d765584 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -486,7 +486,6 @@
void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
outBitmap->setHasHardwareMipMap(mHasHardwareMipMap);
if (isHardware()) {
- ALOGW("Warning: attempt to read pixels from hardware bitmap, which is very slow operation");
outBitmap->allocPixels(info());
uirenderer::renderthread::RenderProxy::copyGraphicBufferInto(graphicBuffer(), outBitmap);
return;
diff --git a/packages/SettingsLib/src/com/android/settingslib/SecureTouchListener.java b/packages/SettingsLib/src/com/android/settingslib/SecureTouchListener.java
new file mode 100644
index 0000000..766c42b
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/SecureTouchListener.java
@@ -0,0 +1,44 @@
+package com.android.settingslib;
+
+import android.os.SystemClock;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.Toast;
+
+/**
+ * A touch listener which consumes touches when another window is partly or wholly obscuring the
+ * window containing the view this listener is attached to.
+ * Optionally accepts a string to show the user as a toast when consuming an insecure touch
+ */
+public class SecureTouchListener implements View.OnTouchListener {
+
+ private static final long TAP_DEBOUNCE_TIME = 2000;
+ private long mLastToastTime = 0;
+ private String mWarningText;
+
+ public SecureTouchListener() {
+ this(null);
+ }
+
+ public SecureTouchListener(String warningText) {
+ mWarningText = warningText;
+ }
+
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0
+ || (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) != 0) {
+ if (mWarningText != null) {
+ // Show a toast warning the user
+ final long currentTime = SystemClock.uptimeMillis();
+ if (currentTime - mLastToastTime > TAP_DEBOUNCE_TIME) {
+ mLastToastTime = currentTime;
+ Toast.makeText(v.getContext(), mWarningText, Toast.LENGTH_SHORT).show();
+ }
+ }
+ // Consume the touch event
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
index ea28fe6..5a57e69 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -152,6 +152,12 @@
final MeasurementDetails details = new MeasurementDetails();
if (mVolume == null) return details;
+ if (mVolume.getType() == VolumeInfo.TYPE_PUBLIC) {
+ details.totalSize = mVolume.getPath().getTotalSpace();
+ details.availSize = mVolume.getPath().getUsableSpace();
+ return details;
+ }
+
try {
details.totalSize = mStats.getTotalBytes(mVolume.fsUuid);
details.availSize = mStats.getFreeBytes(mVolume.fsUuid);
@@ -161,7 +167,6 @@
return details;
}
-
final long finishTotal = SystemClock.elapsedRealtime();
Log.d(TAG, "Measured total storage in " + (finishTotal - start) + "ms");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 4b304b2..14e2a85 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1142,28 +1142,55 @@
} finally {
Binder.restoreCallingIdentity(token);
}
+
+ final SettingsState ssaidSettings = mSettingsRegistry.getSettingsLocked(
+ SETTINGS_TYPE_SSAID, owningUserId);
+
if (instantSsaid != null) {
// Use the stored value if it is still valid.
if (ssaid != null && instantSsaid.equals(ssaid.getValue())) {
- return ssaid;
+ return mascaradeSsaidSetting(ssaidSettings, ssaid);
}
// The value has changed, update the stored value.
- final SettingsState ssaidSettings = mSettingsRegistry.getSettingsLocked(
- SETTINGS_TYPE_SSAID, owningUserId);
final boolean success = ssaidSettings.insertSettingLocked(name, instantSsaid, null,
true, callingPkg.packageName);
if (!success) {
throw new IllegalStateException("Failed to update instant app android id");
}
- return mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SSAID, owningUserId, name);
+ Setting setting = mSettingsRegistry.getSettingLocked(SETTINGS_TYPE_SSAID,
+ owningUserId, name);
+ return mascaradeSsaidSetting(ssaidSettings, setting);
}
// Lazy initialize ssaid if not yet present in ssaid table.
if (ssaid == null || ssaid.isNull() || ssaid.getValue() == null) {
- return mSettingsRegistry.generateSsaidLocked(callingPkg, owningUserId);
+ Setting setting = mSettingsRegistry.generateSsaidLocked(callingPkg, owningUserId);
+ return mascaradeSsaidSetting(ssaidSettings, setting);
}
- return ssaid;
+ return mascaradeSsaidSetting(ssaidSettings, ssaid);
+ }
+
+ private Setting mascaradeSsaidSetting(SettingsState settingsState, Setting ssaidSetting) {
+ // SSAID settings are located in a dedicated table for internal bookkeeping
+ // but for the world they reside in the secure table, so adjust the key here.
+ // We have a special name when looking it up but want the world to see it as
+ // "android_id".
+ if (ssaidSetting != null) {
+ return settingsState.new Setting(ssaidSetting) {
+ @Override
+ public int getKey() {
+ final int userId = getUserIdFromKey(super.getKey());
+ return makeKey(SETTINGS_TYPE_SECURE, userId);
+ }
+
+ @Override
+ public String getName() {
+ return Settings.Secure.ANDROID_ID;
+ }
+ };
+ }
+ return null;
}
private boolean insertSecureSetting(String name, String value, String tag,
@@ -1173,7 +1200,6 @@
+ ", " + tag + ", " + makeDefault + ", " + requestingUserId
+ ", " + forceNotify + ")");
}
-
return mutateSecureSetting(name, value, tag, makeDefault, requestingUserId,
MUTATION_OPERATION_INSERT, forceNotify, 0);
}
@@ -1879,6 +1905,7 @@
Bundle result = new Bundle();
result.putString(Settings.NameValueTable.VALUE,
!setting.isNull() ? setting.getValue() : null);
+
mSettingsRegistry.mGenerationRegistry.addGenerationData(result, setting.getKey());
return result;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 5690495..41fb5f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -579,6 +579,7 @@
.setComponent(aiaComponent)
.setAction(Intent.ACTION_VIEW)
.addCategory(Intent.CATEGORY_BROWSABLE)
+ .addCategory("unique:" + System.currentTimeMillis())
.putExtra(Intent.EXTRA_PACKAGE_NAME, appInfo.packageName)
.putExtra(Intent.EXTRA_VERSION_CODE, appInfo.versionCode)
.putExtra(Intent.EXTRA_EPHEMERAL_FAILURE, pendingIntent);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
index a2d1baf..c726189 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EmergencyCryptkeeperText.java
@@ -47,6 +47,11 @@
public void onPhoneStateChanged(int phoneState) {
update();
}
+
+ @Override
+ public void onRefreshCarrierInfo() {
+ update();
+ }
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index ac81565..b2712ff 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -373,11 +373,6 @@
}
private void onPackageBroadcastReceived(Intent intent, int userId) {
- if (!mUserManager.isUserUnlockingOrUnlocked(userId) ||
- isProfileWithLockedParent(userId)) {
- return;
- }
-
final String action = intent.getAction();
boolean added = false;
boolean changed = false;
@@ -408,7 +403,11 @@
}
synchronized (mLock) {
- ensureGroupStateLoadedLocked(userId);
+ if (!mUserManager.isUserUnlockingOrUnlocked(userId) ||
+ isProfileWithLockedParent(userId)) {
+ return;
+ }
+ ensureGroupStateLoadedLocked(userId, /* enforceUserUnlockingOrUnlocked */ false);
Bundle extras = intent.getExtras();
@@ -844,7 +843,7 @@
mSecurityPolicy.enforceCallFromPackage(callingPackage);
synchronized (mLock) {
- ensureGroupStateLoadedLocked(userId);
+ ensureGroupStateLoadedLocked(userId, /* enforceUserUnlockingOrUnlocked */ false);
// NOTE: The lookup is enforcing security across users by making
// sure the caller can only access hosts it owns.
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index b536ad9..9081746 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -213,7 +213,7 @@
if (!doit) {
return true;
}
- handleActiveAutofillServiceRemoved(getChangingUserId());
+ removeCachedServiceLocked(getChangingUserId());
}
}
}
@@ -602,6 +602,31 @@
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
+ boolean showHistory = true;
+ boolean uiOnly = false;
+ if (args != null) {
+ for (String arg : args) {
+ switch(arg) {
+ case "--no-history":
+ showHistory = false;
+ break;
+ case "--ui-only":
+ uiOnly = true;
+ break;
+ case "--help":
+ pw.println("Usage: dumpsys autofill [--ui-only|--no-history]");
+ return;
+ default:
+ throw new IllegalArgumentException("Invalid dump arg: " + arg);
+ }
+ }
+ }
+
+ if (uiOnly) {
+ mUi.dump(pw);
+ return;
+ }
+
boolean oldDebug = sDebug;
try {
synchronized (mLock) {
@@ -624,8 +649,10 @@
}
mUi.dump(pw);
}
- pw.println("Requests history:");
- mRequestsHistory.reverseDump(fd, pw, args);
+ if (showHistory) {
+ pw.println("Requests history:");
+ mRequestsHistory.reverseDump(fd, pw, args);
+ }
} finally {
setDebugLocked(oldDebug);
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 4507eae..3ab8b8d 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -19,17 +19,21 @@
import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
import static android.view.autofill.AutofillManager.NO_SESSION;
+import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.app.AppGlobals;
+import android.app.IActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.graphics.Rect;
+import android.os.AsyncTask;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -46,6 +50,7 @@
import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
import android.text.TextUtils;
+import android.util.ArrayMap;
import android.util.LocalLog;
import android.util.Slog;
import android.util.SparseArray;
@@ -72,6 +77,9 @@
private static final String TAG = "AutofillManagerServiceImpl";
private static final int MAX_SESSION_ID_CREATE_TRIES = 2048;
+ /** Minimum interval to prune abandoned sessions */
+ private static final int MAX_ABANDONED_SESSION_MILLIS = 30000;
+
static final int MSG_SERVICE_SAVE = 1;
private final int mUserId;
@@ -104,10 +112,10 @@
mHandlerCallback, true);
/**
- * Cache of pending {@link Session}s, keyed by {@code activityToken}.
+ * Cache of pending {@link Session}s, keyed by sessionId.
*
* <p>They're kept until the {@link AutofillService} finished handling a request, an error
- * occurs, or the session times out.
+ * occurs, or the session is abandoned.
*/
@GuardedBy("mLock")
private final SparseArray<Session> mSessions = new SparseArray<>();
@@ -116,6 +124,9 @@
@GuardedBy("mLock")
private FillEventHistory mEventHistory;
+ /** When was {@link PruneTask} last executed? */
+ private long mLastPrune = 0;
+
AutofillManagerServiceImpl(Context context, Object lock, LocalLog requestsHistory,
int userId, AutoFillUI ui, boolean disabled) {
mContext = context;
@@ -260,6 +271,9 @@
return 0;
}
+ // Occasionally clean up abandoned sessions
+ pruneAbandonedSessionsLocked();
+
final Session newSession = createSessionByTokenLocked(activityToken, uid, windowToken,
appCallbackToken, hasCallback, flags, packageName);
if (newSession == null) {
@@ -277,6 +291,20 @@
return newSession.id;
}
+ /**
+ * Remove abandoned sessions if needed.
+ */
+ private void pruneAbandonedSessionsLocked() {
+ long now = System.currentTimeMillis();
+ if (mLastPrune < now - MAX_ABANDONED_SESSION_MILLIS) {
+ mLastPrune = now;
+
+ if (mSessions.size() > 0) {
+ (new PruneTask()).execute();
+ }
+ }
+ }
+
void finishSessionLocked(int sessionId, int uid) {
if (!isEnabled()) {
return;
@@ -513,6 +541,7 @@
pw.print(prefix); pw.print("Default component: ");
pw.println(mContext.getString(R.string.config_defaultAutofillService));
pw.print(prefix); pw.print("Disabled: "); pw.println(mDisabled);
+ pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune);
final int size = mSessions.size();
if (size == 0) {
@@ -604,4 +633,62 @@
+ ", component=" + (mInfo != null
? mInfo.getServiceInfo().getComponentName() : null) + "]";
}
+
+ /** Task used to prune abandoned session */
+ private class PruneTask extends AsyncTask<Void, Void, Void> {
+ @Override
+ protected Void doInBackground(Void... ignored) {
+ int numSessionsToRemove;
+ ArrayMap<IBinder, Integer> sessionsToRemove;
+
+ synchronized (mLock) {
+ numSessionsToRemove = mSessions.size();
+ sessionsToRemove = new ArrayMap<>(numSessionsToRemove);
+
+ for (int i = 0; i < numSessionsToRemove; i++) {
+ Session session = mSessions.valueAt(i);
+
+ sessionsToRemove.put(session.getActivityTokenLocked(), session.id);
+ }
+ }
+
+ IActivityManager am = ActivityManager.getService();
+
+ // Only remove sessions which's activities are not known to the activity manager anymore
+ for (int i = 0; i < numSessionsToRemove; i++) {
+ try {
+ // The activity manager cannot resolve activities that have been removed
+ if (am.getActivityClassForToken(sessionsToRemove.keyAt(i)) != null) {
+ sessionsToRemove.removeAt(i);
+ i--;
+ numSessionsToRemove--;
+ }
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Cannot figure out if activity is finished", e);
+ }
+ }
+
+ synchronized (mLock) {
+ for (int i = 0; i < numSessionsToRemove; i++) {
+ Session sessionToRemove = mSessions.get(sessionsToRemove.valueAt(i));
+
+ if (sessionToRemove != null) {
+ if (sessionToRemove.isSavingLocked()) {
+ if (sVerbose) {
+ Slog.v(TAG, "Session " + sessionToRemove.id + " is saving");
+ }
+ } else {
+ if (sDebug) {
+ Slog.i(TAG, "Prune session " + sessionToRemove.id + " ("
+ + sessionToRemove.getActivityTokenLocked() + ")");
+ }
+ sessionToRemove.removeSelfLocked();
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 25bb7cc..a98821d 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -176,6 +176,10 @@
@GuardedBy("mLock")
private boolean mDestroyed;
+ /** Whether the session is currently saving */
+ @GuardedBy("mLock")
+ private boolean mIsSaving;
+
/**
* Receiver of assist data from the app's {@link Activity}.
*/
@@ -360,7 +364,7 @@
*
* @return The activity token
*/
- IBinder getActivityTokenLocked() {
+ @NonNull IBinder getActivityTokenLocked() {
return mActivityToken;
}
@@ -473,6 +477,8 @@
@Override
public void onSaveRequestSuccess(@NonNull String servicePackageName) {
synchronized (mLock) {
+ mIsSaving = false;
+
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onSaveRequestSuccess() rejected - session: "
+ id + " destroyed");
@@ -495,6 +501,8 @@
public void onSaveRequestFailure(@Nullable CharSequence message,
@NonNull String servicePackageName) {
synchronized (mLock) {
+ mIsSaving = false;
+
if (mDestroyed) {
Slog.w(TAG, "Call to Session#onSaveRequestFailure() rejected - session: "
+ id + " destroyed");
@@ -595,6 +603,8 @@
@Override
public void cancelSave() {
synchronized (mLock) {
+ mIsSaving = false;
+
if (mDestroyed) {
Slog.w(TAG, "Call to Session#cancelSave() rejected - session: "
+ id + " destroyed");
@@ -842,6 +852,8 @@
if (atLeastOneChanged) {
mService.setSaveShown();
getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo, mPackageName);
+
+ mIsSaving = true;
return false;
}
}
@@ -855,6 +867,13 @@
}
/**
+ * Returns whether the session is currently showing the save UI
+ */
+ boolean isSavingLocked() {
+ return mIsSaving;
+ }
+
+ /**
* Calls service when user requested save.
*/
void callSaveLocked() {
@@ -1050,6 +1069,9 @@
}
break;
case ACTION_VIEW_ENTERED:
+ if (sVerbose && virtualBounds != null) {
+ Slog.w(TAG, "entered on virtual child " + id + ": " + virtualBounds);
+ }
requestNewFillResponseIfNecessaryLocked(id, viewState, flags);
// Remove the UI if the ViewState has changed.
@@ -1351,6 +1373,7 @@
pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId);
pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size());
pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
+ pw.print(prefix); pw.print("mIsSaving: "); pw.println(mIsSaving);
final String prefix2 = prefix + " ";
for (Map.Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) {
pw.print(prefix); pw.print("State for id "); pw.println(entry.getKey());
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 086742e..7428460 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -264,6 +264,10 @@
public void onDestroy() {
if (log.getType() == MetricsProto.MetricsEvent.TYPE_UNKNOWN) {
log.setType(MetricsProto.MetricsEvent.TYPE_CLOSE);
+
+ if (mCallback != null) {
+ mCallback.cancelSave();
+ }
}
mMetricsLogger.write(log);
}
@@ -282,13 +286,18 @@
pw.println("Autofill UI");
final String prefix = " ";
final String prefix2 = " ";
- pw.print(prefix); pw.print("showsSaveUi: "); pw.println(mSaveUi != null);
if (mFillUi != null) {
pw.print(prefix); pw.println("showsFillUi: true");
mFillUi.dump(pw, prefix2);
} else {
pw.print(prefix); pw.println("showsFillUi: false");
}
+ if (mSaveUi != null) {
+ pw.print(prefix); pw.println("showsSaveUi: true");
+ mSaveUi.dump(pw, prefix2);
+ } else {
+ pw.print(prefix); pw.println("showsSaveUi: false");
+ }
}
@android.annotation.UiThread
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index dd297a6..922962f 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -398,6 +398,7 @@
}
return false;
}
+
}
public void dump(PrintWriter pw, String prefix) {
@@ -408,5 +409,21 @@
pw.print(prefix); pw.print("mContentWidth: "); pw.println(mContentWidth);
pw.print(prefix); pw.print("mContentHeight: "); pw.println(mContentHeight);
pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
+ pw.print(prefix); pw.print("mWindow: ");
+ if (mWindow == null) {
+ pw.println("N/A");
+ } else {
+ final String prefix2 = prefix + " ";
+ pw.println();
+ pw.print(prefix2); pw.print("showing: "); pw.println(mWindow.mShowing);
+ pw.print(prefix2); pw.print("view: "); pw.println(mWindow.mContentView);
+ pw.print(prefix2); pw.print("screen coordinates: ");
+ if (mWindow.mContentView == null) {
+ pw.println("N/A");
+ } else {
+ final int[] coordinates = mWindow.mContentView.getLocationOnScreen();
+ pw.print(coordinates[0]); pw.print("x"); pw.println(coordinates[1]);
+ }
+ }
}
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index bcdb118..d25ffce 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -37,6 +37,8 @@
import com.android.internal.R;
import com.android.server.UiThread;
+import java.io.PrintWriter;
+
/**
* Autofill Save Prompt
*/
@@ -96,6 +98,9 @@
private final @NonNull OneTimeListener mListener;
+ private final CharSequence mTitle;
+ private final CharSequence mSubTitle;
+
private boolean mDestroyed;
SaveUi(@NonNull Context context, @NonNull CharSequence providerLabel, @NonNull SaveInfo info,
@@ -126,37 +131,36 @@
types.add(context.getString(R.string.autofill_save_type_email_address));
}
- final CharSequence title;
switch (types.size()) {
case 1:
- title = Html.fromHtml(context.getString(R.string.autofill_save_title_with_type,
+ mTitle = Html.fromHtml(context.getString(R.string.autofill_save_title_with_type,
types.valueAt(0), providerLabel), 0);
break;
case 2:
- title = Html.fromHtml(context.getString(R.string.autofill_save_title_with_2types,
+ mTitle = Html.fromHtml(context.getString(R.string.autofill_save_title_with_2types,
types.valueAt(0), types.valueAt(1), providerLabel), 0);
break;
case 3:
- title = Html.fromHtml(context.getString(R.string.autofill_save_title_with_3types,
+ mTitle = Html.fromHtml(context.getString(R.string.autofill_save_title_with_3types,
types.valueAt(0), types.valueAt(1), types.valueAt(2), providerLabel), 0);
break;
default:
// Use generic if more than 3 or invalid type (size 0).
- title = Html.fromHtml(
+ mTitle = Html.fromHtml(
context.getString(R.string.autofill_save_title, providerLabel), 0);
}
- titleView.setText(title);
- final CharSequence subTitle = info.getDescription();
- if (subTitle != null) {
+ titleView.setText(mTitle);
+ mSubTitle = info.getDescription();
+ if (mSubTitle != null) {
final TextView subTitleView = (TextView) view.findViewById(R.id.autofill_save_subtitle);
- subTitleView.setText(subTitle);
+ subTitleView.setText(mSubTitle);
subTitleView.setVisibility(View.VISIBLE);
}
- Slog.i(TAG, "Showing save dialog: " + title);
+ Slog.i(TAG, "Showing save dialog: " + mTitle);
if (sDebug) {
- Slog.d(TAG, "SubTitle: " + subTitle);
+ Slog.d(TAG, "SubTitle: " + mSubTitle);
}
final TextView noButton = view.findViewById(R.id.autofill_save_no);
@@ -207,4 +211,18 @@
throw new IllegalStateException("cannot interact with a destroyed instance");
}
}
+
+ void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("title: "); pw.println(mTitle);
+ pw.print(prefix); pw.print("subtitle: "); pw.println(mSubTitle);
+
+ final View view = mDialog.getWindow().getDecorView();
+ final int[] loc = view.getLocationOnScreen();
+ pw.print(prefix); pw.print("coordinates: ");
+ pw.print('('); pw.print(loc[0]); pw.print(','); pw.print(loc[1]);pw.print(')');
+ pw.print('(');
+ pw.print(loc[0] + view.getWidth()); pw.print(',');
+ pw.print(loc[1] + view.getHeight());pw.println(')');
+ pw.print(prefix); pw.print("destroyed: "); pw.println(mDestroyed);
+ }
}
diff --git a/services/core/Android.mk b/services/core/Android.mk
index f896478..15493467 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -13,7 +13,6 @@
../../../../system/netd/server/binder/android/net/INetd.aidl \
../../../../system/netd/server/binder/android/net/metrics/INetdEventListener.aidl \
../../../native/cmds/installd/binder/android/os/IInstalld.aidl \
- ../../../native/services/vr/vr_window_manager/aidl/android/service/vr/IVrWindowManager.aidl \
LOCAL_AIDL_INCLUDES += \
system/netd/server/binder
diff --git a/services/core/java/com/android/server/SyntheticPasswordManager.java b/services/core/java/com/android/server/SyntheticPasswordManager.java
index 6ec74e1..f797517 100644
--- a/services/core/java/com/android/server/SyntheticPasswordManager.java
+++ b/services/core/java/com/android/server/SyntheticPasswordManager.java
@@ -346,11 +346,14 @@
PasswordData pwd = PasswordData.create(credentialType);
byte[] pwdToken = computePasswordToken(credential, pwd);
+ // In case GK enrollment leaves persistent state around (in RPMB), this will nuke them
+ // to prevent them from accumulating and causing problems.
+ gatekeeper.clearSecureUserId(fakeUid(userId));
GateKeeperResponse response = gatekeeper.enroll(fakeUid(userId), null, null,
passwordTokenToGkInput(pwdToken));
if (response.getResponseCode() != GateKeeperResponse.RESPONSE_OK) {
Log.e(TAG, "Fail to enroll user password when creating SP for user " + userId);
- return 0;
+ return DEFAULT_HANDLE;
}
pwd.passwordHandle = response.getPayload();
long sid = sidFromPasswordHandle(pwd.passwordHandle);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index a5615a9..1ed46a0 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -622,6 +622,17 @@
!= ActivityManager.APP_START_MODE_NORMAL) {
if (stopping == null) {
stopping = new ArrayList<>();
+ String compName = service.name.flattenToShortString();
+ EventLogTags.writeAmStopIdleService(service.appInfo.uid, compName);
+ StringBuilder sb = new StringBuilder(64);
+ sb.append("Stopping service due to app idle: ");
+ UserHandle.formatUid(sb, service.appInfo.uid);
+ sb.append(" ");
+ TimeUtils.formatDuration(service.createTime
+ - SystemClock.elapsedRealtime(), sb);
+ sb.append(" ");
+ sb.append(compName);
+ Slog.w(TAG, sb.toString());
stopping.add(service);
}
}
@@ -1364,7 +1375,7 @@
// This could have made the service more important.
mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities
|| s.app.treatLikeActivity, b.client);
- mAm.updateOomAdjLocked(s.app);
+ mAm.updateOomAdjLocked(s.app, true);
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bind " + s + " with " + b
@@ -1479,13 +1490,15 @@
r.binding.service.app.hasClientActivities
|| r.binding.service.app.treatLikeActivity, null);
}
- mAm.updateOomAdjLocked(r.binding.service.app);
+ mAm.updateOomAdjLocked(r.binding.service.app, false);
}
}
} finally {
Binder.restoreCallingIdentity(origId);
}
+ mAm.updateOomAdjLocked();
+
return true;
}
@@ -2225,7 +2238,7 @@
bumpServiceExecutingLocked(r, execInFg, "start");
if (!oomAdjusted) {
oomAdjusted = true;
- mAm.updateOomAdjLocked(r.app);
+ mAm.updateOomAdjLocked(r.app, true);
}
if (r.fgRequired && !r.fgWaiting) {
if (!r.isForeground) {
@@ -2349,7 +2362,7 @@
if (ibr.hasBound) {
try {
bumpServiceExecutingLocked(r, false, "bring down unbind");
- mAm.updateOomAdjLocked(r.app);
+ mAm.updateOomAdjLocked(r.app, true);
ibr.hasBound = false;
ibr.requested = false;
r.app.thread.scheduleUnbindService(r,
@@ -2441,7 +2454,7 @@
bumpServiceExecutingLocked(r, false, "destroy");
mDestroyingServices.add(r);
r.destroying = true;
- mAm.updateOomAdjLocked(r.app);
+ mAm.updateOomAdjLocked(r.app, true);
r.app.thread.scheduleStopService(r);
} catch (Exception e) {
Slog.w(TAG, "Exception when destroying service "
@@ -2542,7 +2555,7 @@
// it to go down there and we want it to start out near the top.
mAm.updateLruProcessLocked(s.app, false, null);
}
- mAm.updateOomAdjLocked(s.app);
+ mAm.updateOomAdjLocked(s.app, true);
b.intent.hasBound = false;
// Assume the client doesn't want to know about a rebind;
// we will deal with that later if it asks for one.
@@ -2695,7 +2708,7 @@
mDestroyingServices.remove(r);
r.bindings.clear();
}
- mAm.updateOomAdjLocked(r.app);
+ mAm.updateOomAdjLocked(r.app, true);
}
r.executeFg = false;
if (r.tracker != null) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6d69915..268adc5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -812,15 +812,28 @@
final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>();
/**
- * All of the processes that have been forced to be foreground. The key
+ * All of the processes that have been forced to be important. The key
* is the pid of the caller who requested it (we hold a death
* link on it).
*/
- abstract class ForegroundToken implements IBinder.DeathRecipient {
- int pid;
- IBinder token;
+ abstract class ImportanceToken implements IBinder.DeathRecipient {
+ final int pid;
+ final IBinder token;
+ final String reason;
+
+ ImportanceToken(int _pid, IBinder _token, String _reason) {
+ pid = _pid;
+ token = _token;
+ reason = _reason;
+ }
+
+ @Override
+ public String toString() {
+ return "ImportanceToken { " + Integer.toHexString(System.identityHashCode(this))
+ + " " + reason + " " + pid + " " + token + " }";
+ }
}
- final SparseArray<ForegroundToken> mForegroundProcesses = new SparseArray<ForegroundToken>();
+ final SparseArray<ImportanceToken> mImportantProcesses = new SparseArray<ImportanceToken>();
/**
* List of records for processes that someone had tried to start before the
@@ -6499,6 +6512,7 @@
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"No more processes in " + old.uidRecord);
enqueueUidChangeLocked(old.uidRecord, -1, UidRecord.CHANGE_GONE);
+ EventLogTags.writeAmUidStopped(uid);
mActiveUids.remove(uid);
noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT);
}
@@ -6530,6 +6544,7 @@
}
uidRec.updateHasInternetPermission();
mActiveUids.put(proc.uid, uidRec);
+ EventLogTags.writeAmUidRunning(uidRec.uid);
noteUidProcessState(uidRec.uid, uidRec.curProcState);
enqueueUidChangeLocked(uidRec, -1, UidRecord.CHANGE_ACTIVE);
}
@@ -6717,7 +6732,7 @@
app.makeActive(thread, mProcessStats);
app.curAdj = app.setAdj = app.verifiedAdj = ProcessList.INVALID_ADJ;
app.curSchedGroup = app.setSchedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- app.forcingToForeground = null;
+ app.forcingToImportant = null;
updateProcessForegroundLocked(app, false, false);
app.hasShownUi = false;
app.debugging = false;
@@ -7717,20 +7732,20 @@
}
}
- void foregroundTokenDied(ForegroundToken token) {
+ void importanceTokenDied(ImportanceToken token) {
synchronized (ActivityManagerService.this) {
synchronized (mPidsSelfLocked) {
- ForegroundToken cur
- = mForegroundProcesses.get(token.pid);
+ ImportanceToken cur
+ = mImportantProcesses.get(token.pid);
if (cur != token) {
return;
}
- mForegroundProcesses.remove(token.pid);
+ mImportantProcesses.remove(token.pid);
ProcessRecord pr = mPidsSelfLocked.get(token.pid);
if (pr == null) {
return;
}
- pr.forcingToForeground = null;
+ pr.forcingToImportant = null;
updateProcessForegroundLocked(pr, false, false);
}
updateOomAdjLocked();
@@ -7738,9 +7753,9 @@
}
@Override
- public void setProcessForeground(IBinder token, int pid, boolean isForeground) {
+ public void setProcessImportant(IBinder token, int pid, boolean isForeground, String reason) {
enforceCallingPermission(android.Manifest.permission.SET_PROCESS_LIMIT,
- "setProcessForeground()");
+ "setProcessImportant()");
synchronized(this) {
boolean changed = false;
@@ -7750,28 +7765,26 @@
Slog.w(TAG, "setProcessForeground called on unknown pid: " + pid);
return;
}
- ForegroundToken oldToken = mForegroundProcesses.get(pid);
+ ImportanceToken oldToken = mImportantProcesses.get(pid);
if (oldToken != null) {
oldToken.token.unlinkToDeath(oldToken, 0);
- mForegroundProcesses.remove(pid);
+ mImportantProcesses.remove(pid);
if (pr != null) {
- pr.forcingToForeground = null;
+ pr.forcingToImportant = null;
}
changed = true;
}
if (isForeground && token != null) {
- ForegroundToken newToken = new ForegroundToken() {
+ ImportanceToken newToken = new ImportanceToken(pid, token, reason) {
@Override
public void binderDied() {
- foregroundTokenDied(this);
+ importanceTokenDied(this);
}
};
- newToken.pid = pid;
- newToken.token = token;
try {
token.linkToDeath(newToken, 0);
- mForegroundProcesses.put(pid, newToken);
- pr.forcingToForeground = token;
+ mImportantProcesses.put(pid, newToken);
+ pr.forcingToImportant = newToken;
changed = true;
} catch (RemoteException e) {
// If the process died while doing this, we will later
@@ -11313,7 +11326,7 @@
checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
final int verifiedAdj = cpr.proc.verifiedAdj;
- boolean success = updateOomAdjLocked(cpr.proc);
+ boolean success = updateOomAdjLocked(cpr.proc, true);
// XXX things have changed so updateOomAdjLocked doesn't actually tell us
// if the process has been successfully adjusted. So to reduce races with
// it, we will check whether the process still exists. Note that this doesn't
@@ -11775,7 +11788,7 @@
dst.proc = r;
dst.notifyAll();
}
- updateOomAdjLocked(r);
+ updateOomAdjLocked(r, true);
maybeUpdateProviderUsageStatsLocked(r, src.info.packageName,
src.info.authority);
}
@@ -13506,7 +13519,7 @@
}
}
if (changed) {
- updateOomAdjLocked(pr);
+ updateOomAdjLocked(pr, true);
}
}
} finally {
@@ -15490,12 +15503,12 @@
}
}
- if (mForegroundProcesses.size() > 0) {
+ if (mImportantProcesses.size() > 0) {
synchronized (mPidsSelfLocked) {
boolean printed = false;
- for (int i=0; i<mForegroundProcesses.size(); i++) {
+ for (int i = 0; i< mImportantProcesses.size(); i++) {
ProcessRecord r = mPidsSelfLocked.get(
- mForegroundProcesses.valueAt(i).pid);
+ mImportantProcesses.valueAt(i).pid);
if (dumpPackage != null && (r == null
|| !r.pkgList.containsKey(dumpPackage))) {
continue;
@@ -15507,8 +15520,8 @@
printed = true;
printedAnything = true;
}
- pw.print(" PID #"); pw.print(mForegroundProcesses.keyAt(i));
- pw.print(": "); pw.println(mForegroundProcesses.valueAt(i));
+ pw.print(" PID #"); pw.print(mImportantProcesses.keyAt(i));
+ pw.print(": "); pw.println(mImportantProcesses.valueAt(i));
}
}
}
@@ -17779,7 +17792,7 @@
app.unlinkDeathRecipient();
app.makeInactive(mProcessStats);
app.waitingToKill = null;
- app.forcingToForeground = null;
+ app.forcingToImportant = null;
updateProcessForegroundLocked(app, false, false);
app.foregroundActivities = false;
app.hasShownUi = false;
@@ -18279,7 +18292,7 @@
mBackupAppName = app.packageName;
// Try not to kill the process during backup
- updateOomAdjLocked(proc);
+ updateOomAdjLocked(proc, true);
// If the process is already attached, schedule the creation of the backup agent now.
// If it is not yet live, this will be done when it attaches to the framework.
@@ -18376,7 +18389,7 @@
// Not backing this app up any more; reset its OOM adjustment
final ProcessRecord proc = mBackupTarget.app;
- updateOomAdjLocked(proc);
+ updateOomAdjLocked(proc, true);
proc.inFullBackup = false;
oldBackupUid = mBackupTarget != null ? mBackupTarget.appInfo.uid : -1;
@@ -20774,14 +20787,6 @@
app.cached = false;
app.adjType = "fg-service";
schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
- } else if (app.forcingToForeground != null) {
- // The user is aware of this app, so make it visible.
- adj = ProcessList.PERCEPTIBLE_APP_ADJ;
- procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
- app.cached = false;
- app.adjType = "force-fg";
- app.adjSource = app.forcingToForeground;
- schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
} else if (app.hasOverlayUi) {
// The process is display an overlay UI.
adj = ProcessList.PERCEPTIBLE_APP_ADJ;
@@ -20792,6 +20797,21 @@
}
}
+ if (adj > ProcessList.PERCEPTIBLE_APP_ADJ
+ || procState > ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND) {
+ if (app.forcingToImportant != null) {
+ // This is currently used for toasts... they are not interactive, and
+ // we don't want them to cause the app to become fully foreground (and
+ // thus out of background check), so we yes the best background level we can.
+ adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+ procState = ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND;
+ app.cached = false;
+ app.adjType = "force-imp";
+ app.adjSource = app.forcingToImportant;
+ schedGroup = ProcessList.SCHED_GROUP_DEFAULT;
+ }
+ }
+
if (app == mHeavyWeightProcess) {
if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
// We don't want to kill the current heavy-weight process.
@@ -22032,10 +22052,7 @@
+ mConstants.SERVICE_USAGE_INTERACTION_TIME;
}
} else {
- // If the app was being forced to the foreground, by say a Toast, then
- // no need to treat it as an interaction
- isInteraction = app.forcingToForeground == null
- && app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+ isInteraction = app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
app.fgInteractionTime = 0;
}
if (isInteraction && (!app.reportedInteraction || (nowElapsed-app.interactionEventTime)
@@ -22136,7 +22153,14 @@
return act;
}
- final boolean updateOomAdjLocked(ProcessRecord app) {
+ /**
+ * Update OomAdj for a specific process.
+ * @param app The process to update
+ * @param oomAdjAll If it's ok to call updateOomAdjLocked() for all running apps
+ * if necessary, or skip.
+ * @return whether updateOomAdjLocked(app) was successful.
+ */
+ final boolean updateOomAdjLocked(ProcessRecord app, boolean oomAdjAll) {
final ActivityRecord TOP_ACT = resumedAppLocked();
final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
final boolean wasCached = app.cached;
@@ -22151,7 +22175,8 @@
? app.curRawAdj : ProcessList.UNKNOWN_ADJ;
boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false,
SystemClock.uptimeMillis());
- if (wasCached != app.cached || app.curRawAdj == ProcessList.UNKNOWN_ADJ) {
+ if (oomAdjAll
+ && (wasCached != app.cached || app.curRawAdj == ProcessList.UNKNOWN_ADJ)) {
// Changed to/from cached state, so apps after it in the LRU
// list may also be changed.
updateOomAdjLocked();
@@ -22565,6 +22590,7 @@
} else {
if (uidRec.idle) {
uidChange = UidRecord.CHANGE_ACTIVE;
+ EventLogTags.writeAmUidActive(uidRec.uid);
uidRec.idle = false;
}
uidRec.lastBackgroundTime = 0;
@@ -22643,6 +22669,7 @@
if (UserHandle.getAppId(uidRec.uid) == appId) {
if (userId == UserHandle.USER_ALL ||
userId == UserHandle.getUserId(uidRec.uid)) {
+ EventLogTags.writeAmUidIdle(uidRec.uid);
uidRec.idle = true;
Slog.w(TAG, "Idling uid " + UserHandle.formatUid(uidRec.uid)
+ " from package " + packageName + " user " + userId);
@@ -22677,6 +22704,7 @@
final long bgTime = uidRec.lastBackgroundTime;
if (bgTime > 0 && !uidRec.idle) {
if (bgTime <= maxBgTime) {
+ EventLogTags.writeAmUidIdle(uidRec.uid);
uidRec.idle = true;
doStopUidLocked(uidRec.uid, uidRec);
} else {
@@ -23729,7 +23757,7 @@
}
pr.hasOverlayUi = hasOverlayUi;
//Slog.i(TAG, "Setting hasOverlayUi=" + pr.hasOverlayUi + " for pid=" + pid);
- updateOomAdjLocked(pr);
+ updateOomAdjLocked(pr, true);
}
}
@@ -23867,6 +23895,34 @@
}
}
+ public void waitForBroadcastIdle(PrintWriter pw) {
+ enforceCallingPermission(permission.DUMP, "waitForBroadcastIdle()");
+ while (true) {
+ boolean idle = true;
+ synchronized (this) {
+ for (BroadcastQueue queue : mBroadcastQueues) {
+ if (!queue.isIdle()) {
+ final String msg = "Waiting for queue " + queue + " to become idle...";
+ pw.println(msg);
+ pw.flush();
+ Slog.v(TAG, msg);
+ idle = false;
+ }
+ }
+ }
+
+ if (idle) {
+ final String msg = "All broadcast queues are idle!";
+ pw.println(msg);
+ pw.flush();
+ Slog.v(TAG, msg);
+ return;
+ } else {
+ SystemClock.sleep(1000);
+ }
+ }
+ }
+
/**
* Return the user id of the last resumed activity.
*/
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index b6bfb00..dab122f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -251,6 +251,8 @@
return runUpdateApplicationInfo(pw);
case "no-home-screen":
return runNoHomeScreen(pw);
+ case "wait-for-broadcast-idle":
+ return runWaitForBroadcastIdle(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -2419,6 +2421,11 @@
return 0;
}
+ int runWaitForBroadcastIdle(PrintWriter pw) throws RemoteException {
+ mInternal.waitForBroadcastIdle(pw);
+ return 0;
+ }
+
private Resources getResources(PrintWriter pw) throws RemoteException {
// system resources does not contain all the device configuration, construct it manually.
Configuration config = mInterface.getConfiguration();
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index d6bfb35..cfb5478 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -740,6 +740,8 @@
}
// If we've created a crash dialog, show it without the lock held
if(data.proc.crashDialog != null) {
+ Slog.i(TAG, "Showing crash dialog for package " + data.proc.info.packageName
+ + " u" + data.proc.userId);
data.proc.crashDialog.show();
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index d08298b..639b7a9 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -51,7 +51,6 @@
import android.util.EventLog;
import android.util.Slog;
import android.util.TimeUtils;
-import com.android.server.DeviceIdleController;
import static com.android.server.am.ActivityManagerDebugConfig.*;
@@ -204,6 +203,11 @@
mDelayBehindServices = allowDelayBehindServices;
}
+ @Override
+ public String toString() {
+ return mQueueName;
+ }
+
public boolean isPendingBroadcastProcessLocked(int pid) {
return mPendingBroadcast != null && mPendingBroadcast.curApp.pid == pid;
}
@@ -682,7 +686,7 @@
// are already core system stuff so don't matter for this.
r.curApp = filter.receiverList.app;
filter.receiverList.app.curReceivers.add(r);
- mService.updateOomAdjLocked(r.curApp);
+ mService.updateOomAdjLocked(r.curApp, true);
}
}
try {
@@ -1579,6 +1583,11 @@
record.intent == null ? "" : record.intent.getAction());
}
+ final boolean isIdle() {
+ return mParallelBroadcasts.isEmpty() && mOrderedBroadcasts.isEmpty()
+ && (mPendingBroadcast == null);
+ }
+
final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
int opti, boolean dumpAll, String dumpPackage, boolean needSep) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index f618fc7..372ab6b 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -114,3 +114,14 @@
# UserState has changed
30051 am_user_state_changed (id|1|5),(state|1|5)
+
+# Note when any processes of a uid have started running
+30052 am_uid_running (UID|1|5)
+# Note when all processes of a uid have stopped.
+30053 am_uid_stopped (UID|1|5)
+# Note when the state of a uid has become active.
+30054 am_uid_active (UID|1|5)
+# Note when the state of a uid has become idle (background check enforced).
+30055 am_uid_idle (UID|1|5)
+# Note when a service is being forcibly stopped because its app went idle.
+30056 am_stop_idle_service (UID|1|5),(Component Name|3)
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index fbc2bd2..b222e3a 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -135,7 +135,7 @@
long interactionEventTime; // The time we sent the last interaction event
long fgInteractionTime; // When we became foreground for interaction purposes
String waitingToKill; // Process is waiting to be killed when in the bg, and reason
- IBinder forcingToForeground;// Token that is forcing this process to be foreground
+ Object forcingToImportant; // Token that is forcing this process to be important
int adjSeq; // Sequence id for identifying oom_adj assignment cycles
int lruSeq; // Sequence id for identifying LRU update cycles
CompatibilityInfo compat; // last used compatibility mode
@@ -302,9 +302,9 @@
pw.print(" hasAboveClient="); pw.print(hasAboveClient);
pw.print(" treatLikeActivity="); pw.println(treatLikeActivity);
}
- if (foregroundServices || forcingToForeground != null) {
+ if (foregroundServices || forcingToImportant != null) {
pw.print(prefix); pw.print("foregroundServices="); pw.print(foregroundServices);
- pw.print(" forcingToForeground="); pw.println(forcingToForeground);
+ pw.print(" forcingToImportant="); pw.println(forcingToImportant);
}
if (reportedInteraction || fgInteractionTime != 0) {
pw.print(prefix); pw.print("reportedInteraction=");
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 3b5e5bc..6a310f2 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -439,7 +439,7 @@
}
}
- Slog.d(TAG, "Sending BOOT_COMPLETE user #" + userId);
+ Slog.i(TAG, "Sending BOOT_COMPLETE user #" + userId);
// Do not report secondary users, runtime restarts or first boot/upgrade
if (userId == UserHandle.USER_SYSTEM
&& !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
@@ -451,7 +451,14 @@
bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
bootIntent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- mInjector.broadcastIntentLocked(bootIntent, null, null, 0, null, null,
+ mInjector.broadcastIntentLocked(bootIntent, null, new IIntentReceiver.Stub() {
+ @Override
+ public void performReceive(Intent intent, int resultCode, String data,
+ Bundle extras, boolean ordered, boolean sticky, int sendingUser)
+ throws RemoteException {
+ Slog.i(UserController.TAG, "Finished processing BOOT_COMPLETED for u" + userId);
+ }
+ }, 0, null, null,
new String[] { android.Manifest.permission.RECEIVE_BOOT_COMPLETED },
AppOpsManager.OP_NONE, null, true, false, MY_PID, SYSTEM_UID, userId);
}
diff --git a/services/core/java/com/android/server/audio/FocusRequester.java b/services/core/java/com/android/server/audio/FocusRequester.java
index 73cbffd..125095c 100644
--- a/services/core/java/com/android/server/audio/FocusRequester.java
+++ b/services/core/java/com/android/server/audio/FocusRequester.java
@@ -322,8 +322,8 @@
if (mFocusLossWasNotified) {
fd.dispatchAudioFocusChange(focusGain, mClientId);
}
- mFocusController.unduckPlayers(this);
}
+ mFocusController.unduckPlayers(this);
mFocusLossWasNotified = false;
} catch (android.os.RemoteException e) {
Log.e(TAG, "Failure to signal gain of audio focus due to: ", e);
@@ -333,6 +333,15 @@
/**
* Called synchronized on MediaFocusControl.mAudioFocusLock
*/
+ void handleFocusGainFromRequest(int focusRequestResult) {
+ if (focusRequestResult == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+ mFocusController.unduckPlayers(this);
+ }
+ }
+
+ /**
+ * Called synchronized on MediaFocusControl.mAudioFocusLock
+ */
void handleFocusLoss(int focusLoss, @Nullable final FocusRequester fr) {
try {
if (focusLoss != mFocusLossReceived) {
@@ -381,6 +390,8 @@
Log.v(TAG, "NOT dispatching " + focusChangeToString(mFocusLossReceived)
+ " to " + mClientId + ", ducking implemented by framework");
}
+ mFocusController.notifyExtPolicyFocusLoss_syncAf(
+ toAudioFocusInfo(), false /* wasDispatched */);
return; // with mFocusLossWasNotified = false
}
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index f5c13c1..7d742ff 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -752,6 +752,7 @@
// push focus requester at the top of the audio focus stack
mFocusStack.push(nfr);
+ nfr.handleFocusGainFromRequest(AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
}
notifyExtPolicyFocusGrant_syncAf(nfr.toAudioFocusInfo(),
AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
diff --git a/services/core/java/com/android/server/display/NightDisplayService.java b/services/core/java/com/android/server/display/NightDisplayService.java
index 7849896..d574265 100644
--- a/services/core/java/com/android/server/display/NightDisplayService.java
+++ b/services/core/java/com/android/server/display/NightDisplayService.java
@@ -567,22 +567,27 @@
private final TwilightManager mTwilightManager;
- private Calendar mLastActivatedTime;
-
TwilightAutoMode() {
mTwilightManager = getLocalService(TwilightManager.class);
}
private void updateActivated(TwilightState state) {
- boolean activate = state != null && state.isNight();
- if (state != null && mLastActivatedTime != null) {
+ if (state == null) {
+ // If there isn't a valid TwilightState then just keep the current activated
+ // state.
+ return;
+ }
+
+ boolean activate = state.isNight();
+ final Calendar lastActivatedTime = getLastActivatedTime();
+ if (lastActivatedTime != null) {
final Calendar now = Calendar.getInstance();
final Calendar sunrise = state.sunrise();
final Calendar sunset = state.sunset();
// Maintain the existing activated state if within the current period.
- if (mLastActivatedTime.before(now)
- && (mLastActivatedTime.after(sunrise) ^ mLastActivatedTime.after(sunset))) {
+ if (lastActivatedTime.before(now)
+ && (lastActivatedTime.after(sunrise) ^ lastActivatedTime.after(sunset))) {
activate = mController.isActivated();
}
}
@@ -595,7 +600,6 @@
@Override
public void onStart() {
mTwilightManager.registerListener(this, mHandler);
- mLastActivatedTime = getLastActivatedTime();
// Force an update to initialize state.
updateActivated(mTwilightManager.getLastTwilightState());
@@ -604,14 +608,10 @@
@Override
public void onStop() {
mTwilightManager.unregisterListener(this);
- mLastActivatedTime = null;
}
@Override
public void onActivated(boolean activated) {
- if (mIsActivated != null) {
- mLastActivatedTime = getLastActivatedTime();
- }
}
@Override
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1d843c1..70766f9 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2871,8 +2871,7 @@
adjustedSbn.getUser(), GroupHelper.AUTOGROUP_KEY,
System.currentTimeMillis());
summaryRecord = new NotificationRecord(getContext(), summarySbn,
- notificationRecord.getChannel(), mRankingHelper.supportsChannels(
- summarySbn.getPackageName(), summarySbn.getUid()));
+ notificationRecord.getChannel());
summaries.put(pkg, summarySbn.getKey());
}
}
@@ -3211,8 +3210,7 @@
final StatusBarNotification n = new StatusBarNotification(
pkg, opPkg, id, tag, notificationUid, callingPid, notification,
user, null, System.currentTimeMillis());
- final NotificationRecord r = new NotificationRecord(getContext(), n, channel,
- mRankingHelper.supportsChannels(pkg, notificationUid));
+ final NotificationRecord r = new NotificationRecord(getContext(), n, channel);
if (!checkDisqualifyingFeatures(userId, notificationUid, id,tag, r)) {
return;
@@ -3937,7 +3935,7 @@
}
}
try {
- mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0);
+ mAm.setProcessImportant(mForegroundToken, pid, toastCount > 0, "toast");
} catch (RemoteException e) {
// Shouldn't happen.
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index f019a5c..5a5e658 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -116,7 +116,7 @@
private int mSuppressedVisualEffects = 0;
private String mUserExplanation;
private String mPeopleExplanation;
- private boolean mSupportsChannels = false;
+ private boolean mPreChannelsNotification = true;
private Uri mSound;
private long[] mVibration;
private AudioAttributes mAttributes;
@@ -129,7 +129,7 @@
@VisibleForTesting
public NotificationRecord(Context context, StatusBarNotification sbn,
- NotificationChannel channel, boolean supportsChannels)
+ NotificationChannel channel)
{
this.sbn = sbn;
mOriginalFlags = sbn.getNotification().flags;
@@ -139,7 +139,7 @@
mContext = context;
stats = new NotificationUsageStats.SingleNotificationStats();
mChannel = channel;
- mSupportsChannels = supportsChannels;
+ mPreChannelsNotification = isPreChannelsNotification();
mSound = calculateSound();
mVibration = calculateVibration();
mAttributes = calculateAttributes();
@@ -147,11 +147,27 @@
mLight = calculateLights();
}
+ private boolean isPreChannelsNotification() {
+ try {
+ if (NotificationChannel.DEFAULT_CHANNEL_ID.equals(getChannel().getId())) {
+ final ApplicationInfo applicationInfo =
+ mContext.getPackageManager().getApplicationInfoAsUser(sbn.getPackageName(),
+ 0, UserHandle.getUserId(sbn.getUid()));
+ if (applicationInfo.targetSdkVersion <= Build.VERSION_CODES.N_MR1) {
+ return true;
+ }
+ }
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Can't find package", e);
+ }
+ return false;
+ }
+
private Uri calculateSound() {
final Notification n = sbn.getNotification();
Uri sound = mChannel.getSound();
- if (!mSupportsChannels && (getChannel().getUserLockedFields()
+ if (mPreChannelsNotification && (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_SOUND) == 0) {
final boolean useDefaultSound = (n.defaults & Notification.DEFAULT_SOUND) != 0;
@@ -176,7 +192,7 @@
: defaultLightColor;
Light light = getChannel().shouldShowLights() ? new Light(channelLightColor,
defaultLightOn, defaultLightOff) : null;
- if (!mSupportsChannels
+ if (mPreChannelsNotification
&& (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_LIGHTS) == 0) {
final Notification notification = sbn.getNotification();
@@ -207,7 +223,7 @@
} else {
vibration = null;
}
- if (!mSupportsChannels
+ if (mPreChannelsNotification
&& (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_VIBRATION) == 0) {
final Notification notification = sbn.getNotification();
@@ -229,7 +245,7 @@
attributes = Notification.AUDIO_ATTRIBUTES_DEFAULT;
}
- if (!mSupportsChannels
+ if (mPreChannelsNotification
&& (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_SOUND) == 0) {
if (n.audioAttributes != null) {
@@ -278,7 +294,7 @@
stats.requestedImportance = requestedImportance;
stats.isNoisy = mSound != null || mVibration != null;
- if (!mSupportsChannels
+ if (mPreChannelsNotification
&& (importance == IMPORTANCE_UNSPECIFIED
|| (getChannel().getUserLockedFields()
& NotificationChannel.USER_LOCKED_IMPORTANCE) == 0)) {
@@ -445,7 +461,7 @@
pw.println(prefix + "mVisibleSinceMs=" + mVisibleSinceMs);
pw.println(prefix + "mUpdateTimeMs=" + mUpdateTimeMs);
pw.println(prefix + "mSuppressedVisualEffects= " + mSuppressedVisualEffects);
- if (!mSupportsChannels) {
+ if (mPreChannelsNotification) {
pw.println(prefix + String.format("defaults=0x%08x flags=0x%08x",
notification.defaults, notification.flags));
pw.println(prefix + "n.sound=" + notification.sound);
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 14d796f..4d19b52 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -42,6 +42,4 @@
void permanentlyDeleteNotificationChannel(String pkg, int uid, String channelId);
void permanentlyDeleteNotificationChannels(String pkg, int uid);
ParceledListSlice<NotificationChannel> getNotificationChannels(String pkg, int uid, boolean includeDeleted);
-
- boolean supportsChannels(String pkg, int uid);
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 1e741de..7758516 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -273,14 +273,8 @@
}
private boolean shouldHaveDefaultChannel(Record r) throws NameNotFoundException {
- if (supportsChannels(r)) {
- return false;
- }
-
final int userId = UserHandle.getUserId(r.uid);
- final ApplicationInfo applicationInfo = mPm.getApplicationInfoAsUser(r.pkg,
- PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- userId);
+ final ApplicationInfo applicationInfo = mPm.getApplicationInfoAsUser(r.pkg, 0, userId);
if (applicationInfo.targetSdkVersion > Build.VERSION_CODES.N_MR1) {
// O apps should not have the default channel.
return false;
@@ -506,31 +500,6 @@
}
@Override
- public boolean supportsChannels(String pkg, int uid) {
- Record r = getOrCreateRecord(pkg, uid);
-
- if (r == null) {
- return false;
- }
-
- if (r.channels.size() == 1
- && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- return false;
- }
-
- return true;
- }
-
- private boolean supportsChannels(Record r) {
- if (r.channels.size() == 1
- && r.channels.containsKey(NotificationChannel.DEFAULT_CHANNEL_ID)) {
- return false;
- }
-
- return (r.channels.size() > 0);
- }
-
- @Override
public void createNotificationChannelGroup(String pkg, int uid, NotificationChannelGroup group,
boolean fromTargetApp) {
Preconditions.checkNotNull(pkg);
@@ -602,10 +571,6 @@
r.channels.put(channel.getId(), channel);
MetricsLogger.action(getChannelLog(channel, pkg).setType(
MetricsProto.MetricsEvent.TYPE_OPEN));
-
- // Remove Default Channel.
- r.channels.remove(NotificationChannel.DEFAULT_CHANNEL_ID);
-
updateConfig();
}
@@ -702,7 +667,13 @@
if (r == null) {
return;
}
- r.channels.clear();
+ int N = r.channels.size() - 1;
+ for (int i = N; i >= 0; i--) {
+ String key = r.channels.keyAt(i);
+ if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(key)) {
+ r.channels.remove(key);
+ }
+ }
updateConfig();
}
@@ -1066,8 +1037,6 @@
final int uid = uidList[i];
synchronized (mRecords) {
mRecords.remove(recordKey(pkg, uid));
- // reset to default settings and re-add misc channel for pre-O apps
- getOrCreateRecord(pkg, uid);
}
mRestoredWithoutUids.remove(pkg);
updated = true;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 55285c8..0173533 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -6498,11 +6498,12 @@
String resolvedType, int flags, int userId) {
// first, check to see if we've got an instant app already installed
final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0;
- boolean localInstantAppAvailable = false;
+ ResolveInfo localInstantApp = null;
boolean blockResolution = false;
if (!alreadyResolvedLocally) {
final List<ResolveInfo> instantApps = mActivities.queryIntent(intent, resolvedType,
flags
+ | PackageManager.GET_RESOLVED_FILTER
| PackageManager.MATCH_INSTANT
| PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY,
userId);
@@ -6529,7 +6530,7 @@
if (DEBUG_EPHEMERAL) {
Slog.v(TAG, "Found installed instant app; pkg: " + packageName);
}
- localInstantAppAvailable = true;
+ localInstantApp = info;
break;
}
}
@@ -6537,17 +6538,29 @@
}
// no app installed, let's see if one's available
AuxiliaryResolveInfo auxiliaryResponse = null;
- if (!localInstantAppAvailable && !blockResolution) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
- final InstantAppRequest requestObject = new InstantAppRequest(
- null /*responseObj*/, intent /*origIntent*/, resolvedType,
- null /*callingPackage*/, userId, null /*verificationBundle*/);
- auxiliaryResponse =
- InstantAppResolver.doInstantAppResolutionPhaseOne(
- mContext, mInstantAppResolverConnection, requestObject);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ if (!blockResolution) {
+ if (localInstantApp == null) {
+ // we don't have an instant app locally, resolve externally
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
+ final InstantAppRequest requestObject = new InstantAppRequest(
+ null /*responseObj*/, intent /*origIntent*/, resolvedType,
+ null /*callingPackage*/, userId, null /*verificationBundle*/);
+ auxiliaryResponse =
+ InstantAppResolver.doInstantAppResolutionPhaseOne(
+ mContext, mInstantAppResolverConnection, requestObject);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ } else {
+ // we have an instant application locally, but, we can't admit that since
+ // callers shouldn't be able to determine prior browsing. create a dummy
+ // auxiliary response so the downstream code behaves as if there's an
+ // instant application available externally. when it comes time to start
+ // the instant application, we'll do the right thing.
+ final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo;
+ auxiliaryResponse = new AuxiliaryResolveInfo(
+ ai.packageName, null /*splitName*/, ai.versionCode, null /*failureIntent*/);
+ }
}
- if (localInstantAppAvailable || auxiliaryResponse != null) {
+ if (auxiliaryResponse != null) {
if (DEBUG_EPHEMERAL) {
Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
}
@@ -6567,7 +6580,7 @@
ephemeralInstaller.filter = new IntentFilter(intent.getAction());
ephemeralInstaller.filter.addDataPath(
intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
- ephemeralInstaller.instantAppAvailable = true;
+ ephemeralInstaller.isInstantAppAvailable = true;
result.add(ephemeralInstaller);
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index bed8f1a..7f0528a 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1611,6 +1611,11 @@
*/
private void fixUpIncomingShortcutInfo(@NonNull ShortcutInfo shortcut, boolean forUpdate,
boolean forPinRequest) {
+ if (shortcut.isReturnedByServer()) {
+ Log.w(TAG,
+ "Re-publishing ShortcutInfo returned by server is not supported."
+ + " Some information such as icon may lost from shortcut.");
+ }
Preconditions.checkNotNull(shortcut, "Null shortcut detected");
if (shortcut.getActivity() != null) {
Preconditions.checkState(
@@ -1670,6 +1675,13 @@
}
}
+ private List<ShortcutInfo> setReturnedByServer(List<ShortcutInfo> shortcuts) {
+ for (int i = shortcuts.size() - 1; i >= 0; i--) {
+ shortcuts.get(i).setReturnedByServer();
+ }
+ return shortcuts;
+ }
+
// === APIs ===
@Override
@@ -2049,7 +2061,7 @@
final ShortcutPackage ps = getPackageShortcutsForPublisherLocked(packageName, userId);
ps.findAll(ret, query, cloneFlags);
- return new ParceledListSlice<>(ret);
+ return new ParceledListSlice<>(setReturnedByServer(ret));
}
@Override
@@ -2406,7 +2418,7 @@
});
}
}
- return ret;
+ return setReturnedByServer(ret);
}
private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
diff --git a/services/core/java/com/android/server/storage/DiskStatsLoggingService.java b/services/core/java/com/android/server/storage/DiskStatsLoggingService.java
index 4035ade..9a08ac3 100644
--- a/services/core/java/com/android/server/storage/DiskStatsLoggingService.java
+++ b/services/core/java/com/android/server/storage/DiskStatsLoggingService.java
@@ -73,13 +73,13 @@
final int userId = UserHandle.myUserId();
UserEnvironment environment = new UserEnvironment(userId);
LogRunnable task = new LogRunnable();
- task.setRootDirectory(environment.getExternalStorageDirectory());
task.setDownloadsDirectory(
environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS));
task.setSystemSize(FileCollector.getSystemSize(this));
task.setLogOutputFile(new File(DUMPSYS_CACHE_PATH));
task.setAppCollector(collector);
task.setJobService(this, params);
+ task.setContext(this);
AsyncTask.execute(task);
return true;
}
@@ -106,7 +106,8 @@
}
private static boolean isCharging(Context context) {
- BatteryManager batteryManager = context.getSystemService(BatteryManager.class);
+ BatteryManager batteryManager =
+ (BatteryManager) context.getSystemService(Context.BATTERY_SERVICE);
if (batteryManager != null) {
return batteryManager.isCharging();
}
@@ -127,14 +128,10 @@
private JobParameters mParams;
private AppCollector mCollector;
private File mOutputFile;
- private File mRootDirectory;
private File mDownloadsDirectory;
+ private Context mContext;
private long mSystemSize;
- public void setRootDirectory(File file) {
- mRootDirectory = file;
- }
-
public void setDownloadsDirectory(File file) {
mDownloadsDirectory = file;
}
@@ -151,14 +148,25 @@
mSystemSize = size;
}
+ public void setContext(Context context) {
+ mContext = context;
+ }
+
public void setJobService(JobService jobService, JobParameters params) {
mJobService = jobService;
mParams = params;
}
public void run() {
- FileCollector.MeasurementResult mainCategories =
- FileCollector.getMeasurementResult(mRootDirectory);
+ FileCollector.MeasurementResult mainCategories;
+ try {
+ mainCategories = FileCollector.getMeasurementResult(mContext);
+ } catch (IllegalStateException e) {
+ // This can occur if installd has an issue.
+ Log.e(TAG, "Error while measuring storage", e);
+ finishJob(true);
+ return;
+ }
FileCollector.MeasurementResult downloads =
FileCollector.getMeasurementResult(mDownloadsDirectory);
@@ -168,12 +176,10 @@
needsReschedule = false;
logToFile(mainCategories, downloads, stats, mSystemSize);
} else {
- Log.w("TAG", "Timed out while fetching package stats.");
+ Log.w(TAG, "Timed out while fetching package stats.");
}
- if (mJobService != null) {
- mJobService.jobFinished(mParams, needsReschedule);
- }
+ finishJob(needsReschedule);
}
private void logToFile(MeasurementResult mainCategories, MeasurementResult downloads,
@@ -187,5 +193,11 @@
Log.e(TAG, "Exception while writing opportunistic disk file cache.", e);
}
}
+
+ private void finishJob(boolean needsReschedule) {
+ if (mJobService != null) {
+ mJobService.jobFinished(mParams, needsReschedule);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/storage/FileCollector.java b/services/core/java/com/android/server/storage/FileCollector.java
index 90f9f139..0c119a7 100644
--- a/services/core/java/com/android/server/storage/FileCollector.java
+++ b/services/core/java/com/android/server/storage/FileCollector.java
@@ -17,13 +17,17 @@
package com.android.server.storage;
import android.annotation.IntDef;
+import android.app.usage.ExternalStorageStats;
+import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.UserHandle;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.util.ArrayMap;
import java.io.File;
+import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Map;
@@ -154,15 +158,46 @@
}
/**
+ * Returns the file categorization result for the primary internal storage UUID.
+ *
+ * @param context
+ */
+ public static MeasurementResult getMeasurementResult(Context context) {
+ MeasurementResult result = new MeasurementResult();
+ StorageStatsManager ssm =
+ (StorageStatsManager) context.getSystemService(Context.STORAGE_STATS_SERVICE);
+ ExternalStorageStats stats = null;
+ try {
+ stats =
+ ssm.queryExternalStatsForUser(
+ StorageManager.UUID_PRIVATE_INTERNAL,
+ UserHandle.of(context.getUserId()));
+ result.imagesSize = stats.getImageBytes();
+ result.videosSize = stats.getVideoBytes();
+ result.audioSize = stats.getAudioBytes();
+ result.miscSize =
+ stats.getTotalBytes()
+ - result.imagesSize
+ - result.videosSize
+ - result.audioSize;
+ } catch (IOException e) {
+ throw new IllegalStateException("Could not query storage");
+ }
+
+ return result;
+ }
+
+ /**
* Returns the size of a system for a given context. This is done by finding the difference
* between the shared data and the total primary storage size.
+ *
* @param context Context to use to get storage information.
*/
public static long getSystemSize(Context context) {
PackageManager pm = context.getPackageManager();
VolumeInfo primaryVolume = pm.getPrimaryStorageCurrentVolume();
- StorageManager sm = context.getSystemService(StorageManager.class);
+ StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
VolumeInfo shared = sm.findEmulatedForPrivate(primaryVolume);
if (shared == null) {
return 0;
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 0e183f0..9ef7410 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -39,7 +39,6 @@
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
@@ -47,7 +46,6 @@
import android.service.vr.IVrListener;
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
-import android.service.vr.IVrWindowManager;
import android.service.vr.VrListenerService;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -440,18 +438,6 @@
}
@Override
- public void connectController(FileDescriptor fd) throws android.os.RemoteException {
- enforceCallerPermission(Manifest.permission.RESTRICTED_VR_ACCESS);
- VrManagerService.this.connectController(fd);
- }
-
- @Override
- public void disconnectController() throws android.os.RemoteException {
- enforceCallerPermission(Manifest.permission.RESTRICTED_VR_ACCESS);
- VrManagerService.this.disconnectController();
- }
-
- @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -1184,20 +1170,4 @@
return mVrModeEnabled;
}
}
-
- private void connectController(FileDescriptor fd) throws android.os.RemoteException {
- // TODO(b/36506799): move vr_wm code to VrCore and remove this.
- IVrWindowManager remote =
- IVrWindowManager.Stub.asInterface(
- ServiceManager.getService(IVrWindowManager.SERVICE_NAME));
- remote.connectController(fd);
- }
-
- private void disconnectController() throws android.os.RemoteException {
- // TODO(b/36506799): move vr_wm code to VrCore and remove this.
- IVrWindowManager remote =
- IVrWindowManager.Stub.asInterface(
- ServiceManager.getService(IVrWindowManager.SERVICE_NAME));
- remote.disconnectController();
- }
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 7a36da2..a38addb 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -54,6 +54,7 @@
import android.graphics.Bitmap;
import android.graphics.Path;
import android.graphics.Rect;
+import android.os.Binder;
import android.os.Debug;
import android.os.IBinder;
import android.os.IRemoteCallback;
@@ -1808,27 +1809,25 @@
final IAppTransitionAnimationSpecsFuture future
= mNextAppTransitionAnimationsSpecsFuture;
mNextAppTransitionAnimationsSpecsFuture = null;
- mDefaultExecutor.execute(new Runnable() {
- @Override
- public void run() {
- AppTransitionAnimationSpec[] specs = null;
- try {
- specs = future.get();
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to fetch app transition specs: " + e);
- }
- synchronized (mService.mWindowMap) {
- mNextAppTransitionAnimationsSpecsPending = false;
- overridePendingAppTransitionMultiThumb(specs,
- mNextAppTransitionFutureCallback, null /* finishedCallback */,
- mNextAppTransitionScaleUp);
- mNextAppTransitionFutureCallback = null;
- if (specs != null) {
- mService.prolongAnimationsFromSpecs(specs, mNextAppTransitionScaleUp);
- }
- }
- mService.requestTraversal();
+ mDefaultExecutor.execute(() -> {
+ AppTransitionAnimationSpec[] specs = null;
+ try {
+ Binder.allowBlocking(future.asBinder());
+ specs = future.get();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to fetch app transition specs: " + e);
}
+ synchronized (mService.mWindowMap) {
+ mNextAppTransitionAnimationsSpecsPending = false;
+ overridePendingAppTransitionMultiThumb(specs,
+ mNextAppTransitionFutureCallback, null /* finishedCallback */,
+ mNextAppTransitionScaleUp);
+ mNextAppTransitionFutureCallback = null;
+ if (specs != null) {
+ mService.prolongAnimationsFromSpecs(specs, mNextAppTransitionScaleUp);
+ }
+ }
+ mService.requestTraversal();
});
}
}
diff --git a/services/core/jni/com_android_server_lights_LightsService.cpp b/services/core/jni/com_android_server_lights_LightsService.cpp
index 813dcf5..514e996 100644
--- a/services/core/jni/com_android_server_lights_LightsService.cpp
+++ b/services/core/jni/com_android_server_lights_LightsService.cpp
@@ -29,16 +29,46 @@
namespace android {
-using ILight = ::android::hardware::light::V2_0::ILight;
using Brightness = ::android::hardware::light::V2_0::Brightness;
using Flash = ::android::hardware::light::V2_0::Flash;
-using Type = ::android::hardware::light::V2_0::Type;
+using ILight = ::android::hardware::light::V2_0::ILight;
using LightState = ::android::hardware::light::V2_0::LightState;
using Status = ::android::hardware::light::V2_0::Status;
+using Type = ::android::hardware::light::V2_0::Type;
template<typename T>
using Return = ::android::hardware::Return<T>;
-static sp<ILight> gLight;
+class LightHal {
+private:
+ static sp<ILight> sLight;
+ static bool sLightInit;
+
+ LightHal() {}
+
+public:
+ static void disassociate() {
+ sLightInit = false;
+ sLight = nullptr;
+ }
+
+ static sp<ILight> associate() {
+ if ((sLight == nullptr && !sLightInit) ||
+ (sLight != nullptr && !sLight->ping().isOk())) {
+ // will return the hal if it exists the first time.
+ sLight = ILight::getService();
+ sLightInit = true;
+
+ if (sLight == nullptr) {
+ ALOGE("Unable to get ILight interface.");
+ }
+ }
+
+ return sLight;
+ }
+};
+
+sp<ILight> LightHal::sLight = nullptr;
+bool LightHal::sLightInit = false;
static bool validate(jint light, jint flash, jint brightness) {
bool valid = true;
@@ -103,7 +133,7 @@
const LightState &state) {
if (!ret.isOk()) {
ALOGE("Failed to issue set light command.");
- gLight = nullptr;
+ LightHal::disassociate();
return;
}
@@ -137,12 +167,9 @@
return;
}
- if (gLight == nullptr || !gLight->ping().isOk()) {
- gLight = ILight::getService();
- }
+ sp<ILight> hal = LightHal::associate();
- if (gLight == nullptr) {
- ALOGE("Unable to get ILight interface.");
+ if (hal == nullptr) {
return;
}
@@ -152,7 +179,7 @@
{
ALOGD_IF_SLOW(50, "Excessive delay setting light");
- Return<Status> ret = gLight->setLight(type, state);
+ Return<Status> ret = hal->setLight(type, state);
processReturn(ret, type, state);
}
}
diff --git a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
index 2e8b068..0cf4994 100644
--- a/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BadgeExtractorTest.java
@@ -69,7 +69,7 @@
Notification n = builder.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid,
mPid, n, mUser, null, System.currentTimeMillis());
- NotificationRecord r = new NotificationRecord(getContext(), sbn, channel, true);
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
return r;
}
diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 5e8b3d4..d4904f5 100644
--- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -259,7 +259,7 @@
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
mPid, n, mUser, null, System.currentTimeMillis());
- NotificationRecord r = new NotificationRecord(getContext(), sbn, channel, true);
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
mService.addNotification(r);
return r;
}
@@ -769,7 +769,7 @@
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, 0, mTag, mUid,
mPid, n, mUser, null, System.currentTimeMillis());
- NotificationRecord r = new NotificationRecord(getContext(), sbn, channel, true);
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
mService.addNotification(r);
mService.buzzBeepBlinkLocked(r);
diff --git a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
index 33d2d07..24cb72e 100644
--- a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
@@ -53,21 +53,21 @@
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
- "", 1499), getDefaultChannel(), true);
+ "", 1499), getDefaultChannel());
left.setGlobalSortKey("first");
NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
- "", 1499), getDefaultChannel(), true);
+ "", 1499), getDefaultChannel());
right.setGlobalSortKey("second");
NotificationRecord last = new NotificationRecord(InstrumentationRegistry.getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
- "", 1499), getDefaultChannel(), true);
+ "", 1499), getDefaultChannel());
final List<NotificationRecord> expected = new ArrayList<>();
@@ -93,13 +93,13 @@
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
- "", 1499), getDefaultChannel(), true);
+ "", 1499), getDefaultChannel());
NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
- "", 1499), getDefaultChannel(), true);
+ "", 1499), getDefaultChannel());
right.setGlobalSortKey("not null");
final List<NotificationRecord> expected = new ArrayList<>();
@@ -124,14 +124,14 @@
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
- "", 1499), getDefaultChannel(), true);
+ "", 1499), getDefaultChannel());
left.setGlobalSortKey("not null");
NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
new StatusBarNotification(PKG,
PKG, 1, "media", UID, UID, n,
new UserHandle(UserHandle.myUserId()),
- "", 1499), getDefaultChannel(), true);
+ "", 1499), getDefaultChannel());
final List<NotificationRecord> expected = new ArrayList<>();
expected.add(left);
diff --git a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
index 7a2dbaf..3dbd803 100644
--- a/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/ImportanceExtractorTest.java
@@ -71,7 +71,7 @@
Notification n = builder.build();
StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, mId, mTag, mUid,
mPid, n, mUser, null, System.currentTimeMillis());
- NotificationRecord r = new NotificationRecord(getContext(), sbn, channel, true);
+ NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
return r;
}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
index ccd2db0..84945ab 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationComparatorTest.java
@@ -108,7 +108,7 @@
.build();
mRecordMinCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
callPkg, 1, "minCall", callUid, callUid, n1,
- new UserHandle(userId), "", 2000), getDefaultChannel(), false);
+ new UserHandle(userId), "", 2000), getDefaultChannel());
mRecordMinCall.setUserImportance(NotificationManager.IMPORTANCE_MIN);
Notification n2 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
@@ -118,7 +118,7 @@
.build();
mRecordHighCall = new NotificationRecord(mContext, new StatusBarNotification(callPkg,
callPkg, 1, "highcall", callUid, callUid, n2,
- new UserHandle(userId), "", 1999), getDefaultChannel(), false);
+ new UserHandle(userId), "", 1999), getDefaultChannel());
mRecordHighCall.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
Notification n3 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
@@ -128,14 +128,14 @@
.build();
mRecordDefaultMedia = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "media", uid2, uid2, n3, new UserHandle(userId),
- "", 1499), getDefaultChannel(), false);
+ "", 1499), getDefaultChannel());
mRecordDefaultMedia.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
Notification n4 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
.setStyle(new Notification.MessagingStyle("sender!")).build();
mRecordInlineReply = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "inlinereply", uid2, uid2, n4, new UserHandle(userId),
- "", 1599), getDefaultChannel(), false);
+ "", 1599), getDefaultChannel());
mRecordInlineReply.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
mRecordInlineReply.setPackagePriority(Notification.PRIORITY_MAX);
@@ -143,27 +143,27 @@
.setCategory(Notification.CATEGORY_MESSAGE).build();
mRecordSms = new NotificationRecord(mContext, new StatusBarNotification(smsPkg,
smsPkg, 1, "sms", smsUid, smsUid, n5, new UserHandle(userId),
- "", 1299), getDefaultChannel(), false);
+ "", 1299), getDefaultChannel());
mRecordSms.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
Notification n6 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build();
mRecordStarredContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "starred", uid2, uid2, n6, new UserHandle(userId),
- "", 1259), getDefaultChannel(), false);
+ "", 1259), getDefaultChannel());
mRecordStarredContact.setContactAffinity(ValidateNotificationPeople.STARRED_CONTACT);
mRecordStarredContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
Notification n7 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build();
mRecordContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "contact", uid2, uid2, n7, new UserHandle(userId),
- "", 1259), getDefaultChannel(), false);
+ "", 1259), getDefaultChannel());
mRecordContact.setContactAffinity(ValidateNotificationPeople.VALID_CONTACT);
mRecordContact.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
Notification n8 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build();
mRecordUrgent = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "urgent", uid2, uid2, n8, new UserHandle(userId),
- "", 1258), getDefaultChannel(), false);
+ "", 1258), getDefaultChannel());
mRecordUrgent.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
Notification n9 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
@@ -173,7 +173,7 @@
.build();
mRecordCheater = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "cheater", uid2, uid2, n9, new UserHandle(userId),
- "", 9258), getDefaultChannel(), false);
+ "", 9258), getDefaultChannel());
mRecordCheater.setUserImportance(NotificationManager.IMPORTANCE_LOW);
mRecordCheater.setPackagePriority(Notification.PRIORITY_MAX);
@@ -181,7 +181,7 @@
.setStyle(new Notification.InboxStyle().setSummaryText("message!")).build();
mRecordEmail = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "email", uid2, uid2, n10, new UserHandle(userId),
- "", 1599), getDefaultChannel(), false);
+ "", 1599), getDefaultChannel());
mRecordEmail.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
Notification n11 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
@@ -190,7 +190,7 @@
.build();
mRecordCheaterColorized = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
pkg2, 1, "cheater", uid2, uid2, n11, new UserHandle(userId),
- "", 9258), getDefaultChannel(), false);
+ "", 9258), getDefaultChannel());
mRecordCheaterColorized.setUserImportance(NotificationManager.IMPORTANCE_LOW);
}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index f3eb4f8..177c02d 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -165,7 +165,7 @@
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id, "tag", uid, 0,
nb.build(), new UserHandle(uid), null, 0);
- return new NotificationRecord(mContext, sbn, channel, true);
+ return new NotificationRecord(mContext, sbn, channel);
}
private NotificationRecord generateNotificationRecord(NotificationChannel channel) {
return generateNotificationRecord(channel, null);
@@ -184,7 +184,7 @@
}
StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", uid, 0,
nb.build(), new UserHandle(uid), null, 0);
- return new NotificationRecord(mContext, sbn, channel, true);
+ return new NotificationRecord(mContext, sbn, channel);
}
@Test
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
index 66f3799..1c8ca84 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
@@ -21,6 +21,7 @@
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.when;
@@ -60,10 +61,6 @@
private final Context mMockContext = Mockito.mock(Context.class);
@Mock PackageManager mPm;
- // constants for targetSdk version. N is pre channels, O is post.
- private final boolean N = false;
- private final boolean O = true;
-
private final String pkg = "com.android.server.notification";
private final int uid = 9583;
private final String pkg2 = "pkg2";
@@ -79,6 +76,7 @@
new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "test",
NotificationManager.IMPORTANCE_UNSPECIFIED);
private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+ final ApplicationInfo legacy = new ApplicationInfo();
final ApplicationInfo upgrade = new ApplicationInfo();
private static final long[] CUSTOM_VIBRATION = new long[] {
@@ -102,13 +100,17 @@
InstrumentationRegistry.getContext().getResources());
when(mMockContext.getPackageManager()).thenReturn(mPm);
+ legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1;
upgrade.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1;
- when(mMockContext.getApplicationInfo()).thenReturn(upgrade);
+ try {
+ when(mPm.getApplicationInfoAsUser(eq(pkg), anyInt(), anyInt())).thenReturn(legacy);
+ when(mPm.getApplicationInfoAsUser(eq(pkg2), anyInt(), anyInt())).thenReturn(upgrade);
+ } catch (PackageManager.NameNotFoundException e) {}
}
- private StatusBarNotification getNotification(boolean supportsChannels, boolean noisy,
- boolean defaultSound, boolean buzzy, boolean defaultVibration, boolean lights,
- boolean defaultLights) {
+ private StatusBarNotification getNotification(boolean preO, boolean noisy, boolean defaultSound,
+ boolean buzzy, boolean defaultVibration, boolean lights, boolean defaultLights) {
+ when(mMockContext.getApplicationInfo()).thenReturn(preO ? legacy : upgrade);
final Builder builder = new Builder(mMockContext)
.setContentTitle("foo")
.setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -147,13 +149,13 @@
}
builder.setDefaults(defaults);
- if (supportsChannels) {
+ if (!preO) {
builder.setChannelId(channelId);
}
Notification n = builder.build();
- if (!supportsChannels) {
+ if (preO) {
return new StatusBarNotification(pkg, pkg, id1, tag1, uid, uid, n,
mUser, null, uid);
} else {
@@ -170,11 +172,11 @@
public void testSound_default_preUpgradeUsesNotification() throws Exception {
defaultChannel.setSound(null, null);
// pre upgrade, default sound.
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, record.getSound());
assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, record.getAudioAttributes());
}
@@ -183,11 +185,11 @@
public void testSound_custom_preUpgradeUsesNotification() throws Exception {
defaultChannel.setSound(null, null);
// pre upgrade, custom sound.
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(CUSTOM_SOUND, record.getSound());
assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes());
}
@@ -197,11 +199,11 @@
defaultChannel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
defaultChannel.lockFields(NotificationChannel.USER_LOCKED_SOUND);
// pre upgrade, default sound.
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(CUSTOM_SOUND, record.getSound());
assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes());
}
@@ -209,11 +211,11 @@
@Test
public void testSound_noSound_preUpgrade() throws Exception {
// pre upgrade, default sound.
- StatusBarNotification sbn = getNotification(N, false /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(null, record.getSound());
assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, record.getAudioAttributes());
}
@@ -222,11 +224,11 @@
public void testSound_default_upgradeUsesChannel() throws Exception {
channel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
// post upgrade, default sound.
- StatusBarNotification sbn = getNotification(O, true /* noisy */,
+ StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(CUSTOM_SOUND, record.getSound());
assertEquals(CUSTOM_ATTRIBUTES, record.getAudioAttributes());
}
@@ -235,11 +237,11 @@
public void testVibration_default_preUpgradeUsesNotification() throws Exception {
defaultChannel.enableVibration(false);
// pre upgrade, default vibration.
- StatusBarNotification sbn = getNotification(N, false /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
false /* defaultSound */, true /* buzzy */, true /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertNotNull(record.getVibration());
}
@@ -247,11 +249,11 @@
public void testVibration_custom_preUpgradeUsesNotification() throws Exception {
defaultChannel.enableVibration(false);
// pre upgrade, custom vibration.
- StatusBarNotification sbn = getNotification(N, false /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(CUSTOM_VIBRATION, record.getVibration());
}
@@ -260,11 +262,11 @@
defaultChannel.enableVibration(true);
defaultChannel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION);
// pre upgrade, custom vibration.
- StatusBarNotification sbn = getNotification(N, false /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertTrue(!Objects.equals(CUSTOM_VIBRATION, record.getVibration()));
}
@@ -272,20 +274,20 @@
public void testVibration_custom_upgradeUsesChannel() throws Exception {
channel.enableVibration(true);
// post upgrade, custom vibration.
- StatusBarNotification sbn = getNotification(O, false /* noisy */,
+ StatusBarNotification sbn = getNotification(false /*preO */, false /* noisy */,
false /* defaultSound */, true /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(CUSTOM_CHANNEL_VIBRATION, record.getVibration());
}
@Test
public void testImportance_preUpgrade() throws Exception {
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(NotificationManager.IMPORTANCE_HIGH, record.getImportance());
}
@@ -293,11 +295,11 @@
public void testImportance_locked_preUpgrade() throws Exception {
defaultChannel.setImportance(NotificationManager.IMPORTANCE_LOW);
defaultChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(NotificationManager.IMPORTANCE_LOW, record.getImportance());
}
@@ -305,39 +307,39 @@
public void testImportance_locked_unspecified_preUpgrade() throws Exception {
defaultChannel.setImportance(NotificationManager.IMPORTANCE_UNSPECIFIED);
defaultChannel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE);
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(NotificationManager.IMPORTANCE_HIGH, record.getImportance());
}
@Test
public void testImportance_upgrade() throws Exception {
- StatusBarNotification sbn = getNotification(O, true /* noisy */,
+ StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(NotificationManager.IMPORTANCE_DEFAULT, record.getImportance());
}
@Test
public void testLights_preUpgrade_noLight() throws Exception {
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertNull(record.getLight());
}
@Test
public void testLights_preUpgrade() throws Exception {
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
true /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertEquals(CUSTOM_LIGHT, record.getLight());
}
@@ -345,11 +347,11 @@
public void testLights_locked_preUpgrade() throws Exception {
defaultChannel.enableLights(true);
defaultChannel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS);
- StatusBarNotification sbn = getNotification(N, true /* noisy */,
+ StatusBarNotification sbn = getNotification(true /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
true /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, N);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertFalse(CUSTOM_LIGHT.equals(record.getLight()));
}
@@ -364,10 +366,10 @@
NotificationRecord.Light expected = new NotificationRecord.Light(
defaultLightColor, defaultLightOn, defaultLightOff);
- StatusBarNotification sbn = getNotification(O, true /* noisy */,
+ StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
true /* lights */, true /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(expected, record.getLight());
}
@@ -380,19 +382,19 @@
NotificationRecord.Light expected = new NotificationRecord.Light(
Color.BLUE, defaultLightOn, defaultLightOff);
- StatusBarNotification sbn = getNotification(O, true /* noisy */,
+ StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
true /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel, O);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel);
assertEquals(expected, record.getLight());
}
@Test
public void testLights_upgrade_noLight() throws Exception {
- StatusBarNotification sbn = getNotification(O, true /* noisy */,
+ StatusBarNotification sbn = getNotification(false /*preO */, true /* noisy */,
true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
false /* lights */, false /*defaultLights */);
- NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel, O);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
assertNull(record.getLight());
}
}
diff --git a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
index 8ab6d08..7bef033 100644
--- a/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/RankingHelperTest.java
@@ -124,7 +124,7 @@
.build();
mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification(
"package", "package", 1, null, 0, 0, mNotiGroupGSortA, user,
- null, System.currentTimeMillis()), getDefaultChannel(), false);
+ null, System.currentTimeMillis()), getDefaultChannel());
mNotiGroupGSortB = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
.setContentTitle("B")
@@ -134,7 +134,7 @@
.build();
mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification(
"package", "package", 1, null, 0, 0, mNotiGroupGSortB, user,
- null, System.currentTimeMillis()), getDefaultChannel(), false);
+ null, System.currentTimeMillis()), getDefaultChannel());
mNotiNoGroup = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
.setContentTitle("C")
@@ -142,7 +142,7 @@
.build();
mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification(
"package", "package", 1, null, 0, 0, mNotiNoGroup, user,
- null, System.currentTimeMillis()), getDefaultChannel(), false);
+ null, System.currentTimeMillis()), getDefaultChannel());
mNotiNoGroup2 = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
.setContentTitle("D")
@@ -150,7 +150,7 @@
.build();
mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification(
"package", "package", 1, null, 0, 0, mNotiNoGroup2, user,
- null, System.currentTimeMillis()), getDefaultChannel(), false);
+ null, System.currentTimeMillis()), getDefaultChannel());
mNotiNoGroupSortA = new Notification.Builder(getContext(), TEST_CHANNEL_ID)
.setContentTitle("E")
@@ -159,7 +159,7 @@
.build();
mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification(
"package", "package", 1, null, 0, 0, mNotiNoGroupSortA, user,
- null, System.currentTimeMillis()), getDefaultChannel(), false);
+ null, System.currentTimeMillis()), getDefaultChannel());
mAudioAttributes = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
@@ -373,7 +373,7 @@
assertNull(mHelper.getNotificationChannel(PKG, UID, channel1.getId(), false));
assertNull(mHelper.getNotificationChannel(PKG, UID, channel3.getId(), false));
assertNull(mHelper.getNotificationChannelGroup(ncg.getId(), PKG, UID));
- assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG, UID));
+ //assertEquals(ncg2, mHelper.getNotificationChannelGroup(ncg2.getId(), PKG, UID));
assertEquals(channel2, mHelper.getNotificationChannel(PKG, UID, channel2.getId(), false));
}
@@ -696,14 +696,20 @@
// Returns only non-deleted channels
List<NotificationChannel> channels =
mHelper.getNotificationChannels(PKG, UID, false).getList();
- assertEquals(1, channels.size());
- compareChannels(channel2, channels.get(0));
+ assertEquals(2, channels.size()); // Default channel + non-deleted channel
+ for (NotificationChannel nc : channels) {
+ if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
+ compareChannels(channel2, nc);
+ }
+ }
// Returns deleted channels too
channels = mHelper.getNotificationChannels(PKG, UID, true).getList();
- assertEquals(2, channels.size());
+ assertEquals(3, channels.size()); // Includes default channel
for (NotificationChannel nc : channels) {
- compareChannels(channelMap.get(nc.getId()), nc);
+ if (!NotificationChannel.DEFAULT_CHANNEL_ID.equals(nc.getId())) {
+ compareChannels(channelMap.get(nc.getId()), nc);
+ }
}
}
@@ -801,8 +807,8 @@
mHelper.permanentlyDeleteNotificationChannels(PKG, UID);
- // No channels remain
- assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
+ // Only default channel remains
+ assertEquals(1, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
}
@Test
@@ -881,32 +887,13 @@
mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
- // since this is a pre upgrade app, clearing data should restore the default channel
- assertEquals(1, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
- assertEquals(NotificationChannel.DEFAULT_CHANNEL_ID,
- mHelper.getNotificationChannels(PKG, UID, true).getList().get(0).getId());
+ assertEquals(0, mHelper.getNotificationChannels(PKG, UID, true).getList().size());
// Not deleted
mHelper.createNotificationChannel(PKG, UID, channel1, true);
+
mHelper.onPackagesChanged(false, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
- assertEquals(1, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
- }
-
- @Test
- public void testOnPackageChanged_packageRemoval_updatedPackage() throws Exception {
- // Deleted
- NotificationChannel channel1 =
- new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
- mHelper.createNotificationChannel(UPDATED_PKG, UID2, channel1, true);
- mHelper.onPackagesChanged(
- true, UserHandle.USER_SYSTEM, new String[]{UPDATED_PKG}, new int[]{UID2});
- assertEquals(0, mHelper.getNotificationChannels(UPDATED_PKG, UID2, true).getList().size());
-
- // Not deleted
- mHelper.createNotificationChannel(UPDATED_PKG, UID2, channel1, true);
- mHelper.onPackagesChanged(
- false, UserHandle.USER_SYSTEM, new String[]{UPDATED_PKG}, new int[]{UID2});
- assertEquals(1, mHelper.getNotificationChannels(UPDATED_PKG, UID2, false).getList().size());
+ assertEquals(2, mHelper.getNotificationChannels(PKG, UID, false).getList().size());
}
@Test
@@ -927,23 +914,7 @@
mHelper.onPackagesChanged(true, UserHandle.USER_SYSTEM, new String[]{PKG}, new int[]{UID});
- // default channel restored
- assertEquals(1, mHelper.getNotificationChannelGroups(PKG, UID, true).getList().size());
- assertNull(mHelper.getNotificationChannelGroups(PKG, UID, true).getList().get(0).getId());
- }
-
- @Test
- public void testOnPackageChanged_packageRemoval_groups_upgraded() throws Exception {
- NotificationChannelGroup ncg = new NotificationChannelGroup("group1", "name1");
- mHelper.createNotificationChannelGroup(UPDATED_PKG, UID2, ncg, true);
- NotificationChannelGroup ncg2 = new NotificationChannelGroup("group2", "name2");
- mHelper.createNotificationChannelGroup(UPDATED_PKG, UID2, ncg2, true);
-
- mHelper.onPackagesChanged(
- true, UserHandle.USER_SYSTEM, new String[]{UPDATED_PKG}, new int[]{UID2});
-
- assertEquals(0,
- mHelper.getNotificationChannelGroups(UPDATED_PKG, UID2, true).getList().size());
+ assertEquals(0, mHelper.getNotificationChannelGroups(PKG, UID, true).getList().size());
}
@Test
@@ -1017,8 +988,9 @@
assertEquals(3, actual.size());
for (NotificationChannelGroup group : actual) {
if (group.getId() == null) {
- assertEquals(1, group.getChannels().size());
- assertTrue(channel3.getId().equals(group.getChannels().get(0).getId()));
+ assertEquals(2, group.getChannels().size()); // misc channel too
+ assertTrue(channel3.getId().equals(group.getChannels().get(0).getId())
+ || channel3.getId().equals(group.getChannels().get(1).getId()));
} else if (group.getId().equals(ncg.getId())) {
assertEquals(2, group.getChannels().size());
if (group.getChannels().get(0).getId().equals(channel1.getId())) {
@@ -1052,8 +1024,12 @@
List<NotificationChannelGroup> actual =
mHelper.getNotificationChannelGroups(PKG, UID, true).getList();
- assertEquals(1, actual.size());
- assertEquals(1, actual.get(0).getChannels().size());
+ assertEquals(2, actual.size());
+ for (NotificationChannelGroup group : actual) {
+ if (Objects.equals(group.getId(), ncg.getId())) {
+ assertEquals(1, group.getChannels().size());
+ }
+ }
}
@Test
diff --git a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
index b1cb4d7..bc25860 100644
--- a/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/notification/src/com/android/server/notification/SnoozeHelperTest.java
@@ -312,7 +312,7 @@
TEST_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_LOW);
return new NotificationRecord(getContext(), new StatusBarNotification(
pkg, pkg, id, tag, 0, 0, n, user, null,
- System.currentTimeMillis()), notificationChannel, true);
+ System.currentTimeMillis()), notificationChannel);
}
private NotificationRecord getNotificationRecord(String pkg, int id, String tag,
diff --git a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java b/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
index 9a9c243..58a4456 100644
--- a/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NightDisplayServiceTest.java
@@ -16,10 +16,12 @@
package com.android.server;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.content.Context;
import android.content.ContextWrapper;
+import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Settings.Secure;
@@ -32,6 +34,7 @@
import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.display.DisplayTransformManager;
import com.android.server.display.NightDisplayService;
+import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
import org.junit.After;
@@ -41,6 +44,10 @@
import org.mockito.Mockito;
import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.Mockito.doReturn;
@@ -51,7 +58,7 @@
private Context mContext;
private int mUserId;
- private TwilightManager mTwilightManager;
+ private MockTwilightManager mTwilightManager;
private NightDisplayController mNightDisplayController;
private NightDisplayService mNightDisplayService;
@@ -73,7 +80,7 @@
final DisplayTransformManager dtm = Mockito.mock(DisplayTransformManager.class);
LocalServices.addService(DisplayTransformManager.class, dtm);
- mTwilightManager = Mockito.mock(TwilightManager.class);
+ mTwilightManager = new MockTwilightManager();
LocalServices.addService(TwilightManager.class, mTwilightManager);
mNightDisplayController = new NightDisplayController(mContext, mUserId);
@@ -526,23 +533,371 @@
assertActivated(true /* activated */);
}
- /**
- * Convenience for making a {@link LocalTime} instance with an offset relative to now.
- *
- * @param offsetMinutes the offset relative to now (in minutes)
- * @return the LocalTime instance
- */
- private LocalTime getLocalTimeRelativeToNow(int offsetMinutes) {
- final Calendar c = Calendar.getInstance();
- c.add(Calendar.MINUTE, offsetMinutes);
- return new LocalTime(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOffAfterNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOffBeforeNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(false /* activated */, -180 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOffDuringNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOffInFuture_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOnAfterNight_turnsOn() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOnBeforeNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(true /* activated */, -180 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOnDuringNight_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedAfterNight_ifOnInFuture_turnsOff() {
+ setAutoModeTwilight(-120 /* sunsetOffset */, -60 /* sunriseOffset */);
+ setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOffAfterNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(false /* activated */, 180 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOffBeforeNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOffDuringNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOffInPast_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOnAfterNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(true /* activated */, 180 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOnBeforeNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOnDuringNight_turnsOff() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedBeforeNight_ifOnInPast_turnsOn() {
+ setAutoModeTwilight(60 /* sunsetOffset */, 120 /* sunriseOffset */);
+ setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOffAfterNight_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(false /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOffBeforeNight_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(false /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOffDuringNightInFuture_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(false /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOffDuringNightInPast_turnsOff() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(false /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(false /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(false /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOnAfterNight_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(true /* activated */, 90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOnBeforeNight_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(true /* activated */, -90 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOnDuringNightInFuture_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(true /* activated */, 30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
+ }
+
+ @Test
+ public void twilightSchedule_whenRebootedDuringNight_ifOnDuringNightInPast_turnsOn() {
+ setAutoModeTwilight(-60 /* sunsetOffset */, 60 /* sunriseOffset */);
+ setActivated(true /* activated */, -30 /* lastActivatedTimeOffset */);
+
+ final TwilightState state = mTwilightManager.getLastTwilightState();
+ mTwilightManager.setTwilightState(null);
+
+ startService();
+ assertActivated(true /* activated */);
+
+ mTwilightManager.setTwilightState(state);
+ assertActivated(true /* activated */);
}
/**
* Configures Night display to use a custom schedule.
*
* @param startTimeOffset the offset relative to now to activate Night display (in minutes)
- * @param endTimeOffset the offset relative to now to deactivate Night display (in minutes)
+ * @param endTimeOffset the offset relative to now to deactivate Night display (in minutes)
*/
private void setAutoModeCustom(int startTimeOffset, int endTimeOffset) {
mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_CUSTOM);
@@ -553,34 +908,21 @@
/**
* Configures Night display to use the twilight schedule.
*
- * @param sunsetOffset the offset relative to now for sunset (in minutes)
+ * @param sunsetOffset the offset relative to now for sunset (in minutes)
* @param sunriseOffset the offset relative to now for sunrise (in minutes)
*/
private void setAutoModeTwilight(int sunsetOffset, int sunriseOffset) {
mNightDisplayController.setAutoMode(NightDisplayController.AUTO_MODE_TWILIGHT);
-
- final LocalTime sunset = getLocalTimeRelativeToNow(sunsetOffset);
- final LocalTime sunrise = getLocalTimeRelativeToNow(sunriseOffset);
-
- final Calendar now = Calendar.getInstance();
- long sunsetMillis = sunset.getDateTimeBefore(now).getTimeInMillis();
- long sunriseMillis = sunrise.getDateTimeBefore(now).getTimeInMillis();
- if (sunsetMillis < sunriseMillis) {
- sunsetMillis = sunset.getDateTimeAfter(now).getTimeInMillis();
- } else {
- sunriseMillis = sunrise.getDateTimeAfter(now).getTimeInMillis();
- }
-
- final TwilightState state = new TwilightState(sunriseMillis, sunsetMillis);
- doReturn(state).when(mTwilightManager).getLastTwilightState();
+ mTwilightManager.setTwilightState(
+ getTwilightStateRelativeToNow(sunsetOffset, sunriseOffset));
}
/**
* Configures the Night display activated state.
*
- * @param activated {@code true} if Night display should be activated
+ * @param activated {@code true} if Night display should be activated
* @param lastActivatedTimeOffset the offset relative to now to record that Night display was
- activated (in minutes)
+ * activated (in minutes)
*/
private void setActivated(boolean activated, int lastActivatedTimeOffset) {
mNightDisplayController.setActivated(activated);
@@ -617,4 +959,93 @@
.that(mNightDisplayController.isActivated())
.isEqualTo(activated);
}
+
+ /**
+ * Convenience for making a {@link LocalTime} instance with an offset relative to now.
+ *
+ * @param offsetMinutes the offset relative to now (in minutes)
+ * @return the LocalTime instance
+ */
+ private static LocalTime getLocalTimeRelativeToNow(int offsetMinutes) {
+ final Calendar c = Calendar.getInstance();
+ c.add(Calendar.MINUTE, offsetMinutes);
+ return new LocalTime(c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE));
+ }
+
+ /**
+ * Convenience for making a {@link TwilightState} instance with sunrise/sunset relative to now.
+ *
+ * @param sunsetOffset the offset relative to now for sunset (in minutes)
+ * @param sunriseOffset the offset relative to now for sunrise (in minutes)
+ * @return the TwilightState instance
+ */
+ private static TwilightState getTwilightStateRelativeToNow(int sunsetOffset,
+ int sunriseOffset) {
+ final LocalTime sunset = getLocalTimeRelativeToNow(sunsetOffset);
+ final LocalTime sunrise = getLocalTimeRelativeToNow(sunriseOffset);
+
+ final Calendar now = Calendar.getInstance();
+ long sunsetMillis = sunset.getDateTimeBefore(now).getTimeInMillis();
+ long sunriseMillis = sunrise.getDateTimeBefore(now).getTimeInMillis();
+ if (sunsetMillis < sunriseMillis) {
+ sunsetMillis = sunset.getDateTimeAfter(now).getTimeInMillis();
+ } else {
+ sunriseMillis = sunrise.getDateTimeAfter(now).getTimeInMillis();
+ }
+
+ return new TwilightState(sunriseMillis, sunsetMillis);
+ }
+
+ private static class MockTwilightManager implements TwilightManager {
+
+ private final Map<TwilightListener, Handler> mListeners = new HashMap<>();
+ private TwilightState mTwilightState;
+
+ /**
+ * Updates the TwilightState and notifies any registered listeners.
+ *
+ * @param state the new TwilightState to use
+ */
+ void setTwilightState(TwilightState state) {
+ synchronized (mListeners) {
+ mTwilightState = state;
+
+ final CountDownLatch latch = new CountDownLatch(mListeners.size());
+ for (Map.Entry<TwilightListener, Handler> entry : mListeners.entrySet()) {
+ entry.getValue().post(new Runnable() {
+ @Override
+ public void run() {
+ entry.getKey().onTwilightStateChanged(state);
+ latch.countDown();
+ }
+ });
+ }
+
+ try {
+ latch.await(5, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
+ public void registerListener(@NonNull TwilightListener listener, @NonNull Handler handler) {
+ synchronized (mListeners) {
+ mListeners.put(listener, handler);
+ }
+ }
+
+ @Override
+ public void unregisterListener(@NonNull TwilightListener listener) {
+ synchronized (mListeners) {
+ mListeners.remove(listener);
+ }
+ }
+
+ @Override
+ public TwilightState getLastTwilightState() {
+ return mTwilightState;
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index 4c7bf4d..f4944f9 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -7399,4 +7399,50 @@
"s21", "s22");
});
}
+
+ public void testReturnedByServer() {
+ // Package 1 updated, with manifest shortcuts.
+ addManifestShortcutResource(
+ new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
+ R.xml.shortcut_1);
+ updatePackageVersion(CALLING_PACKAGE_1, 1);
+ mService.mPackageMonitor.onReceive(getTestContext(),
+ genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(mManager.getManifestShortcuts())
+ .haveIds("ms1")
+ .forAllShortcuts(si -> assertTrue(si.isReturnedByServer()));
+
+ assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
+
+ assertWith(mManager.getDynamicShortcuts())
+ .haveIds("s1")
+ .forAllShortcuts(si -> assertTrue(si.isReturnedByServer()));
+ });
+
+ // Pin them.
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("ms1", "s1"), getCallingUser());
+ assertWith(getShortcutAsLauncher(USER_0))
+ .haveIds("ms1", "s1")
+ .forAllShortcuts(si -> assertTrue(si.isReturnedByServer()));
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertWith(mManager.getPinnedShortcuts())
+ .haveIds("ms1", "s1")
+ .forAllShortcuts(si -> assertTrue(si.isReturnedByServer()));
+ });
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ // This shows a warning log, but should still work.
+ assertTrue(mManager.setDynamicShortcuts(mManager.getDynamicShortcuts()));
+
+ assertWith(mManager.getDynamicShortcuts())
+ .haveIds("s1")
+ .forAllShortcuts(si -> assertTrue(si.isReturnedByServer()));
+ });
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java b/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
index 3789086..375edf3 100644
--- a/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/storage/DiskStatsLoggingServiceTest.java
@@ -20,16 +20,31 @@
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
-import static org.mockito.Mockito.eq;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.job.JobService;
-import android.app.job.JobParameters;
+import android.app.job.JobServiceEngine;
+import android.app.usage.ExternalStorageStats;
+import android.app.usage.StorageStatsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
+import android.os.BatteryManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.provider.Settings;
import android.test.AndroidTestCase;
+import android.test.mock.MockContentResolver;
+import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.storage.DiskStatsLoggingService.LogRunnable;
import libcore.io.IoUtils;
@@ -46,14 +61,17 @@
import java.io.File;
import java.io.PrintStream;
+import java.lang.reflect.Field;
import java.util.ArrayList;
@RunWith(JUnit4.class)
public class DiskStatsLoggingServiceTest extends AndroidTestCase {
@Rule public TemporaryFolder mTemporaryFolder;
@Rule public TemporaryFolder mDownloads;
- @Rule public TemporaryFolder mRootDirectory;
@Mock private AppCollector mCollector;
+ @Mock private JobService mJobService;
+ @Mock private StorageStatsManager mSsm;
+ private ExternalStorageStats mStorageStats;
private File mInputFile;
@@ -66,8 +84,10 @@
mInputFile = mTemporaryFolder.newFile();
mDownloads = new TemporaryFolder();
mDownloads.create();
- mRootDirectory = new TemporaryFolder();
- mRootDirectory.create();
+ mStorageStats = new ExternalStorageStats();
+ when(mSsm.queryExternalStatsForUser(isNull(String.class), any(UserHandle.class)))
+ .thenReturn(mStorageStats);
+ when(mJobService.getSystemService(anyString())).thenReturn(mSsm);
}
@Test
@@ -75,9 +95,9 @@
LogRunnable task = new LogRunnable();
task.setAppCollector(mCollector);
task.setDownloadsDirectory(mDownloads.getRoot());
- task.setRootDirectory(mRootDirectory.getRoot());
task.setLogOutputFile(mInputFile);
task.setSystemSize(0L);
+ task.setContext(mJobService);
task.run();
JSONObject json = getJsonOutput();
@@ -99,10 +119,10 @@
public void testPopulatedLogTask() throws Exception {
// Write data to directories.
writeDataToFile(mDownloads.newFile(), "lol");
- writeDataToFile(mRootDirectory.newFile("test.jpg"), "1234");
- writeDataToFile(mRootDirectory.newFile("test.mp4"), "12345");
- writeDataToFile(mRootDirectory.newFile("test.mp3"), "123456");
- writeDataToFile(mRootDirectory.newFile("test.whatever"), "1234567");
+ mStorageStats.audioBytes = 6L;
+ mStorageStats.imageBytes = 4L;
+ mStorageStats.videoBytes = 5L;
+ mStorageStats.totalBytes = 22L;
// Write apps.
ArrayList<PackageStats> apps = new ArrayList<>();
@@ -110,15 +130,16 @@
testApp.dataSize = 5L;
testApp.cacheSize = 55L;
testApp.codeSize = 10L;
+ testApp.userHandle = UserHandle.USER_SYSTEM;
apps.add(testApp);
- when(mCollector.getPackageStats(anyInt())).thenReturn(apps);
+ when(mCollector.getPackageStats(anyLong())).thenReturn(apps);
LogRunnable task = new LogRunnable();
task.setAppCollector(mCollector);
task.setDownloadsDirectory(mDownloads.getRoot());
- task.setRootDirectory(mRootDirectory.getRoot());
task.setLogOutputFile(mInputFile);
task.setSystemSize(10L);
+ task.setContext(mJobService);
task.run();
JSONObject json = getJsonOutput();
@@ -143,14 +164,57 @@
LogRunnable task = new LogRunnable();
task.setAppCollector(mCollector);
task.setDownloadsDirectory(mDownloads.getRoot());
- task.setRootDirectory(mRootDirectory.getRoot());
task.setLogOutputFile(mInputFile);
task.setSystemSize(10L);
+ task.setContext(mJobService);
task.run();
// No exception should be thrown.
}
+ @Test
+ public void testDontCrashOnRun() throws Exception {
+ DiskStatsLoggingService service = spy(new DiskStatsLoggingService());
+ BatteryManager batteryManager = mock(BatteryManager.class);
+ when(batteryManager.isCharging()).thenReturn(true);
+ doReturn(batteryManager).when(service).getSystemService(Context.BATTERY_SERVICE);
+ UserManager userManager = mock(UserManager.class);
+ when(userManager.getUsers()).thenReturn(new ArrayList<>());
+ doReturn(userManager).when(service).getSystemService(Context.USER_SERVICE);
+ doReturn(mSsm).when(service).getSystemService(Context.STORAGE_STATS_SERVICE);
+ doReturn(mock(StorageManager.class))
+ .when(service)
+ .getSystemService(Context.STORAGE_SERVICE);
+
+ MockContentResolver cr = new MockContentResolver();
+ cr.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ doReturn(cr).when(service).getContentResolver();
+
+ PackageManager pm = mock(PackageManager.class);
+ VolumeInfo volumeInfo =
+ new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL, VolumeInfo.TYPE_PRIVATE, null, null);
+ when(pm.getPrimaryStorageCurrentVolume()).thenReturn(volumeInfo);
+ doReturn(pm).when(service).getPackageManager();
+
+ doReturn(0).when(service).getUserId();
+
+ // UGGGGGHHHHHHH! jobFinished is a final method on JobService which crashes when called if
+ // the JobService isn't initialized for real. ServiceTestCase doesn't let us initialize a
+ // service which is built into the framework without crashing, though, so we can't make a
+ // real initialized service.
+ //
+ // And so, we use reflection to set the JobServiceEngine, which is used by the final method,
+ // to be something which won't crash when called.
+ final Field field = JobService.class.getDeclaredField("mEngine");
+ field.setAccessible(true);
+ field.set(service, mock(JobServiceEngine.class));
+
+ // Note: This won't clobber your on-device cache file because, technically,
+ // FrameworkServicesTests don't have write permission to actually overwrite the cache file.
+ // We log and fail on the write silently in this case.
+ service.onStartJob(null);
+ }
+
private void writeDataToFile(File f, String data) throws Exception{
PrintStream out = new PrintStream(f);
out.print(data);
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index e13665b..dae74db 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -16,6 +16,8 @@
package com.android.server.usage;
+import static com.android.internal.util.ArrayUtils.defeatNullable;
+
import android.app.AppOpsManager;
import android.app.usage.ExternalStorageStats;
import android.app.usage.IStorageStatsManager;
@@ -112,9 +114,12 @@
mStorage.registerListener(new StorageEventListener() {
@Override
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
- if ((vol.type == VolumeInfo.TYPE_PRIVATE)
- && (newState == VolumeInfo.STATE_MOUNTED)) {
- invalidateMounts();
+ switch (vol.type) {
+ case VolumeInfo.TYPE_PRIVATE:
+ case VolumeInfo.TYPE_EMULATED:
+ if (newState == VolumeInfo.STATE_MOUNTED) {
+ invalidateMounts();
+ }
}
}
});
@@ -178,9 +183,11 @@
long cacheBytes = 0;
final long token = Binder.clearCallingIdentity();
try {
- for (UserInfo user : mUser.getUsers()) {
- final StorageStats stats = queryStatsForUser(volumeUuid, user.id, null);
- cacheBytes += stats.cacheBytes;
+ if (isQuotaSupported(volumeUuid, callingPackage)) {
+ for (UserInfo user : mUser.getUsers()) {
+ final StorageStats stats = queryStatsForUser(volumeUuid, user.id, null);
+ cacheBytes += stats.cacheBytes;
+ }
}
} finally {
Binder.restoreCallingIdentity(token);
@@ -232,7 +239,7 @@
enforcePermission(Binder.getCallingUid(), callingPackage);
}
- if (mPackage.getPackagesForUid(appInfo.uid).length == 1) {
+ if (defeatNullable(mPackage.getPackagesForUid(appInfo.uid)).length == 1) {
// Only one package inside UID means we can fast-path
return queryStatsForUid(volumeUuid, appInfo.uid, callingPackage);
} else {
@@ -276,7 +283,7 @@
enforcePermission(Binder.getCallingUid(), callingPackage);
}
- final String[] packageNames = mPackage.getPackagesForUid(uid);
+ final String[] packageNames = defeatNullable(mPackage.getPackagesForUid(uid));
final long[] ceDataInodes = new long[packageNames.length];
String[] codePaths = new String[0];
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index b34fcdc..adf897d 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -336,14 +336,13 @@
*/
@Test
public void testCorrectLooperIsUsedForHandler() throws Exception {
- // record thread from looper.getThread and check ids.
TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback();
when(mWifiService.startLocalOnlyHotspot(any(Messenger.class), any(IBinder.class)))
.thenReturn(ERROR_INCOMPATIBLE_MODE);
mWifiManager.startLocalOnlyHotspot(callback, mHandler);
mLooper.dispatchAll();
assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
- assertEquals(mLooper.getLooper().getThread().getId(), callback.mCallingThreadId);
+ verify(mContext, never()).getMainLooper();
}
/**
@@ -362,6 +361,7 @@
altLooper.dispatchAll();
assertEquals(ERROR_INCOMPATIBLE_MODE, callback.mFailureReason);
assertEquals(altLooper.getLooper().getThread().getId(), callback.mCallingThreadId);
+ verify(mContext).getMainLooper();
}
/**
@@ -632,12 +632,11 @@
*/
@Test
public void testCorrectLooperIsUsedForObserverHandler() throws Exception {
- // record thread from looper.getThread and check ids.
TestLocalOnlyHotspotObserver observer = new TestLocalOnlyHotspotObserver();
mWifiManager.watchLocalOnlyHotspot(observer, mHandler);
mLooper.dispatchAll();
assertTrue(observer.mOnRegistered);
- assertEquals(mLooper.getLooper().getThread().getId(), observer.mCallingThreadId);
+ verify(mContext, never()).getMainLooper();
}
/**
@@ -654,6 +653,7 @@
altLooper.dispatchAll();
assertTrue(observer.mOnRegistered);
assertEquals(altLooper.getLooper().getThread().getId(), observer.mCallingThreadId);
+ verify(mContext).getMainLooper();
}
/**