diff options
81 files changed, 2038 insertions, 1035 deletions
diff --git a/Android.mk b/Android.mk index 313f329f50af..8a530eb479cc 100644 --- a/Android.mk +++ b/Android.mk @@ -609,6 +609,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \ android.hardware.usb-V1.1-java-constants \ android.hardware.vibrator-V1.0-java-constants \ android.hardware.vibrator-V1.1-java-constants \ + android.hardware.wifi-V1.0-java-constants \ # Loaded with System.loadLibrary by android.view.textclassifier LOCAL_REQUIRED_MODULES += libtextclassifier diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index df550807dd30..905a3ee9d25b 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -122,7 +122,6 @@ import com.android.internal.app.IVoiceInteractor; import com.android.internal.content.ReferrerIntent; import com.android.internal.os.BinderInternal; import com.android.internal.os.RuntimeInit; -import com.android.internal.os.SamplingProfilerIntegration; import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; @@ -1608,7 +1607,6 @@ public final class ActivityThread { handlePauseActivity((IBinder) args.arg1, false, (args.argi1 & USER_LEAVING) != 0, args.argi2, (args.argi1 & DONT_REPORT) != 0, args.argi3); - maybeSnapshot(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; case PAUSE_ACTIVITY_FINISHING: { @@ -1678,7 +1676,6 @@ public final class ActivityThread { case RECEIVER: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveComp"); handleReceiver((ReceiverData)msg.obj); - maybeSnapshot(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case CREATE_SERVICE: @@ -1704,7 +1701,6 @@ public final class ActivityThread { case STOP_SERVICE: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceStop"); handleStopService((IBinder)msg.obj); - maybeSnapshot(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case CONFIGURATION_CHANGED: @@ -1866,32 +1862,6 @@ public final class ActivityThread { } if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what)); } - - private void maybeSnapshot() { - if (mBoundApplication != null && SamplingProfilerIntegration.isEnabled()) { - // convert the *private* ActivityThread.PackageInfo to *public* known - // android.content.pm.PackageInfo - String packageName = mBoundApplication.info.mPackageName; - android.content.pm.PackageInfo packageInfo = null; - try { - Context context = getSystemContext(); - if(context == null) { - Log.e(TAG, "cannot get a valid context"); - return; - } - PackageManager pm = context.getPackageManager(); - if(pm == null) { - Log.e(TAG, "cannot get a valid PackageManager"); - return; - } - packageInfo = pm.getPackageInfo( - packageName, PackageManager.GET_ACTIVITIES); - } catch (NameNotFoundException e) { - Log.e(TAG, "cannot get package info for " + packageName, e); - } - SamplingProfilerIntegration.writeSnapshot(mBoundApplication.processName, packageInfo); - } - } } private class Idler implements MessageQueue.IdleHandler { @@ -6501,7 +6471,6 @@ public final class ActivityThread { public static void main(String[] args) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain"); - SamplingProfilerIntegration.start(); // CloseGuard defaults to true and can be quite spammy. We // disable it here, but selectively enable it later (via diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 03e9c0a1c9f7..4f9ed834318f 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -1171,7 +1171,7 @@ public class Notification implements Parcelable */ public static final int GROUP_ALERT_CHILDREN = 2; - private int mGroupAlertBehavior = GROUP_ALERT_CHILDREN; + private int mGroupAlertBehavior = GROUP_ALERT_ALL; /** * If this notification is being shown as a badge, always show as a number. diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 4cee2dfb66cb..99700df2b990 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -18,7 +18,11 @@ package android.content.pm; import android.content.ComponentName; import android.content.Intent; +import android.content.pm.PackageManager.ApplicationInfoFlags; +import android.content.pm.PackageManager.ComponentInfoFlags; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageManager.PackageInfoFlags; +import android.content.pm.PackageManager.ResolveInfoFlags; import android.os.Bundle; import android.util.SparseArray; @@ -133,16 +137,40 @@ public abstract class PackageManagerInternal { public abstract boolean isPermissionsReviewRequired(String packageName, int userId); /** - * Gets all of the information we know about a particular package. - * - * @param packageName The package name to find. - * @param userId The user under which to check. - * - * @return An {@link ApplicationInfo} containing information about the - * package, or {@code null} if no application exists with that - * package name. + * Retrieve all of the information we know about a particular package/application. + * @param filterCallingUid The results will be filtered in the context of this UID instead + * of the calling UID. + * @see PackageManager#getPackageInfo(String, int) + */ + public abstract PackageInfo getPackageInfo(String packageName, + @PackageInfoFlags int flags, int filterCallingUid, int userId); + + /** + * Retrieve all of the information we know about a particular package/application. + * @param filterCallingUid The results will be filtered in the context of this UID instead + * of the calling UID. + * @see PackageManager#getApplicationInfo(String, int) + */ + public abstract ApplicationInfo getApplicationInfo(String packageName, + @ApplicationInfoFlags int flags, int filterCallingUid, int userId); + + /** + * Retrieve all of the information we know about a particular activity class. + * @param filterCallingUid The results will be filtered in the context of this UID instead + * of the calling UID. + * @see PackageManager#getActivityInfo(ComponentName, int) + */ + public abstract ActivityInfo getActivityInfo(ComponentName component, + @ComponentInfoFlags int flags, int filterCallingUid, int userId); + + /** + * Retrieve all activities that can be performed for the given intent. + * @param filterCallingUid The results will be filtered in the context of this UID instead + * of the calling UID. + * @see PackageManager#queryIntentActivities(Intent, int) */ - public abstract ApplicationInfo getApplicationInfo(String packageName, int userId); + public abstract List<ResolveInfo> queryIntentActivities(Intent intent, + @ResolveInfoFlags int flags, int filterCallingUid, int userId); /** * Interface to {@link com.android.server.pm.PackageManagerService#getHomeActivitiesAsUser}. diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 417a95fbc417..88c1627f955b 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -16,7 +16,10 @@ package android.content.res; +import android.graphics.Point; import android.graphics.Rect; +import android.util.DisplayMetrics; +import android.view.Display; import android.view.DisplayInfo; import com.android.internal.util.XmlUtils; @@ -42,12 +45,6 @@ import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Locale; -import static android.view.Surface.ROTATION_UNDEFINED; -import static android.view.Surface.ROTATION_0; -import static android.view.Surface.ROTATION_90; -import static android.view.Surface.ROTATION_180; -import static android.view.Surface.ROTATION_270; - /** * This class describes all device configuration information that can * impact the resources the application retrieves. This includes both @@ -603,13 +600,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration */ public int orientation; - /** - * The rotation used at the time orientation was determined. - * TODO(b/36812336): Move rotation out of {@link Configuration}. - * {@hide} - */ - private int rotation; - /** Constant for {@link #uiMode}: bits that encode the mode type. */ public static final int UI_MODE_TYPE_MASK = 0x0f; /** Constant for {@link #uiMode}: a {@link #UI_MODE_TYPE_MASK} @@ -897,7 +887,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration navigation = o.navigation; navigationHidden = o.navigationHidden; orientation = o.orientation; - rotation = o.rotation; screenLayout = o.screenLayout; colorMode = o.colorMode; uiMode = o.uiMode; @@ -1001,14 +990,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration case ORIENTATION_PORTRAIT: sb.append(" port"); break; default: sb.append(" orien="); sb.append(orientation); break; } - switch (rotation) { - case ROTATION_UNDEFINED: sb.append(" ?rotation"); break; - case ROTATION_0: sb.append(" rot0"); break; - case ROTATION_90: sb.append(" rot90"); break; - case ROTATION_180: sb.append(" rot180"); break; - case ROTATION_270: sb.append(" rot270"); break; - default: sb.append(" rot="); sb.append(rotation); break; - } switch ((uiMode&UI_MODE_TYPE_MASK)) { case UI_MODE_TYPE_UNDEFINED: sb.append(" ?uimode"); break; case UI_MODE_TYPE_NORMAL: /* normal is not interesting to print */ break; @@ -1096,7 +1077,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration navigation = NAVIGATION_UNDEFINED; navigationHidden = NAVIGATIONHIDDEN_UNDEFINED; orientation = ORIENTATION_UNDEFINED; - rotation = ROTATION_UNDEFINED; screenLayout = SCREENLAYOUT_UNDEFINED; colorMode = COLOR_MODE_UNDEFINED; uiMode = UI_MODE_TYPE_UNDEFINED; @@ -1205,11 +1185,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration changed |= ActivityInfo.CONFIG_ORIENTATION; orientation = delta.orientation; } - if (delta.rotation != ROTATION_UNDEFINED - && rotation != delta.rotation) { - changed |= ActivityInfo.CONFIG_ORIENTATION; - rotation = delta.rotation; - } if (((delta.screenLayout & SCREENLAYOUT_SIZE_MASK) != SCREENLAYOUT_SIZE_UNDEFINED) && (delta.screenLayout & SCREENLAYOUT_SIZE_MASK) @@ -1404,10 +1379,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration && orientation != delta.orientation) { changed |= ActivityInfo.CONFIG_ORIENTATION; } - if ((compareUndefined || delta.rotation != ROTATION_UNDEFINED) - && rotation != delta.rotation) { - changed |= ActivityInfo.CONFIG_ORIENTATION; - } if ((compareUndefined || getScreenLayoutNoDirection(delta.screenLayout) != (SCREENLAYOUT_SIZE_UNDEFINED | SCREENLAYOUT_LONG_UNDEFINED)) && getScreenLayoutNoDirection(screenLayout) != @@ -1544,7 +1515,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration dest.writeInt(navigation); dest.writeInt(navigationHidden); dest.writeInt(orientation); - dest.writeInt(rotation); dest.writeInt(screenLayout); dest.writeInt(colorMode); dest.writeInt(uiMode); @@ -1581,7 +1551,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration navigation = source.readInt(); navigationHidden = source.readInt(); orientation = source.readInt(); - rotation = source.readInt(); screenLayout = source.readInt(); colorMode = source.readInt(); uiMode = source.readInt(); @@ -1666,8 +1635,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration if (n != 0) return n; n = this.orientation - that.orientation; if (n != 0) return n; - n = this.rotation - that.rotation; - if (n != 0) return n; n = this.colorMode - that.colorMode; if (n != 0) return n; n = this.screenLayout - that.screenLayout; @@ -1799,24 +1766,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration /** * @hide * - * Setter for orientation converts from {@link Surface} values to internal representation. - */ - public void setRotation(int rotation) { - this.rotation = rotation; - } - - /** - * @hide - * - * Getter for orientation. Converts from internal representation to {@link Surface} values. - */ - public int getRotation() { - return rotation != ROTATION_UNDEFINED ? rotation : ROTATION_0; - } - - /** - * @hide - * * Clears the locale without changing layout direction. */ public void clearLocales() { @@ -2051,23 +2000,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration break; } - switch (config.rotation) { - case ROTATION_0: - parts.add("rot0"); - break; - case ROTATION_90: - parts.add("rot90"); - break; - case ROTATION_180: - parts.add("rot180"); - break; - case ROTATION_270: - parts.add("rot270"); - break; - default: - break; - } - switch (config.uiMode & Configuration.UI_MODE_TYPE_MASK) { case Configuration.UI_MODE_TYPE_APPLIANCE: parts.add("appliance"); @@ -2262,10 +2194,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration delta.orientation = change.orientation; } - if (base.rotation != change.rotation) { - base.rotation = change.rotation; - } - if ((base.screenLayout & SCREENLAYOUT_SIZE_MASK) != (change.screenLayout & SCREENLAYOUT_SIZE_MASK)) { delta.screenLayout |= change.screenLayout & SCREENLAYOUT_SIZE_MASK; @@ -2337,7 +2265,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration private static final String XML_ATTR_NAVIGATION = "nav"; private static final String XML_ATTR_NAVIGATION_HIDDEN = "navHid"; private static final String XML_ATTR_ORIENTATION = "ori"; - private static final String XML_ATTR_ROTATION = "rot"; private static final String XML_ATTR_SCREEN_LAYOUT = "scrLay"; private static final String XML_ATTR_COLOR_MODE = "clrMod"; private static final String XML_ATTR_UI_MODE = "ui"; @@ -2397,8 +2324,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration DENSITY_DPI_UNDEFINED); configOut.appBounds = Rect.unflattenFromString(XmlUtils.readStringAttribute(parser, XML_ATTR_APP_BOUNDS)); - configOut.rotation = XmlUtils.readIntAttribute(parser, XML_ATTR_ROTATION, - ROTATION_UNDEFINED); // For persistence, we don't care about assetsSeq, so do not read it out. } @@ -2475,10 +2400,6 @@ public final class Configuration implements Parcelable, Comparable<Configuration config.appBounds.flattenToString()); } - if (config.rotation != ROTATION_UNDEFINED) { - XmlUtils.writeIntAttribute(xml, XML_ATTR_ROTATION, config.rotation); - } - // For persistence, we do not care about assetsSeq, so do not write it out. } } diff --git a/core/java/android/net/NetworkBadging.java b/core/java/android/net/NetworkBadging.java index 6de28b7146e8..c119b637140f 100644 --- a/core/java/android/net/NetworkBadging.java +++ b/core/java/android/net/NetworkBadging.java @@ -66,15 +66,7 @@ public class NetworkBadging { */ @NonNull public static Drawable getWifiIcon( @IntRange(from=0, to=4) int signalLevel, @Badging int badging, @Nullable Theme theme) { - Resources resources = Resources.getSystem(); - if (badging == BADGING_NONE) { - return resources.getDrawable(getWifiSignalResource(signalLevel), theme); - } - Drawable[] layers = new Drawable[] { - resources.getDrawable(getBadgedWifiSignalResource(signalLevel), theme), - resources.getDrawable(getWifiBadgeResource(badging), theme) - }; - return new LayerDrawable(layers); + return Resources.getSystem().getDrawable(getWifiSignalResource(signalLevel), theme); } /** @@ -134,31 +126,4 @@ public class NetworkBadging { throw new IllegalArgumentException("Invalid signal level: " + signalLevel); } } - - /** - * Returns the wifi quality badge resource id for the the given badging balue. - * - * <p>This badge should be displayed with the badge signal resource retrieved from - * {@link #getBadgedWifiSignalResource(int)}. - * - * @param badging {@see NetworkBadging#Badging} from {@link ScoredNetwork#calculateBadge(int)}. - * @return the @DrawableRes for the icon or {@link View#NO_ID} for - * {@link NetworkBadging#BADGING_NONE} - * @throws IllegalArgumentException for an invalid badging value. - * @hide - */ - @DrawableRes private static int getWifiBadgeResource(@Badging int badging) { - switch (badging) { - case BADGING_NONE: - return View.NO_ID; - case BADGING_SD: - return com.android.internal.R.drawable.ic_signal_wifi_badged_sd; - case BADGING_HD: - return com.android.internal.R.drawable.ic_signal_wifi_badged_hd; - case BADGING_4K: - return com.android.internal.R.drawable.ic_signal_wifi_badged_4k; - default: - throw new IllegalArgumentException("No resource found for badge: " + badging); - } - } } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 162dddb1f797..9bea403c3642 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -201,7 +201,7 @@ public abstract class BatteryStats implements Parcelable { * New in version 22: * - BLE scan result background count, BLE unoptimized scan time */ - static final String CHECKIN_VERSION = "22"; + static final String CHECKIN_VERSION = "23"; /** * Old version, we hit 9 and ran out of room, need to remove. @@ -3054,6 +3054,7 @@ public abstract class BatteryStats implements Parcelable { boolean wifiOnly) { final long rawUptime = SystemClock.uptimeMillis() * 1000; final long rawRealtime = SystemClock.elapsedRealtime() * 1000; + final long rawRealtimeMs = (rawRealtime + 500) / 1000; final long batteryUptime = getBatteryUptime(rawUptime); final long whichBatteryUptime = computeBatteryUptime(rawUptime, which); final long whichBatteryRealtime = computeBatteryRealtime(rawRealtime, which); @@ -3440,7 +3441,6 @@ public abstract class BatteryStats implements Parcelable { final int count = bleTimer.getCountLocked(which); final Timer bleTimerBg = u.getBluetoothScanBackgroundTimer(); final int countBg = bleTimerBg != null ? bleTimerBg.getCountLocked(which) : 0; - final long rawRealtimeMs = (rawRealtime + 500) / 1000; // 'actualTime' are unpooled and always since reset (regardless of 'which') final long actualTime = bleTimer.getTotalDurationMsLocked(rawRealtimeMs); final long actualTimeBg = bleTimerBg != null ? @@ -3489,11 +3489,11 @@ public abstract class BatteryStats implements Parcelable { if (u.getAggregatedPartialWakelockTimer() != null) { final Timer timer = u.getAggregatedPartialWakelockTimer(); - // Convert from microseconds to milliseconds with rounding - final long totTimeMs = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000; + // Times are since reset (regardless of 'which') + final long totTimeMs = timer.getTotalDurationMsLocked(rawRealtimeMs); final Timer bgTimer = timer.getSubTimer(); final long bgTimeMs = bgTimer != null ? - (bgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : 0; + bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0; dumpLine(pw, uid, category, AGGREGATED_WAKELOCK_DATA, totTimeMs, bgTimeMs); } @@ -3530,7 +3530,7 @@ public abstract class BatteryStats implements Parcelable { final int count = timer.getCountLocked(which); final Timer bgTimer = timer.getSubTimer(); final long bgTime = bgTimer != null ? - (bgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : -1; + bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : -1; final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1; if (totalTime != 0) { dumpLine(pw, uid, category, SYNC_DATA, "\"" + syncs.keyAt(isy) + "\"", @@ -3546,7 +3546,7 @@ public abstract class BatteryStats implements Parcelable { final int count = timer.getCountLocked(which); final Timer bgTimer = timer.getSubTimer(); final long bgTime = bgTimer != null ? - (bgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : -1; + bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : -1; final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1; if (totalTime != 0) { dumpLine(pw, uid, category, JOB_DATA, "\"" + jobs.keyAt(ij) + "\"", @@ -3577,7 +3577,6 @@ public abstract class BatteryStats implements Parcelable { final int count = timer.getCountLocked(which); final Timer bgTimer = se.getSensorBackgroundTime(); final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : 0; - final long rawRealtimeMs = (rawRealtime + 500) / 1000; // 'actualTime' are unpooled and always since reset (regardless of 'which') final long actualTime = timer.getTotalDurationMsLocked(rawRealtimeMs); final long bgActualTime = bgTimer != null ? @@ -3718,6 +3717,7 @@ public abstract class BatteryStats implements Parcelable { int reqUid, boolean wifiOnly) { final long rawUptime = SystemClock.uptimeMillis() * 1000; final long rawRealtime = SystemClock.elapsedRealtime() * 1000; + final long rawRealtimeMs = (rawRealtime + 500) / 1000; final long batteryUptime = getBatteryUptime(rawUptime); final long whichBatteryUptime = computeBatteryUptime(rawUptime, which); @@ -4658,7 +4658,6 @@ public abstract class BatteryStats implements Parcelable { final int count = bleTimer.getCountLocked(which); final Timer bleTimerBg = u.getBluetoothScanBackgroundTimer(); final int countBg = bleTimerBg != null ? bleTimerBg.getCountLocked(which) : 0; - final long rawRealtimeMs = (rawRealtime + 500) / 1000; // 'actualTime' are unpooled and always since reset (regardless of 'which') final long actualTimeMs = bleTimer.getTotalDurationMsLocked(rawRealtimeMs); final long actualTimeMsBg = bleTimerBg != null ? @@ -4826,10 +4825,10 @@ public abstract class BatteryStats implements Parcelable { final Timer aggTimer = u.getAggregatedPartialWakelockTimer(); // Convert from microseconds to milliseconds with rounding actualTotalPartialWakelock = - (aggTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000; + aggTimer.getTotalDurationMsLocked(rawRealtimeMs); final Timer bgAggTimer = aggTimer.getSubTimer(); actualBgPartialWakelock = bgAggTimer != null ? - (bgAggTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : 0; + bgAggTimer.getTotalDurationMsLocked(rawRealtimeMs) : 0; } if (actualTotalPartialWakelock != 0 || actualBgPartialWakelock != 0 || @@ -4897,7 +4896,7 @@ public abstract class BatteryStats implements Parcelable { final int count = timer.getCountLocked(which); final Timer bgTimer = timer.getSubTimer(); final long bgTime = bgTimer != null ? - (bgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : -1; + bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : -1; final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1; sb.setLength(0); sb.append(prefix); @@ -4931,7 +4930,7 @@ public abstract class BatteryStats implements Parcelable { final int count = timer.getCountLocked(which); final Timer bgTimer = timer.getSubTimer(); final long bgTime = bgTimer != null ? - (bgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : -1; + bgTimer.getTotalDurationMsLocked(rawRealtimeMs) : -1; final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1; sb.setLength(0); sb.append(prefix); @@ -4990,7 +4989,6 @@ public abstract class BatteryStats implements Parcelable { final int count = timer.getCountLocked(which); final Timer bgTimer = se.getSensorBackgroundTime(); final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : 0; - final long rawRealtimeMs = (rawRealtime + 500) / 1000; // 'actualTime' are unpooled and always since reset (regardless of 'which') final long actualTime = timer.getTotalDurationMsLocked(rawRealtimeMs); final long bgActualTime = bgTimer != null ? diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index a6cdb03c8b67..504673529238 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1403,23 +1403,7 @@ public class StorageManager { /** {@hide} */ public static File maybeTranslateEmulatedPathToInternal(File path) { - final IStorageManager storageManager = IStorageManager.Stub.asInterface( - ServiceManager.getService("mount")); - try { - final VolumeInfo[] vols = storageManager.getVolumes(0); - for (VolumeInfo vol : vols) { - if ((vol.getType() == VolumeInfo.TYPE_EMULATED - || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) { - final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(), - vol.getInternalPath(), path); - if (internalPath != null && internalPath.exists()) { - return internalPath; - } - } - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + // Disabled now that FUSE has been replaced by sdcardfs return path; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ba2f7f060b82..f04e70d0ee08 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6811,6 +6811,13 @@ public final class Settings { public static final String ASSIST_GESTURE_SENSITIVITY = "assist_gesture_sensitivity"; /** + * Whether Assist Gesture Deferred Setup has been completed + * + * @hide + */ + public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete"; + + /** * Control whether Night display is currently activated. * @hide */ diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index e4ed62a1ead9..d3f698286ab1 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -986,7 +986,17 @@ class TextLine { return 0f; } + final boolean needsSpanMeasurement; if (mSpanned == null) { + needsSpanMeasurement = false; + } else { + mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit); + mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit); + needsSpanMeasurement = mMetricAffectingSpanSpanSet.numberOfSpans != 0 + || mCharacterStyleSpanSet.numberOfSpans != 0; + } + + if (!needsSpanMeasurement) { final TextPaint wp = mWorkPaint; wp.set(mPaint); wp.setHyphenEdit(adjustHyphenEdit(start, limit, wp.getHyphenEdit())); @@ -994,9 +1004,6 @@ class TextLine { y, bottom, fmi, needWidth, measureLimit, null); } - mMetricAffectingSpanSpanSet.init(mSpanned, mStart + start, mStart + limit); - mCharacterStyleSpanSet.init(mSpanned, mStart + start, mStart + limit); - // Shaping needs to take into account context up to metric boundaries, // but rendering needs to take into account character style boundaries. // So we iterate through metric runs to get metric bounds, diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 286e79055448..51d65144f260 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -96,7 +96,7 @@ interface IWindowSession { int flags, out Rect outFrame, out Rect outOverscanInsets, out Rect outContentInsets, out Rect outVisibleInsets, out Rect outStableInsets, out Rect outOutsets, out Rect outBackdropFrame, - inout MergedConfiguration mergedConfiguration, out Surface outSurface); + out MergedConfiguration outMergedConfiguration, out Surface outSurface); /* * Notify the window manager that an application is relaunching and diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 0ad591be2cf8..8bb3fa988a45 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -129,17 +129,11 @@ public class Surface implements Parcelable { public static final int SCALING_MODE_NO_SCALE_CROP = 3; /** @hide */ - @IntDef({ROTATION_UNDEFINED, ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) + @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) @Retention(RetentionPolicy.SOURCE) public @interface Rotation {} /** - * Rotation constant: undefined - * @hide - */ - public static final int ROTATION_UNDEFINED = -1; - - /** * Rotation constant: 0 degree rotation (natural orientation) */ public static final int ROTATION_0 = 0; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index be0bd840c8fa..4a231efcdd07 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -1850,12 +1850,8 @@ public final class ViewRootImpl implements ViewParent, final boolean isViewVisible = viewVisibility == View.VISIBLE; final boolean windowRelayoutWasForced = mForceNextWindowRelayout; - final int contextConfigSeq = mContext.getResources().getConfiguration().seq; - final int lastConfigSeq = mLastReportedMergedConfiguration.getMergedConfiguration().seq; - final boolean staleConfig = lastConfigSeq != 0 && contextConfigSeq != lastConfigSeq; - - if (mFirst || windowShouldResize || insetsChanged || staleConfig || viewVisibilityChanged - || params != null || mForceNextWindowRelayout) { + if (mFirst || windowShouldResize || insetsChanged || + viewVisibilityChanged || params != null || mForceNextWindowRelayout) { mForceNextWindowRelayout = false; if (isViewVisible) { @@ -6092,13 +6088,7 @@ public final class ViewRootImpl implements ViewParent, if (params != null) { if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params); } - - if (mPendingMergedConfiguration.getMergedConfiguration().seq == 0) { - mPendingMergedConfiguration.setTo(mLastReportedMergedConfiguration); - } - - int initialConfigSeq = mPendingMergedConfiguration.getMergedConfiguration().seq; - + mPendingMergedConfiguration.getMergedConfiguration().seq = 0; //Log.d(mTag, ">>>>>> CALLING relayout"); if (params != null && mOrigWindowType != params.type) { // For compatibility with old apps, don't crash here. @@ -6117,10 +6107,6 @@ public final class ViewRootImpl implements ViewParent, mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingMergedConfiguration, mSurface); - if (initialConfigSeq == mPendingMergedConfiguration.getMergedConfiguration().seq) { - mPendingMergedConfiguration.getMergedConfiguration().seq = 0; - } - mPendingAlwaysConsumeNavBar = (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0; diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index cbe4c15cf790..2363f6820da4 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -3525,6 +3525,7 @@ public class BatteryStatsImpl extends BatteryStats { public void removeIsolatedUidLocked(int isolatedUid) { mIsolatedUids.delete(isolatedUid); mKernelUidCpuTimeReader.removeUid(isolatedUid); + mKernelUidCpuFreqTimeReader.removeUid(isolatedUid); } public int mapUid(int uid) { @@ -10358,6 +10359,9 @@ public class BatteryStatsImpl extends BatteryStats { public void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs) { uid = mapUid(uid); if (Process.isIsolated(uid)) { + mKernelUidCpuFreqTimeReader.removeUid(uid); + Slog.d(TAG, "Got freq readings for an isolated uid with" + + " no mapping to owning uid: " + uid); return; } final Uid u = getUidStatsLocked(uid); @@ -11026,6 +11030,7 @@ public class BatteryStatsImpl extends BatteryStats { */ public void removeUidStatsLocked(int uid) { mKernelUidCpuTimeReader.removeUid(uid); + mKernelUidCpuFreqTimeReader.removeUid(uid); mUidStats.remove(uid); } diff --git a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java index 9fbc4a8871b3..ff521c242fad 100644 --- a/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java +++ b/core/java/com/android/internal/os/KernelUidCpuFreqTimeReader.java @@ -72,6 +72,10 @@ public class KernelUidCpuFreqTimeReader { } } + public void removeUid(int uid) { + mLastUidCpuFreqTimeMs.delete(uid); + } + @VisibleForTesting public void readDelta(BufferedReader reader, @Nullable Callback callback) throws IOException { String line = reader.readLine(); diff --git a/core/java/com/android/internal/os/SamplingProfilerIntegration.java b/core/java/com/android/internal/os/SamplingProfilerIntegration.java deleted file mode 100644 index 6429aa420fd0..000000000000 --- a/core/java/com/android/internal/os/SamplingProfilerIntegration.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.internal.os; - -import android.content.pm.PackageInfo; -import android.os.Build; -import android.os.SystemProperties; -import android.util.Log; -import dalvik.system.profiler.BinaryHprofWriter; -import dalvik.system.profiler.SamplingProfiler; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.io.PrintStream; -import java.util.Date; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicBoolean; -import libcore.io.IoUtils; - -/** - * Integrates the framework with Dalvik's sampling profiler. - */ -public class SamplingProfilerIntegration { - - private static final String TAG = "SamplingProfilerIntegration"; - - public static final String SNAPSHOT_DIR = "/data/snapshots"; - - private static final boolean enabled; - private static final Executor snapshotWriter; - private static final int samplingProfilerMilliseconds; - private static final int samplingProfilerDepth; - - /** Whether or not a snapshot is being persisted. */ - private static final AtomicBoolean pending = new AtomicBoolean(false); - - static { - samplingProfilerMilliseconds = SystemProperties.getInt("persist.sys.profiler_ms", 0); - samplingProfilerDepth = SystemProperties.getInt("persist.sys.profiler_depth", 4); - if (samplingProfilerMilliseconds > 0) { - File dir = new File(SNAPSHOT_DIR); - dir.mkdirs(); - // the directory needs to be writable to anybody to allow file writing - dir.setWritable(true, false); - // the directory needs to be executable to anybody to allow file creation - dir.setExecutable(true, false); - if (dir.isDirectory()) { - snapshotWriter = Executors.newSingleThreadExecutor(new ThreadFactory() { - public Thread newThread(Runnable r) { - return new Thread(r, TAG); - } - }); - enabled = true; - Log.i(TAG, "Profiling enabled. Sampling interval ms: " - + samplingProfilerMilliseconds); - } else { - snapshotWriter = null; - enabled = true; - Log.w(TAG, "Profiling setup failed. Could not create " + SNAPSHOT_DIR); - } - } else { - snapshotWriter = null; - enabled = false; - Log.i(TAG, "Profiling disabled."); - } - } - - private static SamplingProfiler samplingProfiler; - private static long startMillis; - - /** - * Is profiling enabled? - */ - public static boolean isEnabled() { - return enabled; - } - - /** - * Starts the profiler if profiling is enabled. - */ - public static void start() { - if (!enabled) { - return; - } - if (samplingProfiler != null) { - Log.e(TAG, "SamplingProfilerIntegration already started at " + new Date(startMillis)); - return; - } - - ThreadGroup group = Thread.currentThread().getThreadGroup(); - SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupThreadSet(group); - samplingProfiler = new SamplingProfiler(samplingProfilerDepth, threadSet); - samplingProfiler.start(samplingProfilerMilliseconds); - startMillis = System.currentTimeMillis(); - } - - /** - * Writes a snapshot if profiling is enabled. - */ - public static void writeSnapshot(final String processName, final PackageInfo packageInfo) { - if (!enabled) { - return; - } - if (samplingProfiler == null) { - Log.e(TAG, "SamplingProfilerIntegration is not started"); - return; - } - - /* - * If we're already writing a snapshot, don't bother enqueueing another - * request right now. This will reduce the number of individual - * snapshots and in turn the total amount of memory consumed (one big - * snapshot is smaller than N subset snapshots). - */ - if (pending.compareAndSet(false, true)) { - snapshotWriter.execute(new Runnable() { - public void run() { - try { - writeSnapshotFile(processName, packageInfo); - } finally { - pending.set(false); - } - } - }); - } - } - - /** - * Writes the zygote's snapshot to internal storage if profiling is enabled. - */ - public static void writeZygoteSnapshot() { - if (!enabled) { - return; - } - writeSnapshotFile("zygote", null); - samplingProfiler.shutdown(); - samplingProfiler = null; - startMillis = 0; - } - - /** - * pass in PackageInfo to retrieve various values for snapshot header - */ - private static void writeSnapshotFile(String processName, PackageInfo packageInfo) { - if (!enabled) { - return; - } - samplingProfiler.stop(); - - /* - * We use the global start time combined with the process name - * as a unique ID. We can't use a counter because processes - * restart. This could result in some overlap if we capture - * two snapshots in rapid succession. - */ - String name = processName.replaceAll(":", "."); - String path = SNAPSHOT_DIR + "/" + name + "-" + startMillis + ".snapshot"; - long start = System.currentTimeMillis(); - OutputStream outputStream = null; - try { - outputStream = new BufferedOutputStream(new FileOutputStream(path)); - PrintStream out = new PrintStream(outputStream); - generateSnapshotHeader(name, packageInfo, out); - if (out.checkError()) { - throw new IOException(); - } - BinaryHprofWriter.write(samplingProfiler.getHprofData(), outputStream); - } catch (IOException e) { - Log.e(TAG, "Error writing snapshot to " + path, e); - return; - } finally { - IoUtils.closeQuietly(outputStream); - } - // set file readable to the world so that SamplingProfilerService - // can put it to dropbox - new File(path).setReadable(true, false); - - long elapsed = System.currentTimeMillis() - start; - Log.i(TAG, "Wrote snapshot " + path + " in " + elapsed + "ms."); - samplingProfiler.start(samplingProfilerMilliseconds); - } - - /** - * generate header for snapshots, with the following format - * (like an HTTP header but without the \r): - * - * Version: <version number of profiler>\n - * Process: <process name>\n - * Package: <package name, if exists>\n - * Package-Version: <version number of the package, if exists>\n - * Build: <fingerprint>\n - * \n - * <the actual snapshot content begins here...> - */ - private static void generateSnapshotHeader(String processName, PackageInfo packageInfo, - PrintStream out) { - // profiler version - out.println("Version: 3"); - out.println("Process: " + processName); - if (packageInfo != null) { - out.println("Package: " + packageInfo.packageName); - out.println("Package-Version: " + packageInfo.versionCode); - } - out.println("Build: " + Build.FINGERPRINT); - // single blank line means the end of snapshot header. - out.println(); - } -} diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 9e61a99096d9..2c0f8e4a373f 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -694,8 +694,6 @@ public class ZygoteInit { Trace.TRACE_TAG_DALVIK); bootTimingsTraceLog.traceBegin("ZygoteInit"); RuntimeInit.enableDdms(); - // Start profiling the zygote initialization. - SamplingProfilerIntegration.start(); boolean startSystemServer = false; String socketName = "zygote"; @@ -734,9 +732,6 @@ public class ZygoteInit { Zygote.resetNicePriority(); } - // Finish profiling the zygote initialization. - SamplingProfilerIntegration.writeZygoteSnapshot(); - // Do an initial gc to clean up after startup bootTimingsTraceLog.traceBegin("PostZygoteInitGC"); gcAndFinalize(); diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 1adc6dde3860..659f47debaf1 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -613,6 +613,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) char useJitProfilesOptsBuf[sizeof("-Xjitsaveprofilinginfo:")-1 + PROPERTY_VALUE_MAX]; char jitprithreadweightOptBuf[sizeof("-Xjitprithreadweight:")-1 + PROPERTY_VALUE_MAX]; char jittransitionweightOptBuf[sizeof("-Xjittransitionweight:")-1 + PROPERTY_VALUE_MAX]; + char hotstartupsamplesOptsBuf[sizeof("-Xps-hot-startup-method-samples:")-1 + PROPERTY_VALUE_MAX]; char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX]; char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX]; char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX]; @@ -739,6 +740,12 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) jittransitionweightOptBuf, "-Xjittransitionweight:"); + /* + * Profile related options. + */ + parseRuntimeOption("dalvik.vm.hot-startup-method-samples", hotstartupsamplesOptsBuf, + "-Xps-hot-startup-method-samples:"); + property_get("ro.config.low_ram", propBuf, ""); if (strcmp(propBuf, "true") == 0) { addOption("-XX:LowMemoryMode"); diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ae7355f77cd0..958592b581a7 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1810,6 +1810,10 @@ <permission android:name="android.permission.MANAGE_USERS" android:protectionLevel="signature|privileged" /> + <!-- @hide Allows an application to configure the assist gesture --> + <permission android:name="android.permission.CONFIGURE_ASSIST_GESTURE" + android:protectionLevel="signature" /> + <!-- @hide Allows an application to create, remove users and get the list of users on the device. Applications holding this permission can only create restricted, guest, managed, demo, and ephemeral users. For creating other kind of users, @@ -2612,6 +2616,12 @@ <permission android:name="android.permission.TV_VIRTUAL_REMOTE_CONTROLLER" android:protectionLevel="signature|privileged" /> + <!-- Allows an application to change HDMI CEC active source. + <p>Not for use by third-party applications. + @hide --> + <permission android:name="android.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE" + android:protectionLevel="signature|privileged" /> + <!-- @SystemApi Allows an application to modify parental controls <p>Not for use by third-party applications. @hide --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 9c4d48965d86..97b6940a1d39 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -191,21 +191,15 @@ <!-- Notification title to tell the user that data service is blocked by access control. --> <string name="RestrictedOnDataTitle">No data service</string> - <!-- Notification title to tell the user that emergency service is blocked by access control. --> - <string name="RestrictedOnEmergencyTitle">No emergency service</string> + <!-- Notification title to tell the user that emergency calling is blocked by access control. --> + <string name="RestrictedOnEmergencyTitle">No emergency calling</string> <!-- Notification title to tell the user that normal service is blocked by access control. --> <string name="RestrictedOnNormalTitle">No voice service</string> <!-- Notification title to tell the user that all emergency and normal voice services are blocked by access control. --> <string name="RestrictedOnAllVoiceTitle">No voice/emergency service</string> - <!-- Notification content to tell the user that data service is blocked by access control. --> - <string name="RestrictedOnDataContent">Your carrier has temporarily suspended data service at this location</string> - <!-- Notification content to tell the user that emergency service is blocked by access control. --> - <string name="RestrictedOnEmergencyContent">Your carrier has temporarily suspended emergency calls at this location</string> - <!-- Notification content to tell the user that normal service is blocked by access control. --> - <string name="RestrictedOnNormalContent">Your carrier has temporarily suspended voice calls at this location</string> - <!-- Notification content to tell the user that all emergency and normal voice services are blocked by access control. --> - <string name="RestrictedOnAllVoiceContent">Your carrier has temporarily suspended voice and emergency calls at this location</string> + <!-- Notification content to tell the user that voice/data/emergency service is blocked by access control. --> + <string name="RestrictedStateContent">Temporarily not offered by the mobile network at your location</string> <!-- Displayed to tell the user that they should switch their network preference. --> <string name="NetworkPreferenceSwitchTitle">Can\u2019t reach network</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index ef7380032b90..1b6be86a2810 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -529,10 +529,7 @@ <java-symbol type="string" name="RestrictedOnDataTitle" /> <java-symbol type="string" name="RestrictedOnEmergencyTitle" /> <java-symbol type="string" name="RestrictedOnNormalTitle" /> - <java-symbol type="string" name="RestrictedOnAllVoiceContent" /> - <java-symbol type="string" name="RestrictedOnDataContent" /> - <java-symbol type="string" name="RestrictedOnEmergencyContent" /> - <java-symbol type="string" name="RestrictedOnNormalContent" /> + <java-symbol type="string" name="RestrictedStateContent" /> <java-symbol type="string" name="notification_channel_network_alert" /> <java-symbol type="string" name="notification_channel_call_forward" /> <java-symbol type="string" name="notification_channel_emergency_callback" /> diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java index 00b5eda6d7ff..90d6ab867fe1 100644 --- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java +++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java @@ -834,6 +834,16 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { final Animator localAnimator = animator.clone(); final String targetName = mTargetNameMap.get(animator); final Object target = mVectorDrawable.getTargetByName(targetName); + if (!mShouldIgnoreInvalidAnim) { + if (target == null) { + throw new IllegalStateException("Target with the name \"" + targetName + + "\" cannot be found in the VectorDrawable to be animated."); + } else if (!(target instanceof VectorDrawable.VectorDrawableState) + && !(target instanceof VectorDrawable.VObject)) { + throw new UnsupportedOperationException("Target should be either VGroup, VPath," + + " or ConstantState, " + target.getClass() + " is not supported"); + } + } localAnimator.setTarget(target); return localAnimator; } @@ -1321,16 +1331,10 @@ public class AnimatedVectorDrawable extends Drawable implements Animatable2 { throw new IllegalArgumentException("ClipPath only supports PathData " + "property"); } - } } else if (target instanceof VectorDrawable.VectorDrawableState) { createRTAnimatorForRootGroup(values, animator, (VectorDrawable.VectorDrawableState) target, startTime); - } else if (!mDrawable.mAnimatedVectorState.mShouldIgnoreInvalidAnim) { - // Should never get here - throw new UnsupportedOperationException("Target should be either VGroup, VPath, " + - "or ConstantState, " + target == null ? "Null target" : target.getClass() + - " is not supported"); } } diff --git a/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java b/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java index b344527e4be5..e18abd5bf23f 100644 --- a/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java +++ b/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java @@ -26,7 +26,7 @@ import java.util.TreeSet; * * @hide */ -//@SystemApi +// @SystemApi public class LowpanBeaconInfo extends LowpanIdentity { private int mRssi = UNKNOWN; @@ -104,7 +104,7 @@ public class LowpanBeaconInfo extends LowpanIdentity { } public Collection<Integer> getFlags() { - return mFlags.clone(); + return (Collection<Integer>) mFlags.clone(); } public boolean isFlagSet(int flag) { diff --git a/lowpan/java/android/net/lowpan/LowpanChannelInfo.java b/lowpan/java/android/net/lowpan/LowpanChannelInfo.java index 50afe6d3a4c0..621affee6d06 100644 --- a/lowpan/java/android/net/lowpan/LowpanChannelInfo.java +++ b/lowpan/java/android/net/lowpan/LowpanChannelInfo.java @@ -16,14 +16,12 @@ package android.net.lowpan; - /** Provides detailed information about a given channel. */ -//@SystemApi +// @SystemApi public class LowpanChannelInfo { public static final int UNKNOWN_POWER = Integer.MAX_VALUE; - ////////////////////////////////////////////////////////////////////////// // Instance Variables private String mName = null; @@ -33,7 +31,6 @@ public class LowpanChannelInfo { private float mSpectrumBandwidth = 0.0f; private int mMaxTransmitPower = UNKNOWN_POWER; - ////////////////////////////////////////////////////////////////////////// // Public Getters and Setters public String getName() { diff --git a/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java b/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java index 9cad00c3415a..1da085ddb294 100644 --- a/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java +++ b/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java @@ -30,7 +30,7 @@ import java.net.InetSocketAddress; * * @hide */ -//@SystemApi +// @SystemApi public abstract class LowpanCommissioningSession { public LowpanCommissioningSession() {} @@ -39,7 +39,7 @@ public abstract class LowpanCommissioningSession { * * @hide */ - //@SystemApi + // @SystemApi public class Callback { public void onReceiveFromCommissioner(@NonNull byte[] packet) {}; diff --git a/lowpan/java/android/net/lowpan/LowpanCredential.java b/lowpan/java/android/net/lowpan/LowpanCredential.java index dea4d7888884..ca8602151515 100644 --- a/lowpan/java/android/net/lowpan/LowpanCredential.java +++ b/lowpan/java/android/net/lowpan/LowpanCredential.java @@ -16,7 +16,6 @@ package android.net.lowpan; - import java.util.Map; /** @@ -24,7 +23,7 @@ import java.util.Map; * * @hide */ -//@SystemApi +// @SystemApi public class LowpanCredential { public static final int UNSPECIFIED_KEY_INDEX = 0; @@ -84,8 +83,7 @@ public class LowpanCredential { void addToMap(Map<String, Object> parameters) throws LowpanException { if (isMasterKey()) { LowpanProperties.KEY_NETWORK_MASTER_KEY.putInMap(parameters, getMasterKey()); - LowpanProperties.KEY_NETWORK_MASTER_KEY_INDEX.putInMap( - parameters, getMasterKeyIndex()); + LowpanProperties.KEY_NETWORK_MASTER_KEY_INDEX.putInMap(parameters, getMasterKeyIndex()); } else { throw new LowpanException("Unsupported Network Credential"); } diff --git a/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java b/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java index c680687d0e09..91ed19c488eb 100644 --- a/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java +++ b/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java @@ -16,13 +16,12 @@ package android.net.lowpan; - /** * Describes the result from one channel of an energy scan. * * @hide */ -//@SystemApi +// @SystemApi public class LowpanEnergyScanResult { public static final int UNKNOWN = Integer.MAX_VALUE; diff --git a/lowpan/java/android/net/lowpan/LowpanException.java b/lowpan/java/android/net/lowpan/LowpanException.java index 8ff37f926899..b43b2fe087e1 100644 --- a/lowpan/java/android/net/lowpan/LowpanException.java +++ b/lowpan/java/android/net/lowpan/LowpanException.java @@ -28,7 +28,7 @@ import android.util.AndroidException; * @see LowpanInterface * @hide */ -//@SystemApi +// @SystemApi public class LowpanException extends AndroidException { // Make the eclipse warning about serializable exceptions go away private static final long serialVersionUID = 0x31863cbe562b0e11l; // randomly generated diff --git a/lowpan/java/android/net/lowpan/LowpanIdentity.java b/lowpan/java/android/net/lowpan/LowpanIdentity.java index 2e7b560fda5e..9be45ef14b98 100644 --- a/lowpan/java/android/net/lowpan/LowpanIdentity.java +++ b/lowpan/java/android/net/lowpan/LowpanIdentity.java @@ -24,10 +24,9 @@ import java.util.Map; * * @hide */ -//@SystemApi +// @SystemApi public class LowpanIdentity { - ////////////////////////////////////////////////////////////////////////// // Constants /** @hide */ @@ -41,11 +40,10 @@ public class LowpanIdentity { public static final int UNKNOWN = Integer.MAX_VALUE; - ////////////////////////////////////////////////////////////////////////// // Builder /** @hide */ - //@SystemApi + // @SystemApi public static class Builder { private final LowpanIdentity identity = new LowpanIdentity(); @@ -102,7 +100,6 @@ public class LowpanIdentity { LowpanIdentity() {} - ////////////////////////////////////////////////////////////////////////// // Instance Variables private String mName = null; @@ -111,7 +108,6 @@ public class LowpanIdentity { private int mPanid = UNKNOWN; private int mChannel = UNKNOWN; - ////////////////////////////////////////////////////////////////////////// // Public Getters and Setters public String getName() { @@ -140,12 +136,10 @@ public class LowpanIdentity { LowpanProperties.KEY_NETWORK_NAME.putInMap(parameters, networkInfo.getName()); } if (networkInfo.getPanid() != LowpanIdentity.UNKNOWN) { - LowpanProperties.KEY_NETWORK_PANID.putInMap( - parameters, networkInfo.getPanid()); + LowpanProperties.KEY_NETWORK_PANID.putInMap(parameters, networkInfo.getPanid()); } if (networkInfo.getChannel() != LowpanIdentity.UNKNOWN) { - LowpanProperties.KEY_CHANNEL.putInMap( - parameters, networkInfo.getChannel()); + LowpanProperties.KEY_CHANNEL.putInMap(parameters, networkInfo.getChannel()); } if (networkInfo.getXpanid() != null) { LowpanProperties.KEY_NETWORK_XPANID.putInMap(parameters, networkInfo.getXpanid()); diff --git a/lowpan/java/android/net/lowpan/LowpanInterface.java b/lowpan/java/android/net/lowpan/LowpanInterface.java index cd548190fa17..2bb4ecd380f2 100644 --- a/lowpan/java/android/net/lowpan/LowpanInterface.java +++ b/lowpan/java/android/net/lowpan/LowpanInterface.java @@ -33,7 +33,7 @@ import java.util.Set; * * @hide */ -//@SystemApi +// @SystemApi public class LowpanInterface { private static final String TAG = LowpanInterface.class.getSimpleName(); @@ -170,7 +170,7 @@ public class LowpanInterface { * * @hide */ - //@SystemApi + // @SystemApi public abstract static class Callback { public void onConnectedChanged(boolean value) {} @@ -244,7 +244,6 @@ public class LowpanInterface { LowpanException.throwAsPublicException(t); } - ////////////////////////////////////////////////////////////////////////// // Private Property Helpers void setProperties(Map properties) throws LowpanException { @@ -311,10 +310,9 @@ public class LowpanInterface { boolean getPropertyAsBoolean(LowpanProperty<Boolean> key) throws LowpanException { Boolean value = getProperty(key); - return (value != null) ? value : 0; + return (value != null) ? value : false; } - ////////////////////////////////////////////////////////////////////////// // Public Actions /** @@ -424,7 +422,6 @@ public class LowpanInterface { } } - ////////////////////////////////////////////////////////////////////////// // Public Getters and Setters /** @@ -448,13 +445,13 @@ public class LowpanInterface { return ""; } - /** - * Indicates if the interface is enabled or disabled. - * - * @see #setEnabled - * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED - */ - public boolean isEnabled() { + /** + * Indicates if the interface is enabled or disabled. + * + * @see #setEnabled + * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED + */ + public boolean isEnabled() { try { return getPropertyAsBoolean(LowpanProperties.KEY_INTERFACE_ENABLED); } catch (LowpanException x) { @@ -462,16 +459,16 @@ public class LowpanInterface { } } - /** - * Enables or disables the LoWPAN interface. When disabled, the interface is put into a low-power - * state and all commands that require the NCP to be queried will fail with {@link - * android.net.lowpan.LowpanException#LOWPAN_DISABLED}. - * - * @see #isEnabled - * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED - * @hide - */ - public void setEnabled(boolean enabled) throws LowpanException { + /** + * Enables or disables the LoWPAN interface. When disabled, the interface is put into a + * low-power state and all commands that require the NCP to be queried will fail with {@link + * android.net.lowpan.LowpanException#LOWPAN_DISABLED}. + * + * @see #isEnabled + * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED + * @hide + */ + public void setEnabled(boolean enabled) throws LowpanException { setProperty(LowpanProperties.KEY_INTERFACE_ENABLED, enabled); } @@ -628,7 +625,6 @@ public class LowpanInterface { setProperties(map); } - ////////////////////////////////////////////////////////////////////////// // Listener Support /** @@ -644,7 +640,7 @@ public class LowpanInterface { public void registerCallback(@NonNull Callback cb, @Nullable Handler handler) { ILowpanInterfaceListener.Stub listenerBinder = new ILowpanInterfaceListener.Stub() { - public void onPropertiesChanged(Map<String, Object> properties) { + public void onPropertiesChanged(Map properties) { Runnable runnable = new Runnable() { @Override @@ -724,36 +720,35 @@ public class LowpanInterface { */ public void unregisterCallback(Callback cb) { int hashCode = System.identityHashCode(cb); - ILowpanInterfaceListener listenerBinder = mListenerMap.get(hashCode); + synchronized (mListenerMap) { + ILowpanInterfaceListener listenerBinder = mListenerMap.get(hashCode); - if (listenerBinder != null) { - synchronized (mListenerMap) { + if (listenerBinder != null) { mListenerMap.remove(hashCode); - } - try { - mBinder.removeListener(listenerBinder); - } catch (RemoteException x) { - // Catch and ignore all binder exceptions - Log.e(TAG, x.toString()); + + try { + mBinder.removeListener(listenerBinder); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } } } } - ////////////////////////////////////////////////////////////////////////// - // Active and Passive Scanning + // Active and Passive Scanning - /** - * Creates a new {@link android.net.lowpan.LowpanScanner} object for this interface. - * - * <p>This method allocates a new unique object for each call. - * - * @see android.net.lowpan.LowpanScanner - */ - public @NonNull LowpanScanner createScanner() { + /** + * Creates a new {@link android.net.lowpan.LowpanScanner} object for this interface. + * + * <p>This method allocates a new unique object for each call. + * + * @see android.net.lowpan.LowpanScanner + */ + public @NonNull LowpanScanner createScanner() { return new LowpanScanner(mBinder); } - ////////////////////////////////////////////////////////////////////////// // Route Management /** diff --git a/lowpan/java/android/net/lowpan/LowpanManager.java b/lowpan/java/android/net/lowpan/LowpanManager.java index b58608da7c21..ecdda49f718a 100644 --- a/lowpan/java/android/net/lowpan/LowpanManager.java +++ b/lowpan/java/android/net/lowpan/LowpanManager.java @@ -33,32 +33,24 @@ import java.util.HashMap; * * @hide */ -//@SystemApi +// @SystemApi public class LowpanManager { private static final String TAG = LowpanManager.class.getSimpleName(); - ////////////////////////////////////////////////////////////////////////// - // Public Classes - /** @hide */ - //@SystemApi + // @SystemApi public abstract static class Callback { - public void onInterfaceAdded(LowpanInterface lowpan_interface) {} + public void onInterfaceAdded(LowpanInterface lowpanInterface) {} - public void onInterfaceRemoved(LowpanInterface lowpan_interface) {} + public void onInterfaceRemoved(LowpanInterface lowpanInterface) {} } - ////////////////////////////////////////////////////////////////////////// - // Instance Variables - + private Context mContext; private ILowpanManager mManager; private HashMap<Integer, ILowpanManagerListener> mListenerMap = new HashMap<>(); - ////////////////////////////////////////////////////////////////////////// - private static LowpanManager sSingletonInstance; - ////////////////////////////////////////////////////////////////////////// // Static Methods /** Returns a reference to the LowpanManager object, allocating it if necessary. */ @@ -75,7 +67,6 @@ public class LowpanManager { return sSingletonInstance; } - ////////////////////////////////////////////////////////////////////////// // Constructors /** @@ -84,28 +75,34 @@ public class LowpanManager { */ private LowpanManager() {} - ////////////////////////////////////////////////////////////////////////// // Private Methods /** * Returns a reference to the ILowpanManager interface, provided by the LoWPAN Manager Service. */ @Nullable - private ILowpanManager getILowpanManager() { + private synchronized ILowpanManager getILowpanManager() { + // Use a local reference of this object for thread safety. ILowpanManager manager = mManager; + if (manager == null) { IBinder serviceBinder = new ServiceManager().getService(ILowpanManager.LOWPAN_SERVICE_NAME); - mManager = manager = ILowpanManager.Stub.asInterface(serviceBinder); + + manager = ILowpanManager.Stub.asInterface(serviceBinder); + + mManager = manager; // Add any listeners synchronized (mListenerMap) { - for (Integer hashObj : mListenerMap.keySet()) { + for (ILowpanManagerListener listener : mListenerMap.values()) { try { - manager.addListener(mListenerMap.get(hashObj)); + manager.addListener(listener); + } catch (RemoteException x) { // Consider any failure here as implying the manager is defunct - mManager = manager = null; + mManager = null; + manager = null; } } } @@ -113,7 +110,6 @@ public class LowpanManager { return manager; } - ////////////////////////////////////////////////////////////////////////// // Public Methods /** @@ -141,6 +137,7 @@ public class LowpanManager { manager = getILowpanManager(); } } + return ret; } @@ -151,7 +148,7 @@ public class LowpanManager { @Nullable public LowpanInterface getInterface() { String[] ifaceList = getInterfaceList(); - if (ifaceList != null && ifaceList.length > 0) { + if (ifaceList.length > 0) { return getInterface(ifaceList[0]); } return null; @@ -165,24 +162,16 @@ public class LowpanManager { public String[] getInterfaceList() { ILowpanManager manager = getILowpanManager(); - if (manager != null) { + // Maximum number of tries is two. We should only try + // more than once if our manager has died or there + // was some sort of AIDL buffer full event. + for (int i = 0; i < 2 && manager != null; i++) { try { return manager.getInterfaceList(); - } catch (RemoteException x) { // In all of the cases when we get this exception, we reconnect and try again mManager = null; - try { - manager = getILowpanManager(); - if (manager != null) { - return manager.getInterfaceList(); - } - } catch (RemoteException ex) { - // Something weird is going on, so we log it - // and fall back thru to returning an empty array. - Log.e(TAG, ex.toString()); - mManager = null; - } + manager = getILowpanManager(); } } @@ -200,14 +189,14 @@ public class LowpanManager { throws LowpanException { ILowpanManagerListener.Stub listenerBinder = new ILowpanManagerListener.Stub() { - public void onInterfaceAdded(ILowpanInterface lowpan_interface) { + public void onInterfaceAdded(ILowpanInterface lowpanInterface) { Runnable runnable = new Runnable() { @Override public void run() { cb.onInterfaceAdded( LowpanInterface.getInterfaceFromBinder( - lowpan_interface.asBinder())); + lowpanInterface.asBinder())); } }; @@ -218,14 +207,14 @@ public class LowpanManager { } } - public void onInterfaceRemoved(ILowpanInterface lowpan_interface) { + public void onInterfaceRemoved(ILowpanInterface lowpanInterface) { Runnable runnable = new Runnable() { @Override public void run() { cb.onInterfaceRemoved( LowpanInterface.getInterfaceFromBinder( - lowpan_interface.asBinder())); + lowpanInterface.asBinder())); } }; diff --git a/lowpan/java/android/net/lowpan/LowpanProvision.java b/lowpan/java/android/net/lowpan/LowpanProvision.java index ace1f9c05c4d..7028807679a1 100644 --- a/lowpan/java/android/net/lowpan/LowpanProvision.java +++ b/lowpan/java/android/net/lowpan/LowpanProvision.java @@ -25,14 +25,13 @@ import java.util.Map; * * @hide */ -//@SystemApi +// @SystemApi public class LowpanProvision { - ////////////////////////////////////////////////////////////////////////// // Builder /** @hide */ - //@SystemApi + // @SystemApi public static class Builder { private final LowpanProvision provision = new LowpanProvision(); @@ -53,13 +52,11 @@ public class LowpanProvision { private LowpanProvision() {} - ////////////////////////////////////////////////////////////////////////// // Instance Variables private LowpanIdentity mIdentity = new LowpanIdentity(); private LowpanCredential mCredential = null; - ////////////////////////////////////////////////////////////////////////// // Public Getters and Setters @NonNull @@ -72,7 +69,6 @@ public class LowpanProvision { return mCredential; } - ////////////////////////////////////////////////////////////////////////// // LoWPAN-Internal Methods static void addToMap(Map<String, Object> parameters, LowpanProvision provision) diff --git a/lowpan/java/android/net/lowpan/LowpanScanner.java b/lowpan/java/android/net/lowpan/LowpanScanner.java index 754f72e3cb84..e0df55d9af3f 100644 --- a/lowpan/java/android/net/lowpan/LowpanScanner.java +++ b/lowpan/java/android/net/lowpan/LowpanScanner.java @@ -25,7 +25,6 @@ import android.util.Log; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; /** @@ -36,11 +35,10 @@ import java.util.Map; * @see LowpanInterface * @hide */ -//@SystemApi +// @SystemApi public class LowpanScanner { private static final String TAG = LowpanInterface.class.getSimpleName(); - ////////////////////////////////////////////////////////////////////////// // Public Classes /** @@ -48,7 +46,7 @@ public class LowpanScanner { * * @hide */ - //@SystemApi + // @SystemApi public abstract static class Callback { public void onNetScanBeacon(LowpanBeaconInfo beacon) {} @@ -57,16 +55,14 @@ public class LowpanScanner { public void onScanFinished() {} } - ////////////////////////////////////////////////////////////////////////// // Instance Variables private ILowpanInterface mBinder; private Callback mCallback = null; private Handler mHandler = null; - private List<Integer> mChannelMask = null; + private ArrayList<Integer> mChannelMask = null; private int mTxPower = Integer.MAX_VALUE; - ////////////////////////////////////////////////////////////////////////// // Constructors/Accessors and Exception Glue LowpanScanner(@NonNull ILowpanInterface binder) { @@ -74,7 +70,7 @@ public class LowpanScanner { } /** Sets an instance of {@link LowpanScanner.Callback} to receive events. */ - public void setCallback(@Nullable Callback cb, @Nullable Handler handler) { + public synchronized void setCallback(@Nullable Callback cb, @Nullable Handler handler) { mCallback = cb; mHandler = handler; } @@ -110,7 +106,7 @@ public class LowpanScanner { * @return the current channel mask, or <code>null</code> if no channel mask is currently set. */ public @Nullable Collection<Integer> getChannelMask() { - return mChannelMask.clone(); + return (Collection<Integer>) mChannelMask.clone(); } /** @@ -179,17 +175,24 @@ public class LowpanScanner { ILowpanNetScanCallback binderListener = new ILowpanNetScanCallback.Stub() { public void onNetScanBeacon(Map parameters) { - Callback callback = mCallback; - Handler handler = mHandler; + Callback callback; + Handler handler; + + synchronized (LowpanScanner.this) { + callback = mCallback; + handler = mHandler; + } if (callback == null) { return; } - Runnable runnable = () -> callback.onNetScanBeacon( - new LowpanBeaconInfo.Builder() - .updateFromMap(parameters) - .build()); + Runnable runnable = + () -> + callback.onNetScanBeacon( + new LowpanBeaconInfo.Builder() + .updateFromMap(parameters) + .build()); if (handler != null) { handler.post(runnable); @@ -199,8 +202,13 @@ public class LowpanScanner { } public void onNetScanFinished() { - Callback callback = mCallback; - Handler handler = mHandler; + Callback callback; + Handler handler; + + synchronized (LowpanScanner.this) { + callback = mCallback; + handler = mHandler; + } if (callback == null) { return; @@ -218,7 +226,7 @@ public class LowpanScanner { try { mBinder.startNetScan(map, binderListener); - } catch (ServiceSpecificException|RemoteException x) { + } catch (ServiceSpecificException | RemoteException x) { LowpanException.throwAsPublicException(x); } } @@ -257,7 +265,8 @@ public class LowpanScanner { return; } - Runnable runnable = () -> { + Runnable runnable = + () -> { if (callback != null) { LowpanEnergyScanResult result = new LowpanEnergyScanResult(); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 455d9cb871c4..2e974061ef61 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2899,7 +2899,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 145; + private static final int SETTINGS_VERSION = 146; private final int mUserId; @@ -3482,22 +3482,25 @@ public class SettingsProvider extends ContentProvider { } if (currentVersion == 144) { - // Version 145: Set the default value for WIFI_WAKEUP_AVAILABLE. + // Version 145: Removed + currentVersion = 145; + } + + if (currentVersion == 145) { + // Version 146: Set the default value for WIFI_WAKEUP_AVAILABLE. if (userId == UserHandle.USER_SYSTEM) { final SettingsState globalSettings = getGlobalSettingsLocked(); final Setting currentSetting = globalSettings.getSettingLocked( Settings.Global.WIFI_WAKEUP_AVAILABLE); - if (currentSetting.isNull()) { - final int defaultValue = getContext().getResources().getInteger( - com.android.internal.R.integer.config_wifi_wakeup_available); - globalSettings.insertSettingLocked( - Settings.Global.WIFI_WAKEUP_AVAILABLE, - String.valueOf(defaultValue), - null, true, SettingsState.SYSTEM_PACKAGE_NAME); - } + final int defaultValue = getContext().getResources().getInteger( + com.android.internal.R.integer.config_wifi_wakeup_available); + globalSettings.insertSettingLocked( + Settings.Global.WIFI_WAKEUP_AVAILABLE, + String.valueOf(defaultValue), + null, true, SettingsState.SYSTEM_PACKAGE_NAME); } - currentVersion = 145; + currentVersion = 146; } // vXXX: Add new settings above this point. diff --git a/packages/SystemUI/res-keyguard/values-h560dp/dimens.xml b/packages/SystemUI/res-keyguard/values-h560dp/dimens.xml index 3fb86d03a167..1b6fa4cf4892 100644 --- a/packages/SystemUI/res-keyguard/values-h560dp/dimens.xml +++ b/packages/SystemUI/res-keyguard/values-h560dp/dimens.xml @@ -16,5 +16,5 @@ --> <resources> - <dimen name="widget_big_font_size">64dp</dimen> + <dimen name="widget_big_font_size">72dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml b/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml index 3fb86d03a167..1b6fa4cf4892 100644 --- a/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml +++ b/packages/SystemUI/res-keyguard/values-h650dp/dimens.xml @@ -16,5 +16,5 @@ --> <resources> - <dimen name="widget_big_font_size">64dp</dimen> + <dimen name="widget_big_font_size">72dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 3ca6e6944a32..41c723e3daf7 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -41,7 +41,7 @@ <!-- Default clock parameters --> <dimen name="bottom_text_spacing_digital">-1dp</dimen> <dimen name="widget_label_font_size">14sp</dimen> - <dimen name="widget_big_font_size">64dp</dimen> + <dimen name="widget_big_font_size">72dp</dimen> <!-- The y translation to apply at the start in appear animations. --> <dimen name="appear_y_translation_start">32dp</dimen> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index fe422dbea41f..fc9e585400c6 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -805,6 +805,14 @@ <dimen name="global_actions_top_padding">100dp</dimen> + <!-- the maximum offset in either direction that elements are moved horizontally to prevent + burn-in on AOD --> + <dimen name="burn_in_prevention_offset_x">8dp</dimen> + + <!-- the maximum offset in either direction that elements are moved vertically to prevent + burn-in on AOD --> + <dimen name="burn_in_prevention_offset_y">50dp</dimen> + <dimen name="corner_size">16dp</dimen> <dimen name="top_padding">0dp</dimen> <dimen name="bottom_padding">48dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index 6a4680a19c6b..86b1d3b7efce 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java @@ -519,6 +519,9 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD RecentsActivityLaunchState launchState = config.getLaunchState(); launchState.reset(); } + + // Force a gc to attempt to clean up bitmap references more quickly (b/38258699) + Recents.getSystemServices().gc(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index cbfa0e58add1..1f13830ec1cc 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -362,6 +362,19 @@ public class SystemServicesProxy { } /** + * Requests a gc() from the background thread. + */ + public void gc() { + BackgroundThread.getHandler().post(new Runnable() { + @Override + public void run() { + System.gc(); + System.runFinalization(); + } + }); + } + + /** * @return whether the provided {@param className} is blacklisted */ public boolean isBlackListedActivity(String className) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index c445c0dd7787..9402c5feea69 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -63,6 +63,8 @@ import com.android.systemui.plugins.PluginManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; import com.android.systemui.statusbar.NotificationGuts.GutsContent; +import com.android.systemui.statusbar.notification.AboveShelfChangedListener; +import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.HybridNotificationView; import com.android.systemui.statusbar.notification.NotificationInflater; import com.android.systemui.statusbar.notification.NotificationUtils; @@ -162,6 +164,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private boolean mIsSystemChildExpanded; private boolean mIsPinned; private FalsingManager mFalsingManager; + private AboveShelfChangedListener mAboveShelfChangedListener; private HeadsUpManager mHeadsUpManager; private boolean mJustClicked; @@ -388,6 +391,10 @@ public class ExpandableNotificationRow extends ActivatableNotificationView expandedIcon.setStaticDrawableColor(color); } + public void setAboveShelfChangedListener(AboveShelfChangedListener aboveShelfChangedListener) { + mAboveShelfChangedListener = aboveShelfChangedListener; + } + @Override public boolean isDimmable() { if (!getShowingLayout().isDimmable()) { @@ -442,6 +449,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } public void setHeadsUp(boolean isHeadsUp) { + boolean wasAboveShelf = isAboveShelf(); int intrinsicBefore = getIntrinsicHeight(); mIsHeadsUp = isHeadsUp; mPrivateLayout.setHeadsUp(isHeadsUp); @@ -454,6 +462,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } if (isHeadsUp) { setAboveShelf(true); + } else if (isAboveShelf() != wasAboveShelf) { + mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf); } } @@ -634,6 +644,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView */ public void setPinned(boolean pinned) { int intrinsicHeight = getIntrinsicHeight(); + boolean wasAboveShelf = isAboveShelf(); mIsPinned = pinned; if (intrinsicHeight != getIntrinsicHeight()) { notifyHeightChanged(false /* needsAnimation */); @@ -645,6 +656,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView setUserExpanded(true); } setChronometerRunning(mLastChronometerRunning); + if (isAboveShelf() != wasAboveShelf) { + mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf); + } } public boolean isPinned() { @@ -993,8 +1007,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } public void setHeadsUpAnimatingAway(boolean headsUpAnimatingAway) { + boolean wasAboveShelf = isAboveShelf(); mHeadsupDisappearRunning = headsUpAnimatingAway; mPrivateLayout.setHeadsUpAnimatingAway(headsUpAnimatingAway); + if (isAboveShelf() != wasAboveShelf) { + mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf); + } } /** @@ -1433,6 +1451,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView if (mIsSummaryWithChildren) { mChildrenContainer.setDark(dark, fade, delay); } + updateShelfIconColor(); } public boolean isExpandable() { @@ -1555,6 +1574,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView */ public void setOnKeyguard(boolean onKeyguard) { if (onKeyguard != mOnKeyguard) { + boolean wasAboveShelf = isAboveShelf(); final boolean wasExpanded = isExpanded(); mOnKeyguard = onKeyguard; onExpansionChanged(false /* userAction */, wasExpanded); @@ -1564,6 +1584,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } notifyHeightChanged(false /* needsAnimation */); } + if (isAboveShelf() != wasAboveShelf) { + mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf); + } } } @@ -2214,7 +2237,11 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } public void setAboveShelf(boolean aboveShelf) { + boolean wasAboveShelf = isAboveShelf(); mAboveShelf = aboveShelf; + if (isAboveShelf() != wasAboveShelf) { + mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf); + } } public static class NotificationViewState extends ExpandableViewState { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java index 531437dff492..58844ad6851a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java @@ -146,12 +146,12 @@ public class NotificationData { // Construct the icon. icon = new StatusBarIconView(context, - sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n); + sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); icon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); // Construct the expanded icon. expandedIcon = new StatusBarIconView(context, - sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), n); + sbn.getPackageName() + "/0x" + Integer.toHexString(sbn.getId()), sbn); expandedIcon.setScaleType(ImageView.ScaleType.CENTER_INSIDE); final StatusBarIcon ic = new StatusBarIcon( sbn.getUser(), @@ -187,9 +187,11 @@ public class NotificationData { * @param n the notification to read the icon from. * @throws InflationException */ - public void updateIcons(Context context, Notification n) throws InflationException { + public void updateIcons(Context context, StatusBarNotification sbn) + throws InflationException { if (icon != null) { // Update the icon + Notification n = sbn.getNotification(); final StatusBarIcon ic = new StatusBarIcon( notification.getUser(), notification.getPackageName(), @@ -197,8 +199,8 @@ public class NotificationData { n.iconLevel, n.number, StatusBarIconView.contentDescForNotification(context, n)); - icon.setNotification(n); - expandedIcon.setNotification(n); + icon.setNotification(sbn); + expandedIcon.setNotification(sbn); if (!icon.set(ic) || !expandedIcon.set(ic)) { throw new InflationException("Couldn't update icon: " + ic); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java index 3f1f82c6cd52..67ea25870a45 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java @@ -179,7 +179,8 @@ public class NotificationShelf extends ActivatableNotificationView implements mShelfState.notGoneIndex = Math.min(mShelfState.notGoneIndex, mNotGoneIndex); } mShelfState.hasItemsInStableShelf = lastViewState.inShelf; - mShelfState.hidden = !mAmbientState.isShadeExpanded(); + mShelfState.hidden = !mAmbientState.isShadeExpanded() + || mAmbientState.isQsCustomizerShowing(); mShelfState.maxShelfEnd = maxShelfEnd; } else { mShelfState.hidden = true; @@ -649,6 +650,10 @@ public class NotificationShelf extends ActivatableNotificationView implements updateRelativeOffset(); } + public void setDarkOffsetX(int offsetX) { + mShelfIcons.setDarkOffsetX(offsetX); + } + private class ShelfState extends ExpandableViewState { private float openedAmount; private boolean hasItemsInStableShelf; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 85475b60902f..3c7ddb502145 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -34,6 +34,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Parcelable; import android.os.UserHandle; +import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.AttributeSet; import android.util.FloatProperty; @@ -99,7 +100,7 @@ public class StatusBarIconView extends AnimatedImageView { private int mNumberX; private int mNumberY; private String mNumberText; - private Notification mNotification; + private StatusBarNotification mNotification; private final boolean mBlocked; private int mDensity; private float mIconScale = 1.0f; @@ -127,11 +128,11 @@ public class StatusBarIconView extends AnimatedImageView { }; private final NotificationIconDozeHelper mDozer; - public StatusBarIconView(Context context, String slot, Notification notification) { - this(context, slot, notification, false); + public StatusBarIconView(Context context, String slot, StatusBarNotification sbn) { + this(context, slot, sbn, false); } - public StatusBarIconView(Context context, String slot, Notification notification, + public StatusBarIconView(Context context, String slot, StatusBarNotification sbn, boolean blocked) { super(context); mDozer = new NotificationIconDozeHelper(context); @@ -141,7 +142,7 @@ public class StatusBarIconView extends AnimatedImageView { mNumberPain.setTextAlign(Paint.Align.CENTER); mNumberPain.setColor(context.getColor(R.drawable.notification_number_text_color)); mNumberPain.setAntiAlias(true); - setNotification(notification); + setNotification(sbn); maybeUpdateIconScaleDimens(); setScaleType(ScaleType.CENTER); mDensity = context.getResources().getDisplayMetrics().densityDpi; @@ -207,9 +208,11 @@ public class StatusBarIconView extends AnimatedImageView { } } - public void setNotification(Notification notification) { + public void setNotification(StatusBarNotification notification) { mNotification = notification; - setContentDescription(notification); + if (notification != null) { + setContentDescription(notification.getNotification()); + } } public StatusBarIconView(Context context, AttributeSet attrs) { @@ -315,6 +318,10 @@ public class StatusBarIconView extends AnimatedImageView { return true; } + public Icon getSourceIcon() { + return mIcon.icon; + } + private Drawable getIcon(StatusBarIcon icon) { return getIcon(getContext(), icon); } @@ -354,7 +361,7 @@ public class StatusBarIconView extends AnimatedImageView { public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); if (mNotification != null) { - event.setParcelableData(mNotification); + event.setParcelableData(mNotification.getNotification()); } } @@ -456,6 +463,10 @@ public class StatusBarIconView extends AnimatedImageView { + " notification=" + mNotification + ")"; } + public StatusBarNotification getNotification() { + return mNotification; + } + public String getSlot() { return mSlot; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index 76177a310a06..4a6bafc50e52 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -51,7 +51,14 @@ import com.android.systemui.statusbar.phone.NavigationBarView; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.UserSwitcherController; - +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.classifier.FalsingLog; +import com.android.systemui.classifier.FalsingManager; +import com.android.systemui.Prefs; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Map; /** * A status bar (and navigation bar) tailored for the automotive use case. */ @@ -71,6 +78,7 @@ public class CarStatusBar extends StatusBar implements private ConnectedDeviceSignalController mConnectedDeviceSignalController; private CarNavigationBarView mNavigationBarView; + private final Object mQueueLock = new Object(); @Override public void start() { super.start(); @@ -170,6 +178,43 @@ public class CarStatusBar extends StatusBar implements } @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + //When executing dump() funciton simultaneously, we need to serialize them + //to get mStackScroller's position correctly. + synchronized (mQueueLock) { + pw.println(" mStackScroller: " + viewInfo(mStackScroller)); + pw.println(" mStackScroller: " + viewInfo(mStackScroller) + + " scroll " + mStackScroller.getScrollX() + + "," + mStackScroller.getScrollY()); + } + + pw.print(" mTaskStackListener="); pw.println(mTaskStackListener); + pw.print(" mController="); + pw.println(mController); + pw.print(" mFullscreenUserSwitcher="); pw.println(mFullscreenUserSwitcher); + pw.print(" mCarBatteryController="); + pw.println(mCarBatteryController); + pw.print(" mBatteryMeterView="); + pw.println(mBatteryMeterView); + pw.print(" mConnectedDeviceSignalController="); + pw.println(mConnectedDeviceSignalController); + pw.print(" mNavigationBarView="); + pw.println(mNavigationBarView); + + if (KeyguardUpdateMonitor.getInstance(mContext) != null) { + KeyguardUpdateMonitor.getInstance(mContext).dump(fd, pw, args); + } + + FalsingManager.getInstance(mContext).dump(pw); + FalsingLog.dump(pw); + + pw.println("SharedPreferences:"); + for (Map.Entry<String, ?> entry : Prefs.getAll(mContext).entrySet()) { + pw.print(" "); pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue()); + } + } + + @Override public NavigationBarView getNavigationBarView() { return mNavigationBarView; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AboveShelfChangedListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AboveShelfChangedListener.java new file mode 100644 index 000000000000..07baedce0276 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AboveShelfChangedListener.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.notification; + +/** + * A listener for when the above shelf state of notification changes + */ +public interface AboveShelfChangedListener { + + /** + * Notifies a listener that the above shelf state changed + */ + void onAboveShelfStateChanged(boolean aboveShelf); +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/AboveShelfObserver.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AboveShelfObserver.java new file mode 100644 index 000000000000..f10d2d7f05c2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/AboveShelfObserver.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.notification; + +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.statusbar.ExpandableNotificationRow; + +/** + * An observer that listens to the above shelf state and can notify listeners + */ +public class AboveShelfObserver implements AboveShelfChangedListener { + + private final ViewGroup mHostLayout; + private boolean mHasViewsAboveShelf = false; + private HasViewAboveShelfChangedListener mListener; + + public AboveShelfObserver(ViewGroup hostLayout) { + mHostLayout = hostLayout; + } + + public void setListener(HasViewAboveShelfChangedListener listener) { + mListener = listener; + } + + @Override + public void onAboveShelfStateChanged(boolean aboveShelf) { + boolean hasViewsAboveShelf = aboveShelf; + if (!hasViewsAboveShelf && mHostLayout != null) { + int n = mHostLayout.getChildCount(); + for (int i = 0; i < n; i++) { + View child = mHostLayout.getChildAt(i); + if (child instanceof ExpandableNotificationRow) { + if (((ExpandableNotificationRow) child).isAboveShelf()) { + hasViewsAboveShelf = true; + break; + } + } + } + } + if (mHasViewsAboveShelf != hasViewsAboveShelf) { + mHasViewsAboveShelf = hasViewsAboveShelf; + if (mListener != null) { + mListener.onHasViewsAboveShelfChanged(hasViewsAboveShelf); + } + } + } + + @VisibleForTesting + boolean hasViewsAboveShelf() { + return mHasViewsAboveShelf; + } + + public interface HasViewAboveShelfChangedListener { + void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 7370c03110e6..652288d57fe3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -16,13 +16,14 @@ package com.android.systemui.statusbar.phone; +import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate; + import android.content.res.Resources; import android.graphics.Path; import android.view.animation.AccelerateInterpolator; import android.view.animation.PathInterpolator; import com.android.systemui.R; -import com.android.systemui.statusbar.notification.NotificationUtils; /** * Utility class to calculate the clock position and top padding of notifications on Keyguard. @@ -40,6 +41,10 @@ public class KeyguardClockPositionAlgorithm { private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN = 1.4f; private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX = 3.2f; + private static final long MILLIS_PER_MINUTES = 1000 * 60; + private static final float BURN_IN_PREVENTION_PERIOD_Y = 521; + private static final float BURN_IN_PREVENTION_PERIOD_X = 83; + private int mClockNotificationsMarginMin; private int mClockNotificationsMarginMax; private float mClockYFractionMin; @@ -52,6 +57,8 @@ public class KeyguardClockPositionAlgorithm { private int mKeyguardStatusHeight; private float mEmptyDragAmount; private float mDensity; + private int mBurnInPreventionOffsetX; + private int mBurnInPreventionOffsetY; /** * The number (fractional) of notifications the "more" card counts when calculating how many @@ -86,6 +93,10 @@ public class KeyguardClockPositionAlgorithm { (float) res.getDimensionPixelSize(R.dimen.notification_shelf_height) / res.getDimensionPixelSize(R.dimen.notification_min_height); mDensity = res.getDisplayMetrics().density; + mBurnInPreventionOffsetX = res.getDimensionPixelSize( + R.dimen.burn_in_prevention_offset_x); + mBurnInPreventionOffsetY = res.getDimensionPixelSize( + R.dimen.burn_in_prevention_offset_y); } public void setup(int maxKeyguardNotifications, int maxPanelHeight, float expandedHeight, @@ -122,10 +133,12 @@ public class KeyguardClockPositionAlgorithm { y + getClockNotificationsPadding() + mKeyguardStatusHeight); result.clockAlpha = getClockAlpha(result.clockScale); - result.stackScrollerPadding = (int) NotificationUtils.interpolate( + result.stackScrollerPadding = (int) interpolate( result.stackScrollerPadding, mClockBottom + y, mDarkAmount); + + result.clockX = (int) interpolate(0, burnInPreventionOffsetX(), mDarkAmount); } private float getClockScale(int notificationPadding, int clockY, int startPadding) { @@ -154,9 +167,39 @@ public class KeyguardClockPositionAlgorithm { private int getClockY() { // Dark: Align the bottom edge of the clock at one third: // clockBottomEdge = result - mKeyguardStatusHeight / 2 + mClockBottom - float clockYDark = (0.33f * mHeight + (float) mKeyguardStatusHeight / 2 - mClockBottom); + float clockYDark = (0.33f * mHeight + (float) mKeyguardStatusHeight / 2 - mClockBottom) + + burnInPreventionOffsetY(); float clockYRegular = getClockYFraction() * mHeight; - return (int) NotificationUtils.interpolate(clockYRegular, clockYDark, mDarkAmount); + return (int) interpolate(clockYRegular, clockYDark, mDarkAmount); + } + + private float burnInPreventionOffsetY() { + return zigzag(System.currentTimeMillis() / MILLIS_PER_MINUTES, + mBurnInPreventionOffsetY * 2, + BURN_IN_PREVENTION_PERIOD_Y) + - mBurnInPreventionOffsetY; + } + + private float burnInPreventionOffsetX() { + return zigzag(System.currentTimeMillis() / MILLIS_PER_MINUTES, + mBurnInPreventionOffsetX * 2, + BURN_IN_PREVENTION_PERIOD_X) + - mBurnInPreventionOffsetX; + } + + /** + * Implements a continuous, piecewise linear, periodic zig-zag function + * + * Can be thought of as a linear approximation of abs(sin(x))) + * + * @param period period of the function, ie. zigzag(x + period) == zigzag(x) + * @param amplitude maximum value of the function + * @return a value between 0 and amplitude + */ + private float zigzag(float x, float amplitude, float period) { + float xprime = (x % period) / (period / 2); + float interpolationAmount = (xprime <= 1) ? xprime : (2 - xprime); + return interpolate(0, amplitude, interpolationAmount); } private float getClockYExpansionAdjustment() { @@ -230,5 +273,8 @@ public class KeyguardClockPositionAlgorithm { * the padding, but not the overall panel size. */ public int stackScrollerPaddingAdjustment; + + /** The x translation of the clock. */ + public int clockX; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index b888e8c71f37..41a69b4ba557 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -4,11 +4,15 @@ import android.content.Context; import android.content.res.Resources; import android.graphics.Color; import android.graphics.Rect; +import android.graphics.drawable.Icon; import android.support.annotation.NonNull; +import android.support.v4.util.ArrayMap; +import android.support.v4.util.ArraySet; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; +import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.util.NotificationColorUtil; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableNotificationRow; @@ -180,14 +184,54 @@ public class NotificationIconAreaController implements DarkReceiver { } } + // In case we are changing the suppression of a group, the replacement shouldn't flicker + // and it should just be replaced instead. We therefore look for notifications that were + // just replaced by the child or vice-versa to suppress this. + ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons = new ArrayMap<>(); ArrayList<View> toRemove = new ArrayList<>(); for (int i = 0; i < hostLayout.getChildCount(); i++) { View child = hostLayout.getChildAt(i); + if (!(child instanceof StatusBarIconView)) { + continue; + } if (!toShow.contains(child)) { - toRemove.add(child); + boolean iconWasReplaced = false; + StatusBarIconView removedIcon = (StatusBarIconView) child; + String removedGroupKey = removedIcon.getNotification().getGroupKey(); + for (int j = 0; j < toShow.size(); j++) { + StatusBarIconView candidate = toShow.get(j); + if (candidate.getSourceIcon().sameAs((removedIcon.getSourceIcon())) + && candidate.getNotification().getGroupKey().equals(removedGroupKey)) { + if (!iconWasReplaced) { + iconWasReplaced = true; + } else { + iconWasReplaced = false; + break; + } + } + } + if (iconWasReplaced) { + ArrayList<StatusBarIcon> statusBarIcons = replacingIcons.get(removedGroupKey); + if (statusBarIcons == null) { + statusBarIcons = new ArrayList<>(); + replacingIcons.put(removedGroupKey, statusBarIcons); + } + statusBarIcons.add(removedIcon.getStatusBarIcon()); + } + toRemove.add(removedIcon); + } + } + // removing all duplicates + ArrayList<String> duplicates = new ArrayList<>(); + for (String key : replacingIcons.keySet()) { + ArrayList<StatusBarIcon> statusBarIcons = replacingIcons.get(key); + if (statusBarIcons.size() != 1) { + duplicates.add(key); } } + replacingIcons.removeAll(duplicates); + hostLayout.setReplacingIcons(replacingIcons); final int toRemoveCount = toRemove.size(); for (int i = 0; i < toRemoveCount; i++) { @@ -217,6 +261,7 @@ public class NotificationIconAreaController implements DarkReceiver { hostLayout.addView(expected, i); } hostLayout.setChangingViewPositions(false); + hostLayout.setReplacingIcons(null); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index f94bb0cdd4f7..3937dd3eea2a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -21,9 +21,13 @@ import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.drawable.Icon; +import android.support.v4.util.ArrayMap; +import android.support.v4.util.ArraySet; import android.util.AttributeSet; import android.view.View; +import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.statusbar.AlphaOptimizedFrameLayout; @@ -33,6 +37,7 @@ import com.android.systemui.statusbar.stack.AnimationProperties; import com.android.systemui.statusbar.stack.StackStateAnimator; import com.android.systemui.statusbar.stack.ViewState; +import java.util.ArrayList; import java.util.HashMap; /** @@ -117,6 +122,8 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { private float mVisualOverflowAdaption; private boolean mDisallowNextAnimation; private boolean mAnimationsEnabled = true; + private ArrayMap<String, ArrayList<StatusBarIcon>> mReplacingIcons; + private int mDarkOffsetX; public NotificationIconContainer(Context context, AttributeSet attrs) { super(context, attrs); @@ -183,11 +190,17 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { @Override public void onViewAdded(View child) { super.onViewAdded(child); + boolean isReplacingIcon = isReplacingIcon(child); if (!mChangingViewPositions) { - mIconStates.put(child, new IconState()); + IconState v = new IconState(); + if (isReplacingIcon) { + v.justAdded = false; + v.justReplaced = true; + } + mIconStates.put(child, v); } int childIndex = indexOfChild(child); - if (childIndex < getChildCount() - 1 + if (childIndex < getChildCount() - 1 && !isReplacingIcon && mIconStates.get(getChildAt(childIndex + 1)).iconAppearAmount > 0.0f) { if (mAddAnimationStartIndex < 0) { mAddAnimationStartIndex = childIndex; @@ -200,13 +213,34 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } } + private boolean isReplacingIcon(View child) { + if (mReplacingIcons == null) { + return false; + } + if (!(child instanceof StatusBarIconView)) { + return false; + } + StatusBarIconView iconView = (StatusBarIconView) child; + Icon sourceIcon = iconView.getSourceIcon(); + String groupKey = iconView.getNotification().getGroupKey(); + ArrayList<StatusBarIcon> statusBarIcons = mReplacingIcons.get(groupKey); + if (statusBarIcons != null) { + StatusBarIcon replacedIcon = statusBarIcons.get(0); + if (sourceIcon.sameAs(replacedIcon.icon)) { + return true; + } + } + return false; + } + @Override public void onViewRemoved(View child) { super.onViewRemoved(child); if (child instanceof StatusBarIconView) { + boolean isReplacingIcon = isReplacingIcon(child); final StatusBarIconView icon = (StatusBarIconView) child; if (icon.getVisibleState() != StatusBarIconView.STATE_HIDDEN - && child.getVisibility() == VISIBLE) { + && child.getVisibility() == VISIBLE && isReplacingIcon) { int animationStartIndex = findFirstViewIndexAfter(icon.getTranslationX()); if (mAddAnimationStartIndex < 0) { mAddAnimationStartIndex = animationStartIndex; @@ -216,9 +250,11 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } if (!mChangingViewPositions) { mIconStates.remove(child); - addTransientView(icon, 0); - icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */, - () -> removeTransientView(icon)); + if (!isReplacingIcon) { + addTransientView(icon, 0); + icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, true /* animate */, + () -> removeTransientView(icon)); + } } } } @@ -354,6 +390,14 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { iconState.xTranslation = getWidth() - iconState.xTranslation - view.getWidth(); } } + + if (mDark && mDarkOffsetX != 0) { + for (int i = 0; i < childCount; i++) { + View view = getChildAt(i); + IconState iconState = mIconStates.get(view); + iconState.xTranslation += mDarkOffsetX; + } + } } private float getLayoutEnd() { @@ -473,11 +517,20 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { mAnimationsEnabled = enabled; } + public void setDarkOffsetX(int offsetX) { + mDarkOffsetX = offsetX; + } + + public void setReplacingIcons(ArrayMap<String, ArrayList<StatusBarIcon>> replacingIcons) { + mReplacingIcons = replacingIcons; + } + public class IconState extends ViewState { public float iconAppearAmount = 1.0f; public float clampedAppearAmount = 1.0f; public int visibleState; public boolean justAdded = true; + private boolean justReplaced; public boolean needsCannedAnimation; public boolean useFullTransitionAmount; public boolean useLinearTransitionAmount; @@ -496,9 +549,9 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { && !mDisallowNextAnimation && !noAnimations; if (animationsAllowed) { - if (justAdded) { + if (justAdded || justReplaced) { super.applyToView(icon); - if (iconAppearAmount != 0.0f) { + if (justAdded && iconAppearAmount != 0.0f) { icon.setAlpha(0.0f); icon.setVisibleState(StatusBarIconView.STATE_HIDDEN, false /* animate */); @@ -557,6 +610,7 @@ public class NotificationIconContainer extends AlphaOptimizedFrameLayout { } } justAdded = false; + justReplaced = false; needsCannedAnimation = false; justUndarkened = false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index e2b9da04fed2..4701f85c5ff3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.phone; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.app.ActivityManager; @@ -43,15 +44,13 @@ import android.view.ViewTreeObserver; import android.view.WindowInsets; import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; -import android.widget.TextView; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.keyguard.KeyguardStatusView; import com.android.systemui.DejankUtils; -import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.SystemUIFactory; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; @@ -69,7 +68,6 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.KeyguardUserSwitcher; import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; -import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.stack.StackStateAnimator; @@ -168,8 +166,9 @@ public class NotificationPanelView extends PanelView implements private int mUnlockMoveDistance; private float mEmptyDragAmount; - private ObjectAnimator mClockAnimator; - private int mClockAnimationTarget = -1; + private Animator mClockAnimator; + private int mClockAnimationTargetX = Integer.MIN_VALUE; + private int mClockAnimationTargetY = Integer.MIN_VALUE; private int mTopPaddingAdjustment; private KeyguardClockPositionAlgorithm mClockPositionAlgorithm = new KeyguardClockPositionAlgorithm(); @@ -459,8 +458,9 @@ public class NotificationPanelView extends PanelView implements mDarkAmount); mClockPositionAlgorithm.run(mClockPositionResult); if (animate || mClockAnimator != null) { - startClockAnimation(mClockPositionResult.clockY); + startClockAnimation(mClockPositionResult.clockX, mClockPositionResult.clockY); } else { + mKeyguardStatusView.setX(mClockPositionResult.clockX); mKeyguardStatusView.setY(mClockPositionResult.clockY); } updateClock(mClockPositionResult.clockAlpha, mClockPositionResult.clockScale); @@ -468,6 +468,7 @@ public class NotificationPanelView extends PanelView implements mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment; } mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding); + mNotificationStackScroller.setDarkShelfOffsetX(mClockPositionResult.clockX); requestScrollerTopPaddingUpdate(animate); } @@ -523,11 +524,12 @@ public class NotificationPanelView extends PanelView implements return count; } - private void startClockAnimation(int y) { - if (mClockAnimationTarget == y) { + private void startClockAnimation(int x, int y) { + if (mClockAnimationTargetX == x && mClockAnimationTargetY == y) { return; } - mClockAnimationTarget = y; + mClockAnimationTargetX = x; + mClockAnimationTargetY = y; getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { @@ -536,15 +538,20 @@ public class NotificationPanelView extends PanelView implements mClockAnimator.removeAllListeners(); mClockAnimator.cancel(); } - mClockAnimator = ObjectAnimator - .ofFloat(mKeyguardStatusView, View.Y, mClockAnimationTarget); + AnimatorSet set = new AnimatorSet(); + set.play(ObjectAnimator.ofFloat( + mKeyguardStatusView, View.Y, mClockAnimationTargetY)) + .with(ObjectAnimator.ofFloat( + mKeyguardStatusView, View.X, mClockAnimationTargetX)); + mClockAnimator = set; mClockAnimator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); mClockAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); mClockAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { mClockAnimator = null; - mClockAnimationTarget = -1; + mClockAnimationTargetX = Integer.MIN_VALUE; + mClockAnimationTargetY = Integer.MIN_VALUE; } }); mClockAnimator.start(); @@ -2613,6 +2620,9 @@ public class NotificationPanelView extends PanelView implements public void refreshTime() { mKeyguardStatusView.refreshTime(); + if (mDarkAmount > 0) { + positionClockAndNotifications(); + } } public void setStatusAccessibilityImportance(int mode) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java index 1952a21057ca..76cc0ff2a2e1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java @@ -34,18 +34,19 @@ import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.plugins.qs.QS; import com.android.systemui.statusbar.NotificationData.Entry; -import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; +import com.android.systemui.statusbar.notification.AboveShelfObserver; +import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; /** * The container with notification stack scroller and quick settings inside. */ public class NotificationsQuickSettingsContainer extends FrameLayout - implements OnInflateListener, FragmentListener, OnHeadsUpChangedListener { + implements OnInflateListener, FragmentListener, + AboveShelfObserver.HasViewAboveShelfChangedListener { private FrameLayout mQsFrame; private View mUserSwitcher; - private View mStackScroller; + private NotificationStackScrollLayout mStackScroller; private View mKeyguardStatusBar; private boolean mInflated; private boolean mQsExpanded; @@ -53,8 +54,7 @@ public class NotificationsQuickSettingsContainer extends FrameLayout private int mBottomPadding; private int mStackScrollerMargin; - private boolean mHeadsUp; - private HeadsUpManager mHeadsUpManager; + private boolean mHasViewsAboveShelf; public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) { super(context, attrs); @@ -76,16 +76,12 @@ public class NotificationsQuickSettingsContainer extends FrameLayout protected void onAttachedToWindow() { super.onAttachedToWindow(); FragmentHostManager.get(this).addTagListener(QS.TAG, this); - mHeadsUpManager = SysUiServiceProvider.getComponent(getContext(), StatusBar.class) - .mHeadsUpManager; - mHeadsUpManager.addListener(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); FragmentHostManager.get(this).removeTagListener(QS.TAG, this); - mHeadsUpManager.removeListener(this); } @Override @@ -116,7 +112,7 @@ public class NotificationsQuickSettingsContainer extends FrameLayout boolean userSwitcherVisible = mInflated && mUserSwitcher.getVisibility() == View.VISIBLE; boolean statusBarVisible = mKeyguardStatusBar.getVisibility() == View.VISIBLE; - final boolean qsBottom = mHeadsUp; + final boolean qsBottom = mHasViewsAboveShelf; View stackQsTop = qsBottom ? mStackScroller : mQsFrame; View stackQsBottom = !qsBottom ? mStackScroller : mQsFrame; // Invert the order of the scroll view and user switcher such that the notifications receive @@ -183,7 +179,7 @@ public class NotificationsQuickSettingsContainer extends FrameLayout setPadding(0, 0, 0, mBottomPadding); setBottomMargin(mStackScroller, mStackScrollerMargin); } - + mStackScroller.setQsCustomizerShowing(isShowing); } private void setBottomMargin(View v, int bottomMargin) { @@ -193,12 +189,8 @@ public class NotificationsQuickSettingsContainer extends FrameLayout } @Override - public void onHeadsUpStateChanged(Entry entry, boolean isHeadsUp) { - boolean hasHeadsUp = mHeadsUpManager.getAllEntries().size() != 0; - if (mHeadsUp == hasHeadsUp) { - return; - } - mHeadsUp = hasHeadsUp; + public void onHasViewsAboveShelfChanged(boolean hasViewsAboveShelf) { + mHasViewsAboveShelf = hasViewsAboveShelf; invalidate(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 73f3048a6e68..75fd4b192b99 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -209,6 +209,7 @@ import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.SignalClusterView; import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.RowInflaterTask; import com.android.systemui.statusbar.notification.VisualStabilityManager; @@ -1000,6 +1001,9 @@ public class StatusBar extends SystemUI implements DemoMode, R.id.notification_stack_scroller); mNotificationPanel.setStatusBar(this); mNotificationPanel.setGroupManager(mGroupManager); + mAboveShelfObserver = new AboveShelfObserver(mStackScroller); + mAboveShelfObserver.setListener(mStatusBarWindow.findViewById( + R.id.notification_container_parent)); mKeyguardStatusBar = (KeyguardStatusBarView) mStatusBarWindow.findViewById(R.id.keyguard_header); mNotificationIconAreaController = SystemUIFactory.getInstance() @@ -4647,6 +4651,11 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onActivated(ActivatableNotificationView view) { + onActivated((View)view); + mStackScroller.setActivatedChild(view); + } + + public void onActivated(View view) { mLockscreenGestureLogger.write( MetricsEvent.ACTION_LS_NOTE, 0 /* lengthDp - N/A */, 0 /* velocityDp - N/A */); @@ -4655,7 +4664,6 @@ public class StatusBar extends SystemUI implements DemoMode, if (previousView != null) { previousView.makeInactive(true /* animate */); } - mStackScroller.setActivatedChild(view); } /** @@ -4689,11 +4697,15 @@ public class StatusBar extends SystemUI implements DemoMode, @Override public void onActivationReset(ActivatableNotificationView view) { if (view == mStackScroller.getActivatedChild()) { - mKeyguardIndicationController.hideTransientIndication(); mStackScroller.setActivatedChild(null); + onActivationReset((View)view); } } + public void onActivationReset(View view) { + mKeyguardIndicationController.hideTransientIndication(); + } + public void onTrackingStarted() { runPostCollapseRunnables(); } @@ -5333,6 +5345,8 @@ public class StatusBar extends SystemUI implements DemoMode, // for heads up notifications protected HeadsUpManager mHeadsUpManager; + private AboveShelfObserver mAboveShelfObserver; + // handling reordering protected VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager(); @@ -6408,6 +6422,7 @@ public class StatusBar extends SystemUI implements DemoMode, row.setExpansionLogger(this, entry.notification.getKey()); row.setGroupManager(mGroupManager); row.setHeadsUpManager(mHeadsUpManager); + row.setAboveShelfChangedListener(mAboveShelfObserver); row.setRemoteInputController(mRemoteInputController); row.setOnExpandClickListener(this); row.setRemoteViewClickHandler(mOnClickHandler); @@ -6977,7 +6992,7 @@ public class StatusBar extends SystemUI implements DemoMode, entry.notification = notification; mGroupManager.onEntryUpdated(entry, oldNotification); - entry.updateIcons(mContext, n); + entry.updateIcons(mContext, notification); inflateViews(entry, mStackScroller); mForegroundServiceController.updateNotification(notification, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index 70f226028e48..53dfb244c776 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.res.Resources; import android.database.ContentObserver; import android.os.Handler; +import android.os.Looper; import android.os.SystemClock; import android.provider.Settings; import android.support.v4.util.ArraySet; @@ -65,7 +66,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL private final ArrayMap<String, Long> mSnoozedPackages; private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>(); private final int mDefaultSnoozeLengthMs; - private final Handler mHandler = new Handler(); + private final Handler mHandler = new Handler(Looper.getMainLooper()); private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() { private Stack<HeadsUpEntry> mPoolObjects = new Stack<>(); @@ -624,6 +625,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL @Override public void onReorderingAllowed() { + mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(false); for (NotificationData.Entry entry : mEntriesToRemoveWhenReorderingAllowed) { if (isHeadsUp(entry.key)) { // Maybe the heads-up was removed already @@ -631,6 +633,7 @@ public class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsL } } mEntriesToRemoveWhenReorderingAllowed.clear(); + mBar.getNotificationScrollLayout().setHeadsUpGoingAwayAnimationsAllowed(true); } public void setVisualStabilityManager(VisualStabilityManager visualStabilityManager) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java index 41ef78121c12..b7b991e36710 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java @@ -61,6 +61,7 @@ public class AmbientState { private boolean mPanelFullWidth; private boolean mHasPulsingNotifications; private boolean mUnlockHintRunning; + private boolean mQsCustomizerShowing; public AmbientState(Context context) { reload(context); @@ -314,4 +315,12 @@ public class AmbientState { public boolean isUnlockHintRunning() { return mUnlockHintRunning; } + + public boolean isQsCustomizerShowing() { + return mQsCustomizerShowing; + } + + public void setQsCustomizerShowing(boolean qsCustomizerShowing) { + mQsCustomizerShowing = qsCustomizerShowing; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 84acaf1e842b..cbd315b940f3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -375,6 +375,7 @@ public class NotificationStackScrollLayout extends ViewGroup private boolean mHeadsUpAnimatingAway; private int mStatusBarState; private int mCachedBackgroundColor; + private boolean mHeadsUpGoingAwayAnimationsAllowed = true; private Runnable mAnimateScroll = this::animateScroll; public NotificationStackScrollLayout(Context context) { @@ -4060,7 +4061,7 @@ public class NotificationStackScrollLayout extends ViewGroup } public void generateHeadsUpAnimation(ExpandableNotificationRow row, boolean isHeadsUp) { - if (mAnimationsEnabled) { + if (mAnimationsEnabled && (isHeadsUp || mHeadsUpGoingAwayAnimationsAllowed)) { mHeadsUpChangeAnimations.add(new Pair<>(row, isHeadsUp)); mNeedsAnimation = true; if (!mIsExpanded && !isHeadsUp) { @@ -4242,6 +4243,19 @@ public class NotificationStackScrollLayout extends ViewGroup mAmbientState.setUnlockHintRunning(running); } + public void setQsCustomizerShowing(boolean isShowing) { + mAmbientState.setQsCustomizerShowing(isShowing); + requestChildrenUpdate(); + } + + public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) { + mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed; + } + + public void setDarkShelfOffsetX(int shelfOffsetX) { + mShelf.setDarkOffsetX(shelfOffsetX); + } + /** * A listener that is notified when some child locations might have changed. */ diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java index 99b664ad9580..664ea710d61e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java @@ -29,6 +29,7 @@ import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.view.View; +import com.android.systemui.statusbar.notification.AboveShelfChangedListener; import com.android.systemui.statusbar.stack.NotificationChildrenContainer; import com.android.systemui.SysuiTestCase; @@ -96,4 +97,47 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { row.setHideSensitive(true, false, 0, 0); verify(row).updateShelfIconColor(); } + + @Test + public void testIconColorShouldBeUpdatedWhenSettingDark() throws Exception { + ExpandableNotificationRow row = spy(mNotificationTestHelper.createRow()); + row.setDark(true, false, 0); + verify(row).updateShelfIconColor(); + } + + @Test + public void testAboveShelfChangedListenerCalled() throws Exception { + ExpandableNotificationRow row = mNotificationTestHelper.createRow(); + AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class); + row.setAboveShelfChangedListener(listener); + row.setHeadsUp(true); + verify(listener).onAboveShelfStateChanged(true); + } + + @Test + public void testAboveShelfChangedListenerCalledPinned() throws Exception { + ExpandableNotificationRow row = mNotificationTestHelper.createRow(); + AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class); + row.setAboveShelfChangedListener(listener); + row.setPinned(true); + verify(listener).onAboveShelfStateChanged(true); + } + + @Test + public void testAboveShelfChangedListenerCalledHeadsUpGoingAway() throws Exception { + ExpandableNotificationRow row = mNotificationTestHelper.createRow(); + AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class); + row.setAboveShelfChangedListener(listener); + row.setHeadsUpAnimatingAway(true); + verify(listener).onAboveShelfStateChanged(true); + } + @Test + public void testAboveShelfChangedListenerCalledWhenGoingBelow() throws Exception { + ExpandableNotificationRow row = mNotificationTestHelper.createRow(); + row.setHeadsUp(true); + AboveShelfChangedListener listener = mock(AboveShelfChangedListener.class); + row.setAboveShelfChangedListener(listener); + row.setAboveShelf(false); + verify(listener).onAboveShelfStateChanged(false); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java index cb238ddbabaa..6e7477fbac38 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java @@ -24,12 +24,16 @@ import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.support.test.InstrumentationRegistry; import android.view.LayoutInflater; +import android.widget.FrameLayout; import android.widget.RemoteViews; import com.android.systemui.R; +import com.android.systemui.statusbar.notification.AboveShelfChangedListener; +import com.android.systemui.statusbar.notification.AboveShelfObserver; import com.android.systemui.statusbar.notification.InflationException; import com.android.systemui.statusbar.notification.NotificationInflaterTest; import com.android.systemui.statusbar.phone.NotificationGroupManager; +import com.android.systemui.statusbar.policy.HeadsUpManager; /** * A helper class to create {@link ExpandableNotificationRow} @@ -42,10 +46,12 @@ public class NotificationTestHelper { private final NotificationGroupManager mGroupManager = new NotificationGroupManager(); private ExpandableNotificationRow mRow; private InflationException mException; + private HeadsUpManager mHeadsUpManager; public NotificationTestHelper(Context context) { mContext = context; mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mHeadsUpManager = new HeadsUpManager(mContext, null, mGroupManager); } public ExpandableNotificationRow createRow() throws Exception { @@ -73,6 +79,8 @@ public class NotificationTestHelper { }); ExpandableNotificationRow row = mRow; row.setGroupManager(mGroupManager); + row.setHeadsUpManager(mHeadsUpManager); + row.setAboveShelfChangedListener(aboveShelf -> {}); UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser()); StatusBarNotification sbn = new StatusBarNotification("com.android.systemui", "com.android.systemui", mId++, null, 1000, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java new file mode 100644 index 000000000000..1ee9b321cf84 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/AboveShelfObserverTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.statusbar.notification; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.support.test.runner.AndroidJUnit4; +import android.test.suitebuilder.annotation.SmallTest; +import android.widget.FrameLayout; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.ExpandableNotificationRow; +import com.android.systemui.statusbar.NotificationTestHelper; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class AboveShelfObserverTest extends SysuiTestCase { + + private AboveShelfObserver mObserver; + private FrameLayout mHostLayout; + private NotificationTestHelper mNotificationTestHelper; + private AboveShelfObserver.HasViewAboveShelfChangedListener mListener; + + @Before + public void setUp() throws Exception { + mNotificationTestHelper = new NotificationTestHelper(getContext()); + mHostLayout = new FrameLayout(getContext()); + mObserver = new AboveShelfObserver(mHostLayout); + ExpandableNotificationRow row = mNotificationTestHelper.createRow(); + row.setAboveShelfChangedListener(mObserver); + mHostLayout.addView(row); + row = mNotificationTestHelper.createRow(); + row.setAboveShelfChangedListener(mObserver); + mHostLayout.addView(row); + mListener = mock(AboveShelfObserver.HasViewAboveShelfChangedListener.class); + } + + @Test + public void testObserverChangesWhenGoingAbove() { + ExpandableNotificationRow row = (ExpandableNotificationRow) mHostLayout.getChildAt(0); + mObserver.setListener(mListener); + row.setHeadsUp(true); + verify(mListener).onHasViewsAboveShelfChanged(true); + } + + @Test + public void testObserverChangesWhenGoingBelow() { + ExpandableNotificationRow row = (ExpandableNotificationRow) mHostLayout.getChildAt(0); + row.setHeadsUp(true); + mObserver.setListener(mListener); + row.setHeadsUp(false); + verify(mListener).onHasViewsAboveShelfChanged(false); + } + + @Test + public void testStaysAboveWhenOneGoesAway() { + ExpandableNotificationRow row = (ExpandableNotificationRow) mHostLayout.getChildAt(0); + row.setHeadsUp(true); + row = (ExpandableNotificationRow) mHostLayout.getChildAt(1); + row.setHeadsUp(true); + row.setHeadsUp(false); + Assert.assertTrue("There are still views above the shelf but removing one cleared it", + mObserver.hasViewsAboveShelf()); + } +} + diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index e1070561a7df..11ea841a10f5 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -4085,6 +4085,11 @@ message MetricsEvent { // Open: Settings > Search > No Result View SETTINGS_SEARCH_NO_RESULT = 1011; + // OPEN: Assist Gesture before training + // CATEGORY: SETTINGS + // OS: O DR + SETTINGS_ASSIST_GESTURE_FIRST_TIME = 1012; + // Add new aosp constants above this line. // END OF AOSP CONSTANTS } diff --git a/services/core/java/com/android/server/SamplingProfilerService.java b/services/core/java/com/android/server/SamplingProfilerService.java deleted file mode 100644 index 5f9269570e9d..000000000000 --- a/services/core/java/com/android/server/SamplingProfilerService.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.content.ContentResolver; -import android.os.DropBoxManager; -import android.os.FileObserver; -import android.os.Binder; - -import android.util.Slog; -import android.content.Context; -import android.database.ContentObserver; -import android.os.SystemProperties; -import android.provider.Settings; -import com.android.internal.os.SamplingProfilerIntegration; -import com.android.internal.util.DumpUtils; - -import java.io.File; -import java.io.FileDescriptor; -import java.io.IOException; -import java.io.PrintWriter; - -public class SamplingProfilerService extends Binder { - - private static final String TAG = "SamplingProfilerService"; - private static final boolean LOCAL_LOGV = false; - public static final String SNAPSHOT_DIR = SamplingProfilerIntegration.SNAPSHOT_DIR; - - private final Context mContext; - private FileObserver snapshotObserver; - - public SamplingProfilerService(Context context) { - mContext = context; - registerSettingObserver(context); - startWorking(context); - } - - private void startWorking(Context context) { - if (LOCAL_LOGV) Slog.v(TAG, "starting SamplingProfilerService!"); - - final DropBoxManager dropbox = - (DropBoxManager) context.getSystemService(Context.DROPBOX_SERVICE); - - // before FileObserver is ready, there could have already been some snapshots - // in the directory, we don't want to miss them - File[] snapshotFiles = new File(SNAPSHOT_DIR).listFiles(); - for (int i = 0; snapshotFiles != null && i < snapshotFiles.length; i++) { - handleSnapshotFile(snapshotFiles[i], dropbox); - } - - // detect new snapshot and put it in dropbox - // delete it afterwards no matter what happened before - // Note: needs listening at event ATTRIB rather than CLOSE_WRITE, because we set the - // readability of snapshot files after writing them! - snapshotObserver = new FileObserver(SNAPSHOT_DIR, FileObserver.ATTRIB) { - @Override - public void onEvent(int event, String path) { - handleSnapshotFile(new File(SNAPSHOT_DIR, path), dropbox); - } - }; - snapshotObserver.startWatching(); - - if (LOCAL_LOGV) Slog.v(TAG, "SamplingProfilerService activated"); - } - - private void handleSnapshotFile(File file, DropBoxManager dropbox) { - try { - dropbox.addFile(TAG, file, 0); - if (LOCAL_LOGV) Slog.v(TAG, file.getPath() + " added to dropbox"); - } catch (IOException e) { - Slog.e(TAG, "Can't add " + file.getPath() + " to dropbox", e); - } finally { - file.delete(); - } - } - - private void registerSettingObserver(Context context) { - ContentResolver contentResolver = context.getContentResolver(); - contentResolver.registerContentObserver( - Settings.Global.getUriFor(Settings.Global.SAMPLING_PROFILER_MS), - false, new SamplingProfilerSettingsObserver(contentResolver)); - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; - - pw.println("SamplingProfilerService:"); - pw.println("Watching directory: " + SNAPSHOT_DIR); - } - - private class SamplingProfilerSettingsObserver extends ContentObserver { - private ContentResolver mContentResolver; - public SamplingProfilerSettingsObserver(ContentResolver contentResolver) { - super(null); - mContentResolver = contentResolver; - onChange(false); - } - @Override - public void onChange(boolean selfChange) { - Integer samplingProfilerMs = Settings.Global.getInt( - mContentResolver, Settings.Global.SAMPLING_PROFILER_MS, 0); - // setting this secure property will start or stop sampling profiler, - // as well as adjust the the time between taking snapshots. - SystemProperties.set("persist.sys.profiler_ms", samplingProfilerMs.toString()); - } - } -} diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index e4a2c2f20762..1604729ee7f3 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -751,6 +751,11 @@ public class AccountManagerService } } + private boolean isVisible(int visibility) { + return visibility == AccountManager.VISIBILITY_VISIBLE || + visibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE; + } + /** * Updates visibility for given account name and package. * @@ -803,8 +808,10 @@ public class AccountManagerService if (notify) { for (Entry<String, Integer> packageToVisibility : packagesToVisibility .entrySet()) { - if (shouldNotifyOnVisibilityChange(packageToVisibility.getValue(), - resolveAccountVisibility(account, packageName, accounts))) { + int oldVisibility = packageToVisibility.getValue(); + int currentVisibility = + resolveAccountVisibility(account, packageName, accounts); + if (isVisible(oldVisibility) != isVisible(currentVisibility)) { notifyPackage(packageToVisibility.getKey(), accounts); } } @@ -1181,8 +1188,7 @@ public class AccountManagerService for (Entry<String, Integer> packageToVisibility : packagesToVisibility.entrySet()) { - if (shouldNotifyOnVisibilityChange(packageToVisibility.getValue(), - AccountManager.VISIBILITY_NOT_VISIBLE)) { + if (isVisible(packageToVisibility.getValue())) { notifyPackage(packageToVisibility.getKey(), accounts); } } @@ -1218,14 +1224,6 @@ public class AccountManagerService } } - private boolean shouldNotifyOnVisibilityChange(int oldVisibility, int newVisibility) { - boolean oldVisible = (oldVisibility == AccountManager.VISIBILITY_VISIBLE) || - (oldVisibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE); - boolean newVisible = (newVisibility == AccountManager.VISIBILITY_VISIBLE) || - (newVisibility == AccountManager.VISIBILITY_USER_MANAGED_VISIBLE); - return oldVisible == newVisible; - } - private SparseBooleanArray getUidsOfInstalledOrUpdatedPackagesAsUser(int userId) { // Get the UIDs of all apps that might have data on the device. We want // to preserve user data if the app might otherwise be storing data. diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 51ad15e743ef..6c500d85f1c9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -19221,10 +19221,11 @@ public class ActivityManagerService extends IActivityManager.Stub final Uri data = intent.getData(); final String ssp; if (data != null && (ssp = data.getSchemeSpecificPart()) != null) { - final ApplicationInfo aInfo = - getPackageManagerInternalLocked().getApplicationInfo( - ssp, - userId); + ApplicationInfo aInfo = null; + try { + aInfo = AppGlobals.getPackageManager() + .getApplicationInfo(ssp, 0 /*flags*/, userId); + } catch (RemoteException ignore) {} if (aInfo == null) { Slog.w(TAG, "Dropping ACTION_PACKAGE_REPLACED for non-existent pkg:" + " ssp=" + ssp + " data=" + data); @@ -24344,7 +24345,6 @@ public class ActivityManagerService extends IActivityManager.Stub } void updateApplicationInfoLocked(@NonNull List<String> packagesToUpdate, int userId) { - final PackageManagerInternal packageManager = getPackageManagerInternalLocked(); final boolean updateFrameworkRes = packagesToUpdate.contains("android"); for (int i = mLruProcesses.size() - 1; i >= 0; i--) { final ProcessRecord app = mLruProcesses.get(i); @@ -24361,8 +24361,8 @@ public class ActivityManagerService extends IActivityManager.Stub final String packageName = app.pkgList.keyAt(j); if (updateFrameworkRes || packagesToUpdate.contains(packageName)) { try { - final ApplicationInfo ai = packageManager.getApplicationInfo( - packageName, app.userId); + final ApplicationInfo ai = AppGlobals.getPackageManager() + .getApplicationInfo(packageName, 0 /*flags*/, app.userId); if (ai != null) { app.thread.scheduleApplicationInfoChanged(ai); } diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 29f9f7c709bb..4a5ce1203f15 100644 --- a/services/core/java/com/android/server/pm/LauncherAppsService.java +++ b/services/core/java/com/android/server/pm/LauncherAppsService.java @@ -34,6 +34,7 @@ import android.content.pm.IPackageManager; import android.content.pm.LauncherApps.ShortcutQuery; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; @@ -100,7 +101,6 @@ public class LauncherAppsService extends SystemService { private static final boolean DEBUG = false; private static final String TAG = "LauncherAppsService"; private final Context mContext; - private final PackageManager mPm; private final UserManager mUm; private final ActivityManagerInternal mActivityManagerInternal; private final ShortcutServiceInternal mShortcutServiceInternal; @@ -113,7 +113,6 @@ public class LauncherAppsService extends SystemService { public LauncherAppsImpl(Context context) { mContext = context; - mPm = mContext.getPackageManager(); mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE); mActivityManagerInternal = Preconditions.checkNotNull( LocalServices.getService(ActivityManagerInternal.class)); @@ -263,15 +262,17 @@ public class LauncherAppsService extends SystemService { void verifyCallingPackage(String callingPackage) { int packageUid = -1; try { - packageUid = mPm.getPackageUidAsUser(callingPackage, + packageUid = AppGlobals.getPackageManager().getPackageUid(callingPackage, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_UNINSTALLED_PACKAGES, UserHandle.getUserId(getCallingUid())); - } catch (NameNotFoundException e) { + } catch (RemoteException ignore) { + } + if (packageUid < 0) { Log.e(TAG, "Package not found: " + callingPackage); } - if (packageUid != Binder.getCallingUid()) { + if (packageUid != injectBinderCallingUid()) { throw new SecurityException("Calling package name mismatch"); } } @@ -315,13 +316,15 @@ public class LauncherAppsService extends SystemService { return null; } + final int callingUid = injectBinderCallingUid(); long ident = Binder.clearCallingIdentity(); try { - IPackageManager pm = AppGlobals.getPackageManager(); - return pm.getActivityInfo(component, + final PackageManagerInternal pmInt = + LocalServices.getService(PackageManagerInternal.class); + return pmInt.getActivityInfo(component, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - user.getIdentifier()); + callingUid, user.getIdentifier()); } finally { Binder.restoreCallingIdentity(ident); } @@ -344,12 +347,15 @@ public class LauncherAppsService extends SystemService { return null; } + final int callingUid = injectBinderCallingUid(); long ident = injectClearCallingIdentity(); try { - List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(intent, + final PackageManagerInternal pmInt = + LocalServices.getService(PackageManagerInternal.class); + List<ResolveInfo> apps = pmInt.queryIntentActivities(intent, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - user.getIdentifier()); + callingUid, user.getIdentifier()); return new ParceledListSlice<>(apps); } finally { injectRestoreCallingIdentity(ident); @@ -390,13 +396,15 @@ public class LauncherAppsService extends SystemService { return false; } + final int callingUid = injectBinderCallingUid(); long ident = Binder.clearCallingIdentity(); try { - IPackageManager pm = AppGlobals.getPackageManager(); - PackageInfo info = pm.getPackageInfo(packageName, + final PackageManagerInternal pmInt = + LocalServices.getService(PackageManagerInternal.class); + PackageInfo info = pmInt.getPackageInfo(packageName, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - user.getIdentifier()); + callingUid, user.getIdentifier()); return info != null && info.applicationInfo.enabled; } finally { Binder.restoreCallingIdentity(ident); @@ -414,11 +422,13 @@ public class LauncherAppsService extends SystemService { return null; } + final int callingUid = injectBinderCallingUid(); long ident = Binder.clearCallingIdentity(); try { - IPackageManager pm = AppGlobals.getPackageManager(); - ApplicationInfo info = pm.getApplicationInfo(packageName, flags, - user.getIdentifier()); + final PackageManagerInternal pmInt = + LocalServices.getService(PackageManagerInternal.class); + ApplicationInfo info = pmInt.getApplicationInfo(packageName, flags, + callingUid, user.getIdentifier()); return info; } finally { Binder.restoreCallingIdentity(ident); @@ -573,13 +583,15 @@ public class LauncherAppsService extends SystemService { return false; } + final int callingUid = injectBinderCallingUid(); long ident = Binder.clearCallingIdentity(); try { - IPackageManager pm = AppGlobals.getPackageManager(); - ActivityInfo info = pm.getActivityInfo(component, + final PackageManagerInternal pmInt = + LocalServices.getService(PackageManagerInternal.class); + ActivityInfo info = pmInt.getActivityInfo(component, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - user.getIdentifier()); + callingUid, user.getIdentifier()); return info != null; } finally { Binder.restoreCallingIdentity(ident); @@ -604,13 +616,15 @@ public class LauncherAppsService extends SystemService { | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); launchIntent.setPackage(component.getPackageName()); + final int callingUid = injectBinderCallingUid(); long ident = Binder.clearCallingIdentity(); try { - IPackageManager pm = AppGlobals.getPackageManager(); - ActivityInfo info = pm.getActivityInfo(component, + final PackageManagerInternal pmInt = + LocalServices.getService(PackageManagerInternal.class); + ActivityInfo info = pmInt.getActivityInfo(component, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - user.getIdentifier()); + callingUid, user.getIdentifier()); if (!info.exported) { throw new SecurityException("Cannot launch non-exported components " + component); @@ -619,10 +633,10 @@ public class LauncherAppsService extends SystemService { // Check that the component actually has Intent.CATEGORY_LAUCNCHER // as calling startActivityAsUser ignores the category and just // resolves based on the component if present. - List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(launchIntent, + List<ResolveInfo> apps = pmInt.queryIntentActivities(launchIntent, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, - user.getIdentifier()); + callingUid, user.getIdentifier()); final int size = apps.size(); for (int i = 0; i < size; ++i) { ActivityInfo activityInfo = apps.get(i).activityInfo; diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 66c870c502c4..48b9c7de650b 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2188,12 +2188,12 @@ public class PackageManagerService extends IPackageManager.Stub private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId, String[] grantedPermissions) { - SettingBase sb = (SettingBase) pkg.mExtras; - if (sb == null) { + PackageSetting ps = (PackageSetting) pkg.mExtras; + if (ps == null) { return; } - PermissionsState permissionsState = sb.getPermissionsState(); + PermissionsState permissionsState = ps.getPermissionsState(); final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED | PackageManager.FLAG_PERMISSION_POLICY_FIXED; @@ -3517,7 +3517,7 @@ public class PackageManagerService extends IPackageManager.Stub * and {@code 0}</li> * <li>The calling application has the permission * {@link android.Manifest.permission#ACCESS_INSTANT_APPS}</li> - * <li>[TODO] The calling application is the default launcher on the + * <li>The calling application is the default launcher on the * system partition.</li> * </ol> */ @@ -3652,22 +3652,27 @@ public class PackageManagerService extends IPackageManager.Stub @Override public PackageInfo getPackageInfo(String packageName, int flags, int userId) { return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST, - flags, userId); + flags, Binder.getCallingUid(), userId); } @Override public PackageInfo getPackageInfoVersioned(VersionedPackage versionedPackage, int flags, int userId) { return getPackageInfoInternal(versionedPackage.getPackageName(), - versionedPackage.getVersionCode(), flags, userId); + versionedPackage.getVersionCode(), flags, Binder.getCallingUid(), userId); } + /** + * Important: The provided filterCallingUid is used exclusively to filter out packages + * that can be seen based on user state. It's typically the original caller uid prior + * to clearing. Because it can only be provided by trusted code, it's value can be + * trusted and will be used as-is; unlike userId which will be validated by this method. + */ private PackageInfo getPackageInfoInternal(String packageName, int versionCode, - int flags, int userId) { + int flags, int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; - final int callingUid = Binder.getCallingUid(); flags = updateFlagsForPackage(flags, userId, packageName); - enforceCrossUserPermission(callingUid, userId, + enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get package info"); // reader @@ -3679,10 +3684,10 @@ public class PackageManagerService extends IPackageManager.Stub if (matchFactoryOnly) { final PackageSetting ps = mSettings.getDisabledSystemPkgLPr(packageName); if (ps != null) { - if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { + if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; } - if (filterAppAccessLPr(ps, callingUid, userId)) { + if (filterAppAccessLPr(ps, filterCallingUid, userId)) { return null; } return generatePackageInfo(ps, flags, userId); @@ -3697,10 +3702,10 @@ public class PackageManagerService extends IPackageManager.Stub Log.v(TAG, "getPackageInfo " + packageName + ": " + p); if (p != null) { final PackageSetting ps = (PackageSetting) p.mExtras; - if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { + if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; } - if (ps != null && filterAppAccessLPr(ps, callingUid, userId)) { + if (ps != null && filterAppAccessLPr(ps, filterCallingUid, userId)) { return null; } return generatePackageInfo((PackageSetting)p.mExtras, flags, userId); @@ -3708,10 +3713,10 @@ public class PackageManagerService extends IPackageManager.Stub if (!matchFactoryOnly && (flags & MATCH_KNOWN_PACKAGES) != 0) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) return null; - if (filterSharedLibPackageLPr(ps, callingUid, userId, flags)) { + if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; } - if (filterAppAccessLPr(ps, callingUid, userId)) { + if (filterAppAccessLPr(ps, filterCallingUid, userId)) { return null; } return generatePackageInfo(ps, flags, userId); @@ -4072,14 +4077,14 @@ public class PackageManagerService extends IPackageManager.Stub } private ApplicationInfo generateApplicationInfoFromSettingsLPw(String packageName, int flags, - int uid, int userId) { + int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; PackageSetting ps = mSettings.mPackages.get(packageName); if (ps != null) { - if (filterSharedLibPackageLPr(ps, uid, userId, flags)) { + if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; } - if (filterAppAccessLPr(ps, uid, userId)) { + if (filterAppAccessLPr(ps, filterCallingUid, userId)) { return null; } if (ps.pkg == null) { @@ -4102,6 +4107,17 @@ public class PackageManagerService extends IPackageManager.Stub @Override public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) { + return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId); + } + + /** + * Important: The provided filterCallingUid is used exclusively to filter out applications + * that can be seen based on user state. It's typically the original caller uid prior + * to clearing. Because it can only be provided by trusted code, it's value can be + * trusted and will be used as-is; unlike userId which will be validated by this method. + */ + private ApplicationInfo getApplicationInfoInternal(String packageName, int flags, + int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; flags = updateFlagsForApplication(flags, userId, packageName); enforceCrossUserPermission(Binder.getCallingUid(), userId, @@ -4120,10 +4136,10 @@ public class PackageManagerService extends IPackageManager.Stub if (p != null) { PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null) return null; - if (filterSharedLibPackageLPr(ps, Binder.getCallingUid(), userId, flags)) { + if (filterSharedLibPackageLPr(ps, filterCallingUid, userId, flags)) { return null; } - if (filterAppAccessLPr(ps, Binder.getCallingUid(), userId)) { + if (filterAppAccessLPr(ps, filterCallingUid, userId)) { return null; } // Note: isEnabledLP() does not apply here - always return info @@ -4141,7 +4157,7 @@ public class PackageManagerService extends IPackageManager.Stub if ((flags & MATCH_KNOWN_PACKAGES) != 0) { // Already generates the external package name return generateApplicationInfoFromSettingsLPw(packageName, - Binder.getCallingUid(), flags, userId); + flags, filterCallingUid, userId); } } return null; @@ -4571,10 +4587,20 @@ public class PackageManagerService extends IPackageManager.Stub @Override public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) { + return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId); + } + + /** + * Important: The provided filterCallingUid is used exclusively to filter out activities + * that can be seen based on user state. It's typically the original caller uid prior + * to clearing. Because it can only be provided by trusted code, it's value can be + * trusted and will be used as-is; unlike userId which will be validated by this method. + */ + private ActivityInfo getActivityInfoInternal(ComponentName component, int flags, + int filterCallingUid, int userId) { if (!sUserManager.exists(userId)) return null; - final int callingUid = Binder.getCallingUid(); flags = updateFlagsForComponent(flags, userId, component); - enforceCrossUserPermission(callingUid, userId, + enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "get activity info"); synchronized (mPackages) { PackageParser.Activity a = mActivities.mActivities.get(component); @@ -4583,7 +4609,7 @@ public class PackageManagerService extends IPackageManager.Stub if (a != null && mSettings.isEnabledAndMatchLPr(a.info, flags, userId)) { PackageSetting ps = mSettings.mPackages.get(component.getPackageName()); if (ps == null) return null; - if (filterAppAccessLPr(ps, callingUid, component, TYPE_ACTIVITY, userId)) { + if (filterAppAccessLPr(ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) { return null; } return generateActivityInfo(a, flags, ps.readUserState(userId), userId); @@ -4879,9 +4905,13 @@ public class PackageManagerService extends IPackageManager.Stub } } - private void updateSequenceNumberLP(String packageName, int[] userList) { + private void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) { for (int i = userList.length - 1; i >= 0; --i) { final int userId = userList[i]; + // don't add instant app to the list of updates + if (pkgSetting.getInstantApp(userId)) { + continue; + } SparseArray<String> changedPackages = mChangedPackages.get(userId); if (changedPackages == null) { changedPackages = new SparseArray<>(); @@ -4892,12 +4922,12 @@ public class PackageManagerService extends IPackageManager.Stub sequenceNumbers = new HashMap<>(); mChangedPackagesSequenceNumbers.put(userId, sequenceNumbers); } - final Integer sequenceNumber = sequenceNumbers.get(packageName); + final Integer sequenceNumber = sequenceNumbers.get(pkgSetting.name); if (sequenceNumber != null) { changedPackages.remove(sequenceNumber); } - changedPackages.put(mChangedPackagesSequenceNumber, packageName); - sequenceNumbers.put(packageName, mChangedPackagesSequenceNumber); + changedPackages.put(mChangedPackagesSequenceNumber, pkgSetting.name); + sequenceNumbers.put(pkgSetting.name, mChangedPackagesSequenceNumber); } mChangedPackagesSequenceNumber++; } @@ -5339,7 +5369,7 @@ public class PackageManagerService extends IPackageManager.Stub "grantRuntimePermission"); final int uid; - final SettingBase sb; + final PackageSetting ps; synchronized (mPackages) { final PackageParser.Package pkg = mPackages.get(packageName); @@ -5350,12 +5380,9 @@ public class PackageManagerService extends IPackageManager.Stub if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + name); } - sb = (SettingBase) pkg.mExtras; - if (sb == null) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - if (sb instanceof PackageSetting - && filterAppAccessLPr((PackageSetting) sb, callingUid, userId)) { + ps = (PackageSetting) pkg.mExtras; + if (ps == null + || filterAppAccessLPr(ps, callingUid, userId)) { throw new IllegalArgumentException("Unknown package: " + packageName); } @@ -5373,7 +5400,7 @@ public class PackageManagerService extends IPackageManager.Stub uid = UserHandle.getUid(userId, pkg.applicationInfo.uid); - final PermissionsState permissionsState = sb.getPermissionsState(); + final PermissionsState permissionsState = ps.getPermissionsState(); final int flags = permissionsState.getPermissionFlags(name, userId); if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { @@ -5395,7 +5422,6 @@ public class PackageManagerService extends IPackageManager.Stub return; } - final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps.getInstantApp(userId) && !bp.isInstant()) { throw new SecurityException("Cannot grant non-ephemeral permission" + name + " for package " + packageName); @@ -5479,7 +5505,11 @@ public class PackageManagerService extends IPackageManager.Stub if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } - + final PackageSetting ps = (PackageSetting) pkg.mExtras; + if (ps == null + || filterAppAccessLPr(ps, Binder.getCallingUid(), userId)) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } final BasePermission bp = mSettings.mPermissions.get(name); if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + name); @@ -5497,12 +5527,7 @@ public class PackageManagerService extends IPackageManager.Stub return; } - SettingBase sb = (SettingBase) pkg.mExtras; - if (sb == null) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - - final PermissionsState permissionsState = sb.getPermissionsState(); + final PermissionsState permissionsState = ps.getPermissionsState(); final int flags = permissionsState.getPermissionFlags(name, userId); if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { @@ -5655,15 +5680,12 @@ public class PackageManagerService extends IPackageManager.Stub if (bp == null) { return 0; } - final SettingBase sb = (SettingBase) pkg.mExtras; - if (sb == null) { - return 0; - } - if (sb instanceof PackageSetting - && filterAppAccessLPr((PackageSetting) sb, callingUid, userId)) { + final PackageSetting ps = (PackageSetting) pkg.mExtras; + if (ps == null + || filterAppAccessLPr(ps, callingUid, userId)) { return 0; } - PermissionsState permissionsState = sb.getPermissionsState(); + PermissionsState permissionsState = ps.getPermissionsState(); return permissionsState.getPermissionFlags(name, userId); } } @@ -5677,7 +5699,8 @@ public class PackageManagerService extends IPackageManager.Stub enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlags"); - enforceCrossUserPermission(Binder.getCallingUid(), userId, + final int callingUid = Binder.getCallingUid(); + enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "updatePermissionFlags"); @@ -5695,18 +5718,18 @@ public class PackageManagerService extends IPackageManager.Stub if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } + final PackageSetting ps = (PackageSetting) pkg.mExtras; + if (ps == null + || filterAppAccessLPr(ps, callingUid, userId)) { + throw new IllegalArgumentException("Unknown package: " + packageName); + } final BasePermission bp = mSettings.mPermissions.get(name); if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + name); } - SettingBase sb = (SettingBase) pkg.mExtras; - if (sb == null) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - - PermissionsState permissionsState = sb.getPermissionsState(); + PermissionsState permissionsState = ps.getPermissionsState(); boolean hadState = permissionsState.getRuntimePermissionState(name, userId) != null; @@ -5750,11 +5773,11 @@ public class PackageManagerService extends IPackageManager.Stub final int packageCount = mPackages.size(); for (int pkgIndex = 0; pkgIndex < packageCount; pkgIndex++) { final PackageParser.Package pkg = mPackages.valueAt(pkgIndex); - SettingBase sb = (SettingBase) pkg.mExtras; - if (sb == null) { + final PackageSetting ps = (PackageSetting) pkg.mExtras; + if (ps == null) { continue; } - PermissionsState permissionsState = sb.getPermissionsState(); + PermissionsState permissionsState = ps.getPermissionsState(); changed |= permissionsState.updatePermissionFlagsForAllPermissions( userId, flagMask, flagValues); } @@ -6304,7 +6327,7 @@ public class PackageManagerService extends IPackageManager.Stub Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities"); final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType, - flags, userId, resolveForStart); + flags, callingUid, userId, resolveForStart); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); final ResolveInfo bestChoice = @@ -6848,15 +6871,16 @@ public class PackageManagerService extends IPackageManager.Stub private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType, int flags, int userId) { - return queryIntentActivitiesInternal(intent, resolvedType, flags, userId, false); + return queryIntentActivitiesInternal( + intent, resolvedType, flags, Binder.getCallingUid(), userId, false); } private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, - String resolvedType, int flags, int userId, boolean resolveForStart) { + String resolvedType, int flags, int filterCallingUid, int userId, + boolean resolveForStart) { if (!sUserManager.exists(userId)) return Collections.emptyList(); - final int callingUid = Binder.getCallingUid(); - final String instantAppPkgName = getInstantAppPackageName(callingUid); - enforceCrossUserPermission(callingUid, userId, + final String instantAppPkgName = getInstantAppPackageName(filterCallingUid); + enforceCrossUserPermission(Binder.getCallingUid(), userId, false /* requireFullPermission */, false /* checkShell */, "query intent activities"); final String pkgName = intent.getPackage(); @@ -6868,7 +6892,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - flags = updateFlagsForResolve(flags, userId, intent, callingUid, resolveForStart, + flags = updateFlagsForResolve(flags, userId, intent, filterCallingUid, resolveForStart, comp != null || pkgName != null /*onlyExposedExplicitly*/); if (comp != null) { final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); @@ -9607,6 +9631,8 @@ public class PackageManagerService extends IPackageManager.Stub public void reconcileSecondaryDexFiles(String packageName) { if (getInstantAppPackageName(Binder.getCallingUid()) != null) { return; + } else if (isInstantApp(packageName, UserHandle.getCallingUserId())) { + return; } mDexManager.reconcileSecondaryDexFiles(packageName); } @@ -14385,8 +14411,8 @@ public class PackageManagerService extends IPackageManager.Stub int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); PackageSetting pkgSetting; - final int uid = Binder.getCallingUid(); - enforceCrossUserPermission(uid, userId, + final int callingUid = Binder.getCallingUid(); + enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "setApplicationHiddenSetting for user " + userId); @@ -14405,6 +14431,9 @@ public class PackageManagerService extends IPackageManager.Stub if (pkgSetting == null) { return false; } + if (filterAppAccessLPr(pkgSetting, callingUid, userId)) { + return false; + } // Do not allow "android" is being disabled if ("android".equals(packageName)) { Slog.w(TAG, "Cannot hide package: android"); @@ -14421,7 +14450,7 @@ public class PackageManagerService extends IPackageManager.Stub return false; } // Only allow protected packages to hide themselves. - if (hidden && !UserHandle.isSameApp(uid, pkgSetting.appId) + if (hidden && !UserHandle.isSameApp(callingUid, pkgSetting.appId) && mProtectedPackages.isPackageStateProtected(userId, packageName)) { Slog.w(TAG, "Not hiding protected package: " + packageName); return false; @@ -14538,6 +14567,20 @@ public class PackageManagerService extends IPackageManager.Stub if (pkgSetting == null) { return PackageManager.INSTALL_FAILED_INVALID_URI; } + if (!canViewInstantApps(callingUid, UserHandle.getUserId(callingUid))) { + // only allow the existing package to be used if it's installed as a full + // application for at least one user + boolean installAllowed = false; + for (int checkUserId : sUserManager.getUserIds()) { + installAllowed = !pkgSetting.getInstantApp(checkUserId); + if (installAllowed) { + break; + } + } + if (!installAllowed) { + return PackageManager.INSTALL_FAILED_INVALID_URI; + } + } if (!pkgSetting.getInstalled(userId)) { pkgSetting.setInstalled(true, userId); pkgSetting.setHidden(false, userId); @@ -14561,7 +14604,7 @@ public class PackageManagerService extends IPackageManager.Stub } sendPackageAddedForUser(packageName, pkgSetting, userId); synchronized (mPackages) { - updateSequenceNumberLP(packageName, new int[]{ userId }); + updateSequenceNumberLP(pkgSetting, new int[]{ userId }); } } } finally { @@ -14607,7 +14650,8 @@ public class PackageManagerService extends IPackageManager.Stub public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); - enforceCrossUserPermission(Binder.getCallingUid(), userId, + final int callingUid = Binder.getCallingUid(); + enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "setPackagesSuspended for user " + userId); @@ -14628,7 +14672,8 @@ public class PackageManagerService extends IPackageManager.Stub final int appId; synchronized (mPackages) { final PackageSetting pkgSetting = mSettings.mPackages.get(packageName); - if (pkgSetting == null) { + if (pkgSetting == null + || filterAppAccessLPr(pkgSetting, callingUid, userId)) { Slog.w(TAG, "Could not find package setting for package \"" + packageName + "\". Skipping suspending/un-suspending."); unactionedPackages.add(packageName); @@ -15038,6 +15083,10 @@ public class PackageManagerService extends IPackageManager.Stub boolean result = false; synchronized (mPackages) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + if (filterAppAccessLPr(ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { + return false; + } result = mSettings.updateIntentFilterVerificationStatusLPw(packageName, status, userId); } if (result) { @@ -15138,7 +15187,9 @@ public class PackageManagerService extends IPackageManager.Stub // writer synchronized (mPackages) { PackageSetting targetPackageSetting = mSettings.mPackages.get(targetPackage); - if (targetPackageSetting == null) { + if (targetPackageSetting == null + || filterAppAccessLPr( + targetPackageSetting, callingUid, UserHandle.getUserId(callingUid))) { throw new IllegalArgumentException("Unknown target package: " + targetPackage); } @@ -15219,7 +15270,9 @@ public class PackageManagerService extends IPackageManager.Stub if (ps == null) { throw new IllegalArgumentException("Unknown target package " + packageName); } - + if (filterAppAccessLPr(ps, Binder.getCallingUid(), UserHandle.getCallingUserId())) { + throw new IllegalArgumentException("Unknown target package " + packageName); + } if (!Objects.equals(callerPackageName, ps.installerPackageName)) { throw new IllegalArgumentException("Calling package " + callerPackageName + " is not installer for " + packageName); @@ -18241,7 +18294,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { - updateSequenceNumberLP(pkgName, res.newUsers); + updateSequenceNumberLP(ps, res.newUsers); updateInstantAppInstallerLocked(pkgName); } } @@ -18467,8 +18520,7 @@ public class PackageManagerService extends IPackageManager.Stub final int callingUid = Binder.getCallingUid(); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DELETE_PACKAGES, null); - final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission( - android.Manifest.permission.ACCESS_INSTANT_APPS); + final boolean canViewInstantApps = canViewInstantApps(callingUid, userId); Preconditions.checkNotNull(versionedPackage); Preconditions.checkNotNull(observer); Preconditions.checkArgumentInRange(versionedPackage.getVersionCode(), @@ -18539,7 +18591,7 @@ public class PackageManagerService extends IPackageManager.Stub final boolean targetIsInstantApp = ps.getInstantApp(UserHandle.getUserId(callingUid)); doDeletePackage = !targetIsInstantApp - || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED; + || canViewInstantApps; } if (doDeletePackage) { if (!deleteAllUsers) { @@ -18850,7 +18902,7 @@ public class PackageManagerService extends IPackageManager.Stub if (pkg != null) { mInstantAppRegistry.onPackageUninstalledLPw(pkg, info.removedUsers); } - updateSequenceNumberLP(packageName, info.removedUsers); + updateSequenceNumberLP(uninstalledPs, info.removedUsers); updateInstantAppInstallerLocked(packageName); } } @@ -21207,7 +21259,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); // Limit who can change which apps if (!UserHandle.isSameApp(callingUid, pkgSetting.appId)) { // Don't allow apps that don't have permission to modify other apps - if (!allowedByPermission) { + if (!allowedByPermission + || filterAppAccessLPr(pkgSetting, callingUid, userId)) { throw new SecurityException( "Attempt to change component state; " + "pid=" + Binder.getCallingPid() @@ -21294,7 +21347,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } } scheduleWritePackageRestrictionsLocked(userId); - updateSequenceNumberLP(packageName, new int[] { userId }); + updateSequenceNumberLP(pkgSetting, new int[] { userId }); final long callingId = Binder.clearCallingIdentity(); try { updateInstantAppInstallerLocked(packageName); @@ -21391,8 +21444,10 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); true /* requireFullPermission */, true /* checkShell */, "stop package"); // writer synchronized (mPackages) { - if (mSettings.setPackageStoppedStateLPw(this, packageName, stopped, - allowedByPermission, callingUid, userId)) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + if (!filterAppAccessLPr(ps, callingUid, userId) + && mSettings.setPackageStoppedStateLPw(this, packageName, stopped, + allowedByPermission, callingUid, userId)) { scheduleWritePackageRestrictionsLocked(userId); } } @@ -21400,11 +21455,16 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); @Override public String getInstallerPackageName(String packageName) { - if (getInstantAppPackageName(Binder.getCallingUid()) != null) { + final int callingUid = Binder.getCallingUid(); + if (getInstantAppPackageName(callingUid) != null) { return null; } // reader synchronized (mPackages) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) { + return null; + } return mSettings.getInstallerPackageNameLPr(packageName); } } @@ -24156,7 +24216,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); @Override public boolean isPackageSignedByKeySet(String packageName, KeySet ks) { - if (getInstantAppPackageName(Binder.getCallingUid()) != null) { + final int callingUid = Binder.getCallingUid(); + if (getInstantAppPackageName(callingUid) != null) { return false; } if (packageName == null || ks == null) { @@ -24164,7 +24225,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } synchronized(mPackages) { final PackageParser.Package pkg = mPackages.get(packageName); - if (pkg == null) { + if (pkg == null + || filterAppAccessLPr((PackageSetting) pkg.mExtras, callingUid, + UserHandle.getUserId(callingUid))) { Slog.w(TAG, "KeySet requested for unknown package: " + packageName); throw new IllegalArgumentException("Unknown package: " + packageName); } @@ -24179,7 +24242,8 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); @Override public boolean isPackageSignedByKeySetExactly(String packageName, KeySet ks) { - if (getInstantAppPackageName(Binder.getCallingUid()) != null) { + final int callingUid = Binder.getCallingUid(); + if (getInstantAppPackageName(callingUid) != null) { return false; } if (packageName == null || ks == null) { @@ -24187,7 +24251,9 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } synchronized(mPackages) { final PackageParser.Package pkg = mPackages.get(packageName); - if (pkg == null) { + if (pkg == null + || filterAppAccessLPr((PackageSetting) pkg.mExtras, callingUid, + UserHandle.getUserId(callingUid))) { Slog.w(TAG, "KeySet requested for unknown package: " + packageName); throw new IllegalArgumentException("Unknown package: " + packageName); } @@ -24504,8 +24570,34 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } @Override - public ApplicationInfo getApplicationInfo(String packageName, int userId) { - return PackageManagerService.this.getApplicationInfo(packageName, 0 /*flags*/, userId); + public PackageInfo getPackageInfo( + String packageName, int flags, int filterCallingUid, int userId) { + return PackageManagerService.this + .getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST, + flags, filterCallingUid, userId); + } + + @Override + public ApplicationInfo getApplicationInfo( + String packageName, int flags, int filterCallingUid, int userId) { + return PackageManagerService.this + .getApplicationInfoInternal(packageName, flags, filterCallingUid, userId); + } + + @Override + public ActivityInfo getActivityInfo( + ComponentName component, int flags, int filterCallingUid, int userId) { + return PackageManagerService.this + .getActivityInfoInternal(component, flags, filterCallingUid, userId); + } + + @Override + public List<ResolveInfo> queryIntentActivities( + Intent intent, int flags, int filterCallingUid, int userId) { + final String resolvedType = intent.resolveTypeIfNeeded(mContext.getContentResolver()); + return PackageManagerService.this + .queryIntentActivitiesInternal(intent, resolvedType, flags, filterCallingUid, + userId, false /*resolveForStart*/); } @Override diff --git a/services/core/java/com/android/server/policy/AccessibilityShortcutController.java b/services/core/java/com/android/server/policy/AccessibilityShortcutController.java index d8e9e16acec5..50900834b16a 100644 --- a/services/core/java/com/android/server/policy/AccessibilityShortcutController.java +++ b/services/core/java/com/android/server/policy/AccessibilityShortcutController.java @@ -234,7 +234,7 @@ public class AccessibilityShortcutController { private AccessibilityServiceInfo getInfoForTargetService() { final String currentShortcutServiceString = getTargetServiceComponentNameString( - mContext, UserHandle.myUserId()); + mContext, UserHandle.USER_CURRENT); if (currentShortcutServiceString == null) { return null; } diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java index 25a0772efd7f..1afde550f027 100644 --- a/services/core/java/com/android/server/tv/TvInputManagerService.java +++ b/services/core/java/com/android/server/tv/TvInputManagerService.java @@ -1218,6 +1218,12 @@ public final class TvInputManagerService extends SystemService { @Override public void setMainSession(IBinder sessionToken, int userId) { + if (mContext.checkCallingPermission( + android.Manifest.permission.CHANGE_HDMI_CEC_ACTIVE_SOURCE) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException( + "The caller does not have CHANGE_HDMI_CEC_ACTIVE_SOURCE permission"); + } if (DEBUG) { Slog.d(TAG, "setMainSession(sessionToken=" + sessionToken + ")"); } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index a8e0d761afc3..ccc8f63e4355 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -1162,8 +1162,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo final int dh = displayInfo.logicalHeight; config.orientation = (dw <= dh) ? Configuration.ORIENTATION_PORTRAIT : Configuration.ORIENTATION_LANDSCAPE; - config.setRotation(displayInfo.rotation); - config.screenWidthDp = (int)(mService.mPolicy.getConfigDisplayWidth(dw, dh, displayInfo.rotation, config.uiMode, mDisplayId) / mDisplayMetrics.density); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 3a116bb10d98..551e3bf13339 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -102,6 +102,8 @@ class TaskSnapshotSurface implements StartingSurface { | FLAG_SCALED | FLAG_SECURE; + private static final int PRIVATE_FLAG_INHERITS = PRIVATE_FLAG_FORCE_DRAW_STATUS_BAR_BACKGROUND; + private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM; private static final int MSG_REPORT_DRAW = 0; private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s"; @@ -160,7 +162,8 @@ class TaskSnapshotSurface implements StartingSurface { layoutParams.flags = (windowFlags & ~FLAG_INHERIT_EXCLUDES) | FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCHABLE; - layoutParams.privateFlags = PRIVATE_FLAG_TASK_SNAPSHOT; + layoutParams.privateFlags = PRIVATE_FLAG_TASK_SNAPSHOT + | (windowPrivateFlags & PRIVATE_FLAG_INHERITS); layoutParams.token = token.token; layoutParams.width = LayoutParams.MATCH_PARENT; layoutParams.height = LayoutParams.MATCH_PARENT; diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 98910ea66ec3..9197fd82238a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -158,6 +158,7 @@ import android.os.Trace; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; +import android.text.format.DateUtils; import android.util.ArraySet; import android.util.DisplayMetrics; import android.util.EventLog; @@ -3600,8 +3601,16 @@ public class WindowManagerService extends IWindowManager.Stub // only allow disables from pids which have count on, etc. @Override public void showStrictModeViolation(boolean on) { - int pid = Binder.getCallingPid(); - mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, on ? 1 : 0, pid)); + final int pid = Binder.getCallingPid(); + if (on) { + // Show the visualization, and enqueue a second message to tear it + // down if we don't hear back from the app. + mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, 1, pid)); + mH.sendMessageDelayed(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, 0, pid), + DateUtils.SECOND_IN_MILLIS); + } else { + mH.sendMessage(mH.obtainMessage(H.SHOW_STRICT_MODE_VIOLATION, 0, pid)); + } } private void showStrictModeViolation(int arg, int pid) { diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 1d08c2ec9151..f74948f8e7f0 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -2231,14 +2231,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mWinAnimator.applyEnterAnimationLocked(); } - // always report back the new configuration - final Configuration globalConfig = mService.mRoot.getConfiguration(); - final Configuration overrideConfig = getMergedOverrideConfiguration(); - mergedConfiguration.setConfiguration(globalConfig, overrideConfig); - if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this - + " reporting new global config: " + globalConfig - + " merged override config: " + overrideConfig); - mLastReportedConfiguration.setTo(getConfiguration()); + if (isConfigChanged()) { + final Configuration globalConfig = mService.mRoot.getConfiguration(); + final Configuration overrideConfig = getMergedOverrideConfiguration(); + mergedConfiguration.setConfiguration(globalConfig, overrideConfig); + if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this + + " visible with new global config: " + globalConfig + + " merged override config: " + overrideConfig); + mLastReportedConfiguration.setTo(getConfiguration()); + } } void adjustStartingWindowFlags() { diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 272c11b985c5..9a756b18c4db 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -55,7 +55,6 @@ import com.android.internal.app.NightDisplayController; import com.android.internal.logging.MetricsLogger; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BinderInternal; -import com.android.internal.os.SamplingProfilerIntegration; import com.android.internal.util.EmergencyAffordanceManager; import com.android.internal.util.ConcurrentUtils; import com.android.internal.widget.ILockSettings; @@ -330,18 +329,6 @@ public final class SystemServer { // the property. http://b/11463182 SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary()); - // Enable the sampling profiler. - if (SamplingProfilerIntegration.isEnabled()) { - SamplingProfilerIntegration.start(); - mProfilerSnapshotTimer = new Timer(); - mProfilerSnapshotTimer.schedule(new TimerTask() { - @Override - public void run() { - SamplingProfilerIntegration.writeSnapshot("system_server", null); - } - }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL); - } - // Mmmmmm... more memory! VMRuntime.getRuntime().clearGrowthLimit(); @@ -710,8 +697,6 @@ public final class SystemServer { false); boolean disableTextServices = SystemProperties.getBoolean("config.disable_textservices", false); - boolean disableSamplingProfiler = SystemProperties.getBoolean("config.disable_samplingprof", - false); boolean disableConsumerIr = SystemProperties.getBoolean("config.disable_consumerir", false); boolean disableVrManager = SystemProperties.getBoolean("config.disable_vrmanager", false); boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice", @@ -1353,21 +1338,6 @@ public final class SystemServer { } traceEnd(); - if (!disableSamplingProfiler) { - traceBeginAndSlog("StartSamplingProfilerService"); - try { - // need to add this service even if SamplingProfilerIntegration.isEnabled() - // is false, because it is this service that detects system property change and - // turns on SamplingProfilerIntegration. Plus, when sampling profiler doesn't work, - // there is little overhead for running this service. - ServiceManager.addService("samplingprofiler", - new SamplingProfilerService(context)); - } catch (Throwable e) { - reportWtf("starting SamplingProfiler Service", e); - } - traceEnd(); - } - if (!disableNetwork && !disableNetworkTime) { traceBeginAndSlog("StartNetworkTimeUpdateService"); try { diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadManager.java index a9ec299c4cd8..c8c6d014a059 100644 --- a/telephony/java/android/telephony/MbmsDownloadManager.java +++ b/telephony/java/android/telephony/MbmsDownloadManager.java @@ -16,19 +16,24 @@ package android.telephony; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.net.Uri; +import android.os.IBinder; import android.os.RemoteException; import android.telephony.mbms.IDownloadCallback; import android.telephony.mbms.DownloadRequest; import android.telephony.mbms.DownloadStatus; import android.telephony.mbms.IMbmsDownloadManagerCallback; import android.telephony.mbms.MbmsException; +import android.telephony.mbms.MbmsUtils; import android.telephony.mbms.vendor.IMbmsDownloadService; import android.util.Log; import java.util.List; +import java.util.concurrent.CountDownLatch; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; @@ -36,6 +41,8 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; public class MbmsDownloadManager { private static final String LOG_TAG = MbmsDownloadManager.class.getSimpleName(); + public static final String MBMS_DOWNLOAD_SERVICE_ACTION = + "android.telephony.action.EmbmsDownload"; /** * The MBMS middleware should send this when a download of single file has completed or * failed. Mandatory extras are @@ -76,15 +83,15 @@ public class MbmsDownloadManager { "android.telephony.mbms.action.CLEANUP"; /** - * Integer extra indicating the result code of the download. - * TODO: put in link to error list - * TODO: future systemapi (here and and all extras) + * Integer extra indicating the result code of the download. One of + * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, or {@link #RESULT_CANCELLED}. */ public static final String EXTRA_RESULT = "android.telephony.mbms.extra.RESULT"; /** * Extra containing the {@link android.telephony.mbms.FileInfo} for which the download result * is for. Must not be null. + * TODO: future systemapi (here and and all extras) except the two for the app intent */ public static final String EXTRA_INFO = "android.telephony.mbms.extra.INFO"; @@ -143,11 +150,23 @@ public class MbmsDownloadManager { public static final String EXTRA_TEMP_FILES_IN_USE = "android.telephony.mbms.extra.TEMP_FILES_IN_USE"; + /** + * Extra containing a single {@link Uri} indicating the location of the successfully + * downloaded file. Set on the intent provided via + * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}. + * Will always be set to a non-null value if {@link #EXTRA_RESULT} is set to + * {@link #RESULT_SUCCESSFUL}. + */ + public static final String EXTRA_COMPLETED_FILE_URI = + "android.telephony.mbms.extra.COMPLETED_FILE_URI"; + public static final int RESULT_SUCCESSFUL = 1; public static final int RESULT_CANCELLED = 2; public static final int RESULT_EXPIRED = 3; // TODO - more results! + private static final long BIND_TIMEOUT_MS = 3000; + private final Context mContext; private int mSubId = INVALID_SUBSCRIPTION_ID; @@ -199,12 +218,31 @@ public class MbmsDownloadManager { } private void bindAndInitialize() throws MbmsException { - // TODO: bind - try { - mService.initialize(mDownloadAppName, mSubId, mCallback); - } catch (RemoteException e) { - throw new MbmsException(0); // TODO: proper error code - } + // TODO: fold binding for download and streaming into a common utils class. + final CountDownLatch latch = new CountDownLatch(1); + ServiceConnection bindListener = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + mService = IMbmsDownloadService.Stub.asInterface(service); + latch.countDown(); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + mService = null; + } + }; + + Intent bindIntent = new Intent(); + bindIntent.setComponent(MbmsUtils.toComponentName( + MbmsUtils.getMiddlewareServiceInfo(mContext, MBMS_DOWNLOAD_SERVICE_ACTION))); + + // Kick off the binding, and synchronously wait until binding is complete + mContext.bindService(bindIntent, bindListener, Context.BIND_AUTO_CREATE); + + MbmsUtils.waitOnLatchWithTimeout(latch, BIND_TIMEOUT_MS); + + // TODO: initialize } /** @@ -245,6 +283,11 @@ public class MbmsDownloadManager { */ public DownloadRequest download(DownloadRequest request, IDownloadCallback listener) { request.setAppName(mDownloadAppName); + try { + mService.download(request, listener); + } catch (RemoteException e) { + mService = null; + } return request; } diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingManager.java index e90a63cb7881..f68e2439971f 100644 --- a/telephony/java/android/telephony/MbmsStreamingManager.java +++ b/telephony/java/android/telephony/MbmsStreamingManager.java @@ -27,6 +27,7 @@ import android.os.IBinder; import android.os.RemoteException; import android.telephony.mbms.MbmsException; import android.telephony.mbms.MbmsStreamingManagerCallback; +import android.telephony.mbms.MbmsUtils; import android.telephony.mbms.StreamingService; import android.telephony.mbms.StreamingServiceCallback; import android.telephony.mbms.StreamingServiceInfo; @@ -62,7 +63,9 @@ public class MbmsStreamingManager { Log.i(LOG_TAG, String.format("Connected to service %s", name)); synchronized (MbmsStreamingManager.this) { mService = IMbmsStreamingService.Stub.asInterface(service); - mServiceListeners.forEach(ServiceListener::onServiceConnected); + for (ServiceListener l : mServiceListeners) { + l.onServiceConnected(); + } } } } @@ -72,10 +75,13 @@ public class MbmsStreamingManager { Log.i(LOG_TAG, String.format("Disconnected from service %s", name)); synchronized (MbmsStreamingManager.this) { mService = null; - mServiceListeners.forEach(ServiceListener::onServiceDisconnected); + for (ServiceListener l : mServiceListeners) { + l.onServiceDisconnected(); + } } } }; + private List<ServiceListener> mServiceListeners = new LinkedList<>(); private MbmsStreamingManagerCallback mCallbackToApp; @@ -218,22 +224,6 @@ public class MbmsStreamingManager { } private void bindAndInitialize() throws MbmsException { - // Query for the proper service - PackageManager packageManager = mContext.getPackageManager(); - Intent queryIntent = new Intent(); - queryIntent.setAction(MBMS_STREAMING_SERVICE_ACTION); - List<ResolveInfo> streamingServices = packageManager.queryIntentServices(queryIntent, - PackageManager.MATCH_SYSTEM_ONLY); - - if (streamingServices == null || streamingServices.size() == 0) { - throw new MbmsException( - MbmsException.ERROR_NO_SERVICE_INSTALLED); - } - if (streamingServices.size() > 1) { - throw new MbmsException( - MbmsException.ERROR_MULTIPLE_SERVICES_INSTALLED); - } - // Kick off the binding, and synchronously wait until binding is complete final CountDownLatch latch = new CountDownLatch(1); ServiceListener bindListener = new ServiceListener() { @@ -252,13 +242,14 @@ public class MbmsStreamingManager { } Intent bindIntent = new Intent(); - bindIntent.setComponent(streamingServices.get(0).getComponentInfo().getComponentName()); + bindIntent.setComponent(MbmsUtils.toComponentName( + MbmsUtils.getMiddlewareServiceInfo(mContext, MBMS_STREAMING_SERVICE_ACTION))); mContext.bindService(bindIntent, mServiceConnection, Context.BIND_AUTO_CREATE); - waitOnLatchWithTimeout(latch, BIND_TIMEOUT_MS); + MbmsUtils.waitOnLatchWithTimeout(latch, BIND_TIMEOUT_MS); - // Remove the listener and call the initialization method through the interface. + // Remove the listener and call the initialization method through the interface. synchronized (this) { mServiceListeners.remove(bindListener); @@ -279,17 +270,4 @@ public class MbmsStreamingManager { } } - private static void waitOnLatchWithTimeout(CountDownLatch l, long timeoutMs) { - long endTime = System.currentTimeMillis() + timeoutMs; - while (System.currentTimeMillis() < endTime) { - try { - l.await(timeoutMs, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - // keep waiting - } - if (l.getCount() <= 0) { - return; - } - } - } } diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java new file mode 100644 index 000000000000..c01ddaedbd88 --- /dev/null +++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java @@ -0,0 +1,365 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.mbms; + +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.telephony.MbmsDownloadManager; +import android.util.Log; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; + +/** + * @hide + */ +public class MbmsDownloadReceiver extends BroadcastReceiver { + private static final String LOG_TAG = "MbmsDownloadReceiver"; + private static final String TEMP_FILE_SUFFIX = ".embms.temp"; + private static final int MAX_TEMP_FILE_RETRIES = 5; + + public static final String MBMS_FILE_PROVIDER_META_DATA_KEY = "mbms-file-provider-authority"; + + private String mFileProviderAuthorityCache = null; + private String mMiddlewarePackageNameCache = null; + + @Override + public void onReceive(Context context, Intent intent) { + if (!verifyIntentContents(intent)) { + setResultCode(1 /* TODO: define error constants */); + return; + } + + if (MbmsDownloadManager.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) { + moveDownloadedFile(context, intent); + cleanupPostMove(context, intent); + } else if (MbmsDownloadManager.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) { + generateTempFiles(context, intent); + } + // TODO: Add handling for ACTION_CLEANUP + } + + private boolean verifyIntentContents(Intent intent) { + if (MbmsDownloadManager.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) { + if (!intent.hasExtra(MbmsDownloadManager.EXTRA_RESULT)) { + Log.w(LOG_TAG, "Download result did not include a result code. Ignoring."); + return false; + } + if (!intent.hasExtra(MbmsDownloadManager.EXTRA_REQUEST)) { + Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring."); + return false; + } + if (!intent.hasExtra(MbmsDownloadManager.EXTRA_INFO)) { + Log.w(LOG_TAG, "Download result did not include the associated file info. " + + "Ignoring."); + return false; + } + if (!intent.hasExtra(MbmsDownloadManager.EXTRA_FINAL_URI)) { + Log.w(LOG_TAG, "Download result did not include the path to the final " + + "temp file. Ignoring."); + return false; + } + return true; + } else if (MbmsDownloadManager.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) { + if (!intent.hasExtra(MbmsDownloadManager.EXTRA_REQUEST)) { + Log.w(LOG_TAG, "Temp file request not include the associated request. Ignoring."); + return false; + } + return true; + } + + Log.w(LOG_TAG, "Received intent with unknown action: " + intent.getAction()); + return false; + } + + private void moveDownloadedFile(Context context, Intent intent) { + DownloadRequest request = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_REQUEST); + // TODO: check request against token + Intent intentForApp = request.getIntentForApp(); + + int result = intent.getIntExtra(MbmsDownloadManager.EXTRA_RESULT, + MbmsDownloadManager.RESULT_CANCELLED); + intentForApp.putExtra(MbmsDownloadManager.EXTRA_RESULT, result); + + if (result != MbmsDownloadManager.RESULT_SUCCESSFUL) { + Log.i(LOG_TAG, "Download request indicated a failed download. Aborting."); + context.sendBroadcast(intentForApp); + return; + } + + Uri destinationUri = request.getDestinationUri(); + Uri finalTempFile = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FINAL_URI); + if (!verifyTempFilePath(context, request, finalTempFile)) { + Log.w(LOG_TAG, "Download result specified an invalid temp file " + finalTempFile); + setResultCode(1); + return; + } + + String relativePath = calculateDestinationFileRelativePath(request, + (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_INFO)); + + if (!moveTempFile(finalTempFile, destinationUri, relativePath)) { + Log.w(LOG_TAG, "Failed to move temp file to final destination"); + setResultCode(1); + } + + context.sendBroadcast(intentForApp); + setResultCode(0); + } + + private void cleanupPostMove(Context context, Intent intent) { + // TODO: account for in-use temp files + DownloadRequest request = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_REQUEST); + if (request == null) { + Log.w(LOG_TAG, "Intent does not include a DownloadRequest. Ignoring."); + return; + } + + List<Uri> tempFiles = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_TEMP_LIST); + if (tempFiles == null) { + return; + } + + for (Uri tempFileUri : tempFiles) { + if (verifyTempFilePath(context, request, tempFileUri)) { + File tempFile = new File(tempFileUri.getSchemeSpecificPart()); + tempFile.delete(); + } + } + } + + private void generateTempFiles(Context context, Intent intent) { + // TODO: update pursuant to final decision on temp file locations + DownloadRequest request = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_REQUEST); + if (request == null) { + Log.w(LOG_TAG, "Temp file request did not include the associated request. Ignoring."); + setResultCode(1 /* TODO: define error constants */); + return; + } + int fdCount = intent.getIntExtra(MbmsDownloadManager.EXTRA_FD_COUNT, 0); + List<Uri> pausedList = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_PAUSED_LIST); + + if (fdCount == 0 && (pausedList == null || pausedList.size() == 0)) { + Log.i(LOG_TAG, "No temp files actually requested. Ending."); + setResultCode(0); + setResultExtras(Bundle.EMPTY); + return; + } + + ArrayList<UriPathPair> freshTempFiles = generateFreshTempFiles(context, request, fdCount); + ArrayList<UriPathPair> pausedFiles = + generateUrisForPausedFiles(context, request, pausedList); + + Bundle result = new Bundle(); + result.putParcelableArrayList(MbmsDownloadManager.EXTRA_FREE_URI_LIST, freshTempFiles); + result.putParcelableArrayList(MbmsDownloadManager.EXTRA_PAUSED_URI_LIST, pausedFiles); + setResultExtras(result); + } + + private ArrayList<UriPathPair> generateFreshTempFiles(Context context, DownloadRequest request, + int freshFdCount) { + File tempFileDir = getEmbmsTempFileDirForRequest(context, request); + if (!tempFileDir.exists()) { + tempFileDir.mkdirs(); + } + + // Name the files with the template "N-UUID", where N is the request ID and UUID is a + // random uuid. + ArrayList<UriPathPair> result = new ArrayList<>(freshFdCount); + for (int i = 0; i < freshFdCount; i++) { + File tempFile = generateSingleTempFile(tempFileDir); + if (tempFile == null) { + setResultCode(2 /* TODO: define error constants */); + Log.w(LOG_TAG, "Failed to generate a temp file. Moving on."); + continue; + } + Uri fileUri = Uri.fromParts(ContentResolver.SCHEME_FILE, tempFile.getPath(), null); + Uri contentUri = MbmsTempFileProvider.getUriForFile( + context, getFileProviderAuthorityCached(context), tempFile); + context.grantUriPermission(getMiddlewarePackageCached(context), contentUri, + Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + result.add(new UriPathPair(fileUri, contentUri)); + } + + return result; + } + + private static File generateSingleTempFile(File tempFileDir) { + int numTries = 0; + while (numTries < MAX_TEMP_FILE_RETRIES) { + numTries++; + String fileName = UUID.randomUUID() + TEMP_FILE_SUFFIX; + File tempFile = new File(tempFileDir, fileName); + try { + if (tempFile.createNewFile()) { + return tempFile.getCanonicalFile(); + } + } catch (IOException e) { + continue; + } + } + return null; + } + + + private ArrayList<UriPathPair> generateUrisForPausedFiles(Context context, + DownloadRequest request, List<Uri> pausedFiles) { + if (pausedFiles == null) { + return new ArrayList<>(0); + } + ArrayList<UriPathPair> result = new ArrayList<>(pausedFiles.size()); + + for (Uri fileUri : pausedFiles) { + if (!verifyTempFilePath(context, request, fileUri)) { + Log.w(LOG_TAG, "Supplied file " + fileUri + " is not a valid temp file to resume"); + setResultCode(2 /* TODO: define error codes */); + continue; + } + File tempFile = new File(fileUri.getSchemeSpecificPart()); + if (!tempFile.exists()) { + Log.w(LOG_TAG, "Supplied file " + fileUri + " does not exist."); + setResultCode(2 /* TODO: define error codes */); + continue; + } + Uri contentUri = MbmsTempFileProvider.getUriForFile( + context, getFileProviderAuthorityCached(context), tempFile); + context.grantUriPermission(getMiddlewarePackageCached(context), contentUri, + Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + + result.add(new UriPathPair(fileUri, contentUri)); + } + return result; + } + + private static String calculateDestinationFileRelativePath(DownloadRequest request, + FileInfo info) { + // TODO: determine whether this is actually the path determination scheme we want to use + List<String> filePathComponents = info.uri.getPathSegments(); + List<String> requestPathComponents = request.getSourceUri().getPathSegments(); + Iterator<String> filePathIter = filePathComponents.iterator(); + Iterator<String> requestPathIter = requestPathComponents.iterator(); + + LinkedList<String> relativePathComponents = new LinkedList<>(); + while (filePathIter.hasNext()) { + String currFilePathComponent = filePathIter.next(); + if (requestPathIter.hasNext()) { + String requestFilePathComponent = requestPathIter.next(); + if (requestFilePathComponent.equals(currFilePathComponent)) { + continue; + } + } + relativePathComponents.add(currFilePathComponent); + } + return String.join("/", relativePathComponents); + } + + private static boolean moveTempFile(Uri fromPath, Uri toPath, String relativePath) { + if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) { + Log.w(LOG_TAG, "Moving source uri " + fromPath+ " does not have a file scheme"); + return false; + } + if (!ContentResolver.SCHEME_FILE.equals(toPath.getScheme())) { + Log.w(LOG_TAG, "Moving destination uri " + toPath + " does not have a file scheme"); + return false; + } + + File fromFile = new File(fromPath.getSchemeSpecificPart()); + File toFile = new File(toPath.getSchemeSpecificPart(), relativePath); + toFile.getParentFile().mkdirs(); + + // TODO: This may not work if the two files are on different filesystems. Should we + // enforce that the temp file storage and the permanent storage are both in the same fs? + return fromFile.renameTo(toFile); + } + + private static boolean verifyTempFilePath(Context context, DownloadRequest request, + Uri filePath) { + // TODO: modify pursuant to final decision on temp file path scheme + if (!ContentResolver.SCHEME_FILE.equals(filePath.getScheme())) { + Log.w(LOG_TAG, "Uri " + filePath + " does not have a file scheme"); + return false; + } + + String path = filePath.getSchemeSpecificPart(); + File tempFile = new File(path); + if (!tempFile.exists()) { + Log.w(LOG_TAG, "File at " + path + " does not exist."); + return false; + } + + if (!MbmsUtils.isContainedIn(getEmbmsTempFileDirForRequest(context, request), tempFile)) { + return false; + } + + return true; + } + + /** + * Returns a File linked to the directory used to store temp files for this request + */ + private static File getEmbmsTempFileDirForRequest(Context context, DownloadRequest request) { + File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir( + context, getFileProviderAuthority(context)); + + // TODO: better naming scheme for temp file dirs + String tempFileDirName = String.valueOf(request.getFileServiceInfo().getServiceId()); + return new File(embmsTempFileDir, tempFileDirName); + } + + private String getFileProviderAuthorityCached(Context context) { + if (mFileProviderAuthorityCache != null) { + return mFileProviderAuthorityCache; + } + + mFileProviderAuthorityCache = getFileProviderAuthority(context); + return mFileProviderAuthorityCache; + } + + private static String getFileProviderAuthority(Context context) { + ApplicationInfo appInfo; + try { + appInfo = context.getPackageManager() + .getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA); + } catch (PackageManager.NameNotFoundException e) { + throw new RuntimeException("Package manager couldn't find " + context.getPackageName()); + } + String authority = appInfo.metaData.getString(MBMS_FILE_PROVIDER_META_DATA_KEY); + if (authority == null) { + throw new RuntimeException("Must declare the file provider authority as meta data"); + } + return authority; + } + + private String getMiddlewarePackageCached(Context context) { + if (mMiddlewarePackageNameCache == null) { + mMiddlewarePackageNameCache = MbmsUtils.getMiddlewareServiceInfo(context, + MbmsDownloadManager.MBMS_DOWNLOAD_SERVICE_ACTION).packageName; + } + return mMiddlewarePackageNameCache; + } +} diff --git a/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java b/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java new file mode 100644 index 000000000000..9842581cdc02 --- /dev/null +++ b/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.mbms; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ContentProvider; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; + +/** + * @hide + */ +public class MbmsTempFileProvider extends ContentProvider { + public static final String META_DATA_USE_EXTERNAL_STORAGE = "use-external-storage"; + public static final String META_DATA_TEMP_FILE_DIRECTORY = "temp-file-path"; + public static final String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot"; + + private String mAuthority; + private Context mContext; + + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(@NonNull Uri uri, @Nullable String[] projection, + @Nullable String selection, @Nullable String[] selectionArgs, + @Nullable String sortOrder) { + throw new UnsupportedOperationException("No querying supported"); + } + + @Override + public String getType(@NonNull Uri uri) { + // EMBMS temp files can contain arbitrary content. + return "application/octet-stream"; + } + + @Override + public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { + throw new UnsupportedOperationException("No inserting supported"); + } + + @Override + public int delete(@NonNull Uri uri, @Nullable String selection, + @Nullable String[] selectionArgs) { + throw new UnsupportedOperationException("No deleting supported"); + } + + @Override + public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String + selection, @Nullable String[] selectionArgs) { + throw new UnsupportedOperationException("No updating supported"); + } + + @Override + public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { + // ContentProvider has already checked granted permissions + final File file = getFileForUri(mContext, mAuthority, uri); + final int fileMode = ParcelFileDescriptor.parseMode(mode); + return ParcelFileDescriptor.open(file, fileMode); + } + + @Override + public void attachInfo(Context context, ProviderInfo info) { + super.attachInfo(context, info); + + // Sanity check our security + if (info.exported) { + throw new SecurityException("Provider must not be exported"); + } + if (!info.grantUriPermissions) { + throw new SecurityException("Provider must grant uri permissions"); + } + + mAuthority = info.authority; + mContext = context; + } + + public static Uri getUriForFile(Context context, String authority, File file) { + // Get the canonical path of the temp file + String filePath; + try { + filePath = file.getCanonicalPath(); + } catch (IOException e) { + throw new IllegalArgumentException("Could not get canonical path for file " + file); + } + + // Make sure the temp file is contained in the temp file directory as configured in the + // manifest + File tempFileDir = getEmbmsTempFileDir(context, authority); + if (!MbmsUtils.isContainedIn(tempFileDir, file)) { + throw new IllegalArgumentException("File " + file + " is not contained in the temp " + + "file directory, which is " + tempFileDir); + } + + // Get the canonical path of the temp file directory + String tempFileDirPath; + try { + tempFileDirPath = tempFileDir.getCanonicalPath(); + } catch (IOException e) { + throw new RuntimeException( + "Could not get canonical path for temp file root dir " + tempFileDir); + } + + // Start at first char of path under temp file directory + String pathFragment; + if (tempFileDirPath.endsWith("/")) { + pathFragment = filePath.substring(tempFileDirPath.length()); + } else { + pathFragment = filePath.substring(tempFileDirPath.length() + 1); + } + + String encodedPath = Uri.encode(pathFragment); + return new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT) + .authority(authority).encodedPath(encodedPath).build(); + } + + public static File getFileForUri(Context context, String authority, Uri uri) + throws FileNotFoundException { + if (!ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) { + throw new IllegalArgumentException("Uri must have scheme content"); + } + + String relPath = Uri.decode(uri.getEncodedPath()); + File file; + File tempFileDir; + + try { + tempFileDir = getEmbmsTempFileDir(context, authority).getCanonicalFile(); + file = new File(tempFileDir, relPath).getCanonicalFile(); + } catch (IOException e) { + throw new FileNotFoundException("Could not resolve paths"); + } + + if (!file.getPath().startsWith(tempFileDir.getPath())) { + throw new SecurityException("Resolved path jumped beyond configured root"); + } + + return file; + } + + /** + * Returns a File for the directory used to store temp files for this app + */ + public static File getEmbmsTempFileDir(Context context, String authority) { + Bundle metadata = getMetadata(context, authority); + File parentDirectory; + if (metadata.getBoolean(META_DATA_USE_EXTERNAL_STORAGE, false)) { + parentDirectory = context.getExternalFilesDir(null); + } else { + parentDirectory = context.getFilesDir(); + } + + String tmpFilePath = metadata.getString(META_DATA_TEMP_FILE_DIRECTORY); + if (tmpFilePath == null) { + tmpFilePath = DEFAULT_TOP_LEVEL_TEMP_DIRECTORY; + } + return new File(parentDirectory, tmpFilePath); + } + + private static Bundle getMetadata(Context context, String authority) { + final ProviderInfo info = context.getPackageManager() + .resolveContentProvider(authority, PackageManager.GET_META_DATA); + return info.metaData; + } +} diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java new file mode 100644 index 000000000000..de308053df56 --- /dev/null +++ b/telephony/java/android/telephony/mbms/MbmsUtils.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.mbms; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.*; +import android.content.pm.ServiceInfo; +import android.telephony.MbmsDownloadManager; +import android.util.Log; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * @hide + */ +public class MbmsUtils { + private static final String LOG_TAG = "MbmsUtils"; + + public static boolean isContainedIn(File parent, File child) { + try { + String parentPath = parent.getCanonicalPath(); + String childPath = child.getCanonicalPath(); + return childPath.startsWith(parentPath); + } catch (IOException e) { + throw new RuntimeException("Failed to resolve canonical paths: " + e); + } + } + + public static void waitOnLatchWithTimeout(CountDownLatch l, long timeoutMs) { + long endTime = System.currentTimeMillis() + timeoutMs; + while (System.currentTimeMillis() < endTime) { + try { + l.await(timeoutMs, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + // keep waiting + } + if (l.getCount() <= 0) { + return; + } + } + } + + public static ComponentName toComponentName(ComponentInfo ci) { + return new ComponentName(ci.packageName, ci.name); + } + + public static ServiceInfo getMiddlewareServiceInfo(Context context, String serviceAction) { + // Query for the proper service + PackageManager packageManager = context.getPackageManager(); + Intent queryIntent = new Intent(); + queryIntent.setAction(serviceAction); + List<ResolveInfo> downloadServices = packageManager.queryIntentServices(queryIntent, + PackageManager.MATCH_SYSTEM_ONLY); + + if (downloadServices == null || downloadServices.size() == 0) { + Log.w(LOG_TAG, "No download services found, cannot get service info"); + return null; + } + + if (downloadServices.size() > 1) { + Log.w(LOG_TAG, "More than one download service found, cannot get unique service"); + return null; + } + return downloadServices.get(0).serviceInfo; + } +} diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java index 8a4ef23238f5..06e3867e4ee3 100644 --- a/wifi/java/android/net/wifi/aware/DiscoverySession.java +++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java @@ -342,8 +342,8 @@ public class DiscoverySession implements AutoCloseable { */ public NetworkSpecifier createNetworkSpecifierPassphrase( @Nullable PeerHandle peerHandle, @NonNull String passphrase) { - if (passphrase == null || passphrase.length() == 0) { - throw new IllegalArgumentException("Passphrase must not be null or empty"); + if (!WifiAwareUtils.validatePassphrase(passphrase)) { + throw new IllegalArgumentException("Passphrase must meet length requirements"); } if (mTerminated) { @@ -405,8 +405,8 @@ public class DiscoverySession implements AutoCloseable { @SystemApi public NetworkSpecifier createNetworkSpecifierPmk(@Nullable PeerHandle peerHandle, @NonNull byte[] pmk) { - if (pmk == null || pmk.length == 0) { - throw new IllegalArgumentException("PMK must not be null or empty"); + if (!WifiAwareUtils.validatePmk(pmk)) { + throw new IllegalArgumentException("PMK must 32 bytes"); } if (mTerminated) { diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java index 678d2e645901..babbed02a11c 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java @@ -269,9 +269,10 @@ public class WifiAwareSession implements AutoCloseable { Log.e(TAG, "createNetworkSpecifierPassphrase: called after termination"); return null; } - if (passphrase == null || passphrase.length() == 0) { - throw new IllegalArgumentException("Passphrase must not be null or empty"); + if (!WifiAwareUtils.validatePassphrase(passphrase)) { + throw new IllegalArgumentException("Passphrase must meet length requirements"); } + return mgr.createNetworkSpecifier(mClientId, role, peer, null, passphrase); } @@ -319,8 +320,8 @@ public class WifiAwareSession implements AutoCloseable { Log.e(TAG, "createNetworkSpecifierPmk: called after termination"); return null; } - if (pmk == null || pmk.length == 0) { - throw new IllegalArgumentException("PMK must not be null or empty"); + if (!WifiAwareUtils.validatePmk(pmk)) { + throw new IllegalArgumentException("PMK must 32 bytes"); } return mgr.createNetworkSpecifier(mClientId, role, peer, pmk, null); } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareUtils.java b/wifi/java/android/net/wifi/aware/WifiAwareUtils.java index 40833887c09e..fda7a9abc318 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareUtils.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareUtils.java @@ -16,6 +16,8 @@ package android.net.wifi.aware; +import android.hardware.wifi.V1_0.Constants; + /** * Provides utilities for the Wifi Aware manager/service. * @@ -51,4 +53,35 @@ public class WifiAwareUtils { ++index; } } + + /** + * Validates that the passphrase is a non-null string of the right size (per the HAL min/max + * length parameters). + * + * @param passphrase Passphrase to test + * @return true if passphrase is valid, false if not + */ + public static boolean validatePassphrase(String passphrase) { + if (passphrase == null + || passphrase.length() < Constants.NanParamSizeLimits.MIN_PASSPHRASE_LENGTH + || passphrase.length() > Constants.NanParamSizeLimits.MAX_PASSPHRASE_LENGTH) { + return false; + } + + return true; + } + + /** + * Validates that the PMK is a non-null byte array of the right size (32 bytes per spec). + * + * @param pmk PMK to test + * @return true if PMK is valid, false if not + */ + public static boolean validatePmk(byte[] pmk) { + if (pmk == null || pmk.length != 32) { + return false; + } + + return true; + } } diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index 694b911a51e5..d9433c5a9d00 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -32,6 +32,7 @@ import android.net.wifi.RttManager; import android.os.Handler; import android.os.IBinder; import android.os.Parcel; +import android.os.RemoteException; import android.os.test.TestLooper; import android.test.suitebuilder.annotation.SmallTest; @@ -938,7 +939,7 @@ public class WifiAwareManagerTest { final int sessionId = 123; final PeerHandle peerHandle = new PeerHandle(123412); final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER; - final byte[] pmk = "Some arbitrary byte array".getBytes(); + final byte[] pmk = "01234567890123456789012345678901".getBytes(); final String passphrase = "A really bad password"; final ConfigRequest configRequest = new ConfigRequest.Builder().build(); final PublishConfig publishConfig = new PublishConfig.Builder().build(); @@ -1019,7 +1020,7 @@ public class WifiAwareManagerTest { final ConfigRequest configRequest = new ConfigRequest.Builder().build(); final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false); final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR; - final byte[] pmk = "Some arbitrary pmk data".getBytes(); + final byte[] pmk = "01234567890123456789012345678901".getBytes(); final String passphrase = "A really bad password"; ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass( @@ -1070,4 +1071,162 @@ public class WifiAwareManagerTest { verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService, mockPublishSession, mockRttListener); } + + /** + * Validate that a null PMK triggers an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testNetworkSpecifierWithClientNullPmk() throws Exception { + executeNetworkSpecifierWithClient(true, null, null); + } + + /** + * Validate that a non-32-bytes PMK triggers an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testNetworkSpecifierWithClientIncorrectLengthPmk() throws Exception { + executeNetworkSpecifierWithClient(true, "012".getBytes(), null); + } + + /** + * Validate that a null Passphrase triggers an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testNetworkSpecifierWithClientNullPassphrase() throws Exception { + executeNetworkSpecifierWithClient(false, null, null); + } + + /** + * Validate that a too short Passphrase triggers an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testNetworkSpecifierWithClientShortPassphrase() throws Exception { + executeNetworkSpecifierWithClient(false, null, "012"); + } + + /** + * Validate that a too long Passphrase triggers an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testNetworkSpecifierWithClientLongPassphrase() throws Exception { + executeNetworkSpecifierWithClient(false, null, + "0123456789012345678901234567890123456789012345678901234567890123456789"); + } + + private void executeNetworkSpecifierWithClient(boolean doPmk, byte[] pmk, String passphrase) + throws Exception { + final int clientId = 4565; + final int sessionId = 123; + final PeerHandle peerHandle = new PeerHandle(123412); + final ConfigRequest configRequest = new ConfigRequest.Builder().build(); + final PublishConfig publishConfig = new PublishConfig.Builder().build(); + + ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass( + WifiAwareSession.class); + ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor + .forClass(IWifiAwareEventCallback.class); + ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor + .forClass(IWifiAwareDiscoverySessionCallback.class); + ArgumentCaptor<PublishDiscoverySession> publishSession = ArgumentCaptor + .forClass(PublishDiscoverySession.class); + + InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService, + mockPublishSession, mockRttListener); + + // (1) connect successfully + mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); + inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + eq(configRequest), eq(false)); + clientProxyCallback.getValue().onConnectSuccess(clientId); + mMockLooper.dispatchAll(); + inOrder.verify(mockCallback).onAttached(sessionCaptor.capture()); + WifiAwareSession session = sessionCaptor.getValue(); + + // (2) publish successfully + session.publish(publishConfig, mockSessionCallback, mMockLooperHandler); + inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig), + sessionProxyCallback.capture()); + sessionProxyCallback.getValue().onSessionStarted(sessionId); + mMockLooper.dispatchAll(); + inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture()); + + // (3) create network specifier + if (doPmk) { + publishSession.getValue().createNetworkSpecifierPmk(peerHandle, pmk); + } else { + publishSession.getValue().createNetworkSpecifierPassphrase(peerHandle, passphrase); + } + } + + /** + * Validate that a null PMK triggers an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testNetworkSpecifierDirectNullPmk() throws Exception { + executeNetworkSpecifierDirect(true, null, null); + } + + /** + * Validate that a non-32-bytes PMK triggers an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testNetworkSpecifierDirectIncorrectLengthPmk() throws Exception { + executeNetworkSpecifierDirect(true, "012".getBytes(), null); + } + + /** + * Validate that a null Passphrase triggers an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testNetworkSpecifierDirectNullPassphrase() throws Exception { + executeNetworkSpecifierDirect(false, null, null); + } + + /** + * Validate that a too short Passphrase triggers an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testNetworkSpecifierDirectShortPassphrase() throws Exception { + executeNetworkSpecifierDirect(false, null, "012"); + } + + /** + * Validate that a too long Passphrase triggers an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testNetworkSpecifierDirectLongPassphrase() throws Exception { + executeNetworkSpecifierDirect(false, null, + "0123456789012345678901234567890123456789012345678901234567890123456789"); + } + + private void executeNetworkSpecifierDirect(boolean doPmk, byte[] pmk, String passphrase) + throws Exception { + final int clientId = 134; + final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false); + final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR; + final ConfigRequest configRequest = new ConfigRequest.Builder().build(); + + ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass( + WifiAwareSession.class); + ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor + .forClass(IWifiAwareEventCallback.class); + + InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService, + mockPublishSession, mockRttListener); + + // (1) connect successfully + mDut.attach(mMockLooperHandler, configRequest, mockCallback, null); + inOrder.verify(mockAwareService).connect(any(), any(), clientProxyCallback.capture(), + eq(configRequest), eq(false)); + clientProxyCallback.getValue().onConnectSuccess(clientId); + mMockLooper.dispatchAll(); + inOrder.verify(mockCallback).onAttached(sessionCaptor.capture()); + + // (2) create network specifier + if (doPmk) { + sessionCaptor.getValue().createNetworkSpecifierPmk(role, someMac, pmk); + } else { + sessionCaptor.getValue().createNetworkSpecifierPassphrase(role, someMac, passphrase); + } + } } |