Merge "Add comment to Typeface.Builder about file descriptor." into oc-dev
diff --git a/api/current.txt b/api/current.txt
index 4149d03..e4169b2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -211,6 +211,7 @@
ctor public R.attr();
field public static final int __removed1 = 16844099; // 0x1010543
field public static final int __removed2 = 16844104; // 0x1010548
+ field public static final int __removed3 = 16844116; // 0x1010554
field public static final int absListViewStyle = 16842858; // 0x101006a
field public static final int accessibilityEventTypes = 16843648; // 0x1010380
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -313,7 +314,6 @@
field public static final int autoUrlDetect = 16843404; // 0x101028c
field public static final int autoVerify = 16844014; // 0x10104ee
field public static final int autofillHints = 16844121; // 0x1010559
- field public static final int autofillMode = 16844116; // 0x1010554
field public static final int background = 16842964; // 0x10100d4
field public static final int backgroundDimAmount = 16842802; // 0x1010032
field public static final int backgroundDimEnabled = 16843295; // 0x101021f
@@ -1710,6 +1710,7 @@
field public static final int ic_notification_clear_all = 17301594; // 0x108005a
field public static final int ic_notification_overlay = 17301595; // 0x108005b
field public static final int ic_partial_secure = 17301596; // 0x108005c
+ field public static final int ic_picture_in_picture = 17301685; // 0x10800b5
field public static final int ic_popup_disk_full = 17301597; // 0x108005d
field public static final int ic_popup_reminder = 17301598; // 0x108005e
field public static final int ic_popup_sync = 17301599; // 0x108005f
@@ -3615,6 +3616,7 @@
method public android.net.Uri getReferrer();
method public int getRequestedOrientation();
method public final android.view.SearchEvent getSearchEvent();
+ method public long getStartInitiatedTime();
method public int getTaskId();
method public final java.lang.CharSequence getTitle();
method public final int getTitleColor();
@@ -3847,7 +3849,7 @@
method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
method public java.util.List<android.app.ActivityManager.RunningAppProcessInfo> getRunningAppProcesses();
method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException;
- method public java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
+ method public deprecated java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
method public deprecated boolean isInLockTaskMode();
method public boolean isLowRamDevice();
@@ -3941,7 +3943,8 @@
field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d
field public static final int IMPORTANCE_GONE = 1000; // 0x3e8
- field public static final int IMPORTANCE_PERCEPTIBLE = 130; // 0x82
+ field public static final int IMPORTANCE_PERCEPTIBLE = 230; // 0xe6
+ field public static final deprecated int IMPORTANCE_PERCEPTIBLE_DEPRECATED = 130; // 0x82
field public static final int IMPORTANCE_SERVICE = 300; // 0x12c
field public static final int IMPORTANCE_TOP_SLEEPING = 150; // 0x96
field public static final int IMPORTANCE_VISIBLE = 200; // 0xc8
@@ -4575,6 +4578,7 @@
method public final android.app.FragmentManager getFragmentManager();
method public final java.lang.Object getHost();
method public final int getId();
+ method public final android.view.LayoutInflater getLayoutInflater();
method public android.app.LoaderManager getLoaderManager();
method public final android.app.Fragment getParentFragment();
method public android.transition.Transition getReenterTransition();
@@ -5602,7 +5606,6 @@
method public boolean removeAutomaticZenRule(java.lang.String);
method public final void setInterruptionFilter(int);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
- method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
@@ -6090,6 +6093,17 @@
method public void onDetached();
}
+ public final class WallpaperColors implements android.os.Parcelable {
+ ctor public WallpaperColors(android.os.Parcel);
+ ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>);
+ ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>, boolean);
+ method public int describeContents();
+ method public java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>> getColors();
+ method public boolean supportsDarkText();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+ }
+
public final class WallpaperInfo implements android.os.Parcelable {
ctor public WallpaperInfo(android.content.Context, android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public int describeContents();
@@ -6112,6 +6126,8 @@
}
public class WallpaperManager {
+ method public void addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener);
+ method public void addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler);
method public void clear() throws java.io.IOException;
method public void clear(int) throws java.io.IOException;
method public void clearWallpaperOffsets(android.os.IBinder);
@@ -6126,6 +6142,7 @@
method public android.graphics.drawable.Drawable getDrawable();
method public android.graphics.drawable.Drawable getFastDrawable();
method public static android.app.WallpaperManager getInstance(android.content.Context);
+ method public android.app.WallpaperColors getWallpaperColors(int);
method public android.os.ParcelFileDescriptor getWallpaperFile(int);
method public int getWallpaperId(int);
method public android.app.WallpaperInfo getWallpaperInfo();
@@ -6134,6 +6151,7 @@
method public boolean isWallpaperSupported();
method public android.graphics.drawable.Drawable peekDrawable();
method public android.graphics.drawable.Drawable peekFastDrawable();
+ method public void removeOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener);
method public void sendWallpaperCommand(android.os.IBinder, java.lang.String, int, int, int, android.os.Bundle);
method public void setBitmap(android.graphics.Bitmap) throws java.io.IOException;
method public int setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean) throws java.io.IOException;
@@ -6158,6 +6176,10 @@
field public static final java.lang.String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview";
}
+ public static abstract interface WallpaperManager.OnColorsChangedListener {
+ method public abstract void onColorsChanged(android.app.WallpaperColors, int);
+ }
+
}
package android.app.admin {
@@ -6604,7 +6626,7 @@
public static class AssistStructure.ViewNode {
method public float getAlpha();
- method public java.lang.String[] getAutoFillHints();
+ method public java.lang.String[] getAutofillHints();
method public android.view.autofill.AutofillId getAutofillId();
method public java.lang.String[] getAutofillOptions();
method public int getAutofillType();
@@ -6868,6 +6890,14 @@
field public static final java.lang.String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE";
}
+ public abstract class JobServiceEngine {
+ ctor public JobServiceEngine(android.app.Service);
+ method public final android.os.IBinder getBinder();
+ method public final void jobFinished(android.app.job.JobParameters, boolean);
+ method public abstract boolean onStartJob(android.app.job.JobParameters);
+ method public abstract boolean onStopJob(android.app.job.JobParameters);
+ }
+
public final class JobWorkItem implements android.os.Parcelable {
ctor public JobWorkItem(android.content.Intent);
ctor public JobWorkItem(android.os.Parcel);
@@ -6965,12 +6995,12 @@
}
public class StorageStatsManager {
- method public long getFreeBytes(java.lang.String);
- method public long getTotalBytes(java.lang.String);
- method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle);
- method public android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle);
- method public android.app.usage.StorageStats queryStatsForUid(java.lang.String, int);
- method public android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle);
+ method public long getFreeBytes(java.util.UUID) throws java.io.IOException;
+ method public long getTotalBytes(java.util.UUID) throws java.io.IOException;
+ method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.util.UUID, android.os.UserHandle) throws java.io.IOException;
+ method public android.app.usage.StorageStats queryStatsForPackage(java.util.UUID, java.lang.String, android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
+ method public android.app.usage.StorageStats queryStatsForUid(java.util.UUID, int) throws java.io.IOException;
+ method public android.app.usage.StorageStats queryStatsForUser(java.util.UUID, android.os.UserHandle) throws java.io.IOException;
}
public final class UsageEvents implements android.os.Parcelable {
@@ -7035,6 +7065,7 @@
method public static void deleteAllHosts();
method public void deleteAppWidgetId(int);
method public void deleteHost();
+ method public int[] getAppWidgetIds();
method protected android.appwidget.AppWidgetHostView onCreateView(android.content.Context, int, android.appwidget.AppWidgetProviderInfo);
method protected void onProviderChanged(int, android.appwidget.AppWidgetProviderInfo);
method protected void onProvidersChanged();
@@ -8068,7 +8099,12 @@
method public void flushPendingScanResults(android.bluetooth.le.ScanCallback);
method public void startScan(android.bluetooth.le.ScanCallback);
method public void startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
+ method public int startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.app.PendingIntent);
method public void stopScan(android.bluetooth.le.ScanCallback);
+ method public void stopScan(android.app.PendingIntent);
+ field public static final java.lang.String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
+ field public static final java.lang.String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE";
+ field public static final java.lang.String EXTRA_LIST_SCAN_RESULT = "android.bluetooth.le.extra.LIST_SCAN_RESULT";
}
public final class PeriodicAdvertisingParameters implements android.os.Parcelable {
@@ -8243,7 +8279,8 @@
method public android.companion.BluetoothLEDeviceFilter build();
method public android.companion.BluetoothLEDeviceFilter.Builder setNamePattern(java.util.regex.Pattern);
method public android.companion.BluetoothLEDeviceFilter.Builder setRawDataFilter(byte[], byte[]);
- method public android.companion.BluetoothLEDeviceFilter.Builder setRename(java.lang.String, java.lang.String, int, int, boolean);
+ method public android.companion.BluetoothLEDeviceFilter.Builder setRenameFromBytes(java.lang.String, java.lang.String, int, int, boolean);
+ method public android.companion.BluetoothLEDeviceFilter.Builder setRenameFromName(java.lang.String, java.lang.String, int, int);
method public android.companion.BluetoothLEDeviceFilter.Builder setScanFilter(android.bluetooth.le.ScanFilter);
}
@@ -8251,6 +8288,8 @@
method public void associate(android.companion.AssociationRequest, android.companion.CompanionDeviceManager.Callback, android.os.Handler);
method public void disassociate(java.lang.String);
method public java.util.List<java.lang.String> getAssociations();
+ method public boolean hasNotificationAccess(android.content.ComponentName);
+ method public void requestNotificationAccess(android.content.ComponentName);
field public static final java.lang.String EXTRA_DEVICE = "android.companion.extra.DEVICE";
}
@@ -9323,7 +9362,6 @@
field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
- field public static final deprecated java.lang.String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
field public static final java.lang.String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON";
field public static final java.lang.String ACTION_MEDIA_CHECKING = "android.intent.action.MEDIA_CHECKING";
@@ -10181,12 +10219,12 @@
field public java.lang.String[] splitNames;
field public java.lang.String[] splitPublicSourceDirs;
field public java.lang.String[] splitSourceDirs;
+ field public java.util.UUID storageUuid;
field public int targetSdkVersion;
field public java.lang.String taskAffinity;
field public int theme;
field public int uiOptions;
field public int uid;
- field public java.lang.String volumeUuid;
}
public static class ApplicationInfo.DisplayNameComparator implements java.util.Comparator {
@@ -10305,7 +10343,7 @@
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
- method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
+ method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
method public java.util.List<android.os.UserHandle> getProfiles();
method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
@@ -10905,12 +10943,13 @@
method public android.content.pm.VersionedPackage getDeclaringPackage();
method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
method public java.lang.String getName();
- method public int getVersion();
- method public boolean isBuiltin();
- method public boolean isDynamic();
- method public boolean isStatic();
+ method public int getType();
+ method public long getVersion();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR;
+ field public static final int TYPE_BUILTIN = 0; // 0x0
+ field public static final int TYPE_DYNAMIC = 1; // 0x1
+ field public static final int TYPE_STATIC = 2; // 0x2
field public static final int VERSION_UNDEFINED = -1; // 0xffffffff
}
@@ -12533,6 +12572,7 @@
field public boolean inJustDecodeBounds;
field public boolean inMutable;
field public deprecated boolean inPreferQualityOverSpeed;
+ field public android.graphics.ColorSpace inPreferredColorSpace;
field public android.graphics.Bitmap.Config inPreferredConfig;
field public boolean inPremultiplied;
field public deprecated boolean inPurgeable;
@@ -12563,7 +12603,6 @@
public class BitmapShader extends android.graphics.Shader {
ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
- method public void set(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
}
public class BlurMaskFilter extends android.graphics.MaskFilter {
@@ -12816,8 +12855,6 @@
ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix);
ctor public ColorMatrixColorFilter(float[]);
method public void getColorMatrix(android.graphics.ColorMatrix);
- method public void setColorMatrix(android.graphics.ColorMatrix);
- method public void setColorMatrixArray(float[]);
}
public abstract class ColorSpace {
@@ -12958,8 +12995,6 @@
public class ComposeShader extends android.graphics.Shader {
ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode);
ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode);
- method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode);
- method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode);
}
public class CornerPathEffect extends android.graphics.PathEffect {
@@ -13032,15 +13067,11 @@
ctor public LightingColorFilter(int, int);
method public int getColorAdd();
method public int getColorMultiply();
- method public void setColorAdd(int);
- method public void setColorMultiply(int);
}
public class LinearGradient extends android.graphics.Shader {
ctor public LinearGradient(float, float, float, float, int[], float[], android.graphics.Shader.TileMode);
ctor public LinearGradient(float, float, float, float, int, int, android.graphics.Shader.TileMode);
- method public void set(float, float, float, float, int[], float[], android.graphics.Shader.TileMode);
- method public void set(float, float, float, float, int, int, android.graphics.Shader.TileMode);
}
public class MaskFilter {
@@ -13550,10 +13581,6 @@
public class PorterDuffColorFilter extends android.graphics.ColorFilter {
ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode);
- method public int getColor();
- method public android.graphics.PorterDuff.Mode getMode();
- method public void setColor(int);
- method public void setMode(android.graphics.PorterDuff.Mode);
}
public class PorterDuffXfermode extends android.graphics.Xfermode {
@@ -13563,8 +13590,6 @@
public class RadialGradient extends android.graphics.Shader {
ctor public RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode);
- method public void set(float, float, float, int[], float[], android.graphics.Shader.TileMode);
- method public void set(float, float, float, int, int, android.graphics.Shader.TileMode);
}
public final class Rect implements android.os.Parcelable {
@@ -13749,8 +13774,6 @@
public class SweepGradient extends android.graphics.Shader {
ctor public SweepGradient(float, float, int[], float[]);
ctor public SweepGradient(float, float, int, int);
- method public void set(float, float, int[], float[]);
- method public void set(float, float, int, int);
}
public class Typeface {
@@ -20764,13 +20787,10 @@
method public boolean hasSpeedAccuracy();
method public boolean hasVerticalAccuracy();
method public boolean isFromMockProvider();
- method public void removeAccuracy();
- method public void removeAltitude();
- method public void removeBearing();
- method public void removeBearingAccuracy();
- method public void removeSpeed();
- method public void removeSpeedAccuracy();
- method public void removeVerticalAccuracy();
+ method public deprecated void removeAccuracy();
+ method public deprecated void removeAltitude();
+ method public deprecated void removeBearing();
+ method public deprecated void removeSpeed();
method public void reset();
method public void set(android.location.Location);
method public void setAccuracy(float);
@@ -21869,7 +21889,7 @@
method public deprecated java.nio.ByteBuffer[] getInputBuffers();
method public final android.media.MediaFormat getInputFormat();
method public android.media.Image getInputImage(int);
- method public android.media.MediaMetricsSet getMetrics();
+ method public android.os.PersistableBundle getMetrics();
method public final java.lang.String getName();
method public java.nio.ByteBuffer getOutputBuffer(int);
method public deprecated java.nio.ByteBuffer[] getOutputBuffers();
@@ -21967,6 +21987,19 @@
method public void set(int, int);
}
+ public static final class MediaCodec.MetricsConstants {
+ field public static final java.lang.String CODEC = "android.media.mediacodec.codec";
+ field public static final java.lang.String ENCODER = "android.media.mediacodec.encoder";
+ field public static final java.lang.String HEIGHT = "android.media.mediacodec.height";
+ field public static final java.lang.String MIME_TYPE = "android.media.mediacodec.mime";
+ field public static final java.lang.String MODE = "android.media.mediacodec.mode";
+ field public static final java.lang.String MODE_AUDIO = "audio";
+ field public static final java.lang.String MODE_VIDEO = "video";
+ field public static final java.lang.String ROTATION = "android.media.mediacodec.rotation";
+ field public static final java.lang.String SECURE = "android.media.mediacodec.secure";
+ field public static final java.lang.String WIDTH = "android.media.mediacodec.width";
+ }
+
public static abstract interface MediaCodec.OnFrameRenderedListener {
method public abstract void onFrameRendered(android.media.MediaCodec, long, long);
}
@@ -22422,7 +22455,7 @@
method public long getCachedDuration();
method public android.media.MediaExtractor.CasInfo getCasInfo(int);
method public android.media.DrmInitData getDrmInitData();
- method public android.media.MediaMetricsSet getMetrics();
+ method public android.os.PersistableBundle getMetrics();
method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
method public int getSampleFlags();
@@ -22457,6 +22490,12 @@
method public int getSystemId();
}
+ public static final class MediaExtractor.MetricsConstants {
+ field public static final java.lang.String FORMAT = "android.media.mediaextractor.fmt";
+ field public static final java.lang.String MIME_TYPE = "android.media.mediaextractor.mime";
+ field public static final java.lang.String TRACKS = "android.media.mediaextractor.ntrk";
+ }
+
public final class MediaFormat {
ctor public MediaFormat();
method public final boolean containsKey(java.lang.String);
@@ -22682,69 +22721,6 @@
field public static final int OPTION_PREVIOUS_SYNC = 0; // 0x0
}
- public final class MediaMetricsSet {
- method public double getDouble(java.lang.String, double);
- method public int getInt(java.lang.String, int);
- method public long getLong(java.lang.String, long);
- method public java.lang.String getString(java.lang.String, java.lang.String);
- method public boolean isEmpty();
- method public java.util.Set<java.lang.String> keySet();
- method public int size();
- }
-
- public static final class MediaMetricsSet.MediaCodec {
- field public static final java.lang.String KEY_CODEC = "android.media.mediacodec.codec";
- field public static final java.lang.String KEY_ENCODER = "android.media.mediacodec.encoder";
- field public static final java.lang.String KEY_HEIGHT = "android.media.mediacodec.height";
- field public static final java.lang.String KEY_MIME = "android.media.mediacodec.mime";
- field public static final java.lang.String KEY_MODE = "android.media.mediacodec.mode";
- field public static final java.lang.String KEY_ROTATION = "android.media.mediacodec.rotation";
- field public static final java.lang.String KEY_SECURE = "android.media.mediacodec.secure";
- field public static final java.lang.String KEY_WIDTH = "android.media.mediacodec.width";
- field public static final java.lang.String MODE_AUDIO = "audio";
- field public static final java.lang.String MODE_VIDEO = "video";
- }
-
- public static final class MediaMetricsSet.MediaExtractor {
- field public static final java.lang.String KEY_FORMAT = "android.media.mediaextractor.fmt";
- field public static final java.lang.String KEY_MIME = "android.media.mediaextractor.mime";
- field public static final java.lang.String KEY_TRACKS = "android.media.mediaextractor.ntrk";
- }
-
- public static final class MediaMetricsSet.MediaPlayer {
- field public static final java.lang.String KEY_CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
- field public static final java.lang.String KEY_CODEC_VIDEO = "android.media.mediaplayer.video.codec";
- field public static final java.lang.String KEY_DURATION = "android.media.mediaplayer.durationMs";
- field public static final java.lang.String KEY_ERRORS = "android.media.mediaplayer.err";
- field public static final java.lang.String KEY_ERROR_CODE = "android.media.mediaplayer.errcode";
- field public static final java.lang.String KEY_FRAMES = "android.media.mediaplayer.frames";
- field public static final java.lang.String KEY_FRAMES_DROPPED = "android.media.mediaplayer.dropped";
- field public static final java.lang.String KEY_HEIGHT = "android.media.mediaplayer.height";
- field public static final java.lang.String KEY_MIME_AUDIO = "android.media.mediaplayer.audio.mime";
- field public static final java.lang.String KEY_MIME_VIDEO = "android.media.mediaplayer.video.mime";
- field public static final java.lang.String KEY_PLAYING = "android.media.mediaplayer.playingMs";
- field public static final java.lang.String KEY_WIDTH = "android.media.mediaplayer.width";
- }
-
- public static final class MediaMetricsSet.MediaRecorder {
- field public static final java.lang.String KEY_AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
- field public static final java.lang.String KEY_AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
- field public static final java.lang.String KEY_AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
- field public static final java.lang.String KEY_AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
- field public static final java.lang.String KEY_CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
- field public static final java.lang.String KEY_CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
- field public static final java.lang.String KEY_FRAMERATE = "android.media.mediarecorder.frame-rate";
- field public static final java.lang.String KEY_HEIGHT = "android.media.mediarecorder.height";
- field public static final java.lang.String KEY_MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
- field public static final java.lang.String KEY_ROTATION = "android.media.mediarecorder.rotation";
- field public static final java.lang.String KEY_VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
- field public static final java.lang.String KEY_VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
- field public static final java.lang.String KEY_VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
- field public static final java.lang.String KEY_VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
- field public static final java.lang.String KEY_VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
- field public static final java.lang.String KEY_WIDTH = "android.media.mediarecorder.width";
- }
-
public final class MediaMuxer {
ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
ctor public MediaMuxer(java.io.FileDescriptor, int) throws java.io.IOException;
@@ -22782,8 +22758,8 @@
method public android.media.MediaPlayer.DrmInfo getDrmInfo();
method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException;
method public int getDuration();
- method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
- method public android.media.MediaMetricsSet getMetrics();
+ method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
+ method public android.os.PersistableBundle getMetrics();
method public android.media.PlaybackParams getPlaybackParams();
method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
method public android.media.SyncParams getSyncParams();
@@ -22796,7 +22772,7 @@
method public void pause() throws java.lang.IllegalStateException;
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
method public void prepareAsync() throws java.lang.IllegalStateException;
- method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
+ method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningNetworkErrorException, android.media.MediaPlayer.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException;
method public void release();
method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException;
@@ -22823,7 +22799,7 @@
method public void setNextMediaPlayer(android.media.MediaPlayer);
method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener);
method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener);
- method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener);
+ method public void setOnDrmConfigHelper(android.media.MediaPlayer.OnDrmConfigHelper);
method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener);
method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler);
method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener);
@@ -22864,6 +22840,10 @@
field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+ field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3
+ field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1
+ field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2
+ field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0
field public static final int SEEK_CLOSEST = 3; // 0x3
field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2
field public static final int SEEK_NEXT_SYNC = 1; // 0x1
@@ -22873,11 +22853,25 @@
}
public static final class MediaPlayer.DrmInfo {
- method public java.lang.String[] getMimes();
method public java.util.Map<java.util.UUID, byte[]> getPssh();
method public java.util.UUID[] getSupportedSchemes();
}
+ public static final class MediaPlayer.MetricsConstants {
+ field public static final java.lang.String CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
+ field public static final java.lang.String CODEC_VIDEO = "android.media.mediaplayer.video.codec";
+ field public static final java.lang.String DURATION = "android.media.mediaplayer.durationMs";
+ field public static final java.lang.String ERRORS = "android.media.mediaplayer.err";
+ field public static final java.lang.String ERROR_CODE = "android.media.mediaplayer.errcode";
+ field public static final java.lang.String FRAMES = "android.media.mediaplayer.frames";
+ field public static final java.lang.String FRAMES_DROPPED = "android.media.mediaplayer.dropped";
+ field public static final java.lang.String HEIGHT = "android.media.mediaplayer.height";
+ field public static final java.lang.String MIME_TYPE_AUDIO = "android.media.mediaplayer.audio.mime";
+ field public static final java.lang.String MIME_TYPE_VIDEO = "android.media.mediaplayer.video.mime";
+ field public static final java.lang.String PLAYING = "android.media.mediaplayer.playingMs";
+ field public static final java.lang.String WIDTH = "android.media.mediaplayer.width";
+ }
+
public static final class MediaPlayer.NoDrmSchemeException extends android.media.MediaDrmException {
ctor public MediaPlayer.NoDrmSchemeException(java.lang.String);
}
@@ -22890,7 +22884,7 @@
method public abstract void onCompletion(android.media.MediaPlayer);
}
- public static abstract interface MediaPlayer.OnDrmConfigListener {
+ public static abstract interface MediaPlayer.OnDrmConfigHelper {
method public abstract void onDrmConfig(android.media.MediaPlayer);
}
@@ -22899,7 +22893,7 @@
}
public static abstract interface MediaPlayer.OnDrmPreparedListener {
- method public abstract void onDrmPrepared(android.media.MediaPlayer, boolean);
+ method public abstract void onDrmPrepared(android.media.MediaPlayer, int);
}
public static abstract interface MediaPlayer.OnErrorListener {
@@ -22930,8 +22924,12 @@
method public abstract void onVideoSizeChanged(android.media.MediaPlayer, int, int);
}
- public static final class MediaPlayer.ProvisioningErrorException extends android.media.MediaDrmException {
- ctor public MediaPlayer.ProvisioningErrorException(java.lang.String);
+ public static final class MediaPlayer.ProvisioningNetworkErrorException extends android.media.MediaDrmException {
+ ctor public MediaPlayer.ProvisioningNetworkErrorException(java.lang.String);
+ }
+
+ public static final class MediaPlayer.ProvisioningServerErrorException extends android.media.MediaDrmException {
+ ctor public MediaPlayer.ProvisioningServerErrorException(java.lang.String);
}
public static class MediaPlayer.TrackInfo implements android.os.Parcelable {
@@ -22952,7 +22950,7 @@
ctor public MediaRecorder();
method public static final int getAudioSourceMax();
method public int getMaxAmplitude() throws java.lang.IllegalStateException;
- method public android.media.MediaMetricsSet getMetrics();
+ method public android.os.PersistableBundle getMetrics();
method public android.view.Surface getSurface();
method public void pause() throws java.lang.IllegalStateException;
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
@@ -22971,11 +22969,12 @@
method public void setMaxDuration(int) throws java.lang.IllegalArgumentException;
method public void setMaxFileSize(long) throws java.lang.IllegalArgumentException;
method public void setNextOutputFile(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalStateException;
- method public void setNextOutputFile(java.lang.String) throws java.io.IOException, java.lang.IllegalStateException;
+ method public void setNextOutputFile(java.io.File) throws java.io.IOException, java.lang.IllegalStateException;
method public void setOnErrorListener(android.media.MediaRecorder.OnErrorListener);
method public void setOnInfoListener(android.media.MediaRecorder.OnInfoListener);
method public void setOrientationHint(int);
method public void setOutputFile(java.io.FileDescriptor) throws java.lang.IllegalStateException;
+ method public void setOutputFile(java.io.File);
method public void setOutputFile(java.lang.String) throws java.lang.IllegalStateException;
method public void setOutputFormat(int) throws java.lang.IllegalStateException;
method public void setPreviewDisplay(android.view.Surface);
@@ -23020,6 +23019,25 @@
field public static final int VOICE_UPLINK = 2; // 0x2
}
+ public static final class MediaRecorder.MetricsConstants {
+ field public static final java.lang.String AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
+ field public static final java.lang.String AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
+ field public static final java.lang.String AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
+ field public static final java.lang.String AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
+ field public static final java.lang.String CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
+ field public static final java.lang.String CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
+ field public static final java.lang.String FRAMERATE = "android.media.mediarecorder.frame-rate";
+ field public static final java.lang.String HEIGHT = "android.media.mediarecorder.height";
+ field public static final java.lang.String MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
+ field public static final java.lang.String ROTATION = "android.media.mediarecorder.rotation";
+ field public static final java.lang.String VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
+ field public static final java.lang.String VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
+ field public static final java.lang.String VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
+ field public static final java.lang.String VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
+ field public static final java.lang.String VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
+ field public static final java.lang.String WIDTH = "android.media.mediarecorder.width";
+ }
+
public static abstract interface MediaRecorder.OnErrorListener {
method public abstract void onError(android.media.MediaRecorder, int, int);
}
@@ -23983,7 +24001,6 @@
method public android.content.ComponentName getServiceComponent();
method public android.media.session.MediaSession.Token getSessionToken();
method public boolean isConnected();
- method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback);
method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(java.lang.String);
@@ -24019,12 +24036,6 @@
field public static final int FLAG_PLAYABLE = 2; // 0x2
}
- public static abstract class MediaBrowser.SearchCallback {
- ctor public MediaBrowser.SearchCallback();
- method public void onError(java.lang.String, android.os.Bundle);
- method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
- }
-
public static abstract class MediaBrowser.SubscriptionCallback {
ctor public MediaBrowser.SubscriptionCallback();
method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -24233,8 +24244,6 @@
public final class MediaController {
ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
- method public void addQueueItem(android.media.MediaDescription);
- method public void addQueueItem(android.media.MediaDescription, int);
method public void adjustVolume(int, int);
method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
method public android.os.Bundle getExtras();
@@ -24246,15 +24255,11 @@
method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
method public java.lang.CharSequence getQueueTitle();
method public int getRatingType();
- method public int getRepeatMode();
method public android.app.PendingIntent getSessionActivity();
method public android.media.session.MediaSession.Token getSessionToken();
method public android.media.session.MediaController.TransportControls getTransportControls();
- method public boolean isShuffleModeEnabled();
method public void registerCallback(android.media.session.MediaController.Callback);
method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
- method public void removeQueueItem(android.media.MediaDescription);
- method public void removeQueueItemAt(int);
method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void setVolumeTo(int, int);
method public void unregisterCallback(android.media.session.MediaController.Callback);
@@ -24268,10 +24273,8 @@
method public void onPlaybackStateChanged(android.media.session.PlaybackState);
method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void onQueueTitleChanged(java.lang.CharSequence);
- method public void onRepeatModeChanged(int);
method public void onSessionDestroyed();
method public void onSessionEvent(java.lang.String, android.os.Bundle);
- method public void onShuffleModeChanged(boolean);
}
public static final class MediaController.PlaybackInfo {
@@ -24300,8 +24303,6 @@
method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
method public void sendCustomAction(java.lang.String, android.os.Bundle);
method public void setRating(android.media.Rating);
- method public void setRepeatMode(int);
- method public void setShuffleModeEnabled(boolean);
method public void skipToNext();
method public void skipToPrevious();
method public void skipToQueueItem(long);
@@ -24328,18 +24329,13 @@
method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void setQueueTitle(java.lang.CharSequence);
method public void setRatingType(int);
- method public void setRepeatMode(int);
method public void setSessionActivity(android.app.PendingIntent);
- method public void setShuffleModeEnabled(boolean);
field public static final deprecated int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
- field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
field public static final deprecated int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
}
public static abstract class MediaSession.Callback {
ctor public MediaSession.Callback();
- method public void onAddQueueItem(android.media.MediaDescription);
- method public void onAddQueueItem(android.media.MediaDescription, int);
method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void onCustomAction(java.lang.String, android.os.Bundle);
method public void onFastForward();
@@ -24353,13 +24349,9 @@
method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
- method public void onRemoveQueueItem(android.media.MediaDescription);
- method public void onRemoveQueueItemAt(int);
method public void onRewind();
method public void onSeekTo(long);
method public void onSetRating(android.media.Rating);
- method public void onSetRepeatMode(int);
- method public void onSetShuffleModeEnabled(boolean);
method public void onSkipToNext();
method public void onSkipToPrevious();
method public void onSkipToQueueItem(long);
@@ -24420,17 +24412,12 @@
field public static final long ACTION_REWIND = 8L; // 0x8L
field public static final long ACTION_SEEK_TO = 256L; // 0x100L
field public static final long ACTION_SET_RATING = 128L; // 0x80L
- field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
- field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
field public static final long ACTION_STOP = 1L; // 0x1L
field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR;
field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
- field public static final int REPEAT_MODE_ALL = 2; // 0x2
- field public static final int REPEAT_MODE_NONE = 0; // 0x0
- field public static final int REPEAT_MODE_ONE = 1; // 0x1
field public static final int STATE_BUFFERING = 6; // 0x6
field public static final int STATE_CONNECTING = 8; // 0x8
field public static final int STATE_ERROR = 7; // 0x7
@@ -25531,14 +25518,10 @@
}
public final class IpSecManager {
- method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException;
- method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
- method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
- method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
- method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform);
+ method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
@@ -25557,7 +25540,7 @@
}
public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
- method public void close();
+ method public void close() throws java.io.IOException;
method public int getPort();
method public java.io.FileDescriptor getSocket();
}
@@ -25779,6 +25762,10 @@
method public android.net.NetworkRequest.Builder removeCapability(int);
method public android.net.NetworkRequest.Builder removeTransportType(int);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(java.lang.String);
+ method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
+ }
+
+ public abstract class NetworkSpecifier {
}
public class ParseException extends java.lang.RuntimeException {
@@ -26751,8 +26738,8 @@
}
public class DiscoverySession {
- method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
- method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
+ method public android.net.NetworkSpecifier createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
+ method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
method public void destroy();
method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]);
}
@@ -26838,8 +26825,8 @@
}
public class WifiAwareSession {
- method public java.lang.String createNetworkSpecifierOpen(int, byte[]);
- method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
+ method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, byte[]);
+ method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
method public void destroy();
method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
@@ -30770,6 +30757,7 @@
method public android.util.SizeF getSizeF(java.lang.String);
method public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(java.lang.String);
method public java.util.ArrayList<java.lang.String> getStringArrayList(java.lang.String);
+ method public java.util.UUID getUuid(java.lang.String);
method public boolean hasFileDescriptors();
method public void putAll(android.os.Bundle);
method public void putBinder(java.lang.String, android.os.IBinder);
@@ -30794,6 +30782,7 @@
method public void putSizeF(java.lang.String, android.util.SizeF);
method public void putSparseParcelableArray(java.lang.String, android.util.SparseArray<? extends android.os.Parcelable>);
method public void putStringArrayList(java.lang.String, java.util.ArrayList<java.lang.String>);
+ method public void putUuid(java.lang.String, java.util.UUID);
method public void readFromParcel(android.os.Parcel);
method public void setClassLoader(java.lang.ClassLoader);
method public void writeToParcel(android.os.Parcel, int);
@@ -31323,6 +31312,7 @@
method public final <T> void readTypedArray(T[], android.os.Parcelable.Creator<T>);
method public final <T> void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>);
method public final <T> T readTypedObject(android.os.Parcelable.Creator<T>);
+ method public final java.util.UUID readUuid();
method public final java.lang.Object readValue(java.lang.ClassLoader);
method public final void recycle();
method public final void setDataCapacity(int);
@@ -31368,6 +31358,7 @@
method public final <T extends android.os.Parcelable> void writeTypedArray(T[], int);
method public final <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>);
method public final <T extends android.os.Parcelable> void writeTypedObject(T, int);
+ method public final void writeUuid(java.util.UUID);
method public final void writeValue(java.lang.Object);
field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR;
}
@@ -31998,15 +31989,16 @@
}
public class StorageManager {
- method public void allocateBytes(java.io.File, long, int) throws java.io.IOException;
+ method public void allocateBytes(java.util.UUID, long, int) throws java.io.IOException;
method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException;
- method public long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
- method public long getCacheQuotaBytes(java.io.File);
- method public long getCacheSizeBytes(java.io.File);
+ method public long getAllocatableBytes(java.util.UUID, int) throws java.io.IOException;
+ method public long getCacheQuotaBytes(java.util.UUID) throws java.io.IOException;
+ method public long getCacheSizeBytes(java.util.UUID) throws java.io.IOException;
method public java.lang.String getMountedObbPath(java.lang.String);
method public android.os.storage.StorageVolume getPrimaryStorageVolume();
method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
+ method public java.util.UUID getUuidForPath(java.io.File) throws java.io.IOException;
method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException;
method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
method public boolean isEncrypted(java.io.File);
@@ -32018,7 +32010,10 @@
method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
+ field public static final java.lang.String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES";
+ field public static final java.lang.String EXTRA_UUID = "android.os.storage.extra.UUID";
field public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1
+ field public static final java.util.UUID UUID_DEFAULT;
}
public final class StorageVolume implements android.os.Parcelable {
@@ -35455,6 +35450,16 @@
field public static final java.lang.String SUBSCRIPTION_ID = "pending_sub_id";
}
+ public static final class Telephony.ServiceStateTable {
+ method public static android.net.Uri getUriForSubId(int);
+ method public static android.net.Uri getUriForSubIdAndField(int, java.lang.String);
+ field public static final java.lang.String AUTHORITY = "service-state";
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String IS_MANUAL_NETWORK_SELECTION = "is_manual_network_selection";
+ field public static final java.lang.String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric";
+ field public static final java.lang.String VOICE_REG_STATE = "voice_reg_state";
+ }
+
public static final class Telephony.Sms implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns {
method public static java.lang.String getDefaultSmsPackage(android.content.Context);
field public static final android.net.Uri CONTENT_URI;
@@ -37029,11 +37034,14 @@
public abstract class AutofillService extends android.app.Service {
ctor public AutofillService();
method public final deprecated void disableSelf();
+ method public final android.service.autofill.FillEventHistory getFillEventHistory();
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConnected();
method public void onDisconnected();
- method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
- method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
+ method public void onFillRequest(android.service.autofill.FillRequest, android.os.CancellationSignal, android.service.autofill.FillCallback);
+ method public abstract deprecated void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
+ method public void onSaveRequest(android.service.autofill.SaveRequest, android.service.autofill.SaveCallback);
+ method public abstract deprecated void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
}
@@ -37049,6 +37057,7 @@
ctor public Dataset.Builder();
method public android.service.autofill.Dataset build();
method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
+ method public android.service.autofill.Dataset.Builder setId(java.lang.String);
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
}
@@ -37058,6 +37067,42 @@
method public void onSuccess(android.service.autofill.FillResponse);
}
+ public final class FillContext implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getRequestId();
+ method public android.app.assist.AssistStructure getStructure();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillContext> CREATOR;
+ }
+
+ public final class FillEventHistory implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
+ }
+
+ public static final class FillEventHistory.Event {
+ method public java.lang.String getDatasetId();
+ method public int getType();
+ field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
+ field public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; // 0x1
+ field public static final int TYPE_DATASET_SELECTED = 0; // 0x0
+ field public static final int TYPE_SAVE_SHOWN = 3; // 0x3
+ }
+
+ public final class FillRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public int getFlags();
+ method public int getId();
+ method public android.app.assist.AssistStructure getStructure();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillRequest> CREATOR;
+ field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
+ }
+
public final class FillResponse implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -37069,7 +37114,9 @@
method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
method public android.service.autofill.FillResponse build();
method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
- method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
+ method public deprecated android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
}
@@ -37082,6 +37129,7 @@
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
+ field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1
field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2
field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4
field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10
@@ -37094,10 +37142,19 @@
ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
method public android.service.autofill.SaveInfo build();
method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
+ method public android.service.autofill.SaveInfo.Builder setFlags(int);
method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender);
method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
}
+ public final class SaveRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public java.util.List<android.service.autofill.FillContext> getFillContexts();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
+ }
+
}
package android.service.carrier {
@@ -37279,7 +37336,6 @@
method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
- method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
method public void setSessionToken(android.media.session.MediaSession.Token);
field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
}
@@ -37359,16 +37415,16 @@
method public final int getCurrentInterruptionFilter();
method public final int getCurrentListenerHints();
method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
- method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String);
- method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String);
+ method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String, android.os.UserHandle);
+ method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String, android.os.UserHandle);
method public final android.service.notification.StatusBarNotification[] getSnoozedNotifications();
method public android.os.IBinder onBind(android.content.Intent);
method public void onInterruptionFilterChanged(int);
method public void onListenerConnected();
method public void onListenerDisconnected();
method public void onListenerHintsChanged(int);
- method public void onNotificationChannelGroupModified(java.lang.String, android.app.NotificationChannelGroup, int);
- method public void onNotificationChannelModified(java.lang.String, android.app.NotificationChannel, int);
+ method public void onNotificationChannelGroupModified(java.lang.String, android.os.UserHandle, android.app.NotificationChannelGroup, int);
+ method public void onNotificationChannelModified(java.lang.String, android.os.UserHandle, android.app.NotificationChannel, int);
method public void onNotificationPosted(android.service.notification.StatusBarNotification);
method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap);
@@ -37381,7 +37437,7 @@
method public final void requestUnbind();
method public final void setNotificationsShown(java.lang.String[]);
method public final void snoozeNotification(java.lang.String, long);
- method public final void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
+ method public final void updateNotificationChannel(java.lang.String, android.os.UserHandle, android.app.NotificationChannel);
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
@@ -37731,10 +37787,12 @@
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
method public android.view.SurfaceHolder getSurfaceHolder();
+ method public void invalidateColors();
method public boolean isPreview();
method public boolean isVisible();
method public void onApplyWindowInsets(android.view.WindowInsets);
method public android.os.Bundle onCommand(java.lang.String, int, int, int, android.os.Bundle, boolean);
+ method public android.app.WallpaperColors onComputeWallpaperColors();
method public void onCreate(android.view.SurfaceHolder);
method public void onDesiredSizeChanged(int, int);
method public void onDestroy();
@@ -39409,6 +39467,8 @@
field public static final java.lang.String ACTION_CONFIGURE_PHONE_ACCOUNT = "android.telecom.action.CONFIGURE_PHONE_ACCOUNT";
field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
field public static final deprecated java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
+ field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
+ field public static final java.lang.String ACTION_PHONE_ACCOUNT_UNREGISTERED = "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
field public static final java.lang.String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
@@ -40853,7 +40913,6 @@
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
method public android.content.ComponentName startService(android.content.Intent);
- method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
@@ -45365,7 +45424,6 @@
method public android.view.animation.Animation getAnimation();
method public android.os.IBinder getApplicationWindowToken();
method public java.lang.String[] getAutofillHints();
- method public int getAutofillMode();
method public int getAutofillType();
method public android.view.autofill.AutofillValue getAutofillValue();
method public android.graphics.drawable.Drawable getBackground();
@@ -45454,7 +45512,6 @@
method public float getPivotX();
method public float getPivotY();
method public android.view.PointerIcon getPointerIcon();
- method public int getResolvedAutofillMode();
method public android.content.res.Resources getResources();
method public final boolean getRevealOnFocusHint();
method public final int getRight();
@@ -45687,7 +45744,6 @@
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
method public void setAutofillHints(java.lang.String...);
- method public void setAutofillMode(int);
method public void setBackground(android.graphics.drawable.Drawable);
method public void setBackgroundColor(int);
method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable);
@@ -45843,9 +45899,6 @@
field public static final java.lang.String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress";
field public static final java.lang.String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
field public static final java.lang.String AUTOFILL_HINT_USERNAME = "username";
- field public static final int AUTOFILL_MODE_AUTO = 1; // 0x1
- field public static final int AUTOFILL_MODE_INHERIT = 0; // 0x0
- field public static final int AUTOFILL_MODE_MANUAL = 2; // 0x2
field public static final int AUTOFILL_TYPE_DATE = 4; // 0x4
field public static final int AUTOFILL_TYPE_LIST = 3; // 0x3
field public static final int AUTOFILL_TYPE_NONE = 0; // 0x0
@@ -45893,7 +45946,9 @@
field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
+ field public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 8; // 0x8
field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
+ field public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 4; // 0x4
field public static final int INVISIBLE = 4; // 0x4
field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
@@ -46413,7 +46468,6 @@
method public abstract int getLayoutDirection();
method public abstract android.view.ViewParent getParent();
method public abstract android.view.ViewParent getParentForAccessibility();
- method public default int getResolvedAutofillMode();
method public abstract int getTextAlignment();
method public abstract int getTextDirection();
method public abstract deprecated void invalidateChild(android.view.View, android.graphics.Rect);
@@ -47800,7 +47854,7 @@
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
field public static final java.lang.String EXTRA_DATA_EXTRAS = "android.view.autofill.extra.DATA_EXTRAS";
- field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
+ field public static final deprecated int FLAG_MANUAL_REQUEST = 1; // 0x1
}
public static abstract class AutofillManager.AutofillCallback {
@@ -49674,6 +49728,7 @@
method public java.lang.String getFormat();
method public android.widget.Chronometer.OnChronometerTickListener getOnChronometerTickListener();
method public boolean isCountDown();
+ method public boolean isTheFinalCountDown();
method public void setBase(long);
method public void setCountDown(boolean);
method public void setFormat(java.lang.String);
@@ -55343,6 +55398,7 @@
method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+ method public static java.lang.invoke.MethodHandle explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType);
method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...);
method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
diff --git a/api/removed.txt b/api/removed.txt
index 82705fd..b6c2a98 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -5,10 +5,6 @@
method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
}
- public static class Notification.Builder {
- method public deprecated android.app.Notification.Builder chooseBadgeIcon(int);
- }
-
public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable {
method public deprecated void showAsNotification(android.content.Context);
}
@@ -26,6 +22,20 @@
}
+package android.app.usage {
+
+ public class StorageStatsManager {
+ method public deprecated long getFreeBytes(java.lang.String) throws java.io.IOException;
+ method public deprecated long getTotalBytes(java.lang.String) throws java.io.IOException;
+ method public deprecated boolean isQuotaSupported(java.lang.String);
+ method public deprecated android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle) throws java.io.IOException;
+ method public deprecated android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
+ method public deprecated android.app.usage.StorageStats queryStatsForUid(java.lang.String, int) throws java.io.IOException;
+ method public deprecated android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle) throws java.io.IOException;
+ }
+
+}
+
package android.content {
public abstract class Context {
@@ -41,6 +51,10 @@
package android.content.pm {
+ public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ field public deprecated java.lang.String volumeUuid;
+ }
+
public class ComponentInfo extends android.content.pm.PackageItemInfo {
field public deprecated boolean encryptionAware;
}
@@ -49,6 +63,12 @@
field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
}
+ public final class SharedLibraryInfo implements android.os.Parcelable {
+ method public boolean isBuiltin();
+ method public boolean isDynamic();
+ method public boolean isStatic();
+ }
+
}
package android.database {
@@ -112,6 +132,16 @@
}
+package android.location {
+
+ public class Location implements android.os.Parcelable {
+ method public deprecated void removeBearingAccuracy();
+ method public deprecated void removeSpeedAccuracy();
+ method public deprecated void removeVerticalAccuracy();
+ }
+
+}
+
package android.media {
public final class AudioFormat implements android.os.Parcelable {
@@ -184,10 +214,14 @@
package android.os.storage {
public class StorageManager {
- method public deprecated long getCacheQuotaBytes();
- method public deprecated long getCacheSizeBytes();
- method public deprecated long getExternalCacheQuotaBytes();
- method public deprecated long getExternalCacheSizeBytes();
+ method public deprecated void allocateBytes(java.io.File, long, int) throws java.io.IOException;
+ method public deprecated long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
+ method public deprecated long getCacheQuotaBytes(java.io.File) throws java.io.IOException;
+ method public deprecated long getCacheQuotaBytes() throws java.io.IOException;
+ method public deprecated long getCacheSizeBytes(java.io.File) throws java.io.IOException;
+ method public deprecated long getCacheSizeBytes() throws java.io.IOException;
+ method public deprecated long getExternalCacheQuotaBytes() throws java.io.IOException;
+ method public deprecated long getExternalCacheSizeBytes() throws java.io.IOException;
method public android.os.storage.StorageVolume getPrimaryVolume();
method public android.os.storage.StorageVolume[] getVolumeList();
method public deprecated boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
diff --git a/api/system-current.txt b/api/system-current.txt
index 4fa96ae..5dd3b88 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -329,6 +329,7 @@
ctor public R.attr();
field public static final int __removed1 = 16844099; // 0x1010543
field public static final int __removed2 = 16844104; // 0x1010548
+ field public static final int __removed3 = 16844116; // 0x1010554
field public static final int absListViewStyle = 16842858; // 0x101006a
field public static final int accessibilityEventTypes = 16843648; // 0x1010380
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -431,7 +432,6 @@
field public static final int autoUrlDetect = 16843404; // 0x101028c
field public static final int autoVerify = 16844014; // 0x10104ee
field public static final int autofillHints = 16844121; // 0x1010559
- field public static final int autofillMode = 16844116; // 0x1010554
field public static final int background = 16842964; // 0x10100d4
field public static final int backgroundDimAmount = 16842802; // 0x1010032
field public static final int backgroundDimEnabled = 16843295; // 0x101021f
@@ -1197,6 +1197,8 @@
field public static final int requiredFeature = 16844119; // 0x1010557
field public static final int requiredForAllUsers = 16843728; // 0x10103d0
field public static final int requiredNotFeature = 16844120; // 0x1010558
+ field public static final int requiredSystemPropertyName = 16844136; // 0x1010568
+ field public static final int requiredSystemPropertyValue = 16844137; // 0x1010569
field public static final int requiresFadingEdge = 16843685; // 0x10103a5
field public static final int requiresSmallestWidthDp = 16843620; // 0x1010364
field public static final int resizeClip = 16843983; // 0x10104cf
@@ -1832,6 +1834,7 @@
field public static final int ic_notification_clear_all = 17301594; // 0x108005a
field public static final int ic_notification_overlay = 17301595; // 0x108005b
field public static final int ic_partial_secure = 17301596; // 0x108005c
+ field public static final int ic_picture_in_picture = 17301685; // 0x10800b5
field public static final int ic_popup_disk_full = 17301597; // 0x108005d
field public static final int ic_popup_reminder = 17301598; // 0x108005e
field public static final int ic_popup_sync = 17301599; // 0x108005f
@@ -3743,6 +3746,7 @@
method public android.net.Uri getReferrer();
method public int getRequestedOrientation();
method public final android.view.SearchEvent getSearchEvent();
+ method public long getStartInitiatedTime();
method public int getTaskId();
method public final java.lang.CharSequence getTitle();
method public final int getTitleColor();
@@ -3986,7 +3990,7 @@
method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
method public java.util.List<android.app.ActivityManager.RunningAppProcessInfo> getRunningAppProcesses();
method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException;
- method public java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
+ method public deprecated java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
method public int getUidImportance(int);
method public deprecated boolean isInLockTaskMode();
@@ -4087,7 +4091,8 @@
field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d
field public static final int IMPORTANCE_GONE = 1000; // 0x3e8
- field public static final int IMPORTANCE_PERCEPTIBLE = 130; // 0x82
+ field public static final int IMPORTANCE_PERCEPTIBLE = 230; // 0xe6
+ field public static final deprecated int IMPORTANCE_PERCEPTIBLE_DEPRECATED = 130; // 0x82
field public static final int IMPORTANCE_SERVICE = 300; // 0x12c
field public static final int IMPORTANCE_TOP_SLEEPING = 150; // 0x96
field public static final int IMPORTANCE_VISIBLE = 200; // 0xc8
@@ -4740,6 +4745,7 @@
method public final android.app.FragmentManager getFragmentManager();
method public final java.lang.Object getHost();
method public final int getId();
+ method public final android.view.LayoutInflater getLayoutInflater();
method public android.app.LoaderManager getLoaderManager();
method public final android.app.Fragment getParentFragment();
method public android.transition.Transition getReenterTransition();
@@ -5802,7 +5808,6 @@
method public boolean removeAutomaticZenRule(java.lang.String);
method public final void setInterruptionFilter(int);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
- method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
@@ -6294,6 +6299,17 @@
method public void setPersistentVrModeEnabled(boolean);
}
+ public final class WallpaperColors implements android.os.Parcelable {
+ ctor public WallpaperColors(android.os.Parcel);
+ ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>);
+ ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>, boolean);
+ method public int describeContents();
+ method public java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>> getColors();
+ method public boolean supportsDarkText();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+ }
+
public final class WallpaperInfo implements android.os.Parcelable {
ctor public WallpaperInfo(android.content.Context, android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public int describeContents();
@@ -6316,6 +6332,8 @@
}
public class WallpaperManager {
+ method public void addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener);
+ method public void addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler);
method public void clear() throws java.io.IOException;
method public void clear(int) throws java.io.IOException;
method public void clearWallpaper();
@@ -6332,6 +6350,7 @@
method public android.graphics.drawable.Drawable getDrawable();
method public android.graphics.drawable.Drawable getFastDrawable();
method public static android.app.WallpaperManager getInstance(android.content.Context);
+ method public android.app.WallpaperColors getWallpaperColors(int);
method public android.os.ParcelFileDescriptor getWallpaperFile(int);
method public int getWallpaperId(int);
method public android.app.WallpaperInfo getWallpaperInfo();
@@ -6340,6 +6359,7 @@
method public boolean isWallpaperSupported();
method public android.graphics.drawable.Drawable peekDrawable();
method public android.graphics.drawable.Drawable peekFastDrawable();
+ method public void removeOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener);
method public void sendWallpaperCommand(android.os.IBinder, java.lang.String, int, int, int, android.os.Bundle);
method public void setBitmap(android.graphics.Bitmap) throws java.io.IOException;
method public int setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean) throws java.io.IOException;
@@ -6367,6 +6387,10 @@
field public static final java.lang.String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview";
}
+ public static abstract interface WallpaperManager.OnColorsChangedListener {
+ method public abstract void onColorsChanged(android.app.WallpaperColors, int);
+ }
+
}
package android.app.admin {
@@ -6847,7 +6871,7 @@
public static class AssistStructure.ViewNode {
method public float getAlpha();
- method public java.lang.String[] getAutoFillHints();
+ method public java.lang.String[] getAutofillHints();
method public android.view.autofill.AutofillId getAutofillId();
method public java.lang.String[] getAutofillOptions();
method public int getAutofillType();
@@ -7302,6 +7326,14 @@
field public static final java.lang.String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE";
}
+ public abstract class JobServiceEngine {
+ ctor public JobServiceEngine(android.app.Service);
+ method public final android.os.IBinder getBinder();
+ method public final void jobFinished(android.app.job.JobParameters, boolean);
+ method public abstract boolean onStartJob(android.app.job.JobParameters);
+ method public abstract boolean onStopJob(android.app.job.JobParameters);
+ }
+
public final class JobWorkItem implements android.os.Parcelable {
ctor public JobWorkItem(android.content.Intent);
ctor public JobWorkItem(android.os.Parcel);
@@ -7428,12 +7460,12 @@
}
public class StorageStatsManager {
- method public long getFreeBytes(java.lang.String);
- method public long getTotalBytes(java.lang.String);
- method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle);
- method public android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle);
- method public android.app.usage.StorageStats queryStatsForUid(java.lang.String, int);
- method public android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle);
+ method public long getFreeBytes(java.util.UUID) throws java.io.IOException;
+ method public long getTotalBytes(java.util.UUID) throws java.io.IOException;
+ method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.util.UUID, android.os.UserHandle) throws java.io.IOException;
+ method public android.app.usage.StorageStats queryStatsForPackage(java.util.UUID, java.lang.String, android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
+ method public android.app.usage.StorageStats queryStatsForUid(java.util.UUID, int) throws java.io.IOException;
+ method public android.app.usage.StorageStats queryStatsForUser(java.util.UUID, android.os.UserHandle) throws java.io.IOException;
}
public final class UsageEvents implements android.os.Parcelable {
@@ -7499,6 +7531,7 @@
method public static void deleteAllHosts();
method public void deleteAppWidgetId(int);
method public void deleteHost();
+ method public int[] getAppWidgetIds();
method protected android.appwidget.AppWidgetHostView onCreateView(android.content.Context, int, android.appwidget.AppWidgetProviderInfo);
method protected void onProviderChanged(int, android.appwidget.AppWidgetProviderInfo);
method protected void onProvidersChanged();
@@ -8540,10 +8573,15 @@
method public void flushPendingScanResults(android.bluetooth.le.ScanCallback);
method public void startScan(android.bluetooth.le.ScanCallback);
method public void startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
+ method public int startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.app.PendingIntent);
method public void startScanFromSource(android.os.WorkSource, android.bluetooth.le.ScanCallback);
method public void startScanFromSource(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.os.WorkSource, android.bluetooth.le.ScanCallback);
method public void startTruncatedScan(java.util.List<android.bluetooth.le.TruncatedFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
method public void stopScan(android.bluetooth.le.ScanCallback);
+ method public void stopScan(android.app.PendingIntent);
+ field public static final java.lang.String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
+ field public static final java.lang.String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE";
+ field public static final java.lang.String EXTRA_LIST_SCAN_RESULT = "android.bluetooth.le.extra.LIST_SCAN_RESULT";
}
public final class PeriodicAdvertisingParameters implements android.os.Parcelable {
@@ -8737,7 +8775,8 @@
method public android.companion.BluetoothLEDeviceFilter build();
method public android.companion.BluetoothLEDeviceFilter.Builder setNamePattern(java.util.regex.Pattern);
method public android.companion.BluetoothLEDeviceFilter.Builder setRawDataFilter(byte[], byte[]);
- method public android.companion.BluetoothLEDeviceFilter.Builder setRename(java.lang.String, java.lang.String, int, int, boolean);
+ method public android.companion.BluetoothLEDeviceFilter.Builder setRenameFromBytes(java.lang.String, java.lang.String, int, int, boolean);
+ method public android.companion.BluetoothLEDeviceFilter.Builder setRenameFromName(java.lang.String, java.lang.String, int, int);
method public android.companion.BluetoothLEDeviceFilter.Builder setScanFilter(android.bluetooth.le.ScanFilter);
}
@@ -8745,6 +8784,8 @@
method public void associate(android.companion.AssociationRequest, android.companion.CompanionDeviceManager.Callback, android.os.Handler);
method public void disassociate(java.lang.String);
method public java.util.List<java.lang.String> getAssociations();
+ method public boolean hasNotificationAccess(android.content.ComponentName);
+ method public void requestNotificationAccess(android.content.ComponentName);
field public static final java.lang.String EXTRA_DEVICE = "android.companion.extra.DEVICE";
}
@@ -10753,12 +10794,12 @@
field public java.lang.String[] splitNames;
field public java.lang.String[] splitPublicSourceDirs;
field public java.lang.String[] splitSourceDirs;
+ field public java.util.UUID storageUuid;
field public int targetSdkVersion;
field public java.lang.String taskAffinity;
field public int theme;
field public int uiOptions;
field public int uid;
- field public java.lang.String volumeUuid;
}
public static class ApplicationInfo.DisplayNameComparator implements java.util.Comparator {
@@ -10967,7 +11008,7 @@
public class LauncherApps {
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
- method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
+ method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
method public java.util.List<android.os.UserHandle> getProfiles();
method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
@@ -11237,6 +11278,7 @@
method public abstract byte[] getInstantAppCookie();
method public abstract int getInstantAppCookieMaxSize();
method public abstract android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String);
+ method public abstract android.content.ComponentName getInstantAppInstallerComponent();
method public abstract android.content.ComponentName getInstantAppResolverSettingsComponent();
method public abstract java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -11661,12 +11703,13 @@
method public android.content.pm.VersionedPackage getDeclaringPackage();
method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
method public java.lang.String getName();
- method public int getVersion();
- method public boolean isBuiltin();
- method public boolean isDynamic();
- method public boolean isStatic();
+ method public int getType();
+ method public long getVersion();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR;
+ field public static final int TYPE_BUILTIN = 0; // 0x0
+ field public static final int TYPE_DYNAMIC = 1; // 0x1
+ field public static final int TYPE_STATIC = 2; // 0x2
field public static final int VERSION_UNDEFINED = -1; // 0xffffffff
}
@@ -13303,6 +13346,7 @@
field public boolean inJustDecodeBounds;
field public boolean inMutable;
field public deprecated boolean inPreferQualityOverSpeed;
+ field public android.graphics.ColorSpace inPreferredColorSpace;
field public android.graphics.Bitmap.Config inPreferredConfig;
field public boolean inPremultiplied;
field public deprecated boolean inPurgeable;
@@ -13333,7 +13377,6 @@
public class BitmapShader extends android.graphics.Shader {
ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
- method public void set(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
}
public class BlurMaskFilter extends android.graphics.MaskFilter {
@@ -13586,8 +13629,6 @@
ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix);
ctor public ColorMatrixColorFilter(float[]);
method public void getColorMatrix(android.graphics.ColorMatrix);
- method public void setColorMatrix(android.graphics.ColorMatrix);
- method public void setColorMatrixArray(float[]);
}
public abstract class ColorSpace {
@@ -13728,8 +13769,6 @@
public class ComposeShader extends android.graphics.Shader {
ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode);
ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode);
- method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode);
- method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode);
}
public class CornerPathEffect extends android.graphics.PathEffect {
@@ -13802,15 +13841,11 @@
ctor public LightingColorFilter(int, int);
method public int getColorAdd();
method public int getColorMultiply();
- method public void setColorAdd(int);
- method public void setColorMultiply(int);
}
public class LinearGradient extends android.graphics.Shader {
ctor public LinearGradient(float, float, float, float, int[], float[], android.graphics.Shader.TileMode);
ctor public LinearGradient(float, float, float, float, int, int, android.graphics.Shader.TileMode);
- method public void set(float, float, float, float, int[], float[], android.graphics.Shader.TileMode);
- method public void set(float, float, float, float, int, int, android.graphics.Shader.TileMode);
}
public class MaskFilter {
@@ -14320,10 +14355,6 @@
public class PorterDuffColorFilter extends android.graphics.ColorFilter {
ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode);
- method public int getColor();
- method public android.graphics.PorterDuff.Mode getMode();
- method public void setColor(int);
- method public void setMode(android.graphics.PorterDuff.Mode);
}
public class PorterDuffXfermode extends android.graphics.Xfermode {
@@ -14333,8 +14364,6 @@
public class RadialGradient extends android.graphics.Shader {
ctor public RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode);
- method public void set(float, float, float, int[], float[], android.graphics.Shader.TileMode);
- method public void set(float, float, float, int, int, android.graphics.Shader.TileMode);
}
public final class Rect implements android.os.Parcelable {
@@ -14519,8 +14548,6 @@
public class SweepGradient extends android.graphics.Shader {
ctor public SweepGradient(float, float, int[], float[]);
ctor public SweepGradient(float, float, int, int);
- method public void set(float, float, int[], float[]);
- method public void set(float, float, int, int);
}
public class Typeface {
@@ -22494,13 +22521,10 @@
method public boolean isComplete();
method public boolean isFromMockProvider();
method public void makeComplete();
- method public void removeAccuracy();
- method public void removeAltitude();
- method public void removeBearing();
- method public void removeBearingAccuracy();
- method public void removeSpeed();
- method public void removeSpeedAccuracy();
- method public void removeVerticalAccuracy();
+ method public deprecated void removeAccuracy();
+ method public deprecated void removeAltitude();
+ method public deprecated void removeBearing();
+ method public deprecated void removeSpeed();
method public void reset();
method public void set(android.location.Location);
method public void setAccuracy(float);
@@ -23701,7 +23725,7 @@
method public deprecated java.nio.ByteBuffer[] getInputBuffers();
method public final android.media.MediaFormat getInputFormat();
method public android.media.Image getInputImage(int);
- method public android.media.MediaMetricsSet getMetrics();
+ method public android.os.PersistableBundle getMetrics();
method public final java.lang.String getName();
method public java.nio.ByteBuffer getOutputBuffer(int);
method public deprecated java.nio.ByteBuffer[] getOutputBuffers();
@@ -23799,6 +23823,19 @@
method public void set(int, int);
}
+ public static final class MediaCodec.MetricsConstants {
+ field public static final java.lang.String CODEC = "android.media.mediacodec.codec";
+ field public static final java.lang.String ENCODER = "android.media.mediacodec.encoder";
+ field public static final java.lang.String HEIGHT = "android.media.mediacodec.height";
+ field public static final java.lang.String MIME_TYPE = "android.media.mediacodec.mime";
+ field public static final java.lang.String MODE = "android.media.mediacodec.mode";
+ field public static final java.lang.String MODE_AUDIO = "audio";
+ field public static final java.lang.String MODE_VIDEO = "video";
+ field public static final java.lang.String ROTATION = "android.media.mediacodec.rotation";
+ field public static final java.lang.String SECURE = "android.media.mediacodec.secure";
+ field public static final java.lang.String WIDTH = "android.media.mediacodec.width";
+ }
+
public static abstract interface MediaCodec.OnFrameRenderedListener {
method public abstract void onFrameRendered(android.media.MediaCodec, long, long);
}
@@ -24254,7 +24291,7 @@
method public long getCachedDuration();
method public android.media.MediaExtractor.CasInfo getCasInfo(int);
method public android.media.DrmInitData getDrmInitData();
- method public android.media.MediaMetricsSet getMetrics();
+ method public android.os.PersistableBundle getMetrics();
method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
method public int getSampleFlags();
@@ -24289,6 +24326,12 @@
method public int getSystemId();
}
+ public static final class MediaExtractor.MetricsConstants {
+ field public static final java.lang.String FORMAT = "android.media.mediaextractor.fmt";
+ field public static final java.lang.String MIME_TYPE = "android.media.mediaextractor.mime";
+ field public static final java.lang.String TRACKS = "android.media.mediaextractor.ntrk";
+ }
+
public final class MediaFormat {
ctor public MediaFormat();
method public final boolean containsKey(java.lang.String);
@@ -24514,69 +24557,6 @@
field public static final int OPTION_PREVIOUS_SYNC = 0; // 0x0
}
- public final class MediaMetricsSet {
- method public double getDouble(java.lang.String, double);
- method public int getInt(java.lang.String, int);
- method public long getLong(java.lang.String, long);
- method public java.lang.String getString(java.lang.String, java.lang.String);
- method public boolean isEmpty();
- method public java.util.Set<java.lang.String> keySet();
- method public int size();
- }
-
- public static final class MediaMetricsSet.MediaCodec {
- field public static final java.lang.String KEY_CODEC = "android.media.mediacodec.codec";
- field public static final java.lang.String KEY_ENCODER = "android.media.mediacodec.encoder";
- field public static final java.lang.String KEY_HEIGHT = "android.media.mediacodec.height";
- field public static final java.lang.String KEY_MIME = "android.media.mediacodec.mime";
- field public static final java.lang.String KEY_MODE = "android.media.mediacodec.mode";
- field public static final java.lang.String KEY_ROTATION = "android.media.mediacodec.rotation";
- field public static final java.lang.String KEY_SECURE = "android.media.mediacodec.secure";
- field public static final java.lang.String KEY_WIDTH = "android.media.mediacodec.width";
- field public static final java.lang.String MODE_AUDIO = "audio";
- field public static final java.lang.String MODE_VIDEO = "video";
- }
-
- public static final class MediaMetricsSet.MediaExtractor {
- field public static final java.lang.String KEY_FORMAT = "android.media.mediaextractor.fmt";
- field public static final java.lang.String KEY_MIME = "android.media.mediaextractor.mime";
- field public static final java.lang.String KEY_TRACKS = "android.media.mediaextractor.ntrk";
- }
-
- public static final class MediaMetricsSet.MediaPlayer {
- field public static final java.lang.String KEY_CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
- field public static final java.lang.String KEY_CODEC_VIDEO = "android.media.mediaplayer.video.codec";
- field public static final java.lang.String KEY_DURATION = "android.media.mediaplayer.durationMs";
- field public static final java.lang.String KEY_ERRORS = "android.media.mediaplayer.err";
- field public static final java.lang.String KEY_ERROR_CODE = "android.media.mediaplayer.errcode";
- field public static final java.lang.String KEY_FRAMES = "android.media.mediaplayer.frames";
- field public static final java.lang.String KEY_FRAMES_DROPPED = "android.media.mediaplayer.dropped";
- field public static final java.lang.String KEY_HEIGHT = "android.media.mediaplayer.height";
- field public static final java.lang.String KEY_MIME_AUDIO = "android.media.mediaplayer.audio.mime";
- field public static final java.lang.String KEY_MIME_VIDEO = "android.media.mediaplayer.video.mime";
- field public static final java.lang.String KEY_PLAYING = "android.media.mediaplayer.playingMs";
- field public static final java.lang.String KEY_WIDTH = "android.media.mediaplayer.width";
- }
-
- public static final class MediaMetricsSet.MediaRecorder {
- field public static final java.lang.String KEY_AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
- field public static final java.lang.String KEY_AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
- field public static final java.lang.String KEY_AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
- field public static final java.lang.String KEY_AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
- field public static final java.lang.String KEY_CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
- field public static final java.lang.String KEY_CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
- field public static final java.lang.String KEY_FRAMERATE = "android.media.mediarecorder.frame-rate";
- field public static final java.lang.String KEY_HEIGHT = "android.media.mediarecorder.height";
- field public static final java.lang.String KEY_MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
- field public static final java.lang.String KEY_ROTATION = "android.media.mediarecorder.rotation";
- field public static final java.lang.String KEY_VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
- field public static final java.lang.String KEY_VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
- field public static final java.lang.String KEY_VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
- field public static final java.lang.String KEY_VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
- field public static final java.lang.String KEY_VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
- field public static final java.lang.String KEY_WIDTH = "android.media.mediarecorder.width";
- }
-
public final class MediaMuxer {
ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
ctor public MediaMuxer(java.io.FileDescriptor, int) throws java.io.IOException;
@@ -24614,8 +24594,8 @@
method public android.media.MediaPlayer.DrmInfo getDrmInfo();
method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException;
method public int getDuration();
- method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
- method public android.media.MediaMetricsSet getMetrics();
+ method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
+ method public android.os.PersistableBundle getMetrics();
method public android.media.PlaybackParams getPlaybackParams();
method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
method public android.media.SyncParams getSyncParams();
@@ -24628,7 +24608,7 @@
method public void pause() throws java.lang.IllegalStateException;
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
method public void prepareAsync() throws java.lang.IllegalStateException;
- method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
+ method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningNetworkErrorException, android.media.MediaPlayer.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException;
method public void release();
method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException;
@@ -24655,7 +24635,7 @@
method public void setNextMediaPlayer(android.media.MediaPlayer);
method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener);
method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener);
- method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener);
+ method public void setOnDrmConfigHelper(android.media.MediaPlayer.OnDrmConfigHelper);
method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener);
method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler);
method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener);
@@ -24696,6 +24676,10 @@
field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+ field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3
+ field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1
+ field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2
+ field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0
field public static final int SEEK_CLOSEST = 3; // 0x3
field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2
field public static final int SEEK_NEXT_SYNC = 1; // 0x1
@@ -24705,11 +24689,25 @@
}
public static final class MediaPlayer.DrmInfo {
- method public java.lang.String[] getMimes();
method public java.util.Map<java.util.UUID, byte[]> getPssh();
method public java.util.UUID[] getSupportedSchemes();
}
+ public static final class MediaPlayer.MetricsConstants {
+ field public static final java.lang.String CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
+ field public static final java.lang.String CODEC_VIDEO = "android.media.mediaplayer.video.codec";
+ field public static final java.lang.String DURATION = "android.media.mediaplayer.durationMs";
+ field public static final java.lang.String ERRORS = "android.media.mediaplayer.err";
+ field public static final java.lang.String ERROR_CODE = "android.media.mediaplayer.errcode";
+ field public static final java.lang.String FRAMES = "android.media.mediaplayer.frames";
+ field public static final java.lang.String FRAMES_DROPPED = "android.media.mediaplayer.dropped";
+ field public static final java.lang.String HEIGHT = "android.media.mediaplayer.height";
+ field public static final java.lang.String MIME_TYPE_AUDIO = "android.media.mediaplayer.audio.mime";
+ field public static final java.lang.String MIME_TYPE_VIDEO = "android.media.mediaplayer.video.mime";
+ field public static final java.lang.String PLAYING = "android.media.mediaplayer.playingMs";
+ field public static final java.lang.String WIDTH = "android.media.mediaplayer.width";
+ }
+
public static final class MediaPlayer.NoDrmSchemeException extends android.media.MediaDrmException {
ctor public MediaPlayer.NoDrmSchemeException(java.lang.String);
}
@@ -24722,7 +24720,7 @@
method public abstract void onCompletion(android.media.MediaPlayer);
}
- public static abstract interface MediaPlayer.OnDrmConfigListener {
+ public static abstract interface MediaPlayer.OnDrmConfigHelper {
method public abstract void onDrmConfig(android.media.MediaPlayer);
}
@@ -24731,7 +24729,7 @@
}
public static abstract interface MediaPlayer.OnDrmPreparedListener {
- method public abstract void onDrmPrepared(android.media.MediaPlayer, boolean);
+ method public abstract void onDrmPrepared(android.media.MediaPlayer, int);
}
public static abstract interface MediaPlayer.OnErrorListener {
@@ -24762,8 +24760,12 @@
method public abstract void onVideoSizeChanged(android.media.MediaPlayer, int, int);
}
- public static final class MediaPlayer.ProvisioningErrorException extends android.media.MediaDrmException {
- ctor public MediaPlayer.ProvisioningErrorException(java.lang.String);
+ public static final class MediaPlayer.ProvisioningNetworkErrorException extends android.media.MediaDrmException {
+ ctor public MediaPlayer.ProvisioningNetworkErrorException(java.lang.String);
+ }
+
+ public static final class MediaPlayer.ProvisioningServerErrorException extends android.media.MediaDrmException {
+ ctor public MediaPlayer.ProvisioningServerErrorException(java.lang.String);
}
public static class MediaPlayer.TrackInfo implements android.os.Parcelable {
@@ -24784,7 +24786,7 @@
ctor public MediaRecorder();
method public static final int getAudioSourceMax();
method public int getMaxAmplitude() throws java.lang.IllegalStateException;
- method public android.media.MediaMetricsSet getMetrics();
+ method public android.os.PersistableBundle getMetrics();
method public android.view.Surface getSurface();
method public void pause() throws java.lang.IllegalStateException;
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
@@ -24803,11 +24805,12 @@
method public void setMaxDuration(int) throws java.lang.IllegalArgumentException;
method public void setMaxFileSize(long) throws java.lang.IllegalArgumentException;
method public void setNextOutputFile(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalStateException;
- method public void setNextOutputFile(java.lang.String) throws java.io.IOException, java.lang.IllegalStateException;
+ method public void setNextOutputFile(java.io.File) throws java.io.IOException, java.lang.IllegalStateException;
method public void setOnErrorListener(android.media.MediaRecorder.OnErrorListener);
method public void setOnInfoListener(android.media.MediaRecorder.OnInfoListener);
method public void setOrientationHint(int);
method public void setOutputFile(java.io.FileDescriptor) throws java.lang.IllegalStateException;
+ method public void setOutputFile(java.io.File);
method public void setOutputFile(java.lang.String) throws java.lang.IllegalStateException;
method public void setOutputFormat(int) throws java.lang.IllegalStateException;
method public void setPreviewDisplay(android.view.Surface);
@@ -24854,6 +24857,25 @@
field public static final int VOICE_UPLINK = 2; // 0x2
}
+ public static final class MediaRecorder.MetricsConstants {
+ field public static final java.lang.String AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
+ field public static final java.lang.String AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
+ field public static final java.lang.String AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
+ field public static final java.lang.String AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
+ field public static final java.lang.String CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
+ field public static final java.lang.String CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
+ field public static final java.lang.String FRAMERATE = "android.media.mediarecorder.frame-rate";
+ field public static final java.lang.String HEIGHT = "android.media.mediarecorder.height";
+ field public static final java.lang.String MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
+ field public static final java.lang.String ROTATION = "android.media.mediarecorder.rotation";
+ field public static final java.lang.String VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
+ field public static final java.lang.String VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
+ field public static final java.lang.String VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
+ field public static final java.lang.String VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
+ field public static final java.lang.String VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
+ field public static final java.lang.String WIDTH = "android.media.mediarecorder.width";
+ }
+
public static abstract interface MediaRecorder.OnErrorListener {
method public abstract void onError(android.media.MediaRecorder, int, int);
}
@@ -25901,7 +25923,6 @@
method public android.content.ComponentName getServiceComponent();
method public android.media.session.MediaSession.Token getSessionToken();
method public boolean isConnected();
- method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback);
method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(java.lang.String);
@@ -25937,12 +25958,6 @@
field public static final int FLAG_PLAYABLE = 2; // 0x2
}
- public static abstract class MediaBrowser.SearchCallback {
- ctor public MediaBrowser.SearchCallback();
- method public void onError(java.lang.String, android.os.Bundle);
- method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
- }
-
public static abstract class MediaBrowser.SubscriptionCallback {
ctor public MediaBrowser.SubscriptionCallback();
method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -26151,8 +26166,6 @@
public final class MediaController {
ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
- method public void addQueueItem(android.media.MediaDescription);
- method public void addQueueItem(android.media.MediaDescription, int);
method public void adjustVolume(int, int);
method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
method public android.os.Bundle getExtras();
@@ -26164,15 +26177,11 @@
method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
method public java.lang.CharSequence getQueueTitle();
method public int getRatingType();
- method public int getRepeatMode();
method public android.app.PendingIntent getSessionActivity();
method public android.media.session.MediaSession.Token getSessionToken();
method public android.media.session.MediaController.TransportControls getTransportControls();
- method public boolean isShuffleModeEnabled();
method public void registerCallback(android.media.session.MediaController.Callback);
method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
- method public void removeQueueItem(android.media.MediaDescription);
- method public void removeQueueItemAt(int);
method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void setVolumeTo(int, int);
method public void unregisterCallback(android.media.session.MediaController.Callback);
@@ -26186,10 +26195,8 @@
method public void onPlaybackStateChanged(android.media.session.PlaybackState);
method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void onQueueTitleChanged(java.lang.CharSequence);
- method public void onRepeatModeChanged(int);
method public void onSessionDestroyed();
method public void onSessionEvent(java.lang.String, android.os.Bundle);
- method public void onShuffleModeChanged(boolean);
}
public static final class MediaController.PlaybackInfo {
@@ -26218,8 +26225,6 @@
method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
method public void sendCustomAction(java.lang.String, android.os.Bundle);
method public void setRating(android.media.Rating);
- method public void setRepeatMode(int);
- method public void setShuffleModeEnabled(boolean);
method public void skipToNext();
method public void skipToPrevious();
method public void skipToQueueItem(long);
@@ -26246,18 +26251,13 @@
method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void setQueueTitle(java.lang.CharSequence);
method public void setRatingType(int);
- method public void setRepeatMode(int);
method public void setSessionActivity(android.app.PendingIntent);
- method public void setShuffleModeEnabled(boolean);
field public static final deprecated int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
- field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
field public static final deprecated int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
}
public static abstract class MediaSession.Callback {
ctor public MediaSession.Callback();
- method public void onAddQueueItem(android.media.MediaDescription);
- method public void onAddQueueItem(android.media.MediaDescription, int);
method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void onCustomAction(java.lang.String, android.os.Bundle);
method public void onFastForward();
@@ -26271,13 +26271,9 @@
method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
- method public void onRemoveQueueItem(android.media.MediaDescription);
- method public void onRemoveQueueItemAt(int);
method public void onRewind();
method public void onSeekTo(long);
method public void onSetRating(android.media.Rating);
- method public void onSetRepeatMode(int);
- method public void onSetShuffleModeEnabled(boolean);
method public void onSkipToNext();
method public void onSkipToPrevious();
method public void onSkipToQueueItem(long);
@@ -26348,17 +26344,12 @@
field public static final long ACTION_REWIND = 8L; // 0x8L
field public static final long ACTION_SEEK_TO = 256L; // 0x100L
field public static final long ACTION_SET_RATING = 128L; // 0x80L
- field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
- field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
field public static final long ACTION_STOP = 1L; // 0x1L
field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR;
field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
- field public static final int REPEAT_MODE_ALL = 2; // 0x2
- field public static final int REPEAT_MODE_NONE = 0; // 0x0
- field public static final int REPEAT_MODE_ONE = 1; // 0x1
field public static final int STATE_BUFFERING = 6; // 0x6
field public static final int STATE_CONNECTING = 8; // 0x8
field public static final int STATE_ERROR = 7; // 0x7
@@ -27716,14 +27707,10 @@
}
public final class IpSecManager {
- method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException;
- method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
- method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
- method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
- method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform);
+ method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
@@ -27742,7 +27729,7 @@
}
public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
- method public void close();
+ method public void close() throws java.io.IOException;
method public int getPort();
method public java.io.FileDescriptor getSocket();
}
@@ -28001,6 +27988,7 @@
method public android.net.NetworkRequest.Builder removeCapability(int);
method public android.net.NetworkRequest.Builder removeTransportType(int);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(java.lang.String);
+ method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
}
public class NetworkScoreManager {
@@ -28019,6 +28007,9 @@
field public static final java.lang.String EXTRA_PACKAGE_NAME = "packageName";
}
+ public abstract class NetworkSpecifier {
+ }
+
public class ParseException extends java.lang.RuntimeException {
field public java.lang.String response;
}
@@ -28141,10 +28132,6 @@
field public static final java.lang.String ATTRIBUTES_KEY_BADGING_CURVE = "android.net.attributes.key.BADGING_CURVE";
field public static final java.lang.String ATTRIBUTES_KEY_HAS_CAPTIVE_PORTAL = "android.net.attributes.key.HAS_CAPTIVE_PORTAL";
field public static final java.lang.String ATTRIBUTES_KEY_RANKING_SCORE_OFFSET = "android.net.attributes.key.RANKING_SCORE_OFFSET";
- field public static final deprecated int BADGING_4K = 30; // 0x1e
- field public static final deprecated int BADGING_HD = 20; // 0x14
- field public static final deprecated int BADGING_NONE = 0; // 0x0
- field public static final deprecated int BADGING_SD = 10; // 0xa
field public static final android.os.Parcelable.Creator<android.net.ScoredNetwork> CREATOR;
field public final android.os.Bundle attributes;
field public final boolean meteredHint;
@@ -28152,9 +28139,6 @@
field public final android.net.RssiCurve rssiCurve;
}
- public static abstract deprecated class ScoredNetwork.Badging implements java.lang.annotation.Annotation {
- }
-
public class TrafficStats {
ctor public TrafficStats();
method public static void clearThreadStatsTag();
@@ -29497,9 +29481,9 @@
}
public class DiscoverySession {
- method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
- method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
- method public java.lang.String createNetworkSpecifierPmk(android.net.wifi.aware.PeerHandle, byte[]);
+ method public android.net.NetworkSpecifier createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
+ method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
+ method public android.net.NetworkSpecifier createNetworkSpecifierPmk(android.net.wifi.aware.PeerHandle, byte[]);
method public void destroy();
method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]);
}
@@ -29585,9 +29569,9 @@
}
public class WifiAwareSession {
- method public java.lang.String createNetworkSpecifierOpen(int, byte[]);
- method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
- method public java.lang.String createNetworkSpecifierPmk(int, byte[], byte[]);
+ method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, byte[]);
+ method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
+ method public android.net.NetworkSpecifier createNetworkSpecifierPmk(int, byte[], byte[]);
method public void destroy();
method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
@@ -33532,6 +33516,7 @@
method public android.util.SizeF getSizeF(java.lang.String);
method public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(java.lang.String);
method public java.util.ArrayList<java.lang.String> getStringArrayList(java.lang.String);
+ method public java.util.UUID getUuid(java.lang.String);
method public boolean hasFileDescriptors();
method public void putAll(android.os.Bundle);
method public void putBinder(java.lang.String, android.os.IBinder);
@@ -33556,6 +33541,7 @@
method public void putSizeF(java.lang.String, android.util.SizeF);
method public void putSparseParcelableArray(java.lang.String, android.util.SparseArray<? extends android.os.Parcelable>);
method public void putStringArrayList(java.lang.String, java.util.ArrayList<java.lang.String>);
+ method public void putUuid(java.lang.String, java.util.UUID);
method public void readFromParcel(android.os.Parcel);
method public void setClassLoader(java.lang.ClassLoader);
method public void writeToParcel(android.os.Parcel, int);
@@ -34115,6 +34101,7 @@
method public final <T> void readTypedArray(T[], android.os.Parcelable.Creator<T>);
method public final <T> void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>);
method public final <T> T readTypedObject(android.os.Parcelable.Creator<T>);
+ method public final java.util.UUID readUuid();
method public final java.lang.Object readValue(java.lang.ClassLoader);
method public final void recycle();
method public final void setDataCapacity(int);
@@ -34160,6 +34147,7 @@
method public final <T extends android.os.Parcelable> void writeTypedArray(T[], int);
method public final <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>);
method public final <T extends android.os.Parcelable> void writeTypedObject(T, int);
+ method public final void writeUuid(java.util.UUID);
method public final void writeValue(java.lang.Object);
field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR;
}
@@ -34375,6 +34363,7 @@
method public static void rebootWipeUserData(android.content.Context) throws java.io.IOException;
method public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
method public static void verifyPackage(java.io.File, android.os.RecoverySystem.ProgressListener, java.io.File) throws java.security.GeneralSecurityException, java.io.IOException;
+ method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException;
}
public static abstract interface RecoverySystem.ProgressListener {
@@ -34895,15 +34884,16 @@
}
public class StorageManager {
- method public void allocateBytes(java.io.File, long, int) throws java.io.IOException;
+ method public void allocateBytes(java.util.UUID, long, int) throws java.io.IOException;
method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException;
- method public long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
- method public long getCacheQuotaBytes(java.io.File);
- method public long getCacheSizeBytes(java.io.File);
+ method public long getAllocatableBytes(java.util.UUID, int) throws java.io.IOException;
+ method public long getCacheQuotaBytes(java.util.UUID) throws java.io.IOException;
+ method public long getCacheSizeBytes(java.util.UUID) throws java.io.IOException;
method public java.lang.String getMountedObbPath(java.lang.String);
method public android.os.storage.StorageVolume getPrimaryStorageVolume();
method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
+ method public java.util.UUID getUuidForPath(java.io.File) throws java.io.IOException;
method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException;
method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
method public boolean isEncrypted(java.io.File);
@@ -34915,7 +34905,10 @@
method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
+ field public static final java.lang.String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES";
+ field public static final java.lang.String EXTRA_UUID = "android.os.storage.extra.UUID";
field public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1
+ field public static final java.util.UUID UUID_DEFAULT;
}
public final class StorageVolume implements android.os.Parcelable {
@@ -38109,7 +38102,6 @@
field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
field public static final java.lang.String MODE_RINGER = "mode_ringer";
field public static final java.lang.String NETWORK_PREFERENCE = "network_preference";
- field public static final java.lang.String NETWORK_RECOMMENDATIONS_ENABLED = "network_recommendations_enabled";
field public static final java.lang.String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
field public static final java.lang.String RADIO_BLUETOOTH = "bluetooth";
field public static final java.lang.String RADIO_CELL = "cell";
@@ -38559,6 +38551,16 @@
field public static final java.lang.String SUBSCRIPTION_ID = "pending_sub_id";
}
+ public static final class Telephony.ServiceStateTable {
+ method public static android.net.Uri getUriForSubId(int);
+ method public static android.net.Uri getUriForSubIdAndField(int, java.lang.String);
+ field public static final java.lang.String AUTHORITY = "service-state";
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String IS_MANUAL_NETWORK_SELECTION = "is_manual_network_selection";
+ field public static final java.lang.String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric";
+ field public static final java.lang.String VOICE_REG_STATE = "voice_reg_state";
+ }
+
public static final class Telephony.Sms implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns {
method public static java.lang.String getDefaultSmsPackage(android.content.Context);
field public static final android.net.Uri CONTENT_URI;
@@ -40145,11 +40147,14 @@
public abstract class AutofillService extends android.app.Service {
ctor public AutofillService();
method public final deprecated void disableSelf();
+ method public final android.service.autofill.FillEventHistory getFillEventHistory();
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConnected();
method public void onDisconnected();
- method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
- method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
+ method public void onFillRequest(android.service.autofill.FillRequest, android.os.CancellationSignal, android.service.autofill.FillCallback);
+ method public abstract deprecated void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
+ method public void onSaveRequest(android.service.autofill.SaveRequest, android.service.autofill.SaveCallback);
+ method public abstract deprecated void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
}
@@ -40165,6 +40170,7 @@
ctor public Dataset.Builder();
method public android.service.autofill.Dataset build();
method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
+ method public android.service.autofill.Dataset.Builder setId(java.lang.String);
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
}
@@ -40174,6 +40180,42 @@
method public void onSuccess(android.service.autofill.FillResponse);
}
+ public final class FillContext implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getRequestId();
+ method public android.app.assist.AssistStructure getStructure();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillContext> CREATOR;
+ }
+
+ public final class FillEventHistory implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
+ }
+
+ public static final class FillEventHistory.Event {
+ method public java.lang.String getDatasetId();
+ method public int getType();
+ field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
+ field public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; // 0x1
+ field public static final int TYPE_DATASET_SELECTED = 0; // 0x0
+ field public static final int TYPE_SAVE_SHOWN = 3; // 0x3
+ }
+
+ public final class FillRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public int getFlags();
+ method public int getId();
+ method public android.app.assist.AssistStructure getStructure();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillRequest> CREATOR;
+ field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
+ }
+
public final class FillResponse implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -40185,7 +40227,9 @@
method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
method public android.service.autofill.FillResponse build();
method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
- method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
+ method public deprecated android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
}
@@ -40198,6 +40242,7 @@
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
+ field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1
field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2
field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4
field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10
@@ -40210,10 +40255,19 @@
ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
method public android.service.autofill.SaveInfo build();
method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
+ method public android.service.autofill.SaveInfo.Builder setFlags(int);
method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender);
method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
}
+ public final class SaveRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public java.util.List<android.service.autofill.FillContext> getFillContexts();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
+ }
+
}
package android.service.carrier {
@@ -40395,7 +40449,6 @@
method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
- method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
method public void setSessionToken(android.media.session.MediaSession.Token);
field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
}
@@ -40503,16 +40556,16 @@
method public final int getCurrentInterruptionFilter();
method public final int getCurrentListenerHints();
method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
- method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String);
- method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String);
+ method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String, android.os.UserHandle);
+ method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String, android.os.UserHandle);
method public final android.service.notification.StatusBarNotification[] getSnoozedNotifications();
method public android.os.IBinder onBind(android.content.Intent);
method public void onInterruptionFilterChanged(int);
method public void onListenerConnected();
method public void onListenerDisconnected();
method public void onListenerHintsChanged(int);
- method public void onNotificationChannelGroupModified(java.lang.String, android.app.NotificationChannelGroup, int);
- method public void onNotificationChannelModified(java.lang.String, android.app.NotificationChannel, int);
+ method public void onNotificationChannelGroupModified(java.lang.String, android.os.UserHandle, android.app.NotificationChannelGroup, int);
+ method public void onNotificationChannelModified(java.lang.String, android.os.UserHandle, android.app.NotificationChannel, int);
method public void onNotificationPosted(android.service.notification.StatusBarNotification);
method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap);
@@ -40529,7 +40582,7 @@
method public final void snoozeNotification(java.lang.String, java.lang.String);
method public final void snoozeNotification(java.lang.String, long);
method public void unregisterAsSystemService() throws android.os.RemoteException;
- method public final void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
+ method public final void updateNotificationChannel(java.lang.String, android.os.UserHandle, android.app.NotificationChannel);
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
@@ -40989,10 +41042,12 @@
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
method public android.view.SurfaceHolder getSurfaceHolder();
+ method public void invalidateColors();
method public boolean isPreview();
method public boolean isVisible();
method public void onApplyWindowInsets(android.view.WindowInsets);
method public android.os.Bundle onCommand(java.lang.String, int, int, int, android.os.Bundle, boolean);
+ method public android.app.WallpaperColors onComputeWallpaperColors();
method public void onCreate(android.view.SurfaceHolder);
method public void onDesiredSizeChanged(int, int);
method public void onDestroy();
@@ -44415,7 +44470,6 @@
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
method public android.content.ComponentName startService(android.content.Intent);
- method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
@@ -44517,6 +44571,7 @@
method public byte[] getInstantAppCookie();
method public int getInstantAppCookieMaxSize();
method public android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String);
+ method public android.content.ComponentName getInstantAppInstallerComponent();
method public android.content.ComponentName getInstantAppResolverSettingsComponent();
method public java.util.List<android.content.pm.InstantAppInfo> getInstantApps();
method public android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -48945,7 +49000,6 @@
method public android.view.animation.Animation getAnimation();
method public android.os.IBinder getApplicationWindowToken();
method public java.lang.String[] getAutofillHints();
- method public int getAutofillMode();
method public int getAutofillType();
method public android.view.autofill.AutofillValue getAutofillValue();
method public android.graphics.drawable.Drawable getBackground();
@@ -49034,7 +49088,6 @@
method public float getPivotX();
method public float getPivotY();
method public android.view.PointerIcon getPointerIcon();
- method public int getResolvedAutofillMode();
method public android.content.res.Resources getResources();
method public final boolean getRevealOnFocusHint();
method public final int getRight();
@@ -49267,7 +49320,6 @@
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
method public void setAutofillHints(java.lang.String...);
- method public void setAutofillMode(int);
method public void setBackground(android.graphics.drawable.Drawable);
method public void setBackgroundColor(int);
method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable);
@@ -49423,9 +49475,6 @@
field public static final java.lang.String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress";
field public static final java.lang.String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
field public static final java.lang.String AUTOFILL_HINT_USERNAME = "username";
- field public static final int AUTOFILL_MODE_AUTO = 1; // 0x1
- field public static final int AUTOFILL_MODE_INHERIT = 0; // 0x0
- field public static final int AUTOFILL_MODE_MANUAL = 2; // 0x2
field public static final int AUTOFILL_TYPE_DATE = 4; // 0x4
field public static final int AUTOFILL_TYPE_LIST = 3; // 0x3
field public static final int AUTOFILL_TYPE_NONE = 0; // 0x0
@@ -49473,7 +49522,9 @@
field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
+ field public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 8; // 0x8
field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
+ field public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 4; // 0x4
field public static final int INVISIBLE = 4; // 0x4
field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
@@ -49993,7 +50044,6 @@
method public abstract int getLayoutDirection();
method public abstract android.view.ViewParent getParent();
method public abstract android.view.ViewParent getParentForAccessibility();
- method public default int getResolvedAutofillMode();
method public abstract int getTextAlignment();
method public abstract int getTextDirection();
method public abstract deprecated void invalidateChild(android.view.View, android.graphics.Rect);
@@ -51383,7 +51433,7 @@
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
field public static final java.lang.String EXTRA_DATA_EXTRAS = "android.view.autofill.extra.DATA_EXTRAS";
- field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
+ field public static final deprecated int FLAG_MANUAL_REQUEST = 1; // 0x1
}
public static abstract class AutofillManager.AutofillCallback {
@@ -53622,6 +53672,7 @@
method public java.lang.String getFormat();
method public android.widget.Chronometer.OnChronometerTickListener getOnChronometerTickListener();
method public boolean isCountDown();
+ method public boolean isTheFinalCountDown();
method public void setBase(long);
method public void setCountDown(boolean);
method public void setFormat(java.lang.String);
@@ -59291,6 +59342,7 @@
method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+ method public static java.lang.invoke.MethodHandle explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType);
method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...);
method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
diff --git a/api/system-removed.txt b/api/system-removed.txt
index bdcafae..6d82a49 100644
--- a/api/system-removed.txt
+++ b/api/system-removed.txt
@@ -5,10 +5,6 @@
method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
}
- public static class Notification.Builder {
- method public deprecated android.app.Notification.Builder chooseBadgeIcon(int);
- }
-
public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable {
method public deprecated void showAsNotification(android.content.Context);
}
@@ -24,6 +20,20 @@
}
+package android.app.usage {
+
+ public class StorageStatsManager {
+ method public deprecated long getFreeBytes(java.lang.String) throws java.io.IOException;
+ method public deprecated long getTotalBytes(java.lang.String) throws java.io.IOException;
+ method public deprecated boolean isQuotaSupported(java.lang.String);
+ method public deprecated android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle) throws java.io.IOException;
+ method public deprecated android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
+ method public deprecated android.app.usage.StorageStats queryStatsForUid(java.lang.String, int) throws java.io.IOException;
+ method public deprecated android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle) throws java.io.IOException;
+ }
+
+}
+
package android.content {
public abstract class Context {
@@ -39,6 +49,10 @@
package android.content.pm {
+ public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ field public deprecated java.lang.String volumeUuid;
+ }
+
public class ComponentInfo extends android.content.pm.PackageItemInfo {
field public deprecated boolean encryptionAware;
}
@@ -47,6 +61,12 @@
field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
}
+ public final class SharedLibraryInfo implements android.os.Parcelable {
+ method public boolean isBuiltin();
+ method public boolean isDynamic();
+ method public boolean isStatic();
+ }
+
}
package android.database {
@@ -110,6 +130,16 @@
}
+package android.location {
+
+ public class Location implements android.os.Parcelable {
+ method public deprecated void removeBearingAccuracy();
+ method public deprecated void removeSpeedAccuracy();
+ method public deprecated void removeVerticalAccuracy();
+ }
+
+}
+
package android.media {
public final class AudioFormat implements android.os.Parcelable {
@@ -178,10 +208,14 @@
package android.os.storage {
public class StorageManager {
- method public deprecated long getCacheQuotaBytes();
- method public deprecated long getCacheSizeBytes();
- method public deprecated long getExternalCacheQuotaBytes();
- method public deprecated long getExternalCacheSizeBytes();
+ method public deprecated void allocateBytes(java.io.File, long, int) throws java.io.IOException;
+ method public deprecated long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
+ method public deprecated long getCacheQuotaBytes(java.io.File) throws java.io.IOException;
+ method public deprecated long getCacheQuotaBytes() throws java.io.IOException;
+ method public deprecated long getCacheSizeBytes(java.io.File) throws java.io.IOException;
+ method public deprecated long getCacheSizeBytes() throws java.io.IOException;
+ method public deprecated long getExternalCacheQuotaBytes() throws java.io.IOException;
+ method public deprecated long getExternalCacheSizeBytes() throws java.io.IOException;
method public android.os.storage.StorageVolume getPrimaryVolume();
method public android.os.storage.StorageVolume[] getVolumeList();
method public deprecated boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
diff --git a/api/test-current.txt b/api/test-current.txt
index 1fed007..47a0fd8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -211,6 +211,7 @@
ctor public R.attr();
field public static final int __removed1 = 16844099; // 0x1010543
field public static final int __removed2 = 16844104; // 0x1010548
+ field public static final int __removed3 = 16844116; // 0x1010554
field public static final int absListViewStyle = 16842858; // 0x101006a
field public static final int accessibilityEventTypes = 16843648; // 0x1010380
field public static final int accessibilityFeedbackType = 16843650; // 0x1010382
@@ -313,7 +314,6 @@
field public static final int autoUrlDetect = 16843404; // 0x101028c
field public static final int autoVerify = 16844014; // 0x10104ee
field public static final int autofillHints = 16844121; // 0x1010559
- field public static final int autofillMode = 16844116; // 0x1010554
field public static final int background = 16842964; // 0x10100d4
field public static final int backgroundDimAmount = 16842802; // 0x1010032
field public static final int backgroundDimEnabled = 16843295; // 0x101021f
@@ -1710,6 +1710,7 @@
field public static final int ic_notification_clear_all = 17301594; // 0x108005a
field public static final int ic_notification_overlay = 17301595; // 0x108005b
field public static final int ic_partial_secure = 17301596; // 0x108005c
+ field public static final int ic_picture_in_picture = 17301685; // 0x10800b5
field public static final int ic_popup_disk_full = 17301597; // 0x108005d
field public static final int ic_popup_reminder = 17301598; // 0x108005e
field public static final int ic_popup_sync = 17301599; // 0x108005f
@@ -3617,6 +3618,7 @@
method public android.net.Uri getReferrer();
method public int getRequestedOrientation();
method public final android.view.SearchEvent getSearchEvent();
+ method public long getStartInitiatedTime();
method public int getTaskId();
method public final java.lang.CharSequence getTitle();
method public final int getTitleColor();
@@ -3851,7 +3853,7 @@
method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
method public java.util.List<android.app.ActivityManager.RunningAppProcessInfo> getRunningAppProcesses();
method public android.app.PendingIntent getRunningServiceControlPanel(android.content.ComponentName) throws java.lang.SecurityException;
- method public java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
+ method public deprecated java.util.List<android.app.ActivityManager.RunningServiceInfo> getRunningServices(int) throws java.lang.SecurityException;
method public deprecated java.util.List<android.app.ActivityManager.RunningTaskInfo> getRunningTasks(int) throws java.lang.SecurityException;
method public int getUidImportance(int);
method public deprecated boolean isInLockTaskMode();
@@ -3951,7 +3953,8 @@
field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d
field public static final int IMPORTANCE_GONE = 1000; // 0x3e8
- field public static final int IMPORTANCE_PERCEPTIBLE = 130; // 0x82
+ field public static final int IMPORTANCE_PERCEPTIBLE = 230; // 0xe6
+ field public static final deprecated int IMPORTANCE_PERCEPTIBLE_DEPRECATED = 130; // 0x82
field public static final int IMPORTANCE_SERVICE = 300; // 0x12c
field public static final int IMPORTANCE_TOP_SLEEPING = 150; // 0x96
field public static final int IMPORTANCE_VISIBLE = 200; // 0xc8
@@ -4588,6 +4591,7 @@
method public final android.app.FragmentManager getFragmentManager();
method public final java.lang.Object getHost();
method public final int getId();
+ method public final android.view.LayoutInflater getLayoutInflater();
method public android.app.LoaderManager getLoaderManager();
method public final android.app.Fragment getParentFragment();
method public android.transition.Transition getReenterTransition();
@@ -5616,7 +5620,6 @@
method public boolean removeAutomaticZenRule(java.lang.String);
method public final void setInterruptionFilter(int);
method public void setNotificationPolicy(android.app.NotificationManager.Policy);
- method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule);
field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED";
field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED";
@@ -6110,6 +6113,17 @@
method public void onDetached();
}
+ public final class WallpaperColors implements android.os.Parcelable {
+ ctor public WallpaperColors(android.os.Parcel);
+ ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>);
+ ctor public WallpaperColors(java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>>, boolean);
+ method public int describeContents();
+ method public java.util.List<android.util.Pair<android.graphics.Color, java.lang.Integer>> getColors();
+ method public boolean supportsDarkText();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR;
+ }
+
public final class WallpaperInfo implements android.os.Parcelable {
ctor public WallpaperInfo(android.content.Context, android.content.pm.ResolveInfo) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method public int describeContents();
@@ -6132,6 +6146,8 @@
}
public class WallpaperManager {
+ method public void addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener);
+ method public void addOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener, android.os.Handler);
method public void clear() throws java.io.IOException;
method public void clear(int) throws java.io.IOException;
method public void clearWallpaperOffsets(android.os.IBinder);
@@ -6146,6 +6162,7 @@
method public android.graphics.drawable.Drawable getDrawable();
method public android.graphics.drawable.Drawable getFastDrawable();
method public static android.app.WallpaperManager getInstance(android.content.Context);
+ method public android.app.WallpaperColors getWallpaperColors(int);
method public android.os.ParcelFileDescriptor getWallpaperFile(int);
method public int getWallpaperId(int);
method public android.app.WallpaperInfo getWallpaperInfo();
@@ -6154,6 +6171,7 @@
method public boolean isWallpaperSupported();
method public android.graphics.drawable.Drawable peekDrawable();
method public android.graphics.drawable.Drawable peekFastDrawable();
+ method public void removeOnColorsChangedListener(android.app.WallpaperManager.OnColorsChangedListener);
method public void sendWallpaperCommand(android.os.IBinder, java.lang.String, int, int, int, android.os.Bundle);
method public void setBitmap(android.graphics.Bitmap) throws java.io.IOException;
method public int setBitmap(android.graphics.Bitmap, android.graphics.Rect, boolean) throws java.io.IOException;
@@ -6178,6 +6196,10 @@
field public static final java.lang.String WALLPAPER_PREVIEW_META_DATA = "android.wallpaper.preview";
}
+ public static abstract interface WallpaperManager.OnColorsChangedListener {
+ method public abstract void onColorsChanged(android.app.WallpaperColors, int);
+ }
+
}
package android.app.admin {
@@ -6634,7 +6656,7 @@
public static class AssistStructure.ViewNode {
method public float getAlpha();
- method public java.lang.String[] getAutoFillHints();
+ method public java.lang.String[] getAutofillHints();
method public android.view.autofill.AutofillId getAutofillId();
method public java.lang.String[] getAutofillOptions();
method public int getAutofillType();
@@ -6898,6 +6920,14 @@
field public static final java.lang.String PERMISSION_BIND = "android.permission.BIND_JOB_SERVICE";
}
+ public abstract class JobServiceEngine {
+ ctor public JobServiceEngine(android.app.Service);
+ method public final android.os.IBinder getBinder();
+ method public final void jobFinished(android.app.job.JobParameters, boolean);
+ method public abstract boolean onStartJob(android.app.job.JobParameters);
+ method public abstract boolean onStopJob(android.app.job.JobParameters);
+ }
+
public final class JobWorkItem implements android.os.Parcelable {
ctor public JobWorkItem(android.content.Intent);
ctor public JobWorkItem(android.os.Parcel);
@@ -6995,13 +7025,13 @@
}
public class StorageStatsManager {
- method public long getFreeBytes(java.lang.String);
- method public long getTotalBytes(java.lang.String);
- method public boolean isQuotaSupported(java.lang.String);
- method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle);
- method public android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle);
- method public android.app.usage.StorageStats queryStatsForUid(java.lang.String, int);
- method public android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle);
+ method public long getFreeBytes(java.util.UUID) throws java.io.IOException;
+ method public long getTotalBytes(java.util.UUID) throws java.io.IOException;
+ method public boolean isQuotaSupported(java.util.UUID);
+ method public android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.util.UUID, android.os.UserHandle) throws java.io.IOException;
+ method public android.app.usage.StorageStats queryStatsForPackage(java.util.UUID, java.lang.String, android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
+ method public android.app.usage.StorageStats queryStatsForUid(java.util.UUID, int) throws java.io.IOException;
+ method public android.app.usage.StorageStats queryStatsForUser(java.util.UUID, android.os.UserHandle) throws java.io.IOException;
}
public final class UsageEvents implements android.os.Parcelable {
@@ -7066,6 +7096,7 @@
method public static void deleteAllHosts();
method public void deleteAppWidgetId(int);
method public void deleteHost();
+ method public int[] getAppWidgetIds();
method protected android.appwidget.AppWidgetHostView onCreateView(android.content.Context, int, android.appwidget.AppWidgetProviderInfo);
method protected void onProviderChanged(int, android.appwidget.AppWidgetProviderInfo);
method protected void onProvidersChanged();
@@ -8099,7 +8130,12 @@
method public void flushPendingScanResults(android.bluetooth.le.ScanCallback);
method public void startScan(android.bluetooth.le.ScanCallback);
method public void startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.bluetooth.le.ScanCallback);
+ method public int startScan(java.util.List<android.bluetooth.le.ScanFilter>, android.bluetooth.le.ScanSettings, android.app.PendingIntent);
method public void stopScan(android.bluetooth.le.ScanCallback);
+ method public void stopScan(android.app.PendingIntent);
+ field public static final java.lang.String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
+ field public static final java.lang.String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE";
+ field public static final java.lang.String EXTRA_LIST_SCAN_RESULT = "android.bluetooth.le.extra.LIST_SCAN_RESULT";
}
public final class PeriodicAdvertisingParameters implements android.os.Parcelable {
@@ -8274,7 +8310,8 @@
method public android.companion.BluetoothLEDeviceFilter build();
method public android.companion.BluetoothLEDeviceFilter.Builder setNamePattern(java.util.regex.Pattern);
method public android.companion.BluetoothLEDeviceFilter.Builder setRawDataFilter(byte[], byte[]);
- method public android.companion.BluetoothLEDeviceFilter.Builder setRename(java.lang.String, java.lang.String, int, int, boolean);
+ method public android.companion.BluetoothLEDeviceFilter.Builder setRenameFromBytes(java.lang.String, java.lang.String, int, int, boolean);
+ method public android.companion.BluetoothLEDeviceFilter.Builder setRenameFromName(java.lang.String, java.lang.String, int, int);
method public android.companion.BluetoothLEDeviceFilter.Builder setScanFilter(android.bluetooth.le.ScanFilter);
}
@@ -8282,6 +8319,8 @@
method public void associate(android.companion.AssociationRequest, android.companion.CompanionDeviceManager.Callback, android.os.Handler);
method public void disassociate(java.lang.String);
method public java.util.List<java.lang.String> getAssociations();
+ method public boolean hasNotificationAccess(android.content.ComponentName);
+ method public void requestNotificationAccess(android.content.ComponentName);
field public static final java.lang.String EXTRA_DEVICE = "android.companion.extra.DEVICE";
}
@@ -9357,7 +9396,6 @@
field public static final java.lang.String ACTION_MANAGED_PROFILE_UNLOCKED = "android.intent.action.MANAGED_PROFILE_UNLOCKED";
field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
- field public static final deprecated java.lang.String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
field public static final java.lang.String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON";
field public static final java.lang.String ACTION_MEDIA_CHECKING = "android.intent.action.MEDIA_CHECKING";
@@ -10217,12 +10255,12 @@
field public java.lang.String[] splitNames;
field public java.lang.String[] splitPublicSourceDirs;
field public java.lang.String[] splitSourceDirs;
+ field public java.util.UUID storageUuid;
field public int targetSdkVersion;
field public java.lang.String taskAffinity;
field public int theme;
field public int uiOptions;
field public int uid;
- field public java.lang.String volumeUuid;
}
public static class ApplicationInfo.DisplayNameComparator implements java.util.Comparator {
@@ -10342,7 +10380,7 @@
public class LauncherApps {
ctor public LauncherApps(android.content.Context);
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
- method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle);
+ method public android.content.pm.ApplicationInfo getApplicationInfo(java.lang.String, int, android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
method public android.content.pm.LauncherApps.PinItemRequest getPinItemRequest(android.content.Intent);
method public java.util.List<android.os.UserHandle> getProfiles();
method public android.graphics.drawable.Drawable getShortcutBadgedIconDrawable(android.content.pm.ShortcutInfo, int);
@@ -10946,12 +10984,13 @@
method public android.content.pm.VersionedPackage getDeclaringPackage();
method public java.util.List<android.content.pm.VersionedPackage> getDependentPackages();
method public java.lang.String getName();
- method public int getVersion();
- method public boolean isBuiltin();
- method public boolean isDynamic();
- method public boolean isStatic();
+ method public int getType();
+ method public long getVersion();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.content.pm.SharedLibraryInfo> CREATOR;
+ field public static final int TYPE_BUILTIN = 0; // 0x0
+ field public static final int TYPE_DYNAMIC = 1; // 0x1
+ field public static final int TYPE_STATIC = 2; // 0x2
field public static final int VERSION_UNDEFINED = -1; // 0xffffffff
}
@@ -12575,6 +12614,7 @@
field public boolean inJustDecodeBounds;
field public boolean inMutable;
field public deprecated boolean inPreferQualityOverSpeed;
+ field public android.graphics.ColorSpace inPreferredColorSpace;
field public android.graphics.Bitmap.Config inPreferredConfig;
field public boolean inPremultiplied;
field public deprecated boolean inPurgeable;
@@ -12605,7 +12645,6 @@
public class BitmapShader extends android.graphics.Shader {
ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
- method public void set(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
}
public class BlurMaskFilter extends android.graphics.MaskFilter {
@@ -12858,8 +12897,6 @@
ctor public ColorMatrixColorFilter(android.graphics.ColorMatrix);
ctor public ColorMatrixColorFilter(float[]);
method public void getColorMatrix(android.graphics.ColorMatrix);
- method public void setColorMatrix(android.graphics.ColorMatrix);
- method public void setColorMatrixArray(float[]);
}
public abstract class ColorSpace {
@@ -13000,8 +13037,6 @@
public class ComposeShader extends android.graphics.Shader {
ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode);
ctor public ComposeShader(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode);
- method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.Xfermode);
- method public void set(android.graphics.Shader, android.graphics.Shader, android.graphics.PorterDuff.Mode);
}
public class CornerPathEffect extends android.graphics.PathEffect {
@@ -13074,15 +13109,11 @@
ctor public LightingColorFilter(int, int);
method public int getColorAdd();
method public int getColorMultiply();
- method public void setColorAdd(int);
- method public void setColorMultiply(int);
}
public class LinearGradient extends android.graphics.Shader {
ctor public LinearGradient(float, float, float, float, int[], float[], android.graphics.Shader.TileMode);
ctor public LinearGradient(float, float, float, float, int, int, android.graphics.Shader.TileMode);
- method public void set(float, float, float, float, int[], float[], android.graphics.Shader.TileMode);
- method public void set(float, float, float, float, int, int, android.graphics.Shader.TileMode);
}
public class MaskFilter {
@@ -13592,10 +13623,6 @@
public class PorterDuffColorFilter extends android.graphics.ColorFilter {
ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode);
- method public int getColor();
- method public android.graphics.PorterDuff.Mode getMode();
- method public void setColor(int);
- method public void setMode(android.graphics.PorterDuff.Mode);
}
public class PorterDuffXfermode extends android.graphics.Xfermode {
@@ -13605,8 +13632,6 @@
public class RadialGradient extends android.graphics.Shader {
ctor public RadialGradient(float, float, float, int[], float[], android.graphics.Shader.TileMode);
ctor public RadialGradient(float, float, float, int, int, android.graphics.Shader.TileMode);
- method public void set(float, float, float, int[], float[], android.graphics.Shader.TileMode);
- method public void set(float, float, float, int, int, android.graphics.Shader.TileMode);
}
public final class Rect implements android.os.Parcelable {
@@ -13791,8 +13816,6 @@
public class SweepGradient extends android.graphics.Shader {
ctor public SweepGradient(float, float, int[], float[]);
ctor public SweepGradient(float, float, int, int);
- method public void set(float, float, int[], float[]);
- method public void set(float, float, int, int);
}
public class Typeface {
@@ -20870,13 +20893,10 @@
method public boolean hasSpeedAccuracy();
method public boolean hasVerticalAccuracy();
method public boolean isFromMockProvider();
- method public void removeAccuracy();
- method public void removeAltitude();
- method public void removeBearing();
- method public void removeBearingAccuracy();
- method public void removeSpeed();
- method public void removeSpeedAccuracy();
- method public void removeVerticalAccuracy();
+ method public deprecated void removeAccuracy();
+ method public deprecated void removeAltitude();
+ method public deprecated void removeBearing();
+ method public deprecated void removeSpeed();
method public void reset();
method public void set(android.location.Location);
method public void setAccuracy(float);
@@ -21976,7 +21996,7 @@
method public deprecated java.nio.ByteBuffer[] getInputBuffers();
method public final android.media.MediaFormat getInputFormat();
method public android.media.Image getInputImage(int);
- method public android.media.MediaMetricsSet getMetrics();
+ method public android.os.PersistableBundle getMetrics();
method public final java.lang.String getName();
method public java.nio.ByteBuffer getOutputBuffer(int);
method public deprecated java.nio.ByteBuffer[] getOutputBuffers();
@@ -22074,6 +22094,19 @@
method public void set(int, int);
}
+ public static final class MediaCodec.MetricsConstants {
+ field public static final java.lang.String CODEC = "android.media.mediacodec.codec";
+ field public static final java.lang.String ENCODER = "android.media.mediacodec.encoder";
+ field public static final java.lang.String HEIGHT = "android.media.mediacodec.height";
+ field public static final java.lang.String MIME_TYPE = "android.media.mediacodec.mime";
+ field public static final java.lang.String MODE = "android.media.mediacodec.mode";
+ field public static final java.lang.String MODE_AUDIO = "audio";
+ field public static final java.lang.String MODE_VIDEO = "video";
+ field public static final java.lang.String ROTATION = "android.media.mediacodec.rotation";
+ field public static final java.lang.String SECURE = "android.media.mediacodec.secure";
+ field public static final java.lang.String WIDTH = "android.media.mediacodec.width";
+ }
+
public static abstract interface MediaCodec.OnFrameRenderedListener {
method public abstract void onFrameRendered(android.media.MediaCodec, long, long);
}
@@ -22529,7 +22562,7 @@
method public long getCachedDuration();
method public android.media.MediaExtractor.CasInfo getCasInfo(int);
method public android.media.DrmInitData getDrmInitData();
- method public android.media.MediaMetricsSet getMetrics();
+ method public android.os.PersistableBundle getMetrics();
method public java.util.Map<java.util.UUID, byte[]> getPsshInfo();
method public boolean getSampleCryptoInfo(android.media.MediaCodec.CryptoInfo);
method public int getSampleFlags();
@@ -22564,6 +22597,12 @@
method public int getSystemId();
}
+ public static final class MediaExtractor.MetricsConstants {
+ field public static final java.lang.String FORMAT = "android.media.mediaextractor.fmt";
+ field public static final java.lang.String MIME_TYPE = "android.media.mediaextractor.mime";
+ field public static final java.lang.String TRACKS = "android.media.mediaextractor.ntrk";
+ }
+
public final class MediaFormat {
ctor public MediaFormat();
method public final boolean containsKey(java.lang.String);
@@ -22789,69 +22828,6 @@
field public static final int OPTION_PREVIOUS_SYNC = 0; // 0x0
}
- public final class MediaMetricsSet {
- method public double getDouble(java.lang.String, double);
- method public int getInt(java.lang.String, int);
- method public long getLong(java.lang.String, long);
- method public java.lang.String getString(java.lang.String, java.lang.String);
- method public boolean isEmpty();
- method public java.util.Set<java.lang.String> keySet();
- method public int size();
- }
-
- public static final class MediaMetricsSet.MediaCodec {
- field public static final java.lang.String KEY_CODEC = "android.media.mediacodec.codec";
- field public static final java.lang.String KEY_ENCODER = "android.media.mediacodec.encoder";
- field public static final java.lang.String KEY_HEIGHT = "android.media.mediacodec.height";
- field public static final java.lang.String KEY_MIME = "android.media.mediacodec.mime";
- field public static final java.lang.String KEY_MODE = "android.media.mediacodec.mode";
- field public static final java.lang.String KEY_ROTATION = "android.media.mediacodec.rotation";
- field public static final java.lang.String KEY_SECURE = "android.media.mediacodec.secure";
- field public static final java.lang.String KEY_WIDTH = "android.media.mediacodec.width";
- field public static final java.lang.String MODE_AUDIO = "audio";
- field public static final java.lang.String MODE_VIDEO = "video";
- }
-
- public static final class MediaMetricsSet.MediaExtractor {
- field public static final java.lang.String KEY_FORMAT = "android.media.mediaextractor.fmt";
- field public static final java.lang.String KEY_MIME = "android.media.mediaextractor.mime";
- field public static final java.lang.String KEY_TRACKS = "android.media.mediaextractor.ntrk";
- }
-
- public static final class MediaMetricsSet.MediaPlayer {
- field public static final java.lang.String KEY_CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
- field public static final java.lang.String KEY_CODEC_VIDEO = "android.media.mediaplayer.video.codec";
- field public static final java.lang.String KEY_DURATION = "android.media.mediaplayer.durationMs";
- field public static final java.lang.String KEY_ERRORS = "android.media.mediaplayer.err";
- field public static final java.lang.String KEY_ERROR_CODE = "android.media.mediaplayer.errcode";
- field public static final java.lang.String KEY_FRAMES = "android.media.mediaplayer.frames";
- field public static final java.lang.String KEY_FRAMES_DROPPED = "android.media.mediaplayer.dropped";
- field public static final java.lang.String KEY_HEIGHT = "android.media.mediaplayer.height";
- field public static final java.lang.String KEY_MIME_AUDIO = "android.media.mediaplayer.audio.mime";
- field public static final java.lang.String KEY_MIME_VIDEO = "android.media.mediaplayer.video.mime";
- field public static final java.lang.String KEY_PLAYING = "android.media.mediaplayer.playingMs";
- field public static final java.lang.String KEY_WIDTH = "android.media.mediaplayer.width";
- }
-
- public static final class MediaMetricsSet.MediaRecorder {
- field public static final java.lang.String KEY_AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
- field public static final java.lang.String KEY_AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
- field public static final java.lang.String KEY_AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
- field public static final java.lang.String KEY_AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
- field public static final java.lang.String KEY_CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
- field public static final java.lang.String KEY_CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
- field public static final java.lang.String KEY_FRAMERATE = "android.media.mediarecorder.frame-rate";
- field public static final java.lang.String KEY_HEIGHT = "android.media.mediarecorder.height";
- field public static final java.lang.String KEY_MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
- field public static final java.lang.String KEY_ROTATION = "android.media.mediarecorder.rotation";
- field public static final java.lang.String KEY_VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
- field public static final java.lang.String KEY_VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
- field public static final java.lang.String KEY_VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
- field public static final java.lang.String KEY_VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
- field public static final java.lang.String KEY_VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
- field public static final java.lang.String KEY_WIDTH = "android.media.mediarecorder.width";
- }
-
public final class MediaMuxer {
ctor public MediaMuxer(java.lang.String, int) throws java.io.IOException;
ctor public MediaMuxer(java.io.FileDescriptor, int) throws java.io.IOException;
@@ -22889,8 +22865,8 @@
method public android.media.MediaPlayer.DrmInfo getDrmInfo();
method public java.lang.String getDrmPropertyString(java.lang.String) throws android.media.MediaPlayer.NoDrmSchemeException;
method public int getDuration();
- method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
- method public android.media.MediaMetricsSet getMetrics();
+ method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.Map<java.lang.String, java.lang.String>) throws android.media.MediaPlayer.NoDrmSchemeException;
+ method public android.os.PersistableBundle getMetrics();
method public android.media.PlaybackParams getPlaybackParams();
method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
method public android.media.SyncParams getSyncParams();
@@ -22903,7 +22879,7 @@
method public void pause() throws java.lang.IllegalStateException;
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
method public void prepareAsync() throws java.lang.IllegalStateException;
- method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
+ method public void prepareDrm(java.util.UUID) throws android.media.MediaPlayer.ProvisioningNetworkErrorException, android.media.MediaPlayer.ProvisioningServerErrorException, android.media.ResourceBusyException, android.media.UnsupportedSchemeException;
method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.MediaPlayer.NoDrmSchemeException;
method public void release();
method public void releaseDrm() throws android.media.MediaPlayer.NoDrmSchemeException;
@@ -22930,7 +22906,7 @@
method public void setNextMediaPlayer(android.media.MediaPlayer);
method public void setOnBufferingUpdateListener(android.media.MediaPlayer.OnBufferingUpdateListener);
method public void setOnCompletionListener(android.media.MediaPlayer.OnCompletionListener);
- method public void setOnDrmConfigListener(android.media.MediaPlayer.OnDrmConfigListener);
+ method public void setOnDrmConfigHelper(android.media.MediaPlayer.OnDrmConfigHelper);
method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener);
method public void setOnDrmInfoListener(android.media.MediaPlayer.OnDrmInfoListener, android.os.Handler);
method public void setOnDrmPreparedListener(android.media.MediaPlayer.OnDrmPreparedListener);
@@ -22971,6 +22947,10 @@
field public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3; // 0x3
field public static final int MEDIA_INFO_VIDEO_TRACK_LAGGING = 700; // 0x2bc
field public static final java.lang.String MEDIA_MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
+ field public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3; // 0x3
+ field public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1; // 0x1
+ field public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2; // 0x2
+ field public static final int PREPARE_DRM_STATUS_SUCCESS = 0; // 0x0
field public static final int SEEK_CLOSEST = 3; // 0x3
field public static final int SEEK_CLOSEST_SYNC = 2; // 0x2
field public static final int SEEK_NEXT_SYNC = 1; // 0x1
@@ -22980,11 +22960,25 @@
}
public static final class MediaPlayer.DrmInfo {
- method public java.lang.String[] getMimes();
method public java.util.Map<java.util.UUID, byte[]> getPssh();
method public java.util.UUID[] getSupportedSchemes();
}
+ public static final class MediaPlayer.MetricsConstants {
+ field public static final java.lang.String CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
+ field public static final java.lang.String CODEC_VIDEO = "android.media.mediaplayer.video.codec";
+ field public static final java.lang.String DURATION = "android.media.mediaplayer.durationMs";
+ field public static final java.lang.String ERRORS = "android.media.mediaplayer.err";
+ field public static final java.lang.String ERROR_CODE = "android.media.mediaplayer.errcode";
+ field public static final java.lang.String FRAMES = "android.media.mediaplayer.frames";
+ field public static final java.lang.String FRAMES_DROPPED = "android.media.mediaplayer.dropped";
+ field public static final java.lang.String HEIGHT = "android.media.mediaplayer.height";
+ field public static final java.lang.String MIME_TYPE_AUDIO = "android.media.mediaplayer.audio.mime";
+ field public static final java.lang.String MIME_TYPE_VIDEO = "android.media.mediaplayer.video.mime";
+ field public static final java.lang.String PLAYING = "android.media.mediaplayer.playingMs";
+ field public static final java.lang.String WIDTH = "android.media.mediaplayer.width";
+ }
+
public static final class MediaPlayer.NoDrmSchemeException extends android.media.MediaDrmException {
ctor public MediaPlayer.NoDrmSchemeException(java.lang.String);
}
@@ -22997,7 +22991,7 @@
method public abstract void onCompletion(android.media.MediaPlayer);
}
- public static abstract interface MediaPlayer.OnDrmConfigListener {
+ public static abstract interface MediaPlayer.OnDrmConfigHelper {
method public abstract void onDrmConfig(android.media.MediaPlayer);
}
@@ -23006,7 +23000,7 @@
}
public static abstract interface MediaPlayer.OnDrmPreparedListener {
- method public abstract void onDrmPrepared(android.media.MediaPlayer, boolean);
+ method public abstract void onDrmPrepared(android.media.MediaPlayer, int);
}
public static abstract interface MediaPlayer.OnErrorListener {
@@ -23037,8 +23031,12 @@
method public abstract void onVideoSizeChanged(android.media.MediaPlayer, int, int);
}
- public static final class MediaPlayer.ProvisioningErrorException extends android.media.MediaDrmException {
- ctor public MediaPlayer.ProvisioningErrorException(java.lang.String);
+ public static final class MediaPlayer.ProvisioningNetworkErrorException extends android.media.MediaDrmException {
+ ctor public MediaPlayer.ProvisioningNetworkErrorException(java.lang.String);
+ }
+
+ public static final class MediaPlayer.ProvisioningServerErrorException extends android.media.MediaDrmException {
+ ctor public MediaPlayer.ProvisioningServerErrorException(java.lang.String);
}
public static class MediaPlayer.TrackInfo implements android.os.Parcelable {
@@ -23059,7 +23057,7 @@
ctor public MediaRecorder();
method public static final int getAudioSourceMax();
method public int getMaxAmplitude() throws java.lang.IllegalStateException;
- method public android.media.MediaMetricsSet getMetrics();
+ method public android.os.PersistableBundle getMetrics();
method public android.view.Surface getSurface();
method public void pause() throws java.lang.IllegalStateException;
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
@@ -23078,11 +23076,12 @@
method public void setMaxDuration(int) throws java.lang.IllegalArgumentException;
method public void setMaxFileSize(long) throws java.lang.IllegalArgumentException;
method public void setNextOutputFile(java.io.FileDescriptor) throws java.io.IOException, java.lang.IllegalStateException;
- method public void setNextOutputFile(java.lang.String) throws java.io.IOException, java.lang.IllegalStateException;
+ method public void setNextOutputFile(java.io.File) throws java.io.IOException, java.lang.IllegalStateException;
method public void setOnErrorListener(android.media.MediaRecorder.OnErrorListener);
method public void setOnInfoListener(android.media.MediaRecorder.OnInfoListener);
method public void setOrientationHint(int);
method public void setOutputFile(java.io.FileDescriptor) throws java.lang.IllegalStateException;
+ method public void setOutputFile(java.io.File);
method public void setOutputFile(java.lang.String) throws java.lang.IllegalStateException;
method public void setOutputFormat(int) throws java.lang.IllegalStateException;
method public void setPreviewDisplay(android.view.Surface);
@@ -23127,6 +23126,25 @@
field public static final int VOICE_UPLINK = 2; // 0x2
}
+ public static final class MediaRecorder.MetricsConstants {
+ field public static final java.lang.String AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
+ field public static final java.lang.String AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
+ field public static final java.lang.String AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
+ field public static final java.lang.String AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
+ field public static final java.lang.String CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
+ field public static final java.lang.String CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
+ field public static final java.lang.String FRAMERATE = "android.media.mediarecorder.frame-rate";
+ field public static final java.lang.String HEIGHT = "android.media.mediarecorder.height";
+ field public static final java.lang.String MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
+ field public static final java.lang.String ROTATION = "android.media.mediarecorder.rotation";
+ field public static final java.lang.String VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
+ field public static final java.lang.String VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
+ field public static final java.lang.String VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
+ field public static final java.lang.String VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
+ field public static final java.lang.String VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
+ field public static final java.lang.String WIDTH = "android.media.mediarecorder.width";
+ }
+
public static abstract interface MediaRecorder.OnErrorListener {
method public abstract void onError(android.media.MediaRecorder, int, int);
}
@@ -24090,7 +24108,6 @@
method public android.content.ComponentName getServiceComponent();
method public android.media.session.MediaSession.Token getSessionToken();
method public boolean isConnected();
- method public void search(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SearchCallback);
method public void subscribe(java.lang.String, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void subscribe(java.lang.String, android.os.Bundle, android.media.browse.MediaBrowser.SubscriptionCallback);
method public void unsubscribe(java.lang.String);
@@ -24126,12 +24143,6 @@
field public static final int FLAG_PLAYABLE = 2; // 0x2
}
- public static abstract class MediaBrowser.SearchCallback {
- ctor public MediaBrowser.SearchCallback();
- method public void onError(java.lang.String, android.os.Bundle);
- method public void onSearchResult(java.lang.String, android.os.Bundle, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
- }
-
public static abstract class MediaBrowser.SubscriptionCallback {
ctor public MediaBrowser.SubscriptionCallback();
method public void onChildrenLoaded(java.lang.String, java.util.List<android.media.browse.MediaBrowser.MediaItem>);
@@ -24340,8 +24351,6 @@
public final class MediaController {
ctor public MediaController(android.content.Context, android.media.session.MediaSession.Token);
- method public void addQueueItem(android.media.MediaDescription);
- method public void addQueueItem(android.media.MediaDescription, int);
method public void adjustVolume(int, int);
method public boolean dispatchMediaButtonEvent(android.view.KeyEvent);
method public android.os.Bundle getExtras();
@@ -24353,15 +24362,11 @@
method public java.util.List<android.media.session.MediaSession.QueueItem> getQueue();
method public java.lang.CharSequence getQueueTitle();
method public int getRatingType();
- method public int getRepeatMode();
method public android.app.PendingIntent getSessionActivity();
method public android.media.session.MediaSession.Token getSessionToken();
method public android.media.session.MediaController.TransportControls getTransportControls();
- method public boolean isShuffleModeEnabled();
method public void registerCallback(android.media.session.MediaController.Callback);
method public void registerCallback(android.media.session.MediaController.Callback, android.os.Handler);
- method public void removeQueueItem(android.media.MediaDescription);
- method public void removeQueueItemAt(int);
method public void sendCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void setVolumeTo(int, int);
method public void unregisterCallback(android.media.session.MediaController.Callback);
@@ -24375,10 +24380,8 @@
method public void onPlaybackStateChanged(android.media.session.PlaybackState);
method public void onQueueChanged(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void onQueueTitleChanged(java.lang.CharSequence);
- method public void onRepeatModeChanged(int);
method public void onSessionDestroyed();
method public void onSessionEvent(java.lang.String, android.os.Bundle);
- method public void onShuffleModeChanged(boolean);
}
public static final class MediaController.PlaybackInfo {
@@ -24407,8 +24410,6 @@
method public void sendCustomAction(android.media.session.PlaybackState.CustomAction, android.os.Bundle);
method public void sendCustomAction(java.lang.String, android.os.Bundle);
method public void setRating(android.media.Rating);
- method public void setRepeatMode(int);
- method public void setShuffleModeEnabled(boolean);
method public void skipToNext();
method public void skipToPrevious();
method public void skipToQueueItem(long);
@@ -24435,18 +24436,13 @@
method public void setQueue(java.util.List<android.media.session.MediaSession.QueueItem>);
method public void setQueueTitle(java.lang.CharSequence);
method public void setRatingType(int);
- method public void setRepeatMode(int);
method public void setSessionActivity(android.app.PendingIntent);
- method public void setShuffleModeEnabled(boolean);
field public static final deprecated int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
- field public static final int FLAG_HANDLES_QUEUE_COMMANDS = 4; // 0x4
field public static final deprecated int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
}
public static abstract class MediaSession.Callback {
ctor public MediaSession.Callback();
- method public void onAddQueueItem(android.media.MediaDescription);
- method public void onAddQueueItem(android.media.MediaDescription, int);
method public void onCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
method public void onCustomAction(java.lang.String, android.os.Bundle);
method public void onFastForward();
@@ -24460,13 +24456,9 @@
method public void onPrepareFromMediaId(java.lang.String, android.os.Bundle);
method public void onPrepareFromSearch(java.lang.String, android.os.Bundle);
method public void onPrepareFromUri(android.net.Uri, android.os.Bundle);
- method public void onRemoveQueueItem(android.media.MediaDescription);
- method public void onRemoveQueueItemAt(int);
method public void onRewind();
method public void onSeekTo(long);
method public void onSetRating(android.media.Rating);
- method public void onSetRepeatMode(int);
- method public void onSetShuffleModeEnabled(boolean);
method public void onSkipToNext();
method public void onSkipToPrevious();
method public void onSkipToQueueItem(long);
@@ -24527,17 +24519,12 @@
field public static final long ACTION_REWIND = 8L; // 0x8L
field public static final long ACTION_SEEK_TO = 256L; // 0x100L
field public static final long ACTION_SET_RATING = 128L; // 0x80L
- field public static final long ACTION_SET_REPEAT_MODE = 262144L; // 0x40000L
- field public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 524288L; // 0x80000L
field public static final long ACTION_SKIP_TO_NEXT = 32L; // 0x20L
field public static final long ACTION_SKIP_TO_PREVIOUS = 16L; // 0x10L
field public static final long ACTION_SKIP_TO_QUEUE_ITEM = 4096L; // 0x1000L
field public static final long ACTION_STOP = 1L; // 0x1L
field public static final android.os.Parcelable.Creator<android.media.session.PlaybackState> CREATOR;
field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
- field public static final int REPEAT_MODE_ALL = 2; // 0x2
- field public static final int REPEAT_MODE_NONE = 0; // 0x0
- field public static final int REPEAT_MODE_ONE = 1; // 0x1
field public static final int STATE_BUFFERING = 6; // 0x6
field public static final int STATE_CONNECTING = 8; // 0x8
field public static final int STATE_ERROR = 7; // 0x7
@@ -25638,14 +25625,10 @@
}
public final class IpSecManager {
- method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException;
- method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
- method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
- method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
- method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform);
+ method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
@@ -25664,7 +25647,7 @@
}
public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
- method public void close();
+ method public void close() throws java.io.IOException;
method public int getPort();
method public java.io.FileDescriptor getSocket();
}
@@ -25886,6 +25869,10 @@
method public android.net.NetworkRequest.Builder removeCapability(int);
method public android.net.NetworkRequest.Builder removeTransportType(int);
method public android.net.NetworkRequest.Builder setNetworkSpecifier(java.lang.String);
+ method public android.net.NetworkRequest.Builder setNetworkSpecifier(android.net.NetworkSpecifier);
+ }
+
+ public abstract class NetworkSpecifier {
}
public class ParseException extends java.lang.RuntimeException {
@@ -26858,8 +26845,8 @@
}
public class DiscoverySession {
- method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
- method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
+ method public android.net.NetworkSpecifier createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
+ method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
method public void destroy();
method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]);
}
@@ -26945,8 +26932,8 @@
}
public class WifiAwareSession {
- method public java.lang.String createNetworkSpecifierOpen(int, byte[]);
- method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
+ method public android.net.NetworkSpecifier createNetworkSpecifierOpen(int, byte[]);
+ method public android.net.NetworkSpecifier createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
method public void destroy();
method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
@@ -30877,6 +30864,7 @@
method public android.util.SizeF getSizeF(java.lang.String);
method public <T extends android.os.Parcelable> android.util.SparseArray<T> getSparseParcelableArray(java.lang.String);
method public java.util.ArrayList<java.lang.String> getStringArrayList(java.lang.String);
+ method public java.util.UUID getUuid(java.lang.String);
method public boolean hasFileDescriptors();
method public void putAll(android.os.Bundle);
method public void putBinder(java.lang.String, android.os.IBinder);
@@ -30901,6 +30889,7 @@
method public void putSizeF(java.lang.String, android.util.SizeF);
method public void putSparseParcelableArray(java.lang.String, android.util.SparseArray<? extends android.os.Parcelable>);
method public void putStringArrayList(java.lang.String, java.util.ArrayList<java.lang.String>);
+ method public void putUuid(java.lang.String, java.util.UUID);
method public void readFromParcel(android.os.Parcel);
method public void setClassLoader(java.lang.ClassLoader);
method public void writeToParcel(android.os.Parcel, int);
@@ -31451,6 +31440,7 @@
method public final <T> void readTypedArray(T[], android.os.Parcelable.Creator<T>);
method public final <T> void readTypedList(java.util.List<T>, android.os.Parcelable.Creator<T>);
method public final <T> T readTypedObject(android.os.Parcelable.Creator<T>);
+ method public final java.util.UUID readUuid();
method public final java.lang.Object readValue(java.lang.ClassLoader);
method public final void recycle();
method public final void setDataCapacity(int);
@@ -31496,6 +31486,7 @@
method public final <T extends android.os.Parcelable> void writeTypedArray(T[], int);
method public final <T extends android.os.Parcelable> void writeTypedList(java.util.List<T>);
method public final <T extends android.os.Parcelable> void writeTypedObject(T, int);
+ method public final void writeUuid(java.util.UUID);
method public final void writeValue(java.lang.Object);
field public static final android.os.Parcelable.Creator<java.lang.String> STRING_CREATOR;
}
@@ -32130,15 +32121,16 @@
}
public class StorageManager {
- method public void allocateBytes(java.io.File, long, int) throws java.io.IOException;
+ method public void allocateBytes(java.util.UUID, long, int) throws java.io.IOException;
method public void allocateBytes(java.io.FileDescriptor, long, int) throws java.io.IOException;
- method public long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
- method public long getCacheQuotaBytes(java.io.File);
- method public long getCacheSizeBytes(java.io.File);
+ method public long getAllocatableBytes(java.util.UUID, int) throws java.io.IOException;
+ method public long getCacheQuotaBytes(java.util.UUID) throws java.io.IOException;
+ method public long getCacheSizeBytes(java.util.UUID) throws java.io.IOException;
method public java.lang.String getMountedObbPath(java.lang.String);
method public android.os.storage.StorageVolume getPrimaryStorageVolume();
method public android.os.storage.StorageVolume getStorageVolume(java.io.File);
method public java.util.List<android.os.storage.StorageVolume> getStorageVolumes();
+ method public java.util.UUID getUuidForPath(java.io.File) throws java.io.IOException;
method public boolean isCacheBehaviorGroup(java.io.File) throws java.io.IOException;
method public boolean isCacheBehaviorTombstone(java.io.File) throws java.io.IOException;
method public boolean isEncrypted(java.io.File);
@@ -32150,7 +32142,10 @@
method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
method public boolean unmountObb(java.lang.String, boolean, android.os.storage.OnObbStateChangeListener);
field public static final java.lang.String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
+ field public static final java.lang.String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES";
+ field public static final java.lang.String EXTRA_UUID = "android.os.storage.extra.UUID";
field public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1
+ field public static final java.util.UUID UUID_DEFAULT;
}
public final class StorageVolume implements android.os.Parcelable {
@@ -35595,6 +35590,16 @@
field public static final java.lang.String SUBSCRIPTION_ID = "pending_sub_id";
}
+ public static final class Telephony.ServiceStateTable {
+ method public static android.net.Uri getUriForSubId(int);
+ method public static android.net.Uri getUriForSubIdAndField(int, java.lang.String);
+ field public static final java.lang.String AUTHORITY = "service-state";
+ field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String IS_MANUAL_NETWORK_SELECTION = "is_manual_network_selection";
+ field public static final java.lang.String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric";
+ field public static final java.lang.String VOICE_REG_STATE = "voice_reg_state";
+ }
+
public static final class Telephony.Sms implements android.provider.BaseColumns android.provider.Telephony.TextBasedSmsColumns {
method public static java.lang.String getDefaultSmsPackage(android.content.Context);
field public static final android.net.Uri CONTENT_URI;
@@ -37182,11 +37187,14 @@
public abstract class AutofillService extends android.app.Service {
ctor public AutofillService();
method public final deprecated void disableSelf();
+ method public final android.service.autofill.FillEventHistory getFillEventHistory();
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConnected();
method public void onDisconnected();
- method public abstract void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
- method public abstract void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
+ method public void onFillRequest(android.service.autofill.FillRequest, android.os.CancellationSignal, android.service.autofill.FillCallback);
+ method public abstract deprecated void onFillRequest(android.app.assist.AssistStructure, android.os.Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback);
+ method public void onSaveRequest(android.service.autofill.SaveRequest, android.service.autofill.SaveCallback);
+ method public abstract deprecated void onSaveRequest(android.app.assist.AssistStructure, android.os.Bundle, android.service.autofill.SaveCallback);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.autofill.AutofillService";
field public static final java.lang.String SERVICE_META_DATA = "android.autofill";
}
@@ -37202,6 +37210,7 @@
ctor public Dataset.Builder();
method public android.service.autofill.Dataset build();
method public android.service.autofill.Dataset.Builder setAuthentication(android.content.IntentSender);
+ method public android.service.autofill.Dataset.Builder setId(java.lang.String);
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue);
method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, android.widget.RemoteViews);
}
@@ -37211,6 +37220,42 @@
method public void onSuccess(android.service.autofill.FillResponse);
}
+ public final class FillContext implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getRequestId();
+ method public android.app.assist.AssistStructure getStructure();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillContext> CREATOR;
+ }
+
+ public final class FillEventHistory implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public java.util.List<android.service.autofill.FillEventHistory.Event> getEvents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillEventHistory> CREATOR;
+ }
+
+ public static final class FillEventHistory.Event {
+ method public java.lang.String getDatasetId();
+ method public int getType();
+ field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2
+ field public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1; // 0x1
+ field public static final int TYPE_DATASET_SELECTED = 0; // 0x0
+ field public static final int TYPE_SAVE_SHOWN = 3; // 0x3
+ }
+
+ public final class FillRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public int getFlags();
+ method public int getId();
+ method public android.app.assist.AssistStructure getStructure();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.FillRequest> CREATOR;
+ field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
+ }
+
public final class FillResponse implements android.os.Parcelable {
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
@@ -37222,7 +37267,9 @@
method public android.service.autofill.FillResponse.Builder addDataset(android.service.autofill.Dataset);
method public android.service.autofill.FillResponse build();
method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
- method public android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
+ method public deprecated android.service.autofill.FillResponse.Builder setExtras(android.os.Bundle);
+ method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
}
@@ -37235,6 +37282,7 @@
method public int describeContents();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.service.autofill.SaveInfo> CREATOR;
+ field public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 1; // 0x1
field public static final int SAVE_DATA_TYPE_ADDRESS = 2; // 0x2
field public static final int SAVE_DATA_TYPE_CREDIT_CARD = 4; // 0x4
field public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 16; // 0x10
@@ -37247,10 +37295,19 @@
ctor public SaveInfo.Builder(int, android.view.autofill.AutofillId[]);
method public android.service.autofill.SaveInfo build();
method public android.service.autofill.SaveInfo.Builder setDescription(java.lang.CharSequence);
+ method public android.service.autofill.SaveInfo.Builder setFlags(int);
method public android.service.autofill.SaveInfo.Builder setNegativeAction(java.lang.CharSequence, android.content.IntentSender);
method public android.service.autofill.SaveInfo.Builder setOptionalIds(android.view.autofill.AutofillId[]);
}
+ public final class SaveRequest implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.os.Bundle getClientState();
+ method public java.util.List<android.service.autofill.FillContext> getFillContexts();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
+ }
+
}
package android.service.carrier {
@@ -37432,7 +37489,6 @@
method public abstract void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
method public void onLoadChildren(java.lang.String, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>, android.os.Bundle);
method public void onLoadItem(java.lang.String, android.service.media.MediaBrowserService.Result<android.media.browse.MediaBrowser.MediaItem>);
- method public void onSearch(java.lang.String, android.os.Bundle, android.service.media.MediaBrowserService.Result<java.util.List<android.media.browse.MediaBrowser.MediaItem>>);
method public void setSessionToken(android.media.session.MediaSession.Token);
field public static final java.lang.String SERVICE_INTERFACE = "android.media.browse.MediaBrowserService";
}
@@ -37538,16 +37594,16 @@
method public final int getCurrentInterruptionFilter();
method public final int getCurrentListenerHints();
method public android.service.notification.NotificationListenerService.RankingMap getCurrentRanking();
- method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String);
- method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String);
+ method public final java.util.List<android.app.NotificationChannelGroup> getNotificationChannelGroups(java.lang.String, android.os.UserHandle);
+ method public final java.util.List<android.app.NotificationChannel> getNotificationChannels(java.lang.String, android.os.UserHandle);
method public final android.service.notification.StatusBarNotification[] getSnoozedNotifications();
method public android.os.IBinder onBind(android.content.Intent);
method public void onInterruptionFilterChanged(int);
method public void onListenerConnected();
method public void onListenerDisconnected();
method public void onListenerHintsChanged(int);
- method public void onNotificationChannelGroupModified(java.lang.String, android.app.NotificationChannelGroup, int);
- method public void onNotificationChannelModified(java.lang.String, android.app.NotificationChannel, int);
+ method public void onNotificationChannelGroupModified(java.lang.String, android.os.UserHandle, android.app.NotificationChannelGroup, int);
+ method public void onNotificationChannelModified(java.lang.String, android.os.UserHandle, android.app.NotificationChannel, int);
method public void onNotificationPosted(android.service.notification.StatusBarNotification);
method public void onNotificationPosted(android.service.notification.StatusBarNotification, android.service.notification.NotificationListenerService.RankingMap);
method public void onNotificationRankingUpdate(android.service.notification.NotificationListenerService.RankingMap);
@@ -37561,7 +37617,7 @@
method public final void setNotificationsShown(java.lang.String[]);
method public final void snoozeNotification(java.lang.String, java.lang.String);
method public final void snoozeNotification(java.lang.String, long);
- method public final void updateNotificationChannel(java.lang.String, android.app.NotificationChannel);
+ method public final void updateNotificationChannel(java.lang.String, android.os.UserHandle, android.app.NotificationChannel);
field public static final int HINT_HOST_DISABLE_CALL_EFFECTS = 4; // 0x4
field public static final int HINT_HOST_DISABLE_EFFECTS = 1; // 0x1
field public static final int HINT_HOST_DISABLE_NOTIFICATION_EFFECTS = 2; // 0x2
@@ -37925,10 +37981,12 @@
method public int getDesiredMinimumHeight();
method public int getDesiredMinimumWidth();
method public android.view.SurfaceHolder getSurfaceHolder();
+ method public void invalidateColors();
method public boolean isPreview();
method public boolean isVisible();
method public void onApplyWindowInsets(android.view.WindowInsets);
method public android.os.Bundle onCommand(java.lang.String, int, int, int, android.os.Bundle, boolean);
+ method public android.app.WallpaperColors onComputeWallpaperColors();
method public void onCreate(android.view.SurfaceHolder);
method public void onDesiredSizeChanged(int, int);
method public void onDestroy();
@@ -39603,6 +39661,8 @@
field public static final java.lang.String ACTION_CONFIGURE_PHONE_ACCOUNT = "android.telecom.action.CONFIGURE_PHONE_ACCOUNT";
field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
field public static final deprecated java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
+ field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
+ field public static final java.lang.String ACTION_PHONE_ACCOUNT_UNREGISTERED = "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
field public static final java.lang.String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
@@ -41048,7 +41108,6 @@
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException;
method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException;
method public android.content.ComponentName startService(android.content.Intent);
- method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification);
method public boolean stopService(android.content.Intent);
method public void unbindService(android.content.ServiceConnection);
method public void unregisterReceiver(android.content.BroadcastReceiver);
@@ -45731,7 +45790,6 @@
method public android.view.animation.Animation getAnimation();
method public android.os.IBinder getApplicationWindowToken();
method public java.lang.String[] getAutofillHints();
- method public int getAutofillMode();
method public int getAutofillType();
method public android.view.autofill.AutofillValue getAutofillValue();
method public android.graphics.drawable.Drawable getBackground();
@@ -45820,7 +45878,6 @@
method public float getPivotX();
method public float getPivotY();
method public android.view.PointerIcon getPointerIcon();
- method public int getResolvedAutofillMode();
method public android.content.res.Resources getResources();
method public final boolean getRevealOnFocusHint();
method public final int getRight();
@@ -46056,7 +46113,6 @@
method public void setAlpha(float);
method public void setAnimation(android.view.animation.Animation);
method public void setAutofillHints(java.lang.String...);
- method public void setAutofillMode(int);
method public void setAutofilled(boolean);
method public void setBackground(android.graphics.drawable.Drawable);
method public void setBackgroundColor(int);
@@ -46213,9 +46269,6 @@
field public static final java.lang.String AUTOFILL_HINT_POSTAL_ADDRESS = "postalAddress";
field public static final java.lang.String AUTOFILL_HINT_POSTAL_CODE = "postalCode";
field public static final java.lang.String AUTOFILL_HINT_USERNAME = "username";
- field public static final int AUTOFILL_MODE_AUTO = 1; // 0x1
- field public static final int AUTOFILL_MODE_INHERIT = 0; // 0x0
- field public static final int AUTOFILL_MODE_MANUAL = 2; // 0x2
field public static final int AUTOFILL_TYPE_DATE = 4; // 0x4
field public static final int AUTOFILL_TYPE_LIST = 3; // 0x3
field public static final int AUTOFILL_TYPE_NONE = 0; // 0x0
@@ -46263,7 +46316,9 @@
field public static final int IMPORTANT_FOR_ACCESSIBILITY_YES = 1; // 0x1
field public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0; // 0x0
field public static final int IMPORTANT_FOR_AUTOFILL_NO = 2; // 0x2
+ field public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 8; // 0x8
field public static final int IMPORTANT_FOR_AUTOFILL_YES = 1; // 0x1
+ field public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 4; // 0x4
field public static final int INVISIBLE = 4; // 0x4
field public static final int KEEP_SCREEN_ON = 67108864; // 0x4000000
field public static final int LAYER_TYPE_HARDWARE = 2; // 0x2
@@ -46787,7 +46842,6 @@
method public abstract int getLayoutDirection();
method public abstract android.view.ViewParent getParent();
method public abstract android.view.ViewParent getParentForAccessibility();
- method public default int getResolvedAutofillMode();
method public abstract int getTextAlignment();
method public abstract int getTextDirection();
method public abstract deprecated void invalidateChild(android.view.View, android.graphics.Rect);
@@ -48178,7 +48232,7 @@
field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
field public static final java.lang.String EXTRA_DATA_EXTRAS = "android.view.autofill.extra.DATA_EXTRAS";
- field public static final int FLAG_MANUAL_REQUEST = 1; // 0x1
+ field public static final deprecated int FLAG_MANUAL_REQUEST = 1; // 0x1
}
public static abstract class AutofillManager.AutofillCallback {
@@ -50053,6 +50107,7 @@
method public java.lang.String getFormat();
method public android.widget.Chronometer.OnChronometerTickListener getOnChronometerTickListener();
method public boolean isCountDown();
+ method public boolean isTheFinalCountDown();
method public void setBase(long);
method public void setCountDown(boolean);
method public void setFormat(java.lang.String);
@@ -55736,6 +55791,7 @@
method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>);
method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...);
method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType);
+ method public static java.lang.invoke.MethodHandle explicitCastArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType);
method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...);
method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle);
diff --git a/api/test-removed.txt b/api/test-removed.txt
index 82705fd..b6c2a98 100644
--- a/api/test-removed.txt
+++ b/api/test-removed.txt
@@ -5,10 +5,6 @@
method public deprecated void setLatestEventInfo(android.content.Context, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent);
}
- public static class Notification.Builder {
- method public deprecated android.app.Notification.Builder chooseBadgeIcon(int);
- }
-
public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable {
method public deprecated void showAsNotification(android.content.Context);
}
@@ -26,6 +22,20 @@
}
+package android.app.usage {
+
+ public class StorageStatsManager {
+ method public deprecated long getFreeBytes(java.lang.String) throws java.io.IOException;
+ method public deprecated long getTotalBytes(java.lang.String) throws java.io.IOException;
+ method public deprecated boolean isQuotaSupported(java.lang.String);
+ method public deprecated android.app.usage.ExternalStorageStats queryExternalStatsForUser(java.lang.String, android.os.UserHandle) throws java.io.IOException;
+ method public deprecated android.app.usage.StorageStats queryStatsForPackage(java.lang.String, java.lang.String, android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException;
+ method public deprecated android.app.usage.StorageStats queryStatsForUid(java.lang.String, int) throws java.io.IOException;
+ method public deprecated android.app.usage.StorageStats queryStatsForUser(java.lang.String, android.os.UserHandle) throws java.io.IOException;
+ }
+
+}
+
package android.content {
public abstract class Context {
@@ -41,6 +51,10 @@
package android.content.pm {
+ public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ field public deprecated java.lang.String volumeUuid;
+ }
+
public class ComponentInfo extends android.content.pm.PackageItemInfo {
field public deprecated boolean encryptionAware;
}
@@ -49,6 +63,12 @@
field public static final int REQUESTED_PERMISSION_REQUIRED = 1; // 0x1
}
+ public final class SharedLibraryInfo implements android.os.Parcelable {
+ method public boolean isBuiltin();
+ method public boolean isDynamic();
+ method public boolean isStatic();
+ }
+
}
package android.database {
@@ -112,6 +132,16 @@
}
+package android.location {
+
+ public class Location implements android.os.Parcelable {
+ method public deprecated void removeBearingAccuracy();
+ method public deprecated void removeSpeedAccuracy();
+ method public deprecated void removeVerticalAccuracy();
+ }
+
+}
+
package android.media {
public final class AudioFormat implements android.os.Parcelable {
@@ -184,10 +214,14 @@
package android.os.storage {
public class StorageManager {
- method public deprecated long getCacheQuotaBytes();
- method public deprecated long getCacheSizeBytes();
- method public deprecated long getExternalCacheQuotaBytes();
- method public deprecated long getExternalCacheSizeBytes();
+ method public deprecated void allocateBytes(java.io.File, long, int) throws java.io.IOException;
+ method public deprecated long getAllocatableBytes(java.io.File, int) throws java.io.IOException;
+ method public deprecated long getCacheQuotaBytes(java.io.File) throws java.io.IOException;
+ method public deprecated long getCacheQuotaBytes() throws java.io.IOException;
+ method public deprecated long getCacheSizeBytes(java.io.File) throws java.io.IOException;
+ method public deprecated long getCacheSizeBytes() throws java.io.IOException;
+ method public deprecated long getExternalCacheQuotaBytes() throws java.io.IOException;
+ method public deprecated long getExternalCacheSizeBytes() throws java.io.IOException;
method public android.os.storage.StorageVolume getPrimaryVolume();
method public android.os.storage.StorageVolume[] getVolumeList();
method public deprecated boolean isCacheBehaviorAtomic(java.io.File) throws java.io.IOException;
diff --git a/cmds/idmap/scan.cpp b/cmds/idmap/scan.cpp
index 67874a8..d69dd79 100644
--- a/cmds/idmap/scan.cpp
+++ b/cmds/idmap/scan.cpp
@@ -10,6 +10,7 @@
#include <androidfw/StreamingZipInflater.h>
#include <androidfw/ZipFileRO.h>
#include <cutils/jstring.h>
+#include <cutils/properties.h>
#include <private/android_filesystem_config.h> // for AID_SYSTEM
#include <utils/SortedVector.h>
#include <utils/String16.h>
@@ -82,12 +83,26 @@
return String8(tmp);
}
+ bool check_property(String16 property, String16 value) {
+ const char *prop;
+ const char *val;
+
+ prop = strndup16to8(property.string(), property.size());
+ char propBuf[PROPERTY_VALUE_MAX];
+ property_get(prop, propBuf, NULL);
+ val = strndup16to8(value.string(), value.size());
+
+ return (strcmp(propBuf, val) == 0);
+ }
+
int parse_overlay_tag(const ResXMLTree& parser, const char *target_package_name,
bool* is_static_overlay)
{
const size_t N = parser.getAttributeCount();
String16 target;
int priority = -1;
+ String16 propName = String16();
+ String16 propValue = String16();
for (size_t i = 0; i < N; ++i) {
size_t len;
String16 key(parser.getAttributeName(i, &len));
@@ -109,36 +124,34 @@
if (parser.getAttributeValue(i, &v) == sizeof(Res_value)) {
*is_static_overlay = (v.data != 0);
}
+ } else if (key == String16("requiredSystemPropertyName")) {
+ const char16_t *p = parser.getAttributeStringValue(i, &len);
+ if (p != NULL) {
+ propName = String16(p, len);
+ }
+ } else if (key == String16("requiredSystemPropertyValue")) {
+ const char16_t *p = parser.getAttributeStringValue(i, &len);
+ if (p != NULL) {
+ propValue = String16(p, len);
+ }
}
}
+
+ // Note that conditional property enablement/exclusion only applies if
+ // the attribute is present. In its absence, all overlays are presumed enabled.
+ if (propName.size() > 0 && propValue.size() > 0) {
+ // if property set & equal to value, then include overlay - otherwise skip
+ if (!check_property(propName, propValue)) {
+ return NO_OVERLAY_TAG;
+ }
+ }
+
if (target == String16(target_package_name)) {
return priority;
}
return NO_OVERLAY_TAG;
}
- String16 parse_package_name(const ResXMLTree& parser)
- {
- const size_t N = parser.getAttributeCount();
- String16 package_name;
- for (size_t i = 0; i < N; ++i) {
- size_t len;
- String16 key(parser.getAttributeName(i, &len));
- if (key == String16("package")) {
- const char16_t *p = parser.getAttributeStringValue(i, &len);
- if (p != NULL) {
- package_name = String16(p, len);
- }
- }
- }
- return package_name;
- }
-
- bool isValidStaticOverlayPackage(const String16& package_name) {
- // TODO(b/35742444): Need to support selection method based on a package name.
- return package_name.size() > 0;
- }
-
int parse_manifest(const void *data, size_t size, const char *target_package_name)
{
ResXMLTree parser;
@@ -149,7 +162,6 @@
}
ResXMLParser::event_code_t type;
- String16 package_name;
bool is_static_overlay = false;
int priority = NO_OVERLAY_TAG;
do {
@@ -157,16 +169,14 @@
if (type == ResXMLParser::START_TAG) {
size_t len;
String16 tag(parser.getElementName(&len));
- if (tag == String16("manifest")) {
- package_name = parse_package_name(parser);
- } else if (tag == String16("overlay")) {
+ if (tag == String16("overlay")) {
priority = parse_overlay_tag(parser, target_package_name, &is_static_overlay);
break;
}
}
} while (type != ResXMLParser::BAD_DOCUMENT && type != ResXMLParser::END_DOCUMENT);
- if (is_static_overlay && isValidStaticOverlayPackage(package_name)) {
+ if (is_static_overlay) {
return priority;
}
return NO_OVERLAY_TAG;
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index 4be4654..9df229c 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -228,16 +228,6 @@
System.out.println("onVolumeInfoChanged " + info);
}
- @Override
- public void onRepeatModeChanged(int repeatMode) throws RemoteException {
- System.out.println("onRepeatModeChanged " + repeatMode);
- }
-
- @Override
- public void onShuffleModeChanged(boolean enabled) throws RemoteException {
- System.out.println("onShuffleModeChanged " + enabled);
- }
-
void printUsageMessage() {
try {
System.out.println("V2Monitoring session " + mController.getTag()
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 950991b..c4b7ed7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,20 +16,16 @@
package android.app;
-import android.metrics.LogMaker;
import android.graphics.Rect;
+import android.os.SystemClock;
import android.view.ViewRootImpl.ActivityConfigCallback;
-import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillPopupWindow;
-import android.view.autofill.AutofillValue;
import android.view.autofill.IAutofillWindowPresenter;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar;
-import com.android.internal.logging.MetricsLogger;
-import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.policy.PhoneWindow;
import android.annotation.CallSuper;
@@ -1233,6 +1229,13 @@
mFragments.doLoaderStart();
getApplication().dispatchActivityStarted(this);
+
+ if (mAutoFillResetNeeded) {
+ AutofillManager afm = getAutofillManager();
+ if (afm != null) {
+ afm.onVisibleForAutofill();
+ }
+ }
}
/**
@@ -2067,6 +2070,7 @@
if (args == null) {
throw new IllegalArgumentException("Expected non-null picture-in-picture args");
}
+ updatePictureInPictureArgsForContentInsets(args);
return ActivityManagerNative.getDefault().enterPictureInPictureMode(mToken, args);
} catch (RemoteException e) {
return false;
@@ -2084,11 +2088,27 @@
if (args == null) {
throw new IllegalArgumentException("Expected non-null picture-in-picture args");
}
+ updatePictureInPictureArgsForContentInsets(args);
ActivityManagerNative.getDefault().setPictureInPictureArgs(mToken, args);
} catch (RemoteException e) {
}
}
+ /**
+ * Updates the provided {@param args} with the last known content insets for this activity, to
+ * be used with the source hint rect for the transition into PiP.
+ */
+ private void updatePictureInPictureArgsForContentInsets(PictureInPictureArgs args) {
+ if (args != null && args.hasSourceBoundsHint() && getWindow() != null &&
+ getWindow().peekDecorView() != null &&
+ getWindow().peekDecorView().getViewRootImpl() != null) {
+ args.setSourceRectHintInsets(
+ getWindow().peekDecorView().getViewRootImpl().getLastContentInsets());
+ } else {
+ args.setSourceRectHintInsets(null);
+ }
+ }
+
void dispatchMovedToDisplay(int displayId, Configuration config) {
updateDisplay(displayId);
onMovedToDisplay(displayId, config);
@@ -7406,6 +7426,54 @@
return true;
}
+ /** @hide */
+ @Override
+ public boolean getViewVisibility(int viewId) {
+ Window window = getWindow();
+ if (window == null) {
+ Log.i(TAG, "no window");
+ return false;
+ }
+
+ View decorView = window.peekDecorView();
+ if (decorView == null) {
+ Log.i(TAG, "no decorView");
+ return false;
+ }
+
+ View view = decorView.findViewByAccessibilityIdTraversal(viewId);
+ if (view == null) {
+ Log.i(TAG, "cannot find view");
+ return false;
+ }
+
+ // Check if the view is visible by checking all parents
+ while (view != null) {
+ if (view == decorView) {
+ break;
+ }
+
+ if (view.getVisibility() != View.VISIBLE) {
+ Log.i(TAG, view + " is not visible");
+ return false;
+ }
+
+ if (view.getParent() instanceof View) {
+ view = (View) view.getParent();
+ } else {
+ break;
+ }
+ }
+
+ return true;
+ }
+
+ /** @hide */
+ @Override
+ public boolean isVisibleForAutofill() {
+ return !mStopped;
+ }
+
/**
* If set to true, this indicates to the system that it should never take a
* screenshot of the activity to be used as a representation while it is not in a started state.
@@ -7433,6 +7501,25 @@
}
}
+ /**
+ * Return the timestamp at which this activity start was last initiated by the system in the
+ * {@link SystemClock#uptimeMillis()} time base.
+ *
+ * This can be used to understand how much time is taken for an activity to be started and
+ * displayed to the user.
+ *
+ * @return timestamp at which this activity start was initiated by the system
+ * or {@code 0} if for any reason the timestamp could not be retrieved.
+ */
+ public long getStartInitiatedTime() {
+ try {
+ return ActivityManager.getService().getActivityStartInitiatedTime(mToken);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to call getActivityStartTime", e);
+ return 0;
+ }
+ }
+
class HostCallbacks extends FragmentHostCallback<Activity> {
public HostCallbacks() {
super(Activity.this /*activity*/);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 8859831..a4dcf63 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -30,6 +30,8 @@
import android.graphics.Matrix;
import android.graphics.Point;
import android.os.BatteryStats;
+import android.os.Build;
+import android.os.Build.VERSION_CODES;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -138,14 +140,17 @@
static final class UidObserver extends IUidObserver.Stub {
final OnUidImportanceListener mListener;
+ final Context mContext;
- UidObserver(OnUidImportanceListener listener) {
+ UidObserver(OnUidImportanceListener listener, Context clientContext) {
mListener = listener;
+ mContext = clientContext;
}
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq) {
- mListener.onUidImportance(uid, RunningAppProcessInfo.procStateToImportance(procState));
+ mListener.onUidImportance(uid, RunningAppProcessInfo.procStateToImportanceForClient(
+ procState, mContext));
}
@Override
@@ -2559,6 +2564,10 @@
* <p><b>Note: this method is only intended for debugging or implementing
* service management type user interfaces.</b></p>
*
+ * @deprecated As of {@link android.os.Build.VERSION_CODES#O}, this method
+ * is no longer available to third party applications. For backwards compatibility,
+ * it will still return the caller's own services.
+ *
* @param maxNum The maximum number of entries to return in the list. The
* actual number returned may be smaller, depending on how many services
* are running.
@@ -2566,6 +2575,7 @@
* @return Returns a list of RunningServiceInfo records describing each of
* the running tasks.
*/
+ @Deprecated
public List<RunningServiceInfo> getRunningServices(int maxNum)
throws SecurityException {
try {
@@ -3102,10 +3112,32 @@
public static final int IMPORTANCE_VISIBLE = 200;
/**
- * Constant for {@link #importance}: This process is not something the user
- * is directly aware of, but is otherwise perceptable to them to some degree.
+ * Constant for {@link #importance}: {@link #IMPORTANCE_PERCEPTIBLE} had this wrong value
+ * before {@link Build.VERSION_CODES#O}. Since the {@link Build.VERSION_CODES#O} SDK,
+ * the value of {@link #IMPORTANCE_PERCEPTIBLE} has been fixed.
+ *
+ * @deprecated Use {@link #IMPORTANCE_PERCEPTIBLE} instead.
*/
- public static final int IMPORTANCE_PERCEPTIBLE = 130;
+ @Deprecated
+ public static final int IMPORTANCE_PERCEPTIBLE_DEPRECATED = 130;
+
+ /**
+ * Constant for {@link #importance}: This process is not something the user
+ * is directly aware of, but is otherwise perceptible to them to some degree.
+ */
+ public static final int IMPORTANCE_PERCEPTIBLE = 230;
+
+ /**
+ * Constant for {@link #importance}: {@link #IMPORTANCE_CANT_SAVE_STATE} had
+ * this wrong value
+ * before {@link Build.VERSION_CODES#O}. Since the {@link Build.VERSION_CODES#O} SDK,
+ * the value of {@link #IMPORTANCE_CANT_SAVE_STATE} has been fixed.
+ *
+ * @deprecated Use {@link #IMPORTANCE_CANT_SAVE_STATE} instead.
+ * @hide
+ */
+ @Deprecated
+ public static final int IMPORTANCE_CANT_SAVE_STATE_DEPRECATED = 170;
/**
* Constant for {@link #importance}: This process is running an
@@ -3113,7 +3145,7 @@
* while in the background.
* @hide
*/
- public static final int IMPORTANCE_CANT_SAVE_STATE = 170;
+ public static final int IMPORTANCE_CANT_SAVE_STATE= 270;
/**
* Constant for {@link #importance}: This process is contains services
@@ -3149,7 +3181,11 @@
*/
public static final int IMPORTANCE_GONE = 1000;
- /** @hide */
+ /**
+ * Convert a proc state to the correspondent IMPORTANCE_* constant. If the return value
+ * will be passed to a client, use {@link #procStateToImportanceForClient}.
+ * @hide
+ */
public static int procStateToImportance(int procState) {
if (procState == PROCESS_STATE_NONEXISTENT) {
return IMPORTANCE_GONE;
@@ -3172,6 +3208,28 @@
}
}
+ /**
+ * Convert a proc state to the correspondent IMPORTANCE_* constant for a client represented
+ * by a given {@link Context}, with converting {@link #IMPORTANCE_PERCEPTIBLE}
+ * and {@link #IMPORTANCE_CANT_SAVE_STATE} to the corresponding "wrong" value if the
+ * client's target SDK < {@link VERSION_CODES#O}.
+ * @hide
+ */
+ public static int procStateToImportanceForClient(int procState, Context clientContext) {
+ final int importance = procStateToImportance(procState);
+
+ // For pre O apps, convert to the old, wrong values.
+ if (clientContext.getApplicationInfo().targetSdkVersion < VERSION_CODES.O) {
+ switch (importance) {
+ case IMPORTANCE_PERCEPTIBLE:
+ return IMPORTANCE_PERCEPTIBLE_DEPRECATED;
+ case IMPORTANCE_CANT_SAVE_STATE:
+ return IMPORTANCE_CANT_SAVE_STATE_DEPRECATED;
+ }
+ }
+ return importance;
+ }
+
/** @hide */
public static int importanceToProcState(int importance) {
if (importance == IMPORTANCE_GONE) {
@@ -3401,7 +3459,7 @@
try {
int procState = getService().getPackageProcessState(packageName,
mContext.getOpPackageName());
- return RunningAppProcessInfo.procStateToImportance(procState);
+ return RunningAppProcessInfo.procStateToImportanceForClient(procState, mContext);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3421,7 +3479,7 @@
try {
int procState = getService().getUidProcessState(uid,
mContext.getOpPackageName());
- return RunningAppProcessInfo.procStateToImportance(procState);
+ return RunningAppProcessInfo.procStateToImportanceForClient(procState, mContext);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3471,7 +3529,7 @@
throw new IllegalArgumentException("Listener already registered: " + listener);
}
// TODO: implement the cut point in the system process to avoid IPCs.
- UidObserver observer = new UidObserver(listener);
+ UidObserver observer = new UidObserver(listener, mContext);
try {
getService().registerUidObserver(observer,
UID_OBSERVER_PROCSTATE | UID_OBSERVER_GONE,
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index a6838f8b..027ddf5 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -79,6 +79,8 @@
import android.os.storage.VolumeInfo;
import android.provider.Settings;
import android.util.ArrayMap;
+import android.util.IconDrawableFactory;
+import android.util.LauncherIcons;
import android.util.Log;
import android.view.Display;
@@ -1245,16 +1247,9 @@
if (!isManagedProfile(user.getIdentifier())) {
return icon;
}
- Drawable badgeShadow = getDrawable("system",
- com.android.internal.R.drawable.ic_corp_icon_badge_shadow, null);
- Drawable badgeColor = getDrawable("system",
- com.android.internal.R.drawable.ic_corp_icon_badge_color, null);
- badgeColor.setTint(getUserBadgeColor(user));
- Drawable badgeForeground = getDrawable("system",
- com.android.internal.R.drawable.ic_corp_icon_badge_case, null);
-
- Drawable badge = new LayerDrawable(
- new Drawable[] {badgeShadow, badgeColor, badgeForeground });
+ Drawable badge = new LauncherIcons(mContext).getBadgeDrawable(
+ com.android.internal.R.drawable.ic_corp_icon_badge_case,
+ getUserBadgeColor(user));
return getBadgedDrawable(icon, badge, null, true);
}
@@ -1268,14 +1263,6 @@
return getBadgedDrawable(drawable, badgeDrawable, badgeLocation, true);
}
- // Should have enough colors to cope with UserManagerService.getMaxManagedProfiles()
- @VisibleForTesting
- public static final int[] CORP_BADGE_COLORS = new int[] {
- com.android.internal.R.color.profile_badge_1,
- com.android.internal.R.color.profile_badge_2,
- com.android.internal.R.color.profile_badge_3
- };
-
@VisibleForTesting
public static final int[] CORP_BADGE_LABEL_RES_ID = new int[] {
com.android.internal.R.string.managed_profile_label_badge,
@@ -1284,12 +1271,7 @@
};
private int getUserBadgeColor(UserHandle user) {
- int badge = getUserManager().getManagedProfileBadge(user.getIdentifier());
- if (badge < 0) {
- badge = 0;
- }
- int resourceId = CORP_BADGE_COLORS[badge % CORP_BADGE_COLORS.length];
- return Resources.getSystem().getColor(resourceId, null);
+ return IconDrawableFactory.getUserBadgeColor(getUserManager(), user.getIdentifier());
}
@Override
@@ -2639,4 +2621,13 @@
throw e.rethrowAsRuntimeException();
}
}
+
+ @Override
+ public ComponentName getInstantAppInstallerComponent() {
+ try {
+ return mPM.getInstantAppInstallerComponent();
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 6cc8a14..80de64b 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1447,21 +1447,13 @@
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
- return startServiceCommon(service, -1, null, false, mUser);
+ return startServiceCommon(service, false, mUser);
}
@Override
public ComponentName startForegroundService(Intent service) {
warnIfCallingFromSystemProcess();
- return startServiceCommon(service, -1, null, true, mUser);
- }
-
- // STOPSHIP: remove when NotificationManager.startServiceInForeground() is retired
- @Override
- public ComponentName startServiceInForeground(Intent service,
- int id, Notification notification) {
- warnIfCallingFromSystemProcess();
- return startServiceCommon(service, id, notification, false, mUser);
+ return startServiceCommon(service, true, mUser);
}
@Override
@@ -1472,29 +1464,22 @@
@Override
public ComponentName startServiceAsUser(Intent service, UserHandle user) {
- return startServiceCommon(service, -1, null, false, user);
+ return startServiceCommon(service, false, user);
}
@Override
public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
- return startServiceCommon(service, -1, null, true, user);
+ return startServiceCommon(service, true, user);
}
- // STOPSHIP: remove when NotificationManager.startServiceInForeground() is retired
- @Override
- public ComponentName startServiceInForegroundAsUser(Intent service,
- int id, Notification notification, UserHandle user) {
- return startServiceCommon(service, id, notification, false, user);
- }
-
- private ComponentName startServiceCommon(Intent service, int id, Notification notification,
- boolean requireForeground, UserHandle user) {
+ private ComponentName startServiceCommon(Intent service, boolean requireForeground,
+ UserHandle user) {
try {
validateServiceIntent(service);
service.prepareToLeaveProcess(this);
ComponentName cn = ActivityManager.getService().startService(
mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
- getContentResolver()), id, notification, requireForeground,
+ getContentResolver()), requireForeground,
getOpPackageName(), user.getIdentifier());
if (cn != null) {
if (cn.getPackageName().equals("!")) {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index c7bcc54..c5b93cd 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -509,6 +509,10 @@
// True if mHidden has been changed and the animation should be scheduled.
boolean mHiddenChanged;
+ // The cached value from onGetLayoutInflater(Bundle) that will be returned from
+ // getLayoutInflater()
+ LayoutInflater mLayoutInflater;
+
/**
* State information that has been retrieved from a fragment instance
* through {@link FragmentManager#saveFragmentInstanceState(Fragment)
@@ -1389,6 +1393,38 @@
}
/**
+ * Returns the cached LayoutInflater used to inflate Views of this Fragment. If
+ * {@link #onGetLayoutInflater(Bundle)} has not been called {@link #onGetLayoutInflater(Bundle)}
+ * will be called with a {@code null} argument and that value will be cached.
+ * <p>
+ * The cached LayoutInflater will be replaced immediately prior to
+ * {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} and cleared immediately after
+ * {@link #onDetach()}.
+ *
+ * @return The LayoutInflater used to inflate Views of this Fragment.
+ */
+ public final LayoutInflater getLayoutInflater() {
+ if (mLayoutInflater == null) {
+ return performGetLayoutInflater(null);
+ }
+ return mLayoutInflater;
+ }
+
+ /**
+ * Calls {@link #onGetLayoutInflater(Bundle)} and caches the result for use by
+ * {@link #getLayoutInflater()}.
+ *
+ * @param savedInstanceState If the fragment is being re-created from
+ * a previous saved state, this is the state.
+ * @return The LayoutInflater used to inflate Views of this Fragment.
+ */
+ LayoutInflater performGetLayoutInflater(Bundle savedInstanceState) {
+ LayoutInflater layoutInflater = onGetLayoutInflater(savedInstanceState);
+ mLayoutInflater = layoutInflater;
+ return mLayoutInflater;
+ }
+
+ /**
* @deprecated Use {@link #onInflate(Context, AttributeSet, Bundle)} instead.
*/
@Deprecated
@@ -2835,6 +2871,7 @@
void performDetach() {
mCalled = false;
onDetach();
+ mLayoutInflater = null;
if (!mCalled) {
throw new SuperNotCalledException("Fragment " + this
+ " did not call through to super.onDetach()");
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 91578a2..c1161a2 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -1259,7 +1259,7 @@
}
}
f.mContainer = container;
- f.mView = f.performCreateView(f.onGetLayoutInflater(
+ f.mView = f.performCreateView(f.performGetLayoutInflater(
f.mSavedFragmentState), container, f.mSavedFragmentState);
if (f.mView != null) {
f.mView.setSaveFromParentEnabled(false);
@@ -1431,7 +1431,7 @@
void ensureInflatedFragmentView(Fragment f) {
if (f.mFromLayout && !f.mPerformedCreateView) {
- f.mView = f.performCreateView(f.onGetLayoutInflater(
+ f.mView = f.performCreateView(f.performGetLayoutInflater(
f.mSavedFragmentState), null, f.mSavedFragmentState);
if (f.mView != null) {
f.mView.setSaveFromParentEnabled(false);
@@ -1462,18 +1462,22 @@
if (fragment.isHideReplaced()) {
fragment.setHideReplaced(false);
} else {
+ final ViewGroup container = fragment.mContainer;
+ final View animatingView = fragment.mView;
+ container.startViewTransition(animatingView);
// Delay the actual hide operation until the animation finishes, otherwise
// the fragment will just immediately disappear
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
+ container.endViewTransition(animatingView);
animation.removeListener(this);
- if (fragment.mView != null) {
- fragment.mView.setVisibility(View.GONE);
- }
+ animatingView.setVisibility(View.GONE);
}
});
}
+ } else {
+ fragment.mView.setVisibility(View.VISIBLE);
}
setHWLayerAnimListenerIfAlpha(fragment.mView, anim);
anim.start();
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index fc827a9..d270244 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -129,8 +129,7 @@
void finishSubActivity(in IBinder token, in String resultWho, int requestCode);
PendingIntent getRunningServiceControlPanel(in ComponentName service);
ComponentName startService(in IApplicationThread caller, in Intent service,
- in String resolvedType, int id, in Notification notification,
- boolean requireForeground, in String callingPackage, int userId);
+ in String resolvedType, boolean requireForeground, in String callingPackage, int userId);
int stopService(in IApplicationThread caller, in Intent service,
in String resolvedType, int userId);
int bindService(in IApplicationThread caller, in IBinder token, in Intent service,
@@ -632,6 +631,8 @@
*/
void backgroundWhitelistUid(int uid);
+ long getActivityStartInitiatedTime(IBinder token);
+
// WARNING: when these transactions are updated, check if they are any callers on the native
// side. If so, make sure they are using the correct transaction ids and arguments.
// If a transaction which will also be used on the native side is being inserted, add it
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index f4e8f3f..cd9c095 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -27,6 +27,7 @@
import android.content.pm.ParceledListSlice;
import android.net.Uri;
import android.os.Bundle;
+import android.os.UserHandle;
import android.service.notification.Adjustment;
import android.service.notification.Condition;
import android.service.notification.IConditionListener;
@@ -101,9 +102,9 @@
void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
void setInterruptionFilter(String pkg, int interruptionFilter);
- void updateNotificationChannelFromPrivilegedListener(in INotificationListener token, String pkg, in NotificationChannel channel);
- ParceledListSlice getNotificationChannelsFromPrivilegedListener(in INotificationListener token, String pkg);
- ParceledListSlice getNotificationChannelGroupsFromPrivilegedListener(in INotificationListener token, String pkg);
+ void updateNotificationChannelFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user, in NotificationChannel channel);
+ ParceledListSlice getNotificationChannelsFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user);
+ ParceledListSlice getNotificationChannelGroupsFromPrivilegedListener(in INotificationListener token, String pkg, in UserHandle user);
void applyEnqueuedAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment);
void applyAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 7f26f4f..cab2114 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2339,7 +2339,9 @@
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("Notification(pri=");
+ sb.append("Notification(channel=");
+ sb.append(getChannel());
+ sb.append(" pri=");
sb.append(priority);
sb.append(" contentView=");
if (contentView != null) {
@@ -2729,21 +2731,6 @@
}
/**
- * @removed
- * Sets which icon to display as a badge for this notification.
- *
- * Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
- * {@link #BADGE_ICON_LARGE}.
- *
- * Note: This value might be ignored, for launchers that don't support badge icons.
- */
- @Deprecated
- public Builder chooseBadgeIcon(int icon) {
- mN.mBadgeIcon = icon;
- return this;
- }
-
- /**
* Sets which icon to display as a badge for this notification.
*
* Must be one of {@link #BADGE_ICON_NONE}, {@link #BADGE_ICON_SMALL},
@@ -3259,9 +3246,10 @@
* This should only be used for high priority ongoing tasks like navigation, an ongoing
* call, or other similarly high-priority events for the user.
* <p>
- * For most styles, the coloring will only be applied if the notification is ongoing.
+ * For most styles, the coloring will only be applied if the notification is for a
+ * foreground service notification.
* However, for {@link MediaStyle} and {@link DecoratedMediaCustomViewStyle} notifications
- * that have a media session attached there is no requirement for it to be ongoing.
+ * that have a media session attached there is no such requirement.
*
* @see Builder#setOngoing(boolean)
* @see Builder#setColor(int)
@@ -4214,9 +4202,22 @@
* @hide
*/
public RemoteViews makePublicContentView() {
+ return makePublicView(false /* ambient */);
+ }
+
+ /**
+ * Construct a RemoteViews for the display in public contexts like on the lockscreen.
+ *
+ * @hide
+ */
+ public RemoteViews makePublicAmbientNotification() {
+ return makePublicView(true /* ambient */);
+ }
+
+ private RemoteViews makePublicView(boolean ambient) {
if (mN.publicVersion != null) {
final Builder builder = recoverBuilder(mContext, mN.publicVersion);
- return builder.createContentView();
+ return ambient ? builder.makeAmbientNotification() : builder.createContentView();
}
Bundle savedBundle = mN.extras;
Style style = mStyle;
@@ -4233,14 +4234,15 @@
publicExtras.putBoolean(EXTRA_CHRONOMETER_COUNT_DOWN,
savedBundle.getBoolean(EXTRA_CHRONOMETER_COUNT_DOWN));
publicExtras.putCharSequence(EXTRA_TITLE,
- mContext.getString(R.string.notification_hidden_text));
+ mContext.getString(com.android.internal.R.string.notification_hidden_text));
mN.extras = publicExtras;
- final RemoteViews publicView = applyStandardTemplate(getBaseLayoutResource());
+ final RemoteViews view = ambient ? makeAmbientNotification()
+ : applyStandardTemplate(getBaseLayoutResource());
mN.extras = savedBundle;
mN.mLargeIcon = largeIcon;
mN.largeIcon = largeIconLegacy;
mStyle = style;
- return publicView;
+ return view;
}
/**
@@ -4759,12 +4761,10 @@
}
/**
- * @return whether this notification is ongoing and can't be dismissed by the user.
+ * @return whether this notification is a foreground service notification
*/
- private boolean isOngoing() {
- final int ongoingFlags = Notification.FLAG_FOREGROUND_SERVICE
- | Notification.FLAG_ONGOING_EVENT | Notification.FLAG_NO_CLEAR;
- return (flags & ongoingFlags) != 0;
+ private boolean isForegroundService() {
+ return (flags & Notification.FLAG_FOREGROUND_SERVICE) != 0;
}
/**
@@ -4789,8 +4789,7 @@
}
/**
- * @return true if this notification is colorized. This also factors in whether the
- * notification is ongoing.
+ * @return true if this notification is colorized.
*
* @hide
*/
@@ -4806,7 +4805,7 @@
return true;
}
}
- return extras.getBoolean(EXTRA_COLORIZED) && isOngoing();
+ return extras.getBoolean(EXTRA_COLORIZED) && isForegroundService();
}
private boolean hasLargeIcon() {
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 72c5978..242d4a5 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1155,40 +1155,4 @@
}
}
- /**
- * Start a service directly into the "foreground service" state. Unlike
- * {@link android.content.Context#startService(Intent)}, this method
- * can be used from within background operations like broadcast receivers
- * or scheduled jobs.
- *
- * @param service Description of the service to be started. The Intent must be either
- * fully explicit (supplying a component name) or specify a specific package
- * name it is targeted to.
- * @param id The identifier for this notification as per
- * {@link #notify(int, Notification) NotificationManager.notify(int, Notification)};
- * must not be 0.
- * @param notification The Notification to be displayed.
- * @return If the service is being started or is already running, the
- * {@link ComponentName} of the actual service that was started is
- * returned; else if the service does not exist null is returned.
- *
- * @deprecated STOPSHIP transition away from this for O
- */
- @Nullable
- @Deprecated
- public ComponentName startServiceInForeground(Intent service,
- int id, Notification notification) {
- return mContext.startServiceInForeground(service, id, notification);
- }
-
- /**
- * @hide like {@link #startServiceInForeground(Intent, int, Notification)}
- * but for a specific user.
- */
- @Nullable
- public ComponentName startServiceInForegroundAsUser(Intent service,
- int id, Notification notification, UserHandle user) {
- return mContext.startServiceInForegroundAsUser(service, id, notification, user);
- }
-
}
diff --git a/core/java/android/app/PictureInPictureArgs.java b/core/java/android/app/PictureInPictureArgs.java
index 0ce5eeb..2fa6360 100644
--- a/core/java/android/app/PictureInPictureArgs.java
+++ b/core/java/android/app/PictureInPictureArgs.java
@@ -49,6 +49,13 @@
@Nullable
private Rect mSourceRectHint;
+ /**
+ * The content insets that are used with the source hint rect for the transition into PiP where
+ * the insets are removed at the beginning of the transition.
+ */
+ @Nullable
+ private Rect mSourceRectHintInsets;
+
PictureInPictureArgs(Parcel in) {
if (in.readInt() != 0) {
mAspectRatio = in.readFloat();
@@ -60,6 +67,9 @@
if (in.readInt() != 0) {
mSourceRectHint = Rect.CREATOR.createFromParcel(in);
}
+ if (in.readInt() != 0) {
+ mSourceRectHintInsets = Rect.CREATOR.createFromParcel(in);
+ }
}
/**
@@ -94,6 +104,9 @@
if (otherArgs.hasSourceBoundsHint()) {
mSourceRectHint = new Rect(otherArgs.getSourceRectHint());
}
+ if (otherArgs.hasSourceBoundsHintInsets()) {
+ mSourceRectHintInsets = new Rect(otherArgs.getSourceRectHintInsets());
+ }
}
/**
@@ -167,7 +180,19 @@
}
/**
- * @return the launch bounds
+ * Sets the insets to be used with the source rect hint bounds.
+ * @hide
+ */
+ public void setSourceRectHintInsets(Rect insets) {
+ if (insets == null) {
+ mSourceRectHintInsets = null;
+ } else {
+ mSourceRectHintInsets = new Rect(insets);
+ }
+ }
+
+ /**
+ * @return the source rect hint
* @hide
*/
public Rect getSourceRectHint() {
@@ -175,6 +200,14 @@
}
/**
+ * @return the source rect hint insets.
+ * @hide
+ */
+ public Rect getSourceRectHintInsets() {
+ return mSourceRectHintInsets;
+ }
+
+ /**
* @return whether there are launch bounds set
* @hide
*/
@@ -182,12 +215,23 @@
return mSourceRectHint != null && !mSourceRectHint.isEmpty();
}
+ /**
+ * @return whether there are source rect hint insets set
+ * @hide
+ */
+ public boolean hasSourceBoundsHintInsets() {
+ return mSourceRectHintInsets != null;
+ }
+
@Override
public PictureInPictureArgs clone() {
PictureInPictureArgs args = new PictureInPictureArgs(mAspectRatio, mUserActions);
if (mSourceRectHint != null) {
args.setSourceRectHint(mSourceRectHint);
}
+ if (mSourceRectHintInsets != null) {
+ args.setSourceRectHintInsets(mSourceRectHintInsets);
+ }
return args;
}
@@ -216,6 +260,12 @@
} else {
out.writeInt(0);
}
+ if (mSourceRectHintInsets != null) {
+ out.writeInt(1);
+ mSourceRectHintInsets.writeToParcel(out, 0);
+ } else {
+ out.writeInt(0);
+ }
}
public static final Creator<PictureInPictureArgs> CREATOR =
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 3191eec..6f326de 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -112,13 +112,6 @@
private final ArrayMap<Pair<Integer, DisplayAdjustments>, WeakReference<Display>>
mAdjustedDisplays = new ArrayMap<>();
- /**
- * A cache of DisplayId, Resources to Display. These display adjustments associated with these
- * {@link Display}s will change as the resources change.
- */
- private final ArrayMap<Pair<Integer, ResourcesKey>, WeakReference<Display>> mResourceDisplays =
- new ArrayMap<>();
-
public static ResourcesManager getInstance() {
synchronized (ResourcesManager.class) {
if (sResourcesManager == null) {
@@ -251,51 +244,16 @@
*/
public Display getAdjustedDisplay(final int displayId, Resources resources) {
synchronized (this) {
- // Note that the ResourcesKey might be {@code null} in the case that the
- // {@link Resources} is actually from {@link Resources#getSystem}. In this case, it is
- // not managed by {@link ResourcesManager}, but we still want to cache the display
- // object.
- final Pair<Integer, ResourcesKey> key = Pair.create(displayId,
- findKeyForResourceImplLocked(resources.getImpl()));
-
- WeakReference<Display> wd = mResourceDisplays.get(key);
- if (wd != null) {
- final Display display = wd.get();
- if (display != null) {
- return display;
- }
- }
final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
if (dm == null) {
// may be null early in system startup
return null;
}
- final Display display = dm.getCompatibleDisplay(displayId, resources);
- if (display != null) {
- mResourceDisplays.put(key, new WeakReference<>(display));
- }
- return display;
+ return dm.getCompatibleDisplay(displayId, resources);
}
}
private void cleanupResourceImpl(ResourcesKey removedKey) {
- // Remove any resource to display mapping based on this key.
- final Iterator<Map.Entry<Pair<Integer, ResourcesKey>, WeakReference<Display>>> iter =
- mResourceDisplays.entrySet().iterator();
- while (iter.hasNext()) {
- final Map.Entry<Pair<Integer, ResourcesKey>, WeakReference<Display>> entry =
- iter.next();
- final ResourcesKey key = entry.getKey().second;
-
- // Do not touch system resource displays (indicated by a {@code null} key) or
- // non-matching keys.
- if (key == null || !key.equals(removedKey)) {
- continue;
- }
-
- iter.remove();
- }
-
// Remove resource key to resource impl mapping and flush cache
final ResourcesImpl res = mResourceImpls.remove(removedKey).get();
@@ -887,7 +845,6 @@
int changes = mResConfiguration.updateFrom(config);
// Things might have changed in display manager, so clear the cached displays.
mAdjustedDisplays.clear();
- mResourceDisplays.clear();
DisplayMetrics defaultDisplayMetrics = getDisplayMetrics();
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
new file mode 100644
index 0000000..5ed66ca
--- /dev/null
+++ b/core/java/android/app/WallpaperColors.java
@@ -0,0 +1,97 @@
+/*
+ * 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.app;
+
+import android.graphics.Color;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import android.util.Pair;
+
+import java.util.List;
+
+/**
+ * A class containing information about the colors of a wallpaper.
+ */
+public final class WallpaperColors implements Parcelable {
+
+ public WallpaperColors(Parcel parcel) {
+ }
+
+ /**
+ * Wallpaper color details containing a list of colors and their weights,
+ * as if it were an histogram.
+ * This list can be extracted from a bitmap by the Palette API.
+ *
+ * Dark text support will be calculated internally based on the histogram.
+ *
+ * @param colors list of pairs where each pair contains a color
+ * and number of occurrences/influence.
+ */
+ public WallpaperColors(List<Pair<Color, Integer>> colors) {
+ }
+
+ /**
+ * Wallpaper color details containing a list of colors and their weights,
+ * as if it were an histogram.
+ * Explicit dark text support.
+ *
+ * @param colors list of pairs where each pair contains a color
+ * and number of occurrences/influence.
+ * @param supportsDarkText can have dark text on top or not
+ */
+ public WallpaperColors(List<Pair<Color, Integer>> colors, boolean supportsDarkText) {
+ }
+
+ public static final Creator<WallpaperColors> CREATOR = new Creator<WallpaperColors>() {
+ @Override
+ public WallpaperColors createFromParcel(Parcel in) {
+ return new WallpaperColors(in);
+ }
+
+ @Override
+ public WallpaperColors[] newArray(int size) {
+ return new WallpaperColors[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ }
+
+ /**
+ * List of colors with their occurrences. The bigger the int, the more relevant the color.
+ * @return list of colors paired with their weights.
+ */
+ public List<Pair<Color, Integer>> getColors() {
+ return null;
+ }
+
+ /**
+ * Whether or not dark text is legible on top of this wallpaper.
+ *
+ * @return true if dark text is supported
+ */
+ public boolean supportsDarkText() {
+ return false;
+ }
+}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index aa0eaae..0676bca 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -17,6 +17,8 @@
package android.app;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RawRes;
import android.annotation.SystemApi;
import android.content.ComponentName;
@@ -741,6 +743,43 @@
return getWallpaperFile(which, mContext.getUserId());
}
+
+ /**
+ * Registers a listener to get notified when the wallpaper colors change.
+ * Callback might be called from an arbitrary background thread.
+ *
+ * @param listener A listener to register
+ */
+ public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener) {
+ }
+
+ /**
+ * Registers a listener to get notified when the wallpaper colors change
+ * @param listener A listener to register
+ * @param handler Where to call it from. Might be called from a background thread
+ * if null.
+ */
+ public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener,
+ @Nullable Handler handler) {
+ }
+
+ /**
+ * Stop listening to color updates.
+ * @param callback A callback to unsubscribe
+ */
+ public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback) {
+ }
+
+ /**
+ * Get the primary colors of a wallpaper
+ * @param which wallpaper type. Must be either {@link #FLAG_SYSTEM} or
+ * {@link #FLAG_LOCK}
+ * @return a list of colors ordered by priority
+ */
+ public @Nullable WallpaperColors getWallpaperColors(int which) {
+ return null;
+ }
+
/**
* Version of {@link #getWallpaperFile(int)} that can access the wallpaper data
* for a given user. The caller must hold the INTERACT_ACROSS_USERS_FULL
@@ -1732,4 +1771,19 @@
mLatch.countDown();
}
}
+
+ /**
+ * Interface definition for a callback to be invoked when colors change on a wallpaper.
+ */
+ public interface OnColorsChangedListener {
+ /**
+ * Called when colors change.
+ * A {@link android.app.WallpaperColors} object containing a simplified
+ * color histogram will be given.
+ *
+ * @param colors Wallpaper color info
+ * @param which A combination of {@link #FLAG_LOCK} and {@link #FLAG_SYSTEM}
+ */
+ void onColorsChanged(WallpaperColors colors, int which);
+ }
}
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 545aef5..9f911f5 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -964,6 +964,16 @@
*
* @return The hints for this view
*/
+ @Nullable public String[] getAutofillHints() {
+ return mAutofillHints;
+ }
+
+ /**
+ * @hide
+ * @deprecated use getAutofillHints() instead.
+ */
+ // TODO(b/33197203): remove once clients don't use it anymore...
+ @Deprecated
@Nullable public String[] getAutoFillHints() {
return mAutofillHints;
}
diff --git a/core/java/android/app/job/JobParameters.java b/core/java/android/app/job/JobParameters.java
index 016a0fa..673d1b8 100644
--- a/core/java/android/app/job/JobParameters.java
+++ b/core/java/android/app/job/JobParameters.java
@@ -164,6 +164,20 @@
* you should not call {@link JobService#jobFinished(JobParameters, boolean)} yourself
* (otherwise you risk losing an upcoming JobWorkItem that is being enqueued at the same time).
*
+ * <p>Once you are done with the {@link JobWorkItem} returned by this method, you must call
+ * {@link #completeWork(JobWorkItem)} with it to inform the system that you are done
+ * executing the work. The job will not be finished until all dequeued work has been
+ * completed. You do not, however, have to complete each returned work item before deqeueing
+ * the next one -- you can use {@link #dequeueWork()} multiple times before completing
+ * previous work if you want to process work in parallel, and you can complete the work
+ * in whatever order you want.</p>
+ *
+ * <p>If the job runs to the end of its available time period before all work has been
+ * completed, it will stop as normal. You should return true from
+ * {@link JobService#onStopJob(JobParameters)} in order to have the job rescheduled, and by
+ * doing so any pending as well as remaining uncompleted work will be re-queued
+ * for the next time the job runs.</p>
+ *
* @return Returns a new {@link JobWorkItem} if there is one pending, otherwise null.
* If null is returned, the system will also stop the job if all work has also been completed.
* (This means that for correct operation, you must always call dequeueWork() after you have
diff --git a/core/java/android/app/job/JobService.java b/core/java/android/app/job/JobService.java
index f4019ce..9096b47 100644
--- a/core/java/android/app/job/JobService.java
+++ b/core/java/android/app/job/JobService.java
@@ -60,161 +60,24 @@
public static final String PERMISSION_BIND =
"android.permission.BIND_JOB_SERVICE";
- /**
- * Identifier for a message that will result in a call to
- * {@link #onStartJob(android.app.job.JobParameters)}.
- */
- private static final int MSG_EXECUTE_JOB = 0;
- /**
- * Message that will result in a call to {@link #onStopJob(android.app.job.JobParameters)}.
- */
- private static final int MSG_STOP_JOB = 1;
- /**
- * Message that the client has completed execution of this job.
- */
- private static final int MSG_JOB_FINISHED = 2;
-
- /** Lock object for {@link #mHandler}. */
- private final Object mHandlerLock = new Object();
-
- /**
- * Handler we post jobs to. Responsible for calling into the client logic, and handling the
- * callback to the system.
- */
- @GuardedBy("mHandlerLock")
- JobHandler mHandler;
-
- static final class JobInterface extends IJobService.Stub {
- final WeakReference<JobService> mService;
-
- JobInterface(JobService service) {
- mService = new WeakReference<>(service);
- }
-
- @Override
- public void startJob(JobParameters jobParams) throws RemoteException {
- JobService service = mService.get();
- if (service != null) {
- service.ensureHandler();
- Message m = Message.obtain(service.mHandler, MSG_EXECUTE_JOB, jobParams);
- m.sendToTarget();
- }
- }
-
- @Override
- public void stopJob(JobParameters jobParams) throws RemoteException {
- JobService service = mService.get();
- if (service != null) {
- service.ensureHandler();
- Message m = Message.obtain(service.mHandler, MSG_STOP_JOB, jobParams);
- m.sendToTarget();
- }
-
- }
- }
-
- IJobService mBinder;
-
- /** @hide */
- void ensureHandler() {
- synchronized (mHandlerLock) {
- if (mHandler == null) {
- mHandler = new JobHandler(getMainLooper());
- }
- }
- }
-
- /**
- * Runs on application's main thread - callbacks are meant to offboard work to some other
- * (app-specified) mechanism.
- * @hide
- */
- class JobHandler extends Handler {
- JobHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- final JobParameters params = (JobParameters) msg.obj;
- switch (msg.what) {
- case MSG_EXECUTE_JOB:
- try {
- boolean workOngoing = JobService.this.onStartJob(params);
- ackStartMessage(params, workOngoing);
- } catch (Exception e) {
- Log.e(TAG, "Error while executing job: " + params.getJobId());
- throw new RuntimeException(e);
- }
- break;
- case MSG_STOP_JOB:
- try {
- boolean ret = JobService.this.onStopJob(params);
- ackStopMessage(params, ret);
- } catch (Exception e) {
- Log.e(TAG, "Application unable to handle onStopJob.", e);
- throw new RuntimeException(e);
- }
- break;
- case MSG_JOB_FINISHED:
- final boolean needsReschedule = (msg.arg2 == 1);
- IJobCallback callback = params.getCallback();
- if (callback != null) {
- try {
- callback.jobFinished(params.getJobId(), needsReschedule);
- } catch (RemoteException e) {
- Log.e(TAG, "Error reporting job finish to system: binder has gone" +
- "away.");
- }
- } else {
- Log.e(TAG, "finishJob() called for a nonexistent job id.");
- }
- break;
- default:
- Log.e(TAG, "Unrecognised message received.");
- break;
- }
- }
-
- private void ackStartMessage(JobParameters params, boolean workOngoing) {
- final IJobCallback callback = params.getCallback();
- final int jobId = params.getJobId();
- if (callback != null) {
- try {
- callback.acknowledgeStartMessage(jobId, workOngoing);
- } catch(RemoteException e) {
- Log.e(TAG, "System unreachable for starting job.");
- }
- } else {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Attempting to ack a job that has already been processed.");
- }
- }
- }
-
- private void ackStopMessage(JobParameters params, boolean reschedule) {
- final IJobCallback callback = params.getCallback();
- final int jobId = params.getJobId();
- if (callback != null) {
- try {
- callback.acknowledgeStopMessage(jobId, reschedule);
- } catch(RemoteException e) {
- Log.e(TAG, "System unreachable for stopping job.");
- }
- } else {
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Attempting to ack a job that has already been processed.");
- }
- }
- }
- }
+ private JobServiceEngine mEngine;
/** @hide */
public final IBinder onBind(Intent intent) {
- if (mBinder == null) {
- mBinder = new JobInterface(this);
+ if (mEngine == null) {
+ mEngine = new JobServiceEngine(this) {
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ return JobService.this.onStartJob(params);
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ return JobService.this.onStopJob(params);
+ }
+ };
}
- return mBinder.asBinder();
+ return mEngine.getBinder();
}
/**
@@ -269,9 +132,6 @@
* criteria specified at schedule-time. False otherwise.
*/
public final void jobFinished(JobParameters params, boolean needsReschedule) {
- ensureHandler();
- Message m = Message.obtain(mHandler, MSG_JOB_FINISHED, params);
- m.arg2 = needsReschedule ? 1 : 0;
- m.sendToTarget();
+ mEngine.jobFinished(params, needsReschedule);
}
}
\ No newline at end of file
diff --git a/core/java/android/app/job/JobServiceEngine.java b/core/java/android/app/job/JobServiceEngine.java
new file mode 100644
index 0000000..a628619
--- /dev/null
+++ b/core/java/android/app/job/JobServiceEngine.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.app.job;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Helper for implementing a {@link android.app.Service} that interacts with
+ * {@link JobScheduler}.
+ */
+public abstract class JobServiceEngine {
+ private static final String TAG = "JobServiceEngine";
+
+ /**
+ * Identifier for a message that will result in a call to
+ * {@link #onStartJob(android.app.job.JobParameters)}.
+ */
+ private static final int MSG_EXECUTE_JOB = 0;
+ /**
+ * Message that will result in a call to {@link #onStopJob(android.app.job.JobParameters)}.
+ */
+ private static final int MSG_STOP_JOB = 1;
+ /**
+ * Message that the client has completed execution of this job.
+ */
+ private static final int MSG_JOB_FINISHED = 2;
+
+ /**
+ * Context we are running in.
+ */
+ private final Service mService;
+
+ private final IJobService mBinder;
+
+ /** Lock object for {@link #mHandler}. */
+ private final Object mHandlerLock = new Object();
+
+ /**
+ * Handler we post jobs to. Responsible for calling into the client logic, and handling the
+ * callback to the system.
+ */
+ @GuardedBy("mHandlerLock")
+ JobHandler mHandler;
+
+ static final class JobInterface extends IJobService.Stub {
+ final WeakReference<JobServiceEngine> mService;
+
+ JobInterface(JobServiceEngine service) {
+ mService = new WeakReference<>(service);
+ }
+
+ @Override
+ public void startJob(JobParameters jobParams) throws RemoteException {
+ JobServiceEngine service = mService.get();
+ if (service != null) {
+ Message m = Message.obtain(service.mHandler, MSG_EXECUTE_JOB, jobParams);
+ m.sendToTarget();
+ }
+ }
+
+ @Override
+ public void stopJob(JobParameters jobParams) throws RemoteException {
+ JobServiceEngine service = mService.get();
+ if (service != null) {
+ Message m = Message.obtain(service.mHandler, MSG_STOP_JOB, jobParams);
+ m.sendToTarget();
+ }
+ }
+ }
+
+ /**
+ * Runs on application's main thread - callbacks are meant to offboard work to some other
+ * (app-specified) mechanism.
+ * @hide
+ */
+ class JobHandler extends Handler {
+ JobHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ final JobParameters params = (JobParameters) msg.obj;
+ switch (msg.what) {
+ case MSG_EXECUTE_JOB:
+ try {
+ boolean workOngoing = JobServiceEngine.this.onStartJob(params);
+ ackStartMessage(params, workOngoing);
+ } catch (Exception e) {
+ Log.e(TAG, "Error while executing job: " + params.getJobId());
+ throw new RuntimeException(e);
+ }
+ break;
+ case MSG_STOP_JOB:
+ try {
+ boolean ret = JobServiceEngine.this.onStopJob(params);
+ ackStopMessage(params, ret);
+ } catch (Exception e) {
+ Log.e(TAG, "Application unable to handle onStopJob.", e);
+ throw new RuntimeException(e);
+ }
+ break;
+ case MSG_JOB_FINISHED:
+ final boolean needsReschedule = (msg.arg2 == 1);
+ IJobCallback callback = params.getCallback();
+ if (callback != null) {
+ try {
+ callback.jobFinished(params.getJobId(), needsReschedule);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error reporting job finish to system: binder has gone" +
+ "away.");
+ }
+ } else {
+ Log.e(TAG, "finishJob() called for a nonexistent job id.");
+ }
+ break;
+ default:
+ Log.e(TAG, "Unrecognised message received.");
+ break;
+ }
+ }
+
+ private void ackStartMessage(JobParameters params, boolean workOngoing) {
+ final IJobCallback callback = params.getCallback();
+ final int jobId = params.getJobId();
+ if (callback != null) {
+ try {
+ callback.acknowledgeStartMessage(jobId, workOngoing);
+ } catch(RemoteException e) {
+ Log.e(TAG, "System unreachable for starting job.");
+ }
+ } else {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Attempting to ack a job that has already been processed.");
+ }
+ }
+ }
+
+ private void ackStopMessage(JobParameters params, boolean reschedule) {
+ final IJobCallback callback = params.getCallback();
+ final int jobId = params.getJobId();
+ if (callback != null) {
+ try {
+ callback.acknowledgeStopMessage(jobId, reschedule);
+ } catch(RemoteException e) {
+ Log.e(TAG, "System unreachable for stopping job.");
+ }
+ } else {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Attempting to ack a job that has already been processed.");
+ }
+ }
+ }
+ }
+
+ /**
+ * Create a new engine, ready for use.
+ *
+ * @param service The {@link Service} that is creating this engine and in which it will run.
+ */
+ public JobServiceEngine(Service service) {
+ mService = service;
+ mBinder = new JobInterface(this);
+ mHandler = new JobHandler(mService.getMainLooper());
+ }
+
+ /**
+ * Retrieve the engine's IPC interface that should be returned by
+ * {@link Service#onBind(Intent)}.
+ */
+ public final IBinder getBinder() {
+ return mBinder.asBinder();
+ }
+
+ /**
+ * Engine's report that a job has started. See
+ * {@link JobService#onStartJob(JobParameters) JobService.onStartJob} for more information.
+ */
+ public abstract boolean onStartJob(JobParameters params);
+
+ /**
+ * Engine's report that a job has stopped. See
+ * {@link JobService#onStopJob(JobParameters) JobService.onStopJob} for more information.
+ */
+ public abstract boolean onStopJob(JobParameters params);
+
+ /**
+ * Call in to engine to report that a job has finished executing. See
+ * {@link JobService#jobFinished(JobParameters, boolean)} JobService.jobFinished} for more
+ * information.
+ */
+ public final void jobFinished(JobParameters params, boolean needsReschedule) {
+ Message m = Message.obtain(mHandler, MSG_JOB_FINISHED, params);
+ m.arg2 = needsReschedule ? 1 : 0;
+ m.sendToTarget();
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/app/job/JobWorkItem.java b/core/java/android/app/job/JobWorkItem.java
index 4bb057e..05687ee 100644
--- a/core/java/android/app/job/JobWorkItem.java
+++ b/core/java/android/app/job/JobWorkItem.java
@@ -27,6 +27,7 @@
final public class JobWorkItem implements Parcelable {
final Intent mIntent;
int mWorkId;
+ Object mGrants;
/**
* Create a new piece of work.
@@ -57,6 +58,20 @@
return mWorkId;
}
+ /**
+ * @hide
+ */
+ public void setGrants(Object grants) {
+ mGrants = grants;
+ }
+
+ /**
+ * @hide
+ */
+ public Object getGrants() {
+ return mGrants;
+ }
+
public String toString() {
return "JobWorkItem{id=" + mWorkId + " intent=" + mIntent + "}";
}
diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java
index 6fc4f5c..4b6479a 100644
--- a/core/java/android/app/usage/StorageStatsManager.java
+++ b/core/java/android/app/usage/StorageStatsManager.java
@@ -16,17 +16,25 @@
package android.app.usage;
+import static android.os.storage.StorageManager.convert;
+
+import android.annotation.NonNull;
import android.annotation.TestApi;
import android.annotation.WorkerThread;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
import com.android.internal.util.Preconditions;
import java.io.File;
+import java.io.IOException;
+import java.util.UUID;
/**
* Provides access to detailed storage statistics.
@@ -50,36 +58,49 @@
/** {@hide} */
@TestApi
- public boolean isQuotaSupported(String volumeUuid) {
+ public boolean isQuotaSupported(@NonNull UUID storageUuid) {
try {
- return mService.isQuotaSupported(volumeUuid, mContext.getOpPackageName());
+ return mService.isQuotaSupported(convert(storageUuid), mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /** @removed */
+ @Deprecated
+ public boolean isQuotaSupported(String uuid) {
+ return isQuotaSupported(convert(uuid));
+ }
+
/**
- * Return the total space on the requested storage volume.
+ * Return the total size of the media hosting this storage volume.
* <p>
- * To reduce end user confusion, this value is the total storage size
+ * To reduce end user confusion, this value matches the total storage size
* advertised in a retail environment, which is typically larger than the
- * actual writable partition total size.
- * <p>
- * This method may take several seconds to calculate the requested values,
- * so it should only be called from a worker thread.
+ * actual usable partition space.
*
- * @param volumeUuid the UUID of the storage volume you're interested in, or
- * {@code null} to specify the default internal storage.
+ * @param storageUuid the UUID of the storage volume you're interested in,
+ * such as {@link StorageManager#UUID_DEFAULT}.
+ * @throws IOException when the storage device isn't present.
*/
@WorkerThread
- public long getTotalBytes(String volumeUuid) {
+ public long getTotalBytes(@NonNull UUID storageUuid) throws IOException {
try {
- return mService.getTotalBytes(volumeUuid, mContext.getOpPackageName());
+ return mService.getTotalBytes(convert(storageUuid), mContext.getOpPackageName());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /** @removed */
+ @Deprecated
+ public long getTotalBytes(String uuid) throws IOException {
+ return getTotalBytes(convert(uuid));
+ }
+
/**
* Return the free space on the requested storage volume.
* <p>
@@ -90,18 +111,28 @@
* This method may take several seconds to calculate the requested values,
* so it should only be called from a worker thread.
*
- * @param volumeUuid the UUID of the storage volume you're interested in, or
- * {@code null} to specify the default internal storage.
+ * @param storageUuid the UUID of the storage volume you're interested in,
+ * such as {@link StorageManager#UUID_DEFAULT}.
+ * @throws IOException when the storage device isn't present.
*/
@WorkerThread
- public long getFreeBytes(String volumeUuid) {
+ public long getFreeBytes(@NonNull UUID storageUuid) throws IOException {
try {
- return mService.getFreeBytes(volumeUuid, mContext.getOpPackageName());
+ return mService.getFreeBytes(convert(storageUuid), mContext.getOpPackageName());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /** @removed */
+ @Deprecated
+ public long getFreeBytes(String uuid) throws IOException {
+ return getFreeBytes(convert(uuid));
+ }
+
/**
* Return storage statistics for a specific package on the requested storage
* volume.
@@ -112,27 +143,41 @@
* Note: if the requested package uses the {@code android:sharedUserId}
* manifest feature, this call will be forced into a slower manual
* calculation path. If possible, consider always using
- * {@link #queryStatsForUid(String, int)}, which is typically faster.
+ * {@link #queryStatsForUid(UUID, int)}, which is typically faster.
* </p>
*
- * @param volumeUuid the UUID of the storage volume you're interested in, or
- * {@code null} to specify the default internal storage.
+ * @param storageUuid the UUID of the storage volume you're interested in,
+ * such as {@link StorageManager#UUID_DEFAULT}.
* @param packageName the package name you're interested in.
* @param user the user you're interested in.
- * @see ApplicationInfo#volumeUuid
+ * @throws PackageManager.NameNotFoundException when the requested package
+ * name isn't installed for the requested user.
+ * @throws IOException when the storage device isn't present.
+ * @see ApplicationInfo#storageUuid
* @see PackageInfo#packageName
*/
@WorkerThread
- public StorageStats queryStatsForPackage(String volumeUuid, String packageName,
- UserHandle user) {
+ public @NonNull StorageStats queryStatsForPackage(@NonNull UUID storageUuid, String packageName,
+ UserHandle user) throws PackageManager.NameNotFoundException, IOException {
try {
- return mService.queryStatsForPackage(volumeUuid, packageName, user.getIdentifier(),
- mContext.getOpPackageName());
+ return mService.queryStatsForPackage(convert(storageUuid), packageName,
+ user.getIdentifier(), mContext.getOpPackageName());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(PackageManager.NameNotFoundException.class);
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /** @removed */
+ @Deprecated
+ public StorageStats queryStatsForPackage(String uuid, String packageName,
+ UserHandle user) throws PackageManager.NameNotFoundException, IOException {
+ return queryStatsForPackage(convert(uuid), packageName, user);
+ }
+
/**
* Return storage statistics for a specific UID on the requested storage
* volume.
@@ -140,21 +185,32 @@
* This method may take several seconds to calculate the requested values,
* so it should only be called from a worker thread.
*
- * @param volumeUuid the UUID of the storage volume you're interested in, or
- * {@code null} to specify the default internal storage.
+ * @param storageUuid the UUID of the storage volume you're interested in,
+ * such as {@link StorageManager#UUID_DEFAULT}.
* @param uid the UID you're interested in.
- * @see ApplicationInfo#volumeUuid
+ * @throws IOException when the storage device isn't present.
+ * @see ApplicationInfo#storageUuid
* @see ApplicationInfo#uid
*/
@WorkerThread
- public StorageStats queryStatsForUid(String volumeUuid, int uid) {
+ public StorageStats queryStatsForUid(@NonNull UUID storageUuid, int uid) throws IOException {
try {
- return mService.queryStatsForUid(volumeUuid, uid, mContext.getOpPackageName());
+ return mService.queryStatsForUid(convert(storageUuid), uid,
+ mContext.getOpPackageName());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /** @removed */
+ @Deprecated
+ public StorageStats queryStatsForUid(String uuid, int uid) throws IOException {
+ return queryStatsForUid(convert(uuid), uid);
+ }
+
/**
* Return storage statistics for a specific {@link UserHandle} on the
* requested storage volume.
@@ -162,21 +218,32 @@
* This method may take several seconds to calculate the requested values,
* so it should only be called from a worker thread.
*
- * @param volumeUuid the UUID of the storage volume you're interested in, or
- * {@code null} to specify the default internal storage.
+ * @param storageUuid the UUID of the storage volume you're interested in,
+ * such as {@link StorageManager#UUID_DEFAULT}.
* @param user the user you're interested in.
+ * @throws IOException when the storage device isn't present.
* @see android.os.Process#myUserHandle()
*/
@WorkerThread
- public StorageStats queryStatsForUser(String volumeUuid, UserHandle user) {
+ public StorageStats queryStatsForUser(@NonNull UUID storageUuid, UserHandle user)
+ throws IOException {
try {
- return mService.queryStatsForUser(volumeUuid, user.getIdentifier(),
+ return mService.queryStatsForUser(convert(storageUuid), user.getIdentifier(),
mContext.getOpPackageName());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /** @removed */
+ @Deprecated
+ public StorageStats queryStatsForUser(String uuid, UserHandle user) throws IOException {
+ return queryStatsForUser(convert(uuid), user);
+ }
+
/**
* Return shared/external storage statistics for a specific
* {@link UserHandle} on the requested storage volume.
@@ -184,20 +251,32 @@
* This method may take several seconds to calculate the requested values,
* so it should only be called from a worker thread.
*
- * @param volumeUuid the UUID of the storage volume you're interested in, or
- * {@code null} to specify the default internal storage.
+ * @param storageUuid the UUID of the storage volume you're interested in,
+ * such as {@link StorageManager#UUID_DEFAULT}.
+ * @throws IOException when the storage device isn't present.
* @see android.os.Process#myUserHandle()
*/
@WorkerThread
- public ExternalStorageStats queryExternalStatsForUser(String volumeUuid, UserHandle user) {
+ public ExternalStorageStats queryExternalStatsForUser(@NonNull UUID storageUuid,
+ UserHandle user) throws IOException {
try {
- return mService.queryExternalStatsForUser(volumeUuid, user.getIdentifier(),
+ return mService.queryExternalStatsForUser(convert(storageUuid), user.getIdentifier(),
mContext.getOpPackageName());
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /** @removed */
+ @Deprecated
+ public ExternalStorageStats queryExternalStatsForUser(String uuid, UserHandle user)
+ throws IOException {
+ return queryExternalStatsForUser(convert(uuid), user);
+ }
+
/** {@hide} */
public long getCacheQuotaBytes(String volumeUuid, int uid) {
try {
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index 6ba52b7..c1ff580 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -276,8 +276,6 @@
/**
* Gets a list of all the appWidgetIds that are bound to the current host
- *
- * @hide
*/
public int[] getAppWidgetIds() {
try {
diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java
index ef3b1c5..624ec87 100644
--- a/core/java/android/appwidget/AppWidgetHostView.java
+++ b/core/java/android/appwidget/AppWidgetHostView.java
@@ -498,8 +498,13 @@
private void updateContentDescription(AppWidgetProviderInfo info) {
if (info != null) {
LauncherApps launcherApps = getContext().getSystemService(LauncherApps.class);
- ApplicationInfo appInfo = launcherApps.getApplicationInfo(
- info.provider.getPackageName(), 0, info.getProfile());
+ ApplicationInfo appInfo = null;
+ try {
+ appInfo = launcherApps.getApplicationInfo(
+ info.provider.getPackageName(), 0, info.getProfile());
+ } catch (NameNotFoundException e) {
+ // ignore -- use null.
+ }
if (appInfo != null &&
(appInfo.flags & ApplicationInfo.FLAG_SUSPENDED) != 0) {
setContentDescription(
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 334e88b..582709c 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -16,6 +16,7 @@
package android.bluetooth;
+import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.le.AdvertiseSettings;
@@ -47,6 +48,9 @@
void unregisterScanner(in int scannerId);
void startScan(in int scannerId, in ScanSettings settings, in List<ScanFilter> filters,
in WorkSource workSource, in List scanStorages, in String callingPackage);
+ void startScanForIntent(in PendingIntent intent, in ScanSettings settings, in List<ScanFilter> filters,
+ in String callingPackage);
+ void stopScanForIntent(in PendingIntent intent, in String callingPackage);
void stopScan(in int scannerId);
void flushPendingBatchResults(in int scannerId);
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index b63c614..b65a7ad 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -17,17 +17,18 @@
package android.bluetooth.le;
import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.ActivityThread;
+import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.IBluetoothGatt;
import android.bluetooth.IBluetoothManager;
-import android.bluetooth.le.IScannerCallback;
import android.os.Handler;
import android.os.Looper;
-import android.os.ParcelUuid;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
@@ -36,7 +37,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import java.util.UUID;
/**
* This class provides methods to perform scan related operations for Bluetooth LE devices. An
@@ -57,6 +57,27 @@
private static final boolean DBG = true;
private static final boolean VDBG = false;
+ /**
+ * Extra containing a list of ScanResults. It can have one or more results if there was no
+ * error. In case of error, {@link #EXTRA_ERROR_CODE} will contain the error code and this
+ * extra will not be available.
+ */
+ public static final String EXTRA_LIST_SCAN_RESULT
+ = "android.bluetooth.le.extra.LIST_SCAN_RESULT";
+
+ /**
+ * Optional extra indicating the error code, if any. The error code will be one of the
+ * SCAN_FAILED_* codes in {@link ScanCallback}.
+ */
+ public static final String EXTRA_ERROR_CODE = "android.bluetooth.le.extra.ERROR_CODE";
+
+ /**
+ * Optional extra indicating the callback type, which will be one of
+ * ScanSettings.CALLBACK_TYPE_*.
+ * @see ScanCallback#onScanResult(int, ScanResult)
+ */
+ public static final String EXTRA_CALLBACK_TYPE = "android.bluetooth.le.extra.CALLBACK_TYPE";
+
private final IBluetoothManager mBluetoothManager;
private final Handler mHandler;
private BluetoothAdapter mBluetoothAdapter;
@@ -110,7 +131,27 @@
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public void startScan(List<ScanFilter> filters, ScanSettings settings,
final ScanCallback callback) {
- startScan(filters, settings, null, callback, null);
+ startScan(filters, settings, null, callback, /*callbackIntent=*/ null, null);
+ }
+
+ /**
+ * Start Bluetooth LE scan using a {@link PendingIntent}. The scan results will be delivered via
+ * the PendingIntent. Use this method of scanning if your process is not always running and it
+ * should be started when scan results are available.
+ *
+ * @param filters Optional list of ScanFilters for finding exact BLE devices.
+ * @param settings Optional settings for the scan.
+ * @param callbackIntent The PendingIntent to deliver the result to.
+ * @return Returns 0 for success or an error code from {@link ScanCallback} if the scan request
+ * could not be sent.
+ * @see #stopScan(PendingIntent)
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public int startScan(@Nullable List<ScanFilter> filters, @Nullable ScanSettings settings,
+ @NonNull PendingIntent callbackIntent) {
+ return startScan(filters,
+ settings != null ? settings : new ScanSettings.Builder().build(),
+ null, null, callbackIntent, null);
}
/**
@@ -145,23 +186,23 @@
Manifest.permission.BLUETOOTH_ADMIN, Manifest.permission.UPDATE_DEVICE_STATS })
public void startScanFromSource(List<ScanFilter> filters, ScanSettings settings,
final WorkSource workSource, final ScanCallback callback) {
- startScan(filters, settings, workSource, callback, null);
+ startScan(filters, settings, workSource, callback, null, null);
}
- private void startScan(List<ScanFilter> filters, ScanSettings settings,
+ private int startScan(List<ScanFilter> filters, ScanSettings settings,
final WorkSource workSource, final ScanCallback callback,
+ final PendingIntent callbackIntent,
List<List<ResultStorageDescriptor>> resultStorages) {
BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
- if (callback == null) {
+ if (callback == null && callbackIntent == null) {
throw new IllegalArgumentException("callback is null");
}
if (settings == null) {
throw new IllegalArgumentException("settings is null");
}
synchronized (mLeScanClients) {
- if (mLeScanClients.containsKey(callback)) {
+ if (callback != null && mLeScanClients.containsKey(callback)) {
postCallbackError(callback, ScanCallback.SCAN_FAILED_ALREADY_STARTED);
- return;
}
IBluetoothGatt gatt;
try {
@@ -170,28 +211,34 @@
gatt = null;
}
if (gatt == null) {
- postCallbackError(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
- return;
+ return postCallbackErrorOrReturn(callback, ScanCallback.SCAN_FAILED_INTERNAL_ERROR);
}
if (!isSettingsConfigAllowedForScan(settings)) {
- postCallbackError(callback,
- ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
- return;
+ return postCallbackErrorOrReturn(callback,
+ ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
}
if (!isHardwareResourcesAvailableForScan(settings)) {
- postCallbackError(callback,
- ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES);
- return;
+ return postCallbackErrorOrReturn(callback,
+ ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES);
}
if (!isSettingsAndFilterComboAllowed(settings, filters)) {
- postCallbackError(callback,
+ return postCallbackErrorOrReturn(callback,
ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
- return;
}
- BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
- settings, workSource, callback, resultStorages);
- wrapper.startRegisteration();
+ if (callback != null) {
+ BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
+ settings, workSource, callback, resultStorages);
+ wrapper.startRegistration();
+ } else {
+ try {
+ gatt.startScanForIntent(callbackIntent, settings, filters,
+ ActivityThread.currentOpPackageName());
+ } catch (RemoteException e) {
+ return ScanCallback.SCAN_FAILED_INTERNAL_ERROR;
+ }
+ }
}
+ return ScanCallback.NO_ERROR;
}
/**
@@ -215,6 +262,25 @@
}
/**
+ * Stops an ongoing Bluetooth LE scan started using a PendingIntent.
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+ *
+ * @param callbackIntent The PendingIntent that was used to start the scan.
+ * @see #startScan(List, ScanSettings, PendingIntent)
+ */
+ @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+ public void stopScan(PendingIntent callbackIntent) {
+ BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
+ IBluetoothGatt gatt;
+ try {
+ gatt = mBluetoothManager.getBluetoothGatt();
+ gatt.stopScanForIntent(callbackIntent, ActivityThread.currentOpPackageName());
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Flush pending batch scan results stored in Bluetooth controller. This will return Bluetooth
* LE scan results batched on bluetooth controller. Returns immediately, batch scan results data
* will be delivered through the {@code callback}.
@@ -252,7 +318,7 @@
scanFilters.add(filter.getFilter());
scanStorages.add(filter.getStorageDescriptors());
}
- startScan(scanFilters, settings, null, callback, scanStorages);
+ startScan(scanFilters, settings, null, callback, null, scanStorages);
}
/**
@@ -295,7 +361,7 @@
mResultStorages = resultStorages;
}
- public void startRegisteration() {
+ public void startRegistration() {
synchronized (this) {
// Scan stopped.
if (mScannerId == -1) return;
@@ -399,7 +465,6 @@
mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
}
});
-
}
@Override
@@ -453,6 +518,15 @@
}
}
+ private int postCallbackErrorOrReturn(final ScanCallback callback, final int errorCode) {
+ if (callback == null) {
+ return errorCode;
+ } else {
+ postCallbackError(callback, errorCode);
+ return ScanCallback.NO_ERROR;
+ }
+ }
+
private void postCallbackError(final ScanCallback callback, final int errorCode) {
mHandler.post(new Runnable() {
@Override
diff --git a/core/java/android/bluetooth/le/ScanCallback.java b/core/java/android/bluetooth/le/ScanCallback.java
index 61b2e78..aff2e90 100644
--- a/core/java/android/bluetooth/le/ScanCallback.java
+++ b/core/java/android/bluetooth/le/ScanCallback.java
@@ -50,6 +50,8 @@
*/
public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5;
+ static final int NO_ERROR = 0;
+
/**
* Callback when a BLE advertisement has been found.
*
diff --git a/core/java/android/companion/BluetoothDeviceFilterUtils.java b/core/java/android/companion/BluetoothDeviceFilterUtils.java
index 8a316f1..3665d1b 100644
--- a/core/java/android/companion/BluetoothDeviceFilterUtils.java
+++ b/core/java/android/companion/BluetoothDeviceFilterUtils.java
@@ -37,7 +37,7 @@
private BluetoothDeviceFilterUtils() {}
private static final boolean DEBUG = false;
- private static final String LOG_TAG = "BluetoothDeviceFilterUtil";
+ private static final String LOG_TAG = "BluetoothDeviceFilterUtils";
@Nullable
static String patternToString(@Nullable Pattern p) {
@@ -50,8 +50,10 @@
}
static boolean matches(ScanFilter filter, BluetoothDevice device) {
- return matchesAddress(filter.getDeviceAddress(), device)
+ boolean result = matchesAddress(filter.getDeviceAddress(), device)
&& matchesServiceUuid(filter.getServiceUuid(), filter.getServiceUuidMask(), device);
+ if (DEBUG) debugLogMatchResult(result, device, filter);
+ return result;
}
static boolean matchesAddress(String deviceAddress, BluetoothDevice device) {
diff --git a/core/java/android/companion/BluetoothLEDeviceFilter.java b/core/java/android/companion/BluetoothLEDeviceFilter.java
index 0444775..76051d7 100644
--- a/core/java/android/companion/BluetoothLEDeviceFilter.java
+++ b/core/java/android/companion/BluetoothLEDeviceFilter.java
@@ -21,6 +21,7 @@
import static android.companion.BluetoothDeviceFilterUtils.patternToString;
import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkState;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -31,6 +32,7 @@
import android.os.Parcel;
import android.provider.OneTimeUseBuilder;
import android.text.TextUtils;
+import android.util.Log;
import com.android.internal.util.BitUtils;
import com.android.internal.util.ObjectUtils;
@@ -47,6 +49,9 @@
*/
public final class BluetoothLEDeviceFilter implements DeviceFilter<ScanResult> {
+ private static final boolean DEBUG = false;
+ private static final String LOG_TAG = "BluetoothLEDeviceFilter";
+
private static final int RENAME_PREFIX_LENGTH_LIMIT = 10;
private final Pattern mNamePattern;
@@ -57,12 +62,14 @@
private final String mRenameSuffix;
private final int mRenameBytesFrom;
private final int mRenameBytesTo;
+ private final int mRenameNameFrom;
+ private final int mRenameNameTo;
private final boolean mRenameBytesReverseOrder;
private BluetoothLEDeviceFilter(Pattern namePattern, ScanFilter scanFilter,
byte[] rawDataFilter, byte[] rawDataFilterMask, String renamePrefix,
String renameSuffix, int renameBytesFrom, int renameBytesTo,
- boolean renameBytesReverseOrder) {
+ int renameNameFrom, int renameNameTo, boolean renameBytesReverseOrder) {
mNamePattern = namePattern;
mScanFilter = ObjectUtils.firstNotNull(scanFilter, ScanFilter.EMPTY);
mRawDataFilter = rawDataFilter;
@@ -71,6 +78,8 @@
mRenameSuffix = renameSuffix;
mRenameBytesFrom = renameBytesFrom;
mRenameBytesTo = renameBytesTo;
+ mRenameNameFrom = renameNameFrom;
+ mRenameNameTo = renameNameTo;
mRenameBytesReverseOrder = renameBytesReverseOrder;
}
@@ -129,15 +138,23 @@
@Override
@Nullable
public String getDeviceDisplayName(ScanResult sr) {
- if (mRenameBytesFrom < 0) return getDeviceDisplayNameInternal(sr.getDevice());
- final byte[] bytes = sr.getScanRecord().getBytes();
+ if (mRenameBytesFrom < 0 && mRenameNameFrom < 0) {
+ return getDeviceDisplayNameInternal(sr.getDevice());
+ }
final StringBuilder sb = new StringBuilder(TextUtils.emptyIfNull(mRenamePrefix));
- int startInclusive = mRenameBytesFrom;
- int endInclusive = mRenameBytesTo - 1;
- int initial = mRenameBytesReverseOrder ? endInclusive : startInclusive;
- int step = mRenameBytesReverseOrder ? -1 : 1;
- for (int i = initial; startInclusive <= i && i <= endInclusive; i+=step) {
- sb.append(Byte.toHexString(bytes[i], true));
+ if (mRenameBytesFrom >= 0) {
+ final byte[] bytes = sr.getScanRecord().getBytes();
+ int startInclusive = mRenameBytesFrom;
+ int endInclusive = mRenameBytesTo - 1;
+ int initial = mRenameBytesReverseOrder ? endInclusive : startInclusive;
+ int step = mRenameBytesReverseOrder ? -1 : 1;
+ for (int i = initial; startInclusive <= i && i <= endInclusive; i += step) {
+ sb.append(Byte.toHexString(bytes[i], true));
+ }
+ } else {
+ sb.append(
+ getDeviceDisplayNameInternal(sr.getDevice())
+ .substring(mRenameNameFrom, mRenameNameTo));
}
return sb.append(TextUtils.emptyIfNull(mRenameSuffix)).toString();
}
@@ -145,9 +162,13 @@
/** @hide */
@Override
public boolean matches(ScanResult device) {
- return matches(device.getDevice())
- && BitUtils.maskedEquals(device.getScanRecord().getBytes(),
- mRawDataFilter, mRawDataFilterMask);
+ boolean result = matches(device.getDevice())
+ && (mRawDataFilter == null
+ || BitUtils.maskedEquals(device.getScanRecord().getBytes(),
+ mRawDataFilter, mRawDataFilterMask));
+ if (DEBUG) Log.i(LOG_TAG, "matches(this = " + this + ", device = " + device +
+ ") -> " + result);
+ return result;
}
private boolean matches(BluetoothDevice device) {
@@ -194,6 +215,8 @@
dest.writeString(mRenameSuffix);
dest.writeInt(mRenameBytesFrom);
dest.writeInt(mRenameBytesTo);
+ dest.writeInt(mRenameNameFrom);
+ dest.writeInt(mRenameNameTo);
dest.writeBoolean(mRenameBytesReverseOrder);
}
@@ -218,9 +241,16 @@
String suffix = in.readString();
int bytesFrom = in.readInt();
int bytesTo = in.readInt();
+ int nameFrom = in.readInt();
+ int nameTo = in.readInt();
boolean bytesReverseOrder = in.readBoolean();
if (renamePrefix != null) {
- builder.setRename(renamePrefix, suffix, bytesFrom, bytesTo, bytesReverseOrder);
+ if (bytesFrom >= 0) {
+ builder.setRenameFromBytes(renamePrefix, suffix, bytesFrom, bytesTo,
+ bytesReverseOrder);
+ } else {
+ builder.setRenameFromName(renamePrefix, suffix, nameFrom, nameTo);
+ }
}
return builder.build();
}
@@ -247,6 +277,8 @@
private String mRenameSuffix;
private int mRenameBytesFrom = -1;
private int mRenameBytesTo;
+ private int mRenameNameFrom = -1;
+ private int mRenameNameTo;
private boolean mRenameBytesReverseOrder = false;
/**
@@ -312,17 +344,57 @@
* @return self for chaining
*/
@NonNull
- public Builder setRename(@NonNull String prefix, @NonNull String suffix,
+ public Builder setRenameFromBytes(@NonNull String prefix, @NonNull String suffix,
int bytesFrom, int bytesTo, boolean bytesReverseOrder) {
- checkNotUsed();
- checkArgument(TextUtils.length(prefix) >= getRenamePrefixLengthLimit(),
- "Prefix is too short");
- mRenamePrefix = prefix;
- mRenameSuffix = suffix;
- checkArgument(bytesFrom < bytesTo, "Byte range must be non-empty");
+ checkRenameNotSet();
+ checkRangeNotEmpty(bytesFrom, bytesTo);
mRenameBytesFrom = bytesFrom;
mRenameBytesTo = bytesTo;
mRenameBytesReverseOrder = bytesReverseOrder;
+ return setRename(prefix, suffix);
+ }
+
+ /**
+ * Rename the devices shown in the list, using specific characters from the advertised name,
+ * as well as a custom prefix/suffix around them
+ *
+ * Note that the prefix length is limited to {@link #getRenamePrefixLengthLimit} characters
+ * to ensure that there's enough space to display the byte data
+ *
+ * The range of name characters to be displayed cannot be empty
+ *
+ * @param prefix to be displayed before the byte data
+ * @param suffix to be displayed after the byte data
+ * @param nameFrom the start name character index to be displayed (inclusive)
+ * @param nameTo the end name character index to be displayed (exclusive)
+ * @return self for chaining
+ */
+ @NonNull
+ public Builder setRenameFromName(@NonNull String prefix, @NonNull String suffix,
+ int nameFrom, int nameTo) {
+ checkRenameNotSet();
+ checkRangeNotEmpty(nameFrom, nameTo);
+ mRenameNameFrom = nameFrom;
+ mRenameNameTo = nameTo;
+ mRenameBytesReverseOrder = false;
+ return setRename(prefix, suffix);
+ }
+
+ private void checkRenameNotSet() {
+ checkState(mRenamePrefix == null, "Renaming rule can only be set once");
+ }
+
+ private void checkRangeNotEmpty(int bytesFrom, int bytesTo) {
+ checkArgument(bytesFrom < bytesTo, "Range must be non-empty");
+ }
+
+ @NonNull
+ private Builder setRename(@NonNull String prefix, @NonNull String suffix) {
+ checkNotUsed();
+ checkArgument(TextUtils.length(prefix) <= getRenamePrefixLengthLimit(),
+ "Prefix is too long");
+ mRenamePrefix = prefix;
+ mRenameSuffix = suffix;
return this;
}
@@ -334,7 +406,9 @@
return new BluetoothLEDeviceFilter(mNamePattern, mScanFilter,
mRawDataFilter, mRawDataFilterMask,
mRenamePrefix, mRenameSuffix,
- mRenameBytesFrom, mRenameBytesTo, mRenameBytesReverseOrder);
+ mRenameBytesFrom, mRenameBytesTo,
+ mRenameNameFrom, mRenameNameTo,
+ mRenameBytesReverseOrder);
}
}
}
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 4d788e7..e50b2a9 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -22,11 +22,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.RemoteException;
+import android.service.notification.NotificationListenerService;
import android.util.Log;
import java.util.Collections;
@@ -195,22 +197,47 @@
}
}
- /** @hide */
- public void requestNotificationAccess() {
+ /**
+ * Request notification access for the given component.
+ *
+ * The given component must follow the protocol specified in {@link NotificationListenerService}
+ *
+ * Only components from the same {@link ComponentName#getPackageName package} as the calling app
+ * are allowed.
+ *
+ * Your app must have an association with a device before calling this API
+ */
+ public void requestNotificationAccess(ComponentName component) {
if (!checkFeaturePresent()) {
return;
}
- //TODO implement
- throw new UnsupportedOperationException("Not yet implemented");
+ try {
+ mService.requestNotificationAccess(component).send();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } catch (PendingIntent.CanceledException e) {
+ throw new RuntimeException(e);
+ }
}
- /** @hide */
- public boolean haveNotificationAccess() {
+ /**
+ * Check whether the given component can access the notifications via a
+ * {@link NotificationListenerService}
+ *
+ * Your app must have an association with a device before calling this API
+ *
+ * @param component the name of the component
+ * @return whether the given component has the notification listener permission
+ */
+ public boolean hasNotificationAccess(ComponentName component) {
if (!checkFeaturePresent()) {
return false;
}
- //TODO implement
- throw new UnsupportedOperationException("Not yet implemented");
+ try {
+ return mService.hasNotificationAccess(component);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
private boolean checkFeaturePresent() {
diff --git a/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl b/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl
index 6bbb58da..5f73e55 100644
--- a/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl
+++ b/core/java/android/companion/ICompanionDeviceDiscoveryServiceCallback.aidl
@@ -19,4 +19,5 @@
/** @hide */
interface ICompanionDeviceDiscoveryServiceCallback {
oneway void onDeviceSelected(String packageName, int userId, String deviceAddress);
+ oneway void onDeviceSelectionCancel();
}
diff --git a/core/java/android/companion/ICompanionDeviceManager.aidl b/core/java/android/companion/ICompanionDeviceManager.aidl
index 7798406..d395208 100644
--- a/core/java/android/companion/ICompanionDeviceManager.aidl
+++ b/core/java/android/companion/ICompanionDeviceManager.aidl
@@ -16,8 +16,10 @@
package android.companion;
+import android.app.PendingIntent;
import android.companion.IFindDeviceCallback;
import android.companion.AssociationRequest;
+import android.content.ComponentName;
/**
* Interface for communication with the core companion device manager service.
@@ -32,7 +34,6 @@
List<String> getAssociations(String callingPackage, int userId);
void disassociate(String deviceMacAddress, String callingPackage);
- //TODO add these
-// boolean haveNotificationAccess(String packageName);
-// oneway void requestNotificationAccess(String packageName);
+ boolean hasNotificationAccess(in ComponentName component);
+ PendingIntent requestNotificationAccess(in ComponentName component);
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 18120c7..0adab1a 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2647,18 +2647,6 @@
public abstract ComponentName startForegroundServiceAsUser(Intent service, UserHandle user);
/**
- * Start a service directly into the "foreground service" state. Unlike {@link #startService},
- * this method can be used from within background operations like broadcast receivers
- * or scheduled jobs. The API entry point for this is in NotificationManager in order to
- * preserve appropriate public package layering.
- * @hide
- * @deprecated STOPSHIP remove in favor of two-step startForegroundService() + startForeground()
- */
- @Nullable
- public abstract ComponentName startServiceInForeground(Intent service,
- int id, Notification notification);
-
- /**
* Request that a given application service be stopped. If the service is
* not running, nothing happens. Otherwise it is stopped. Note that calls
* to startService() are not counted -- this stops the service no matter
@@ -2696,16 +2684,6 @@
public abstract ComponentName startServiceAsUser(Intent service, UserHandle user);
/**
- * @hide like {@link #startServiceInForeground(Intent, int, Notification)}
- * but for a specific user.
- * @deprecated STOPSHIP remove when trial API is turned off
- */
- @Deprecated
- @Nullable
- public abstract ComponentName startServiceInForegroundAsUser(Intent service,
- int id, Notification notification, UserHandle user);
-
- /**
* @hide like {@link #stopService(Intent)} but for a specific user.
*/
public abstract boolean stopServiceAsUser(Intent service, UserHandle user);
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 53b021c..b59fc3dd 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -649,13 +649,6 @@
return mBase.startForegroundService(service);
}
- /** @hide STOPSHIP remove when trial API is turned down */
- @Override
- public ComponentName startServiceInForeground(Intent service,
- int id, Notification notification) {
- return mBase.startServiceInForeground(service, id, notification);
- }
-
@Override
public boolean stopService(Intent name) {
return mBase.stopService(name);
@@ -673,13 +666,6 @@
return mBase.startForegroundServiceAsUser(service, user);
}
- /** @hide STOPSHIP removed when trial API is turned down */
- @Override
- public ComponentName startServiceInForegroundAsUser(Intent service,
- int id, Notification notification, UserHandle user) {
- return mBase.startServiceInForegroundAsUser(service, id, notification, user);
- }
-
/** @hide */
@Override
public boolean stopServiceAsUser(Intent name, UserHandle user) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 0da4f8d..5ca4fa3 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3424,8 +3424,10 @@
/**
* Deprecated - use ACTION_FACTORY_RESET instead.
+ * @hide
*/
@Deprecated
+ @SystemApi
public static final String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
/**
diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java
index 23e54ba..d64f018 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -1787,7 +1787,7 @@
sb.append(", mHasPartialTypes="); sb.append(mHasPartialTypes);
du.println(sb.toString());
}
- {
+ if (getAutoVerify()) {
sb.setLength(0);
sb.append(prefix); sb.append("AutoVerify="); sb.append(getAutoVerify());
du.println(sb.toString());
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 5f53e27..8b2809a 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -29,6 +29,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
import android.text.TextUtils;
import android.util.Printer;
import android.util.SparseArray;
@@ -41,6 +42,7 @@
import java.util.Arrays;
import java.util.Comparator;
import java.util.Objects;
+import java.util.UUID;
/**
* Information you can retrieve about a particular application. This
@@ -621,12 +623,17 @@
*/
public float maxAspectRatio;
+ /** @removed */
+ @Deprecated
+ public String volumeUuid;
+
/**
* UUID of the storage volume on which this application is being hosted. For
* apps hosted on the default internal storage at
- * {@link Environment#getDataDirectory()}, the UUID value is {@code null}.
+ * {@link Environment#getDataDirectory()}, the UUID value is
+ * {@link StorageManager#UUID_DEFAULT}.
*/
- public String volumeUuid;
+ public UUID storageUuid;
/** {@hide} */
public String scanSourceDir;
@@ -1134,6 +1141,7 @@
compatibleWidthLimitDp = orig.compatibleWidthLimitDp;
largestWidthLimitDp = orig.largestWidthLimitDp;
volumeUuid = orig.volumeUuid;
+ storageUuid = orig.storageUuid;
scanSourceDir = orig.scanSourceDir;
scanPublicSourceDir = orig.scanPublicSourceDir;
sourceDir = orig.sourceDir;
@@ -1195,7 +1203,7 @@
dest.writeInt(requiresSmallestWidthDp);
dest.writeInt(compatibleWidthLimitDp);
dest.writeInt(largestWidthLimitDp);
- dest.writeString(volumeUuid);
+ dest.writeUuid(storageUuid);
dest.writeString(scanSourceDir);
dest.writeString(scanPublicSourceDir);
dest.writeString(sourceDir);
@@ -1257,7 +1265,8 @@
requiresSmallestWidthDp = source.readInt();
compatibleWidthLimitDp = source.readInt();
largestWidthLimitDp = source.readInt();
- volumeUuid = source.readString();
+ storageUuid = source.readUuid();
+ volumeUuid = StorageManager.convert(storageUuid);
scanSourceDir = source.readString();
scanPublicSourceDir = source.readString();
sourceDir = source.readString();
diff --git a/core/java/android/content/pm/ChangedPackages.java b/core/java/android/content/pm/ChangedPackages.java
index 94b8a5d..78c057d 100644
--- a/core/java/android/content/pm/ChangedPackages.java
+++ b/core/java/android/content/pm/ChangedPackages.java
@@ -17,6 +17,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TestApi;
import android.content.Intent;
import android.os.Parcel;
import android.os.Parcelable;
@@ -26,6 +27,7 @@
/**
* Packages that have been changed since the last time they
* were requested.
+ * @see PackageManager#getChangedPackages(int)
*/
public final class ChangedPackages implements Parcelable {
/** The last known sequence number for these changes */
@@ -33,6 +35,7 @@
/** The names of the packages that have changed */
private final List<String> mPackageNames;
+ @TestApi
public ChangedPackages(int sequenceNumber, @NonNull List<String> packageNames) {
this.mSequenceNumber = sequenceNumber;
this.mPackageNames = packageNames;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 147df76..bbc942a 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -632,4 +632,6 @@
void deletePreloadsFileCache();
ComponentName getInstantAppResolverSettingsComponent();
+
+ ComponentName getInstantAppInstallerComponent();
}
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index c3bdde5..8ead0ec 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -22,6 +22,8 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.TestApi;
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
@@ -52,6 +54,8 @@
import android.util.DisplayMetrics;
import android.util.Log;
+import com.android.internal.util.Preconditions;
+
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -612,11 +616,20 @@
* null if the package isn't installed for the given user, or the target user
* is not enabled.
*/
- public ApplicationInfo getApplicationInfo(String packageName, @ApplicationInfoFlags int flags,
- UserHandle user) {
+ public ApplicationInfo getApplicationInfo(@NonNull String packageName,
+ @ApplicationInfoFlags int flags, @NonNull UserHandle user)
+ throws PackageManager.NameNotFoundException {
+ Preconditions.checkNotNull(packageName, "packageName");
+ Preconditions.checkNotNull(packageName, "user");
logErrorForInvalidProfileAccess(user);
try {
- return mService.getApplicationInfo(mContext.getPackageName(), packageName, flags, user);
+ final ApplicationInfo ai = mService
+ .getApplicationInfo(mContext.getPackageName(), packageName, flags, user);
+ if (ai == null) {
+ throw new NameNotFoundException("Package " + packageName + " not found for user "
+ + user.getIdentifier());
+ }
+ return ai;
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
@@ -1268,15 +1281,34 @@
* an {@link #ACTION_CONFIRM_PIN_SHORTCUT} or {@link #ACTION_CONFIRM_PIN_APPWIDGET} intent
* respectively to the default launcher app.
*
- * <p>Note the launcher may receive a request to pin a shortcut that is already pinned, because
- * the user may actually want to have multiple icons of the same shortcut on the launcher.
- * The launcher can tell this case by calling {@link ShortcutInfo#isPinned()} on the shortcut
- * returned by {@link #getShortcutInfo()}. In this case, calling {@link #accept()} is optional;
- * even if the launcher does not call it, the shortcut is already pinned. Also in this case,
- * the {@code options} argument to {@link #accept(Bundle)} will be ignored.
+ * <h3>Request of the {@link #REQUEST_TYPE_SHORTCUT} type.
*
- * <p>For AppWidget pin requests launcher should send back the appwidget id as an extra for
- * {@link #accept(Bundle)} as {@link android.appwidget.AppWidgetManager#EXTRA_APPWIDGET_ID}.
+ * <p>A {@link #REQUEST_TYPE_SHORTCUT} request represents a request to pin a
+ * {@link ShortcutInfo}. If the launcher accepts a request, call {@link #accept()},
+ * or {@link #accept(Bundle)} with a null or empty Bundle. No options are defined for
+ * pin-shortcuts requests.
+ *
+ * <p>{@link #getShortcutInfo()} always returns a non-null {@link ShortcutInfo} for this type.
+ *
+ * <p>The launcher may receive a request with a {@link ShortcutInfo} that is already pinned, in
+ * which case {@link ShortcutInfo#isPinned()} returns true. This means the user wants to create
+ * another pinned shortcut for a shortcut that's already pinned. If the launcher accepts it,
+ * {@link #accept()} must still be called even though the shortcut is already pinned, and
+ * create a new pinned shortcut icon for it.
+ *
+ * <p>See also {@link ShortcutManager} for more details.
+ *
+ * <h3>Request of the {@link #REQUEST_TYPE_APPWIDGET} type.
+ *
+ * <p>A {@link #REQUEST_TYPE_SHORTCUT} request represents a request to pin a
+ * an AppWidget. If the launcher accepts a request, call {@link #accept(Bundle)} with
+ * the appwidget integer ID set to the
+ * {@link android.appwidget.AppWidgetManager#EXTRA_APPWIDGET_ID} extra.
+ *
+ * <p>{@link #getAppWidgetProviderInfo(Context)} always returns a non-null
+ * {@link AppWidgetProviderInfo} for this type.
+ *
+ * <p>See also {@link AppWidgetManager} for more details.
*
* @see #EXTRA_PIN_ITEM_REQUEST
* @see #getPinItemRequest(Intent)
@@ -1306,8 +1338,9 @@
}
/**
- * Represents the type of a request. For now {@link #REQUEST_TYPE_SHORTCUT} is the only
- * valid type.
+ * Represents the type of a request, which is one of the {@code REQUEST_TYPE_} constants.
+ *
+ * @return one of the {@code REQUEST_TYPE_} constants.
*/
@RequestType
public int getRequestType() {
@@ -1315,8 +1348,12 @@
}
/**
- * {@link ShortcutInfo} sent by the requesting app. Always non-null for a
- * {@link #REQUEST_TYPE_SHORTCUT} request.
+ * {@link ShortcutInfo} sent by the requesting app.
+ * Always non-null for a {@link #REQUEST_TYPE_SHORTCUT} request, and always null for a
+ * different request type.
+ *
+ * @return requested {@link ShortcutInfo} when a request is of the
+ * {@link #REQUEST_TYPE_SHORTCUT} type. Null otherwise.
*/
@Nullable
public ShortcutInfo getShortcutInfo() {
@@ -1328,8 +1365,12 @@
}
/**
- * {@link AppWidgetProviderInfo} sent by the requesting app. Always non-null for a
- * {@link #REQUEST_TYPE_APPWIDGET} request.
+ * {@link AppWidgetProviderInfo} sent by the requesting app.
+ * Always non-null for a {@link #REQUEST_TYPE_APPWIDGET} request, and always null for a
+ * different request type.
+ *
+ * @return requested {@link AppWidgetProviderInfo} when a request is of the
+ * {@link #REQUEST_TYPE_APPWIDGET} type. Null otherwise.
*/
@Nullable
public AppWidgetProviderInfo getAppWidgetProviderInfo(Context context) {
@@ -1347,6 +1388,11 @@
/**
* Any extras sent by the requesting app.
+ *
+ * @return For a shortcut request, this method always return null. For an AppWidget
+ * request, this method returns the extras passed to the
+ * {@link android.appwidget.AppWidgetManager#requestPinAppWidget(
+ * ComponentName, Bundle, PendingIntent)} API. See {@link AppWidgetManager} for details.
*/
@Nullable
public Bundle getExtras() {
@@ -1358,8 +1404,9 @@
}
/**
- * Return {@code TRUE} if a request is valid -- i.e. {@link #accept(Bundle)} has not been
- * called yet.
+ * Return whether a request is still valid.
+ *
+ * @return {@code TRUE} if a request is valid and {@link #accept(Bundle)} may be called.
*/
public boolean isValid() {
try {
@@ -1371,6 +1418,12 @@
/**
* Called by the receiving launcher app when the user accepts the request.
+ *
+ * @param options must be set for a {@link #REQUEST_TYPE_APPWIDGET} request.
+ *
+ * @return {@code TRUE} if the shortcut or the AppWidget has actually been pinned.
+ * {@code FALSE} if the item hasn't been pinned, for example, because the request had
+ * already been canceled, in which case the launcher must not pin the requested item.
*/
public boolean accept(@Nullable Bundle options) {
try {
@@ -1381,7 +1434,11 @@
}
/**
- * Same as as {@link #accept(Bundle)} with no options.
+ * Called by the receiving launcher app when the user accepts the request, with no options.
+ *
+ * @return {@code TRUE} if the shortcut or the AppWidget has actually been pinned.
+ * {@code FALSE} if the item hasn't been pinned, for example, because the request had
+ * already been canceled, in which case the launcher must not pin the requested item.
*/
public boolean accept() {
return accept(/* options= */ null);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e5c8f0d..178b967 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -54,6 +54,7 @@
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
+import android.provider.Settings;
import android.util.AndroidException;
import android.util.Log;
@@ -3983,6 +3984,8 @@
* <p>If no packages have been changed, returns <code>null</code>.
* <p>The sequence number starts at <code>0</code> and is
* reset every boot.
+ * @param sequenceNumber The first sequence number for which to retrieve package changes.
+ * @see Settings.Global#BOOT_COUNT
*/
public abstract @Nullable ChangedPackages getChangedPackages(
@IntRange(from=0) int sequenceNumber);
@@ -6256,18 +6259,18 @@
/**
* Checks whether the calling package is allowed to request package installs through package
- * installer. Apps are encouraged to call this api before launching the package installer via
+ * installer. Apps are encouraged to call this API before launching the package installer via
* intent {@link android.content.Intent#ACTION_INSTALL_PACKAGE}. Starting from Android O, the
* user can explicitly choose what external sources they trust to install apps on the device.
- * If this api returns false, the install request will be blocked by the package installer and
+ * If this API returns false, the install request will be blocked by the package installer and
* a dialog will be shown to the user with an option to launch settings to change their
* preference. An application must target Android O or higher and declare permission
- * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES} in order to use this api.
+ * {@link android.Manifest.permission#REQUEST_INSTALL_PACKAGES} in order to use this API.
*
* @return true if the calling package is trusted by the user to request install packages on
* the device, false otherwise.
- * @see {@link android.content.Intent#ACTION_INSTALL_PACKAGE}
- * @see {@link android.provider.Settings#ACTION_MANAGE_UNKNOWN_APP_SOURCES}
+ * @see android.content.Intent#ACTION_INSTALL_PACKAGE
+ * @see android.provider.Settings#ACTION_MANAGE_UNKNOWN_APP_SOURCES
*/
public abstract boolean canRequestPackageInstalls();
@@ -6275,9 +6278,19 @@
* Return the {@link ComponentName} of the activity providing Settings for the Instant App
* resolver.
*
- * @see {@link android.content.intent#ACTION_INSTANT_APP_RESOLVER_SETTINGS}
+ * @see {@link android.content.Intent#ACTION_INSTANT_APP_RESOLVER_SETTINGS}
* @hide
*/
@SystemApi
public abstract ComponentName getInstantAppResolverSettingsComponent();
+
+ /**
+ * Return the {@link ComponentName} of the activity responsible for installing instant
+ * applications.
+ *
+ * @see {@link android.content.Intent#ACTION_INSTALL_INSTANT_APP_PACKAGE}
+ * @hide
+ */
+ @SystemApi
+ public abstract ComponentName getInstantAppInstallerComponent();
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 430d8b1..1f78bff 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -64,8 +64,10 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.PatternMatcher;
+import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
+import android.os.storage.StorageManager;
import android.system.ErrnoException;
import android.system.OsConstants;
import android.system.StructStat;
@@ -116,6 +118,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.zip.ZipEntry;
@@ -2111,6 +2114,12 @@
pkg.mIsStaticOverlay = sa.getBoolean(
com.android.internal.R.styleable.AndroidManifestResourceOverlay_isStatic,
false);
+ final String propName = sa.getString(
+ com.android.internal.R.styleable
+ .AndroidManifestResourceOverlay_requiredSystemPropertyName);
+ final String propValue = sa.getString(
+ com.android.internal.R.styleable
+ .AndroidManifestResourceOverlay_requiredSystemPropertyValue);
sa.recycle();
if (pkg.mOverlayTarget == null) {
@@ -2118,15 +2127,22 @@
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
+
if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) {
outError[0] = "<overlay> priority must be between 0 and 9999";
mParseError =
PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return null;
}
- if (pkg.mIsStaticOverlay) {
- // TODO(b/35742444): Need to support selection method based on a package name.
+
+ // check to see if overlay should be excluded based on system property condition
+ if (!checkOverlayRequiredSystemProperty(propName, propValue)) {
+ Slog.i(TAG, "Skipping target and overlay pair " + pkg.mOverlayTarget + " and "
+ + pkg.baseCodePath+ ": overlay ignored due to required system property: "
+ + propName + " with value: " + propValue);
+ return null;
}
+
XmlUtils.skipCurrentTag(parser);
} else if (tagName.equals(TAG_KEY_SETS)) {
@@ -2531,6 +2547,25 @@
return pkg;
}
+ private boolean checkOverlayRequiredSystemProperty(String propName, String propValue) {
+
+ if (TextUtils.isEmpty(propName) || TextUtils.isEmpty(propValue)) {
+ if (!TextUtils.isEmpty(propName) || !TextUtils.isEmpty(propValue)) {
+ // malformed condition - incomplete
+ Slog.w(TAG, "Disabling overlay - incomplete property :'" + propName
+ + "=" + propValue + "' - require both requiredSystemPropertyName"
+ + " AND requiredSystemPropertyValue to be specified.");
+ return false;
+ }
+ // no valid condition set - so no exclusion criteria, overlay will be included.
+ return true;
+ }
+
+ // check property value - make sure it is both set and equal to expected value
+ final String currValue = SystemProperties.get(propName);
+ return (currValue != null && currValue.equals(propValue));
+ }
+
/**
* This is a pre-density application which will get scaled - instead of being pixel perfect.
* This type of application is not resizable.
@@ -5741,11 +5776,14 @@
}
public void setApplicationVolumeUuid(String volumeUuid) {
+ final UUID storageUuid = StorageManager.convert(volumeUuid);
this.applicationInfo.volumeUuid = volumeUuid;
+ this.applicationInfo.storageUuid = storageUuid;
if (childPackages != null) {
final int packageCount = childPackages.size();
for (int i = 0; i < packageCount; i++) {
childPackages.get(i).applicationInfo.volumeUuid = volumeUuid;
+ childPackages.get(i).applicationInfo.storageUuid = storageUuid;
}
}
}
diff --git a/core/java/android/content/pm/SharedLibraryInfo.java b/core/java/android/content/pm/SharedLibraryInfo.java
index d79deb2..0ad4874 100644
--- a/core/java/android/content/pm/SharedLibraryInfo.java
+++ b/core/java/android/content/pm/SharedLibraryInfo.java
@@ -16,11 +16,14 @@
package android.content.pm;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
import java.util.List;
@@ -31,28 +34,37 @@
* static - updatable non backwards-compatible emulating static linking.
*/
public final class SharedLibraryInfo implements Parcelable {
+
+ /** @hide */
+ @IntDef(
+ flag = true,
+ value = {
+ TYPE_BUILTIN,
+ TYPE_DYNAMIC,
+ TYPE_STATIC,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface Type{}
+
/**
* Shared library type: this library is a part of the OS
* and cannot be updated or uninstalled.
- * @hide
*/
- public static final int TYPE_BUILTIN = 0x1<<0;
+ public static final int TYPE_BUILTIN = 0;
/**
* Shared library type: this library is backwards-compatible, can
* be updated, and updates can be uninstalled. Clients link against
* the latest version of the library.
- * @hide
*/
- public static final int TYPE_DYNAMIC = 0x1<<1;
+ public static final int TYPE_DYNAMIC = 1;
/**
* Shared library type: this library is <strong>not</strong> backwards
* -compatible, can be updated and updates can be uninstalled. Clients
* link against a specific version of the library.
- * @hide
*/
- public static final int TYPE_STATIC = 0x1<<2;
+ public static final int TYPE_STATIC = 2;
/**
* Constant for referring to an undefined version.
@@ -60,8 +72,10 @@
public static final int VERSION_UNDEFINED = -1;
private final String mName;
+
+ // TODO: Make long when we change the paltform to use longs
private final int mVersion;
- private final int mType;
+ private final @Type int mType;
private final VersionedPackage mDeclaringPackage;
private final List<VersionedPackage> mDependentPackages;
@@ -90,13 +104,18 @@
parcel.readParcelable(null), parcel.readArrayList(null));
}
- /** @hide */
- public int getType() {
+ /**
+ * Gets the type of this library.
+ *
+ * @return The library type.
+ */
+ public @Type int getType() {
return mType;
}
/**
- * Gets the library name.
+ * Gets the library name an app defines in its manifest
+ * to depend on the library.
*
* @return The name.
*/
@@ -105,40 +124,36 @@
}
/**
- * Gets the version of the library. For {@link #isStatic()} static} libraries
- * this is the declared version and for {@link #isDynamic()} dynamic} and
- * {@link #isBuiltin()} builtin} it is {@link #VERSION_UNDEFINED} as these
+ * Gets the version of the library. For {@link #TYPE_STATIC static} libraries
+ * this is the declared version and for {@link #TYPE_DYNAMIC dynamic} and
+ * {@link #TYPE_BUILTIN builtin} it is {@link #VERSION_UNDEFINED} as these
* are not versioned.
*
* @return The version.
*/
- public @IntRange(from = -1) int getVersion() {
+ public @IntRange(from = -1) long getVersion() {
return mVersion;
}
/**
- * @return whether this library is builtin which means that it
- * is a part of the OS and cannot be updated or uninstalled.
+ * @hide
+ * @removed
*/
public boolean isBuiltin() {
return mType == TYPE_BUILTIN;
}
/**
- * @return whether this library is dynamic which means that it
- * is backwards-compatible, can be updated, and updates can be
- * uninstalled. Clients link against the latest version of the
- * library.
+ * @hide
+ * @removed
*/
public boolean isDynamic() {
return mType == TYPE_DYNAMIC;
}
/**
- * @return whether this library is dynamic which means that it
- * is <strong>not</strong> backwards-compatible, can be updated
- * and updates can be uninstalled. Clients link against a specific
- * version of the library.
+ * @hide
+ * @removed
*/
public boolean isStatic() {
return mType == TYPE_STATIC;
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index f48afb5..88bb1a4 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -56,12 +56,13 @@
// Reset the assets, which may have changed due to configuration changes
// or further resource loading.
attrs.mAssets = res.getAssets();
+ attrs.mMetrics = res.getDisplayMetrics();
attrs.resize(len);
return attrs;
}
private final Resources mResources;
- private final DisplayMetrics mMetrics;
+ private DisplayMetrics mMetrics;
private AssetManager mAssets;
private boolean mRecycled;
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index e8e989f..f61032e 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -290,7 +290,8 @@
cameraId,
callback,
handler,
- characteristics);
+ characteristics,
+ mContext.getApplicationInfo().targetSdkVersion);
ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index e75b375..ab87f15 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -36,6 +36,7 @@
import android.hardware.camera2.utils.SubmitInfo;
import android.hardware.camera2.utils.SurfaceUtils;
import android.hardware.ICameraService;
+import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -118,6 +119,8 @@
private CameraCaptureSessionCore mCurrentSession;
private int mNextSessionId = 0;
+ private final int mAppTargetSdkVersion;
+
// Runnables for all state transitions, except error, which needs the
// error code argument
@@ -234,7 +237,7 @@
};
public CameraDeviceImpl(String cameraId, StateCallback callback, Handler handler,
- CameraCharacteristics characteristics) {
+ CameraCharacteristics characteristics, int appTargetSdkVersion) {
if (cameraId == null || callback == null || handler == null || characteristics == null) {
throw new IllegalArgumentException("Null argument given");
}
@@ -242,6 +245,7 @@
mDeviceCallback = callback;
mDeviceHandler = handler;
mCharacteristics = characteristics;
+ mAppTargetSdkVersion = appTargetSdkVersion;
final int MAX_TAG_LEN = 23;
String tag = String.format("CameraDevice-JV-%s", mCameraId);
@@ -671,6 +675,16 @@
}
}
+ private void overrideEnableZsl(CameraMetadataNative request, boolean newValue) {
+ Boolean enableZsl = request.get(CaptureRequest.CONTROL_ENABLE_ZSL);
+ if (enableZsl == null) {
+ // If enableZsl is not available, don't override.
+ return;
+ }
+
+ request.set(CaptureRequest.CONTROL_ENABLE_ZSL, newValue);
+ }
+
@Override
public CaptureRequest.Builder createCaptureRequest(int templateType)
throws CameraAccessException {
@@ -681,6 +695,13 @@
templatedRequest = mRemoteDevice.createDefaultRequest(templateType);
+ // If app target SDK is older than O, or it's not a still capture template, enableZsl
+ // must be false in the default request.
+ if (mAppTargetSdkVersion < Build.VERSION_CODES.O ||
+ templateType != TEMPLATE_STILL_CAPTURE) {
+ overrideEnableZsl(templatedRequest, false);
+ }
+
CaptureRequest.Builder builder = new CaptureRequest.Builder(
templatedRequest, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java
index 0448221..3c6baa7 100644
--- a/core/java/android/metrics/LogMaker.java
+++ b/core/java/android/metrics/LogMaker.java
@@ -54,7 +54,11 @@
/* Deserialize from the eventlog */
public LogMaker(Object[] items) {
- deserialize(items);
+ if (items != null) {
+ deserialize(items);
+ } else {
+ setCategory(MetricsEvent.VIEW_UNKNOWN);
+ }
}
/** @param category to replace the existing setting. */
@@ -94,6 +98,16 @@
}
/**
+ * Set event latency.
+ *
+ * @hide // TODO Expose in the future? Too late for O.
+ */
+ public LogMaker setLatency(long milliseconds) {
+ entries.put(MetricsEvent.NOTIFICATION_SINCE_CREATE_MILLIS, milliseconds);
+ return this;
+ }
+
+ /**
* This will be set by the system when the log is persisted.
* Client-supplied values will be ignored.
*
@@ -363,13 +377,13 @@
*/
public void deserialize(Object[] items) {
int i = 0;
- while (i < items.length) {
+ while (items != null && i < items.length) {
Object key = items[i++];
Object value = i < items.length ? items[i++] : null;
if (key instanceof Integer) {
entries.put((Integer) key, value);
} else {
- Log.i(TAG, "Invalid key " + key.toString());
+ Log.i(TAG, "Invalid key " + (key == null ? "null" : key.toString()));
}
}
}
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index f8702e2..375b7ee 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -245,6 +245,7 @@
*
* @param socket a stream socket
* @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform.
+ * @hide
*/
public void applyTransportModeTransform(Socket socket, IpSecTransform transform)
throws IOException {
@@ -262,6 +263,7 @@
*
* @param socket a datagram socket
* @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform.
+ * @hide
*/
public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
throws IOException {
@@ -284,7 +286,7 @@
* address associated with that transform will throw an IOException. In addition, if the
* IpSecTransform is later deactivated, the socket will throw an IOException on any calls to
* send() or receive() until the transform is removed from the socket by calling {@link
- * #removeTransportModeTransform(Socket, IpSecTransform)};
+ * #removeTransportModeTransform(FileDescriptor, IpSecTransform)};
*
* @param socket a socket file descriptor
* @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform.
@@ -316,8 +318,10 @@
*
* @param socket a socket that previously had a transform applied to it.
* @param transform the IPsec Transform that was previously applied to the given socket
+ * @hide
*/
- public void removeTransportModeTransform(Socket socket, IpSecTransform transform) {
+ public void removeTransportModeTransform(Socket socket, IpSecTransform transform)
+ throws IOException {
removeTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform);
}
@@ -330,8 +334,10 @@
*
* @param socket a socket that previously had a transform applied to it.
* @param transform the IPsec Transform that was previously applied to the given socket
+ * @hide
*/
- public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform) {
+ public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
+ throws IOException {
removeTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform);
}
@@ -345,7 +351,8 @@
* @param socket a socket file descriptor that previously had a transform applied to it.
* @param transform the IPsec Transform that was previously applied to the given socket
*/
- public void removeTransportModeTransform(FileDescriptor socket, IpSecTransform transform) {
+ public void removeTransportModeTransform(FileDescriptor socket, IpSecTransform transform)
+ throws IOException {
removeTransportModeTransform(new ParcelFileDescriptor(socket), transform);
}
@@ -419,7 +426,7 @@
*
* @param fd a file descriptor previously returned as a UDP Encapsulation socket.
*/
- public void close() {
+ public void close() throws IOException {
// TODO: Go close the socket
mCloseGuard.close();
}
diff --git a/core/java/android/net/MatchAllNetworkSpecifier.java b/core/java/android/net/MatchAllNetworkSpecifier.java
new file mode 100644
index 0000000..7aafc93
--- /dev/null
+++ b/core/java/android/net/MatchAllNetworkSpecifier.java
@@ -0,0 +1,80 @@
+/*
+ * 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.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * MatchAllNetworkSpecifier is a marker class used by NetworkFactory classes to indicate
+ * that they accept (match) any network specifier in requests.
+ *
+ * The class must never be used as part of a network request (those semantics aren't specified).
+ *
+ * @hide
+ */
+public final class MatchAllNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+ /**
+ * Utility method which verifies that the ns argument is not a MatchAllNetworkSpecifier and
+ * throws an IllegalArgumentException if it is.
+ */
+ public static void checkNotMatchAllNetworkSpecifier(NetworkSpecifier ns) {
+ if (ns instanceof MatchAllNetworkSpecifier) {
+ throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
+ }
+ }
+
+ public boolean satisfiedBy(NetworkSpecifier other) {
+ /*
+ * The method is called by a NetworkRequest to see if it is satisfied by a proposed
+ * network (e.g. as offered by a network factory). Since MatchAllNetweorkSpecifier must
+ * not be used in network requests this method should never be called.
+ */
+ throw new IllegalStateException(
+ "MatchAllNetworkSpecifier must not be used in NetworkRequests");
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof MatchAllNetworkSpecifier;
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ // Nothing to write.
+ }
+
+ public static final Parcelable.Creator<MatchAllNetworkSpecifier> CREATOR =
+ new Parcelable.Creator<MatchAllNetworkSpecifier>() {
+ public MatchAllNetworkSpecifier createFromParcel(Parcel in) {
+ return new MatchAllNetworkSpecifier();
+ }
+ public MatchAllNetworkSpecifier[] newArray(int size) {
+ return new MatchAllNetworkSpecifier[size];
+ }
+ };
+}
diff --git a/core/java/android/net/NetworkBadging.java b/core/java/android/net/NetworkBadging.java
index 4409d0a..b4ef695 100644
--- a/core/java/android/net/NetworkBadging.java
+++ b/core/java/android/net/NetworkBadging.java
@@ -56,7 +56,7 @@
*
* @param signalLevel The level returned by {@link WifiManager#calculateSignalLevel(int, int)}
* for a network. Must be between 0 and {@link WifiManager#RSSI_LEVELS}-1.
- * @param badging {@see ScoredNetwork#Badging}, retrieved from
+ * @param badging {@see NetworkBadging#Badging}, retrieved from
* {@link ScoredNetwork#calculateBadge(int)}.
* @param theme The theme for the current application, may be null.
* @return Drawable for the given icon
@@ -140,7 +140,7 @@
* <p>This badge should be displayed with the badge signal resource retrieved from
* {@link #getBadgedWifiSignalResource(int)}.
*
- * @param badging {@see ScoredNetwork#Badging} from {@link ScoredNetwork#calculateBadge(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.
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index a594bef..bf7207c 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -18,9 +18,12 @@
import android.os.Parcel;
import android.os.Parcelable;
-import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.BitUtils;
+import java.util.Objects;
+
/**
* This class represents the capabilities of a network. This is used both to specify
* needs to {@link ConnectivityManager} and when inspecting a network.
@@ -33,6 +36,8 @@
* all cellular based connections are metered and all Wi-Fi based connections are not.
*/
public final class NetworkCapabilities implements Parcelable {
+ private static final String TAG = "NetworkCapabilities";
+
/**
* @hide
*/
@@ -205,19 +210,6 @@
(1 << NET_CAPABILITY_FOREGROUND);
/**
- * Network specifier for factories which want to match any network specifier
- * (NS) in a request. Behavior:
- * <li>Empty NS in request matches any network factory NS</li>
- * <li>Empty NS in the network factory NS only matches a request with an
- * empty NS</li>
- * <li>"*" (this constant) NS in the network factory matches requests with
- * any NS</li>
- *
- * @hide
- */
- public static final String MATCH_ALL_REQUESTS_NETWORK_SPECIFIER = "*";
-
- /**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
* NetworkFactory / NetworkAgent model does not deal well with the situation where a
* capability's presence cannot be known in advance. If such a capability is requested, then we
@@ -239,7 +231,8 @@
* Capabilities that suggest that a network is restricted.
* {@see #maybeMarkCapabilitiesRestricted}.
*/
- private static final long RESTRICTED_CAPABILITIES =
+ @VisibleForTesting
+ /* package */ static final long RESTRICTED_CAPABILITIES =
(1 << NET_CAPABILITY_CBS) |
(1 << NET_CAPABILITY_DUN) |
(1 << NET_CAPABILITY_EIMS) |
@@ -250,6 +243,17 @@
(1 << NET_CAPABILITY_XCAP);
/**
+ * Capabilities that suggest that a network is unrestricted.
+ * {@see #maybeMarkCapabilitiesRestricted}.
+ */
+ @VisibleForTesting
+ /* package */ static final long UNRESTRICTED_CAPABILITIES =
+ (1 << NET_CAPABILITY_INTERNET) |
+ (1 << NET_CAPABILITY_MMS) |
+ (1 << NET_CAPABILITY_SUPL) |
+ (1 << NET_CAPABILITY_WIFI_P2P);
+
+ /**
* Adds the given capability to this {@code NetworkCapability} instance.
* Multiple capabilities may be applied sequentially. Note that when searching
* for a network to satisfy a request, all capabilities requested must be satisfied.
@@ -362,12 +366,16 @@
* @hide
*/
public void maybeMarkCapabilitiesRestricted() {
- // If all the capabilities are typically provided by restricted networks, conclude that this
- // network is restricted.
- if ((mNetworkCapabilities & ~(DEFAULT_CAPABILITIES | RESTRICTED_CAPABILITIES)) == 0 &&
- // Must have at least some restricted capabilities, otherwise a request for an
- // internet-less network will get marked restricted.
- (mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0) {
+ // Verify there aren't any unrestricted capabilities. If there are we say
+ // the whole thing is unrestricted.
+ final boolean hasUnrestrictedCapabilities =
+ ((mNetworkCapabilities & UNRESTRICTED_CAPABILITIES) != 0);
+
+ // Must have at least some restricted capabilities.
+ final boolean hasRestrictedCapabilities =
+ ((mNetworkCapabilities & RESTRICTED_CAPABILITIES) != 0);
+
+ if (hasRestrictedCapabilities && !hasUnrestrictedCapabilities) {
removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
}
}
@@ -579,63 +587,56 @@
this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps);
}
- private String mNetworkSpecifier;
+ private NetworkSpecifier mNetworkSpecifier = null;
+
/**
* Sets the optional bearer specific network specifier.
* This has no meaning if a single transport is also not specified, so calling
* this without a single transport set will generate an exception, as will
* subsequently adding or removing transports after this is set.
* </p>
- * The interpretation of this {@code String} is bearer specific and bearers that use
- * it should document their particulars. For example, Bluetooth may use some sort of
- * device id while WiFi could used SSID and/or BSSID. Cellular may use carrier SPN (name)
- * or Subscription ID.
*
- * @param networkSpecifier An {@code String} of opaque format used to specify the bearer
- * specific network specifier where the bearer has a choice of
- * networks.
+ * @param networkSpecifier A concrete, parcelable framework class that extends
+ * NetworkSpecifier.
* @return This NetworkCapabilities instance, to facilitate chaining.
* @hide
*/
- public NetworkCapabilities setNetworkSpecifier(String networkSpecifier) {
- if (TextUtils.isEmpty(networkSpecifier) == false && Long.bitCount(mTransportTypes) != 1) {
+ public NetworkCapabilities setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
+ if (networkSpecifier != null && Long.bitCount(mTransportTypes) != 1) {
throw new IllegalStateException("Must have a single transport specified to use " +
"setNetworkSpecifier");
}
+
mNetworkSpecifier = networkSpecifier;
+
return this;
}
/**
* Gets the optional bearer specific network specifier.
*
- * @return The optional {@code String} specifying the bearer specific network specifier.
- * See {@link #setNetworkSpecifier}.
+ * @return The optional {@link NetworkSpecifier} specifying the bearer specific network
+ * specifier. See {@link #setNetworkSpecifier}.
* @hide
*/
- public String getNetworkSpecifier() {
+ public NetworkSpecifier getNetworkSpecifier() {
return mNetworkSpecifier;
}
private void combineSpecifiers(NetworkCapabilities nc) {
- String otherSpecifier = nc.getNetworkSpecifier();
- if (TextUtils.isEmpty(otherSpecifier)) return;
- if (TextUtils.isEmpty(mNetworkSpecifier) == false) {
+ if (mNetworkSpecifier != null && !mNetworkSpecifier.equals(nc.mNetworkSpecifier)) {
throw new IllegalStateException("Can't combine two networkSpecifiers");
}
- setNetworkSpecifier(otherSpecifier);
+ setNetworkSpecifier(nc.mNetworkSpecifier);
}
+
private boolean satisfiedBySpecifier(NetworkCapabilities nc) {
- return (TextUtils.isEmpty(mNetworkSpecifier) ||
- mNetworkSpecifier.equals(nc.mNetworkSpecifier) ||
- MATCH_ALL_REQUESTS_NETWORK_SPECIFIER.equals(nc.mNetworkSpecifier));
+ return mNetworkSpecifier == null || mNetworkSpecifier.satisfiedBy(nc.mNetworkSpecifier)
+ || nc.mNetworkSpecifier instanceof MatchAllNetworkSpecifier;
}
+
private boolean equalsSpecifier(NetworkCapabilities nc) {
- if (TextUtils.isEmpty(mNetworkSpecifier)) {
- return TextUtils.isEmpty(nc.mNetworkSpecifier);
- } else {
- return mNetworkSpecifier.equals(nc.mNetworkSpecifier);
- }
+ return Objects.equals(mNetworkSpecifier, nc.mNetworkSpecifier);
}
/**
@@ -797,7 +798,7 @@
((int)(mTransportTypes >> 32) * 7) +
(mLinkUpBandwidthKbps * 11) +
(mLinkDownBandwidthKbps * 13) +
- (TextUtils.isEmpty(mNetworkSpecifier) ? 0 : mNetworkSpecifier.hashCode() * 17) +
+ Objects.hashCode(mNetworkSpecifier) * 17 +
(mSignalStrength * 19));
}
@@ -811,7 +812,7 @@
dest.writeLong(mTransportTypes);
dest.writeInt(mLinkUpBandwidthKbps);
dest.writeInt(mLinkDownBandwidthKbps);
- dest.writeString(mNetworkSpecifier);
+ dest.writeParcelable((Parcelable) mNetworkSpecifier, flags);
dest.writeInt(mSignalStrength);
}
@@ -825,7 +826,7 @@
netCap.mTransportTypes = in.readLong();
netCap.mLinkUpBandwidthKbps = in.readInt();
netCap.mLinkDownBandwidthKbps = in.readInt();
- netCap.mNetworkSpecifier = in.readString();
+ netCap.mNetworkSpecifier = in.readParcelable(null);
netCap.mSignalStrength = in.readInt();
return netCap;
}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index cb78009..95a8bb4 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import java.util.Objects;
@@ -259,10 +260,27 @@
* networks.
*/
public Builder setNetworkSpecifier(String networkSpecifier) {
- if (NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER.equals(networkSpecifier)) {
- throw new IllegalArgumentException("Invalid network specifier - must not be '"
- + NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER + "'");
- }
+ /*
+ * A StringNetworkSpecifier does not accept null or empty ("") strings. When network
+ * specifiers were strings a null string and an empty string were considered equivalent.
+ * Hence no meaning is attached to a null or empty ("") string.
+ */
+ return setNetworkSpecifier(TextUtils.isEmpty(networkSpecifier) ? null
+ : new StringNetworkSpecifier(networkSpecifier));
+ }
+
+ /**
+ * Sets the optional bearer specific network specifier.
+ * This has no meaning if a single transport is also not specified, so calling
+ * this without a single transport set will generate an exception, as will
+ * subsequently adding or removing transports after this is set.
+ * </p>
+ *
+ * @param networkSpecifier A concrete, parcelable framework class that extends
+ * NetworkSpecifier.
+ */
+ public Builder setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
+ MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(networkSpecifier);
mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
return this;
}
diff --git a/core/java/android/net/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java
new file mode 100644
index 0000000..87a2b05
--- /dev/null
+++ b/core/java/android/net/NetworkSpecifier.java
@@ -0,0 +1,36 @@
+/*
+ * 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.net;
+
+/**
+ * Describes specific properties of a network for use in a {@link NetworkRequest}.
+ *
+ * Applications cannot instantiate this class by themselves, but can obtain instances of
+ * subclasses of this class via other APIs.
+ */
+public abstract class NetworkSpecifier {
+ /** @hide */
+ public NetworkSpecifier() {}
+
+ /**
+ * Returns true if a request with this {@link NetworkSpecifier} is satisfied by a network
+ * with the given NetworkSpecifier.
+ *
+ * @hide
+ */
+ public abstract boolean satisfiedBy(NetworkSpecifier other);
+}
diff --git a/core/java/android/net/ScoredNetwork.java b/core/java/android/net/ScoredNetwork.java
index a664a8b..666da0a 100644
--- a/core/java/android/net/ScoredNetwork.java
+++ b/core/java/android/net/ScoredNetwork.java
@@ -73,29 +73,6 @@
/** A {@link NetworkKey} uniquely identifying this network. */
public final NetworkKey networkKey;
- // TODO(b/35323372): Delete these once external references are switched.
- /** @deprecated Use {@link NetworkBadging#Badging} instead. */
- @Deprecated
- @IntDef({BADGING_NONE, BADGING_SD, BADGING_HD, BADGING_4K})
- @Retention(RetentionPolicy.SOURCE)
- public @interface Badging {}
-
- /** @deprecated Use {@link NetworkBadging#BADGING_NONE} instead. */
- @Deprecated
- public static final int BADGING_NONE = 0;
-
- /** @deprecated Use {@link NetworkBadging#BADGING_SD} instead. */
- @Deprecated
- public static final int BADGING_SD = 10;
-
- /** @deprecated Use {@link NetworkBadging#BADGING_HD} instead. */
- @Deprecated
- public static final int BADGING_HD = 20;
-
- /** @deprecated Use {@link NetworkBadging#BADGING_4K} instead. */
- @Deprecated
- public static final int BADGING_4K = 30;
-
/**
* The {@link RssiCurve} representing the scores for this network based on the RSSI.
*
diff --git a/core/java/android/net/StringNetworkSpecifier.java b/core/java/android/net/StringNetworkSpecifier.java
new file mode 100644
index 0000000..cb7f6bf
--- /dev/null
+++ b/core/java/android/net/StringNetworkSpecifier.java
@@ -0,0 +1,79 @@
+/*
+ * 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.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/** @hide */
+public final class StringNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+ /**
+ * Arbitrary string used to pass (additional) information to the network factory.
+ */
+ public final String specifier;
+
+ public StringNetworkSpecifier(String specifier) {
+ Preconditions.checkStringNotEmpty(specifier);
+ this.specifier = specifier;
+ }
+
+ @Override
+ public boolean satisfiedBy(NetworkSpecifier other) {
+ return equals(other);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof StringNetworkSpecifier)) return false;
+ return TextUtils.equals(specifier, ((StringNetworkSpecifier) o).specifier);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hashCode(specifier);
+ }
+
+ @Override
+ public String toString() {
+ return specifier;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(specifier);
+ }
+
+ public static final Parcelable.Creator<StringNetworkSpecifier> CREATOR =
+ new Parcelable.Creator<StringNetworkSpecifier>() {
+ public StringNetworkSpecifier createFromParcel(Parcel in) {
+ return new StringNetworkSpecifier(in.readString());
+ }
+ public StringNetworkSpecifier[] newArray(int size) {
+ return new StringNetworkSpecifier[size];
+ }
+ };
+}
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 15bd175..ff0bc69 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -16,9 +16,15 @@
package android.os;
+import android.util.ExceptionUtils;
import android.util.Log;
import android.util.Slog;
+
import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.FunctionalUtils;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
+
import libcore.io.IoUtils;
import java.io.FileDescriptor;
@@ -26,7 +32,6 @@
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.lang.reflect.Modifier;
-import java.util.function.Supplier;
/**
* Base class for a remotable object, the core part of a lightweight
@@ -251,14 +256,23 @@
* Convenience method for running the provided action enclosed in
* {@link #clearCallingIdentity}/{@link #restoreCallingIdentity}
*
+ * Any exception thrown by the given action will be caught and rethrown after the call to
+ * {@link #restoreCallingIdentity}
+ *
* @hide
*/
- public static final void withCleanCallingIdentity(Runnable action) {
+ public static final void withCleanCallingIdentity(ThrowingRunnable action) {
long callingIdentity = clearCallingIdentity();
+ Throwable throwableToPropagate = null;
try {
action.run();
+ } catch (Throwable throwable) {
+ throwableToPropagate = throwable;
} finally {
restoreCallingIdentity(callingIdentity);
+ if (throwableToPropagate != null) {
+ throw ExceptionUtils.propagate(throwableToPropagate);
+ }
}
}
@@ -266,14 +280,24 @@
* Convenience method for running the provided action enclosed in
* {@link #clearCallingIdentity}/{@link #restoreCallingIdentity} returning the result
*
+ * Any exception thrown by the given action will be caught and rethrown after the call to
+ * {@link #restoreCallingIdentity}
+ *
* @hide
*/
- public static final <T> T withCleanCallingIdentity(Supplier<T> action) {
+ public static final <T> T withCleanCallingIdentity(ThrowingSupplier<T> action) {
long callingIdentity = clearCallingIdentity();
+ Throwable throwableToPropagate = null;
try {
return action.get();
+ } catch (Throwable throwable) {
+ throwableToPropagate = throwable;
+ return null; // overridden by throwing in finally block
} finally {
restoreCallingIdentity(callingIdentity);
+ if (throwableToPropagate != null) {
+ throw ExceptionUtils.propagate(throwableToPropagate);
+ }
}
}
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 9b5ff29..167c46d 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -25,6 +25,7 @@
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
+import java.util.UUID;
/**
* A mapping from String keys to various {@link Parcelable} values.
@@ -476,6 +477,18 @@
}
/**
+ * Inserts a UUID value into the mapping of this Bundle, replacing
+ * any existing value for the given key. Either key or value may be null.
+ *
+ * @param key a String, or null
+ * @param value a UUID object, or null
+ */
+ public void putUuid(@Nullable String key, @Nullable UUID value) {
+ unparcel();
+ mMap.put(key, value);
+ }
+
+ /**
* Inserts an array of Parcelable values into the mapping of this Bundle,
* replacing any existing value for the given key. Either key or value may
* be null.
@@ -858,6 +871,26 @@
* value is explicitly associated with the key.
*
* @param key a String, or null
+ * @return a UUID value, or null
+ */
+ @Nullable
+ public UUID getUuid(@Nullable String key) {
+ unparcel();
+ final Object o = mMap.get(key);
+ try {
+ return (UUID) o;
+ } catch (ClassCastException e) {
+ typeWarning(key, o, "UUID", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns the value associated with the given key, or null if
+ * no mapping of the desired type exists for the given key or a null
+ * value is explicitly associated with the key.
+ *
+ * @param key a String, or null
* @return a Bundle value, or null
*/
@Nullable
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index c3836a3..c1647c7 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -16,8 +16,6 @@
package android.os;
-import android.annotation.IntegerRes;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -29,6 +27,9 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import dalvik.annotation.optimization.FastNative;
+import dalvik.system.VMRuntime;
+
import libcore.util.SneakyThrow;
import java.io.ByteArrayInputStream;
@@ -49,9 +50,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
-
-import dalvik.annotation.optimization.FastNative;
-import dalvik.system.VMRuntime;
+import java.util.UUID;
/**
* Container for a message (data and object references) that can
@@ -243,6 +242,7 @@
private static final int VAL_SIZE = 26;
private static final int VAL_SIZEF = 27;
private static final int VAL_DOUBLEARRAY = 28;
+ private static final int VAL_UUID = 29;
// The initial int32 in a Binder call's reply Parcel header:
// Keep these in sync with libbinder's binder/Status.h.
@@ -831,6 +831,15 @@
}
/**
+ * Flatten a UUID into the parcel at the current dataPosition(),
+ * growing dataCapacity() if needed.
+ */
+ public final void writeUuid(UUID val) {
+ writeLong(val.getMostSignificantBits());
+ writeLong(val.getLeastSignificantBits());
+ }
+
+ /**
* Flatten a List into the parcel at the current dataPosition(), growing
* dataCapacity() if needed. The List values are written using
* {@link #writeValue} and must follow the specification there.
@@ -1678,6 +1687,9 @@
} else if (v instanceof double[]) {
writeInt(VAL_DOUBLEARRAY);
writeDoubleArray((double[]) v);
+ } else if (v instanceof UUID) {
+ writeInt(VAL_UUID);
+ writeUuid((UUID) v);
} else {
Class<?> clazz = v.getClass();
if (clazz.isArray() && clazz.getComponentType() == Object.class) {
@@ -2182,6 +2194,13 @@
}
/**
+ * Read a UUID from the parcel at the current dataPosition().
+ */
+ public final UUID readUuid() {
+ return new UUID(readLong(), readLong());
+ }
+
+ /**
* Read and return a byte[] object from the parcel.
*/
public final byte[] createByteArray() {
@@ -2731,6 +2750,9 @@
case VAL_DOUBLEARRAY:
return createDoubleArray();
+ case VAL_UUID:
+ return readUuid();
+
default:
int off = dataPosition() - 4;
throw new RuntimeException(
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 5f66abd..447f280 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -16,6 +16,8 @@
package android.os;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import android.annotation.SystemApi;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -24,9 +26,12 @@
import android.text.TextUtils;
import android.util.Log;
+import libcore.io.Streams;
+
import java.io.ByteArrayInputStream;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
@@ -39,6 +44,7 @@
import java.security.SignatureException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
@@ -46,6 +52,7 @@
import java.util.Locale;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
import com.android.internal.logging.MetricsLogger;
@@ -317,6 +324,70 @@
} finally {
raf.close();
}
+
+ // Additionally verify the package compatibility.
+ if (!readAndVerifyPackageCompatibilityEntry(packageFile)) {
+ throw new SignatureException("package compatibility verification failed");
+ }
+ }
+
+ /**
+ * Verifies the compatibility entry from an {@link InputStream}.
+ *
+ * @return the verification result.
+ */
+ private static boolean verifyPackageCompatibility(InputStream inputStream) throws IOException {
+ ArrayList<String> list = new ArrayList<>();
+ ZipInputStream zis = new ZipInputStream(inputStream);
+ ZipEntry entry;
+ while ((entry = zis.getNextEntry()) != null) {
+ long entrySize = entry.getSize();
+ if (entrySize > Integer.MAX_VALUE || entrySize < 0) {
+ throw new IOException(
+ "invalid entry size (" + entrySize + ") in the compatibility file");
+ }
+ byte[] bytes = new byte[(int) entrySize];
+ Streams.readFully(zis, bytes);
+ list.add(new String(bytes, UTF_8));
+ }
+ if (list.isEmpty()) {
+ throw new IOException("no entries found in the compatibility file");
+ }
+ return (VintfObject.verify(list.toArray(new String[list.size()])) == 0);
+ }
+
+ /**
+ * Reads and verifies the compatibility entry in an OTA zip package. The compatibility entry is
+ * a zip file (inside the OTA package zip).
+ *
+ * @return {@code true} if the entry doesn't exist or verification passes.
+ */
+ private static boolean readAndVerifyPackageCompatibilityEntry(File packageFile)
+ throws IOException {
+ try (ZipFile zip = new ZipFile(packageFile)) {
+ ZipEntry entry = zip.getEntry("compatibility.zip");
+ if (entry == null) {
+ return true;
+ }
+ InputStream inputStream = zip.getInputStream(entry);
+ return verifyPackageCompatibility(inputStream);
+ }
+ }
+
+ /**
+ * Verifies the package compatibility info against the current system.
+ *
+ * @param compatibilityFile the {@link File} that contains the package compatibility info.
+ * @throws IOException if there were any errors reading the compatibility file.
+ * @return the compatibility verification result.
+ *
+ * {@hide}
+ */
+ @SystemApi
+ public static boolean verifyPackageCompatibility(File compatibilityFile) throws IOException {
+ try (InputStream inputStream = new FileInputStream(compatibilityFile)) {
+ return verifyPackageCompatibility(inputStream);
+ }
}
/**
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index b5af766..3942531 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -66,6 +66,7 @@
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -77,6 +78,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
+import java.util.UUID;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
@@ -117,19 +119,66 @@
public static final String UUID_PRIVATE_INTERNAL = null;
/** {@hide} */
public static final String UUID_PRIMARY_PHYSICAL = "primary_physical";
+ /** {@hide} */
+ public static final String UUID_SYSTEM = "system";
+ // NOTE: UUID constants below are namespaced
+ // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad default
+ // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad primary_physical
+ // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad system
/**
- * Activity Action: Allows the user to manage their storage. This activity provides the ability
- * to free up space on the device by deleting data such as apps.
+ * UUID representing the default internal storage of this device which
+ * provides {@link Environment#getDataDirectory()}.
* <p>
- * Input: Nothing.
+ * This value is constant across all devices and it will never change, and
+ * thus it cannot be used to uniquely identify a particular physical device.
+ *
+ * @see #getUuidForPath(File)
+ */
+ public static final UUID UUID_DEFAULT = UUID
+ .fromString("41217664-9172-527a-b3d5-edabb50a7d69");
+
+ /** {@hide} */
+ public static final UUID UUID_PRIMARY_PHYSICAL_ = UUID
+ .fromString("0f95a519-dae7-5abf-9519-fbd6209e05fd");
+
+ /** {@hide} */
+ public static final UUID UUID_SYSTEM_ = UUID
+ .fromString("5d258386-e60d-59e3-826d-0089cdd42cc0");
+
+ /**
+ * Activity Action: Allows the user to manage their storage. This activity
+ * provides the ability to free up space on the device by deleting data such
+ * as apps.
* <p>
- * Output: Nothing.
+ * If the sending application has a specific storage device or allocation
+ * size in mind, they can optionally define {@link #EXTRA_UUID} or
+ * {@link #EXTRA_REQUESTED_BYTES}, respectively.
*/
@SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
- public static final String ACTION_MANAGE_STORAGE
- = "android.os.storage.action.MANAGE_STORAGE";
+ public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
+
+ /**
+ * Extra {@link UUID} used to indicate the storage volume where an
+ * application is interested in allocating or managing disk space.
+ *
+ * @see #ACTION_MANAGE_STORAGE
+ * @see #UUID_DEFAULT
+ * @see #getUuidForPath(File)
+ */
+ public static final String EXTRA_UUID = "android.os.storage.extra.UUID";
+
+ /**
+ * Extra used to indicate the total size (in bytes) that an application is
+ * interested in allocating.
+ * <p>
+ * When defined, the management UI will help guide the user to free up
+ * enough disk space to reach this requested value.
+ *
+ * @see #ACTION_MANAGE_STORAGE
+ */
+ public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES";
/** {@hide} */
public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0;
@@ -668,34 +717,44 @@
}
}
- /** {@hide} */
- public @Nullable String findUuidForPath(File path) {
+ /**
+ * Return a UUID identifying the storage volume that hosts the given
+ * filesystem path.
+ * <p>
+ * If this path is hosted by the default internal storage of the device at
+ * {@link Environment#getDataDirectory()}, the returned value will be
+ * {@link #UUID_DEFAULT}.
+ *
+ * @throws IOException when the storage device at the given path isn't
+ * present, or when it doesn't have a valid UUID.
+ */
+ public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException {
Preconditions.checkNotNull(path);
final String pathString = path.getAbsolutePath();
if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) {
- return StorageManager.UUID_PRIVATE_INTERNAL;
+ return UUID_DEFAULT;
}
try {
for (VolumeInfo vol : mStorageManager.getVolumes(0)) {
if (vol.path != null && FileUtils.contains(vol.path, pathString)) {
// TODO: verify that emulated adopted devices have UUID of
// underlying volume
- return vol.fsUuid;
+ return convert(vol.fsUuid);
}
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
- throw new IllegalStateException("Failed to find a storage device for " + path);
+ throw new FileNotFoundException("Failed to find a storage device for " + path);
}
/** {@hide} */
- public @Nullable File findPathForUuid(String volumeUuid) {
+ public @NonNull File findPathForUuid(String volumeUuid) throws FileNotFoundException {
final VolumeInfo vol = findVolumeByQualifiedUuid(volumeUuid);
if (vol != null) {
return vol.getPath();
}
- throw new IllegalStateException("Failed to find a storage device for " + volumeUuid);
+ throw new FileNotFoundException("Failed to find a storage device for " + volumeUuid);
}
/** {@hide} */
@@ -1451,7 +1510,7 @@
/**
* Return quota size in bytes for all cached data belonging to the calling
- * app on the filesystem that hosts the given path.
+ * app on the given storage volume.
* <p>
* If your app goes above this quota, your cached files will be some of the
* first to be deleted when additional disk space is needed. Conversely, if
@@ -1466,21 +1525,34 @@
* as a single unit.
* </p>
*
- * @see #getCacheSizeBytes(File)
+ * @param storageUuid the UUID of the storage volume that you're interested
+ * in. The UUID for a specific path can be obtained using
+ * {@link #getUuidForPath(File)}.
+ * @throws IOException when the storage device isn't present, or when it
+ * doesn't support cache quotas.
+ * @see #getCacheSizeBytes(UUID)
*/
- public long getCacheQuotaBytes(File path) {
+ public long getCacheQuotaBytes(@NonNull UUID storageUuid) throws IOException {
try {
- final String volumeUuid = findUuidForPath(path);
final ApplicationInfo app = mContext.getApplicationInfo();
- return mStorageManager.getCacheQuotaBytes(volumeUuid, app.uid);
+ return mStorageManager.getCacheQuotaBytes(convert(storageUuid), app.uid);
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
+ /** @removed */
+ @Deprecated
+ public long getCacheQuotaBytes(@NonNull File path) throws IOException {
+ return getCacheQuotaBytes(getUuidForPath(path));
+ }
+
/**
* Return total size in bytes of all cached data belonging to the calling
- * app on the filesystem that hosts the given path.
+ * app on the given storage volume.
* <p>
* Cached data tracked by this method always includes
* {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and
@@ -1493,13 +1565,20 @@
* as a single unit.
* </p>
*
- * @see #getCacheQuotaBytes()
+ * @param storageUuid the UUID of the storage volume that you're interested
+ * in. The UUID for a specific path can be obtained using
+ * {@link #getUuidForPath(File)}.
+ * @throws IOException when the storage device isn't present, or when it
+ * doesn't support cache quotas.
+ * @see #getCacheQuotaBytes(UUID)
*/
- public long getCacheSizeBytes(File path) {
+ public long getCacheSizeBytes(@NonNull UUID storageUuid) throws IOException {
try {
- final String volumeUuid = findUuidForPath(path);
final ApplicationInfo app = mContext.getApplicationInfo();
- return mStorageManager.getCacheSizeBytes(volumeUuid, app.uid);
+ return mStorageManager.getCacheSizeBytes(convert(storageUuid), app.uid);
+ } catch (ParcelableException e) {
+ e.maybeRethrow(IOException.class);
+ throw new RuntimeException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1507,25 +1586,31 @@
/** @removed */
@Deprecated
- public long getCacheQuotaBytes() {
+ public long getCacheSizeBytes(@NonNull File path) throws IOException {
+ return getCacheSizeBytes(getUuidForPath(path));
+ }
+
+ /** @removed */
+ @Deprecated
+ public long getCacheQuotaBytes() throws IOException {
return getCacheQuotaBytes(mContext.getCacheDir());
}
/** @removed */
@Deprecated
- public long getCacheSizeBytes() {
+ public long getCacheSizeBytes() throws IOException {
return getCacheSizeBytes(mContext.getCacheDir());
}
/** @removed */
@Deprecated
- public long getExternalCacheQuotaBytes() {
+ public long getExternalCacheQuotaBytes() throws IOException {
return getCacheQuotaBytes(mContext.getExternalCacheDir());
}
/** @removed */
@Deprecated
- public long getExternalCacheSizeBytes() {
+ public long getExternalCacheSizeBytes() throws IOException {
return getCacheSizeBytes(mContext.getExternalCacheDir());
}
@@ -1542,8 +1627,8 @@
* this flag to take effect.
* </p>
*
- * @see #getAllocatableBytes(File, int)
- * @see #allocateBytes(File, long, int)
+ * @see #getAllocatableBytes(UUID, int)
+ * @see #allocateBytes(UUID, long, int)
* @see #allocateBytes(FileDescriptor, long, int)
*/
@RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE)
@@ -1558,32 +1643,43 @@
/**
* Return the maximum number of new bytes that your app can allocate for
- * itself using {@link #allocateBytes(File, long, int)} at the given path.
- * This value is typically larger than {@link File#getUsableSpace()}, since
- * the system may be willing to delete cached files to satisfy an allocation
- * request.
+ * itself on the given storage volume. This value is typically larger than
+ * {@link File#getUsableSpace()}, since the system may be willing to delete
+ * cached files to satisfy an allocation request. You can then allocate
+ * space for yourself using {@link #allocateBytes(UUID, long, int)} or
+ * {@link #allocateBytes(FileDescriptor, long, int)}.
* <p>
* This method is best used as a pre-flight check, such as deciding if there
* is enough space to store an entire music album before you allocate space
* for each audio file in the album. Attempts to allocate disk space beyond
* the returned value will fail.
+ * <p>
+ * If the returned value is not large enough for the data you'd like to
+ * store, you can launch {@link #ACTION_MANAGE_STORAGE} with the
+ * {@link #EXTRA_UUID} and {@link #EXTRA_REQUESTED_BYTES} options to help
+ * involve the user in freeing up disk space.
* <p class="note">
* Note: if your app uses the {@code android:sharedUserId} manifest feature,
* then allocatable space for all packages in your shared UID is tracked
* together as a single unit.
* </p>
*
- * @param path the path where you're considering allocating disk space,
- * since allocatable space can vary widely depending on the
- * underlying storage device.
+ * @param storageUuid the UUID of the storage volume where you're
+ * considering allocating disk space, since allocatable space can
+ * vary widely depending on the underlying storage device. The
+ * UUID for a specific path can be obtained using
+ * {@link #getUuidForPath(File)}.
* @param flags to apply to the request.
* @return the maximum number of new bytes that the calling app can allocate
- * using {@link #allocateBytes(File, long, int)}.
+ * using {@link #allocateBytes(UUID, long, int)} or
+ * {@link #allocateBytes(FileDescriptor, long, int)}.
+ * @throws IOException when the storage device isn't present, or when it
+ * doesn't support allocating space.
*/
- public long getAllocatableBytes(File path, @AllocateFlags int flags) throws IOException {
+ public long getAllocatableBytes(@NonNull UUID storageUuid, @AllocateFlags int flags)
+ throws IOException {
try {
- final String volumeUuid = findUuidForPath(path);
- return mStorageManager.getAllocatableBytes(volumeUuid, flags);
+ return mStorageManager.getAllocatableBytes(convert(storageUuid), flags);
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
throw new RuntimeException(e);
@@ -1592,28 +1688,40 @@
}
}
+ /** @removed */
+ @Deprecated
+ public long getAllocatableBytes(@NonNull File path, @AllocateFlags int flags)
+ throws IOException {
+ return getAllocatableBytes(getUuidForPath(path), flags);
+ }
+
/**
- * Allocate the requested number of bytes for your application to use at the
- * given path. This will cause the system to delete any cached files
- * necessary to satisfy your request.
+ * Allocate the requested number of bytes for your application to use on the
+ * given storage volume. This will cause the system to delete any cached
+ * files necessary to satisfy your request.
* <p>
* Attempts to allocate disk space beyond the value returned by
- * {@link #getAllocatableBytes(File, int)} will fail.
+ * {@link #getAllocatableBytes(UUID, int)} will fail.
* <p>
* Since multiple apps can be running simultaneously, this method may be
* subject to race conditions. If possible, consider using
* {@link #allocateBytes(FileDescriptor, long, int)} which will guarantee
* that bytes are allocated to an opened file.
*
- * @param path the path where you'd like to allocate disk space.
+ * @param storageUuid the UUID of the storage volume where you'd like to
+ * allocate disk space. The UUID for a specific path can be
+ * obtained using {@link #getUuidForPath(File)}.
* @param bytes the number of bytes to allocate.
* @param flags to apply to the request.
- * @see #getAllocatableBytes(File, int)
+ * @throws IOException when the storage device isn't present, or when it
+ * doesn't support allocating space, or if the device had
+ * trouble allocating the requested space.
+ * @see #getAllocatableBytes(UUID, int)
*/
- public void allocateBytes(File path, long bytes, @AllocateFlags int flags) throws IOException {
+ public void allocateBytes(@NonNull UUID storageUuid, long bytes, @AllocateFlags int flags)
+ throws IOException {
try {
- final String volumeUuid = findUuidForPath(path);
- mStorageManager.allocateBytes(volumeUuid, bytes, flags);
+ mStorageManager.allocateBytes(convert(storageUuid), bytes, flags);
} catch (ParcelableException e) {
e.maybeRethrow(IOException.class);
} catch (RemoteException e) {
@@ -1621,13 +1729,20 @@
}
}
+ /** @removed */
+ @Deprecated
+ public void allocateBytes(@NonNull File path, long bytes, @AllocateFlags int flags)
+ throws IOException {
+ allocateBytes(getUuidForPath(path), bytes, flags);
+ }
+
/**
* Allocate the requested number of bytes for your application to use in the
* given open file. This will cause the system to delete any cached files
* necessary to satisfy your request.
* <p>
* Attempts to allocate disk space beyond the value returned by
- * {@link #getAllocatableBytes(File, int)} will fail.
+ * {@link #getAllocatableBytes(UUID, int)} will fail.
* <p>
* This method guarantees that bytes have been allocated to the opened file,
* otherwise it will throw if fast allocation is not possible. Fast
@@ -1636,9 +1751,15 @@
*
* @param fd the open file that you'd like to allocate disk space for.
* @param bytes the number of bytes to allocate. This is the desired final
- * size of the open file.
+ * size of the open file. If the open file is smaller than this
+ * requested size, it will be extended without modifying any
+ * existing contents. If the open file is larger than this
+ * requested size, it will be truncated.
* @param flags to apply to the request.
- * @see #getAllocatableBytes(File, int)
+ * @throws IOException when the storage device isn't present, or when it
+ * doesn't support allocating space, or if the device had
+ * trouble allocating the requested space.
+ * @see #getAllocatableBytes(UUID, int)
* @see Environment#isExternalStorageEmulated(File)
*/
public void allocateBytes(FileDescriptor fd, long bytes, @AllocateFlags int flags)
@@ -1777,6 +1898,32 @@
return isCacheBehavior(path, XATTR_CACHE_TOMBSTONE);
}
+ /** {@hide} */
+ public static UUID convert(String uuid) {
+ if (Objects.equals(uuid, UUID_PRIVATE_INTERNAL)) {
+ return UUID_DEFAULT;
+ } else if (Objects.equals(uuid, UUID_PRIMARY_PHYSICAL)) {
+ return UUID_PRIMARY_PHYSICAL_;
+ } else if (Objects.equals(uuid, UUID_SYSTEM)) {
+ return UUID_SYSTEM_;
+ } else {
+ return UUID.fromString(uuid);
+ }
+ }
+
+ /** {@hide} */
+ public static String convert(UUID storageUuid) {
+ if (UUID_DEFAULT.equals(storageUuid)) {
+ return UUID_PRIVATE_INTERNAL;
+ } else if (UUID_PRIMARY_PHYSICAL_.equals(storageUuid)) {
+ return UUID_PRIMARY_PHYSICAL;
+ } else if (UUID_SYSTEM_.equals(storageUuid)) {
+ return UUID_SYSTEM;
+ } else {
+ return storageUuid.toString();
+ }
+ }
+
private final Object mFuseAppLoopLock = new Object();
@GuardedBy("mFuseAppLoopLock")
diff --git a/core/java/android/preference/Preference.java b/core/java/android/preference/Preference.java
index d3adce7..7496cb2 100644
--- a/core/java/android/preference/Preference.java
+++ b/core/java/android/preference/Preference.java
@@ -135,6 +135,7 @@
private boolean mDependencyMet = true;
private boolean mParentDependencyMet = true;
private boolean mRecycleEnabled = true;
+ private boolean mHasSingleLineTitleAttr;
private boolean mSingleLineTitle = true;
private boolean mIconSpaceReserved;
@@ -303,6 +304,7 @@
case com.android.internal.R.styleable.Preference_singleLineTitle:
mSingleLineTitle = a.getBoolean(attr, mSingleLineTitle);
+ mHasSingleLineTitleAttr = true;
break;
case com.android.internal.R.styleable.Preference_iconSpaceReserved:
@@ -609,7 +611,9 @@
if (!TextUtils.isEmpty(title)) {
titleView.setText(title);
titleView.setVisibility(View.VISIBLE);
- titleView.setSingleLine(mSingleLineTitle);
+ if (mHasSingleLineTitleAttr) {
+ titleView.setSingleLine(mSingleLineTitle);
+ }
} else {
titleView.setVisibility(View.GONE);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 539559d..f32f163 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7077,6 +7077,7 @@
CLONE_TO_MANAGED_PROFILE.add(ACCESSIBILITY_ENABLED);
CLONE_TO_MANAGED_PROFILE.add(ALLOW_MOCK_LOCATION);
CLONE_TO_MANAGED_PROFILE.add(ALLOWED_GEOLOCATION_ORIGINS);
+ CLONE_TO_MANAGED_PROFILE.add(AUTOFILL_SERVICE);
CLONE_TO_MANAGED_PROFILE.add(DEFAULT_INPUT_METHOD);
CLONE_TO_MANAGED_PROFILE.add(ENABLED_ACCESSIBILITY_SERVICES);
CLONE_TO_MANAGED_PROFILE.add(ENABLED_INPUT_METHODS);
@@ -8330,7 +8331,6 @@
* enabled state.
* @hide
*/
- @SystemApi
public static final String NETWORK_RECOMMENDATIONS_ENABLED =
"network_recommendations_enabled";
diff --git a/core/java/android/provider/SettingsStringUtil.java b/core/java/android/provider/SettingsStringUtil.java
index 3dfedea..a3dc947 100644
--- a/core/java/android/provider/SettingsStringUtil.java
+++ b/core/java/android/provider/SettingsStringUtil.java
@@ -23,6 +23,7 @@
import com.android.internal.util.ArrayUtils;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.function.Function;
@@ -80,6 +81,12 @@
return s;
}
+ public static String addAll(String delimitedElements, Collection<String> elements) {
+ final ColonDelimitedSet<String> set
+ = new ColonDelimitedSet.OfStrings(delimitedElements);
+ return set.addAll(elements) ? set.toString() : delimitedElements;
+ }
+
public static String add(String delimitedElements, String element) {
final ColonDelimitedSet<String> set
= new ColonDelimitedSet.OfStrings(delimitedElements);
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index a4d3fb2..813c54f 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -34,6 +34,8 @@
import com.android.internal.os.SomeArgs;
+import java.util.List;
+
//TODO(b/33197203): improve javadoc (of both class and methods); in particular, make sure the
//life-cycle (and how state could be maintained on server-side) is well documented.
@@ -103,24 +105,22 @@
}
@Override
- public void onFillRequest(AssistStructure structure, Bundle extras,
- IFillCallback callback, int flags) {
+ public void onFillRequest(FillRequest request, IFillCallback callback) {
ICancellationSignal transport = CancellationSignal.createTransport();
try {
callback.onCancellable(transport);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
- mHandlerCaller.obtainMessageIIOOOO(MSG_ON_FILL_REQUEST, flags, UNUSED_ARG, structure,
- CancellationSignal.fromTransport(transport), extras, callback)
+ mHandlerCaller.obtainMessageOOO(MSG_ON_FILL_REQUEST, request,
+ CancellationSignal.fromTransport(transport), callback)
.sendToTarget();
}
@Override
- public void onSaveRequest(AssistStructure structure, Bundle extras,
- ISaveCallback callback) {
- mHandlerCaller.obtainMessageOOO(MSG_ON_SAVE_REQUEST, structure,
- extras, callback).sendToTarget();
+ public void onSaveRequest(SaveRequest request, ISaveCallback callback) {
+ mHandlerCaller.obtainMessageOO(MSG_ON_SAVE_REQUEST, request,
+ callback).sendToTarget();
}
};
@@ -131,23 +131,20 @@
break;
} case MSG_ON_FILL_REQUEST: {
final SomeArgs args = (SomeArgs) msg.obj;
- final AssistStructure structure = (AssistStructure) args.arg1;
+ final FillRequest request = (FillRequest) args.arg1;
final CancellationSignal cancellation = (CancellationSignal) args.arg2;
- final Bundle extras = (Bundle) args.arg3;
- final IFillCallback callback = (IFillCallback) args.arg4;
- final FillCallback fillCallback = new FillCallback(callback);
- final int flags = msg.arg1;
+ final IFillCallback callback = (IFillCallback) args.arg3;
+ final FillCallback fillCallback = new FillCallback(callback, request.getId());
args.recycle();
- onFillRequest(structure, extras, flags, cancellation, fillCallback);
+ onFillRequest(request, cancellation, fillCallback);
break;
} case MSG_ON_SAVE_REQUEST: {
final SomeArgs args = (SomeArgs) msg.obj;
- final AssistStructure structure = (AssistStructure) args.arg1;
- final Bundle extras = (Bundle) args.arg2;
- final ISaveCallback callback = (ISaveCallback) args.arg3;
+ final SaveRequest request = (SaveRequest) args.arg1;
+ final ISaveCallback callback = (ISaveCallback) args.arg2;
final SaveCallback saveCallback = new SaveCallback(callback);
args.recycle();
- onSaveRequest(structure, extras, saveCallback);
+ onSaveRequest(request, saveCallback);
break;
} case MSG_DISCONNECT: {
onDisconnected();
@@ -198,6 +195,28 @@
* or {@link FillCallback#onFailure(CharSequence)})
* to notify the result of the request.
*
+ * @param request the {@link FillRequest request} to handle.
+ * See {@link FillResponse} for examples of multiple-sections requests.
+ * @param cancellationSignal signal for observing cancellation requests. The system will use
+ * this to notify you that the fill result is no longer needed and you should stop
+ * handling this fill request in order to save resources.
+ * @param callback object used to notify the result of the request.
+ */
+ public void onFillRequest(@NonNull FillRequest request,
+ @NonNull CancellationSignal cancellationSignal, @NonNull FillCallback callback) {
+ onFillRequest(request.getStructure(), request.getClientState(), request.getFlags(),
+ cancellationSignal, callback);
+ }
+
+ /**
+ * Called by the Android system do decide if an {@link Activity} can be autofilled by the
+ * service.
+ *
+ * <p>Service must call one of the {@link FillCallback} methods (like
+ * {@link FillCallback#onSuccess(FillResponse)}
+ * or {@link FillCallback#onFailure(CharSequence)})
+ * to notify the result of the request.
+ *
* @param structure {@link Activity}'s view structure.
* @param data bundle containing data passed by the service in a last call to
* {@link FillResponse.Builder#setExtras(Bundle)}, if any. This bundle allows your
@@ -211,6 +230,7 @@
* handling this fill request in order to save resources.
* @param callback object used to notify the result of the request.
*/
+ @Deprecated
public abstract void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle data,
int flags, @NonNull CancellationSignal cancellationSignal,
@NonNull FillCallback callback);
@@ -222,6 +242,23 @@
* {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
* to notify the result of the request.
*
+ * @param request the {@link SaveRequest request} to handle.
+ * See {@link FillResponse} for examples of multiple-sections requests.
+ * @param callback object used to notify the result of the request.
+ */
+ public void onSaveRequest(@NonNull SaveRequest request, @NonNull SaveCallback callback) {
+ final List<FillContext> contexts = request.getFillContexts();
+ onSaveRequest(contexts.get(contexts.size() - 1).getStructure(),
+ request.getClientState(), callback);
+ }
+
+ /**
+ * Called when user requests service to save the fields of an {@link Activity}.
+ *
+ * <p>Service must call one of the {@link SaveCallback} methods (like
+ * {@link SaveCallback#onSuccess()} or {@link SaveCallback#onFailure(CharSequence)})
+ * to notify the result of the request.
+ *
* @param structure {@link Activity}'s view structure.
* @param data bundle containing data passed by the service in a last call to
* {@link FillResponse.Builder#setExtras(Bundle)}, if any. This bundle allows your
@@ -231,6 +268,7 @@
* See {@link FillResponse} for examples of multiple-sections requests.
* @param callback object used to notify the result of the request.
*/
+ @Deprecated
public abstract void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle data,
@NonNull SaveCallback callback);
@@ -247,4 +285,22 @@
// TODO(b/33197203): Remove when GCore has migrated off this API
getSystemService(AutofillManager.class).disableOwnedAutofillServices();
}
+
+ /**
+ * Returns the {@link FillEventHistory.Event events} since the last {@link FillResponse} was
+ * returned.
+ *
+ * <p>The history is not persisted over reboots.
+ *
+ * @return The history or {@code null} if there are not events.
+ */
+ @Nullable public final FillEventHistory getFillEventHistory() {
+ AutofillManager afm = getSystemService(AutofillManager.class);
+
+ if (afm == null) {
+ return null;
+ } else {
+ return afm.getFillEventHistory();
+ }
+ }
}
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index e77bd0d..e04fae7 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -24,6 +24,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import android.widget.RemoteViews;
import com.android.internal.util.Preconditions;
@@ -50,6 +51,7 @@
private final ArrayList<RemoteViews> mFieldPresentations;
private final RemoteViews mPresentation;
private final IntentSender mAuthentication;
+ @Nullable String mId;
private Dataset(Builder builder) {
mFieldIds = builder.mFieldIds;
@@ -57,6 +59,7 @@
mFieldPresentations = builder.mFieldPresentations;
mPresentation = builder.mPresentation;
mAuthentication = builder.mAuthentication;
+ mId = builder.mId;
}
/** @hide */
@@ -89,7 +92,7 @@
public String toString() {
if (!DEBUG) return super.toString();
- return new StringBuilder("Dataset [")
+ return new StringBuilder("Dataset " + mId + " [")
.append("fieldIds=").append(mFieldIds)
.append(", fieldValues=").append(mFieldValues)
.append(", fieldPresentations=")
@@ -100,6 +103,17 @@
}
/**
+ * Gets the id of this dataset.
+ *
+ * @return The id of this dataset or {@code null} if not set
+ *
+ * @hide
+ */
+ public String getId() {
+ return mId;
+ }
+
+ /**
* A builder for {@link Dataset} objects. You must to provide at least
* one value for a field or set an authentication intent.
*/
@@ -110,6 +124,7 @@
private RemoteViews mPresentation;
private IntentSender mAuthentication;
private boolean mDestroyed;
+ @Nullable private String mId;
/**
* Creates a new builder.
@@ -173,6 +188,25 @@
}
/**
+ * Sets the id for the dataset.
+ *
+ * <p>The id of the last selected dataset can be read from
+ * {@link AutofillService#getFillEventHistory()}. If the id is not set it will not be clear
+ * if a dataset was selected as {@link AutofillService#getFillEventHistory()} uses
+ * {@code null} to indicate that no dataset was selected.
+ *
+ * @param id id for this dataset or {@code null} to unset.
+
+ * @return This builder.
+ */
+ public @NonNull Builder setId(@Nullable String id) {
+ throwIfDestroyed();
+
+ mId = id;
+ return this;
+ }
+
+ /**
* Sets the value of a field.
*
* @param id id returned by {@link
@@ -269,6 +303,7 @@
parcel.writeTypedArrayList(mFieldValues, flags);
parcel.writeParcelableList(mFieldPresentations, flags);
parcel.writeParcelable(mAuthentication, flags);
+ parcel.writeString(mId);
}
public static final Creator<Dataset> CREATOR = new Creator<Dataset>() {
@@ -295,6 +330,7 @@
builder.setValueAndPresentation(id, value, fieldPresentation);
}
builder.setAuthentication(parcel.readParcelable(null));
+ builder.setId(parcel.readString());
return builder.build();
}
diff --git a/core/java/android/service/autofill/FillCallback.java b/core/java/android/service/autofill/FillCallback.java
index e8ad14f..7774bdd 100644
--- a/core/java/android/service/autofill/FillCallback.java
+++ b/core/java/android/service/autofill/FillCallback.java
@@ -18,7 +18,6 @@
import android.annotation.Nullable;
import android.app.Activity;
-import android.os.Bundle;
import android.os.RemoteException;
/**
@@ -27,17 +26,19 @@
*/
public final class FillCallback {
private final IFillCallback mCallback;
+ private final int mRequestId;
private boolean mCalled;
/** @hide */
- public FillCallback(IFillCallback callback) {
+ public FillCallback(IFillCallback callback, int requestId) {
mCallback = callback;
+ mRequestId = requestId;
}
/**
* Notifies the Android System that an
- * {@link AutofillService#onFillRequest(android.app.assist.AssistStructure, Bundle,
- * int, android.os.CancellationSignal, FillCallback)} was successfully fulfilled by the service.
+ * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
+ * FillCallback)} was successfully fulfilled by the service.
*
* @param response autofill information for that activity, or {@code null} when the activity
* cannot be autofilled (for example, if it only contains read-only fields). See
@@ -47,7 +48,7 @@
assertNotCalled();
mCalled = true;
try {
- mCallback.onSuccess(response);
+ mCallback.onSuccess(response, mRequestId);
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
@@ -55,9 +56,8 @@
/**
* Notifies the Android System that an
- * {@link AutofillService#onFillRequest(android.app.assist.AssistStructure,
- * Bundle, int, android.os.CancellationSignal, FillCallback)}
- * could not be fulfilled by the service.
+ * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
+ * FillCallback)} could not be fulfilled by the service.
*
* @param message error message to be displayed to the user.
*/
diff --git a/core/java/android/service/autofill/FillContext.java b/core/java/android/service/autofill/FillContext.java
new file mode 100644
index 0000000..2efa08c
--- /dev/null
+++ b/core/java/android/service/autofill/FillContext.java
@@ -0,0 +1,96 @@
+/*
+ * 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.service.autofill;
+
+import android.annotation.NonNull;
+import android.app.assist.AssistStructure;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class represents a context for each fill request made via {@link
+ * AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)}.
+ * It contains a snapshot of the UI state, the view ids that were returned by
+ * the {@link AutofillService autofill service} as both required to trigger a save
+ * and optional that can be saved, and the id of the corresponding {@link
+ * FillRequest}.
+ * <p>
+ * This context allows you to inspect the values for the interesting views
+ * in the context they appeared. Also a reference to the corresponding fill
+ * request is useful to store meta-data in the client state bundle passed
+ * to {@link FillResponse.Builder#setClientState(Bundle)} to avoid interpreting
+ * the UI state again while saving.
+ */
+public final class FillContext implements Parcelable {
+ private final int mRequestId;
+ private final @NonNull AssistStructure mStructure;
+
+ /** @hide */
+ public FillContext(int requestId, @NonNull AssistStructure structure) {
+ mRequestId = requestId;
+ mStructure = structure;
+ }
+
+ private FillContext(Parcel parcel) {
+ this(parcel.readInt(), parcel.readParcelable(null));
+ }
+
+ /**
+ * Gets the id of the {@link FillRequest fill request} this context
+ * corresponds to. This is useful to associate your custom client
+ * state with every request to avoid reinterpreting the UI when saving
+ * user data.
+ *
+ * @return The request id.
+ */
+ public int getRequestId() {
+ return mRequestId;
+ }
+
+ /**
+ * @return The screen content.
+ */
+ public AssistStructure getStructure() {
+ return mStructure;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mRequestId);
+ parcel.writeParcelable(mStructure, flags);
+ }
+
+ public static final Parcelable.Creator<FillContext> CREATOR =
+ new Parcelable.Creator<FillContext>() {
+ @Override
+ public FillContext createFromParcel(Parcel parcel) {
+ return new FillContext(parcel);
+ }
+
+ @Override
+ public FillContext[] newArray(int size) {
+ return new FillContext[size];
+ }
+ };
+}
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/core/java/android/service/autofill/FillEventHistory.aidl
similarity index 67%
rename from packages/SystemUI/res/values/dimens_tv.xml
rename to core/java/android/service/autofill/FillEventHistory.aidl
index 30355d4..3c48524 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/core/java/android/service/autofill/FillEventHistory.aidl
@@ -1,7 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 2016, The Android Open Source Project
+/**
+ * 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.
@@ -15,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--->
-<resources>
- <!-- Extra space around the PIP and its outline in PIP onboarding activity -->
- <dimen name="tv_pip_bounds_space">3dp</dimen>
-</resources>
+
+package android.service.autofill;
+
+parcelable FillEventHistory;
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
new file mode 100644
index 0000000..3d72fcc
--- /dev/null
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -0,0 +1,208 @@
+/*
+ * 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.service.autofill;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.autofill.AutofillId;
+import android.widget.RemoteViews;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Describes what happened after the latest call to {@link FillCallback#onSuccess(FillResponse)}.
+ */
+public final class FillEventHistory implements Parcelable {
+ /**
+ * Not in parcel. The UID of the {@link AutofillService} that created the {@link FillResponse}.
+ */
+ private final int mServiceUid;
+
+ @Nullable private final Bundle mClientState;
+ @Nullable List<Event> mEvents;
+
+ /**
+ * Gets the UID of the {@link AutofillService} that created the {@link FillResponse}.
+ *
+ * @return The UID of the {@link AutofillService}
+ *
+ * @hide
+ */
+ public int getServiceUid() {
+ return mServiceUid;
+ }
+
+ /**
+ * Returns the client state of the {@link FillResponse}.
+ *
+ * @return The client state set by the last {@link FillResponse}
+ */
+ @Nullable public Bundle getClientState() {
+ return mClientState;
+ }
+
+ /**
+ * Returns the events occurred after the latest call to
+ * {@link FillCallback#onSuccess(FillResponse)}.
+ *
+ * @return The list of events or {@code null} if non occurred.
+ */
+ @Nullable public List<Event> getEvents() {
+ return mEvents;
+ }
+
+ /**
+ * @hide
+ */
+ public void addEvent(Event event) {
+ if (mEvents == null) {
+ mEvents = new ArrayList<>(1);
+ }
+ mEvents.add(event);
+ }
+
+ /**
+ * @hide
+ */
+ public FillEventHistory(int serviceUid, @Nullable Bundle clientState) {
+ mClientState = clientState;
+ mServiceUid = serviceUid;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeBundle(mClientState);
+
+ if (mEvents == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(mEvents.size());
+
+ int numEvents = mEvents.size();
+ for (int i = 0; i < numEvents; i++) {
+ Event event = mEvents.get(i);
+ dest.writeInt(event.getType());
+ dest.writeString(event.getDatasetId());
+ }
+ }
+ }
+
+ /**
+ * Description of an event that occured after the latest call to
+ * {@link FillCallback#onSuccess(FillResponse)}.
+ */
+ public static final class Event {
+ /**
+ * A dataset was selected. The dataset selected can be read from {@link #getDatasetId()}.
+ */
+ public static final int TYPE_DATASET_SELECTED = 0;
+
+ /**
+ * A {@link Dataset.Builder#setAuthentication(IntentSender) dataset authentication} was
+ * selected. The dataset authenticated can be read from {@link #getDatasetId()}.
+ */
+ public static final int TYPE_DATASET_AUTHENTICATION_SELECTED = 1;
+
+ /**
+ * A {@link FillResponse.Builder#setAuthentication(AutofillId[], IntentSender, RemoteViews)
+ * fill response authentication} was selected.
+ */
+ public static final int TYPE_AUTHENTICATION_SELECTED = 2;
+
+ /** A save UI was shown. */
+ public static final int TYPE_SAVE_SHOWN = 3;
+
+ /** @hide */
+ @IntDef(
+ value = {TYPE_DATASET_SELECTED,
+ TYPE_DATASET_AUTHENTICATION_SELECTED,
+ TYPE_AUTHENTICATION_SELECTED,
+ TYPE_SAVE_SHOWN})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface EventIds{}
+
+ @EventIds private final int mEventType;
+ @Nullable private final String mDatasetId;
+
+ /**
+ * Returns the type of the event.
+ *
+ * @return The type of the event
+ */
+ public int getType() {
+ return mEventType;
+ }
+
+ /**
+ * Returns the id of dataset the id was on.
+ *
+ * @return The id of dataset, or {@code null} the event is not associated with a dataset.
+ */
+ @Nullable public String getDatasetId() {
+ return mDatasetId;
+ }
+
+ /**
+ * Creates a new event.
+ *
+ * @param eventType The type of the event
+ * @param datasetId The dataset the event was on, or {@code null} if the event was on the
+ * whole response.
+ *
+ * @hide
+ */
+ public Event(int eventType, String datasetId) {
+ mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_SAVE_SHOWN,
+ "eventType");
+ mDatasetId = datasetId;
+ }
+ }
+
+ public static final Parcelable.Creator<FillEventHistory> CREATOR =
+ new Parcelable.Creator<FillEventHistory>() {
+ @Override
+ public FillEventHistory createFromParcel(Parcel parcel) {
+ FillEventHistory selection = new FillEventHistory(0, parcel.readBundle());
+
+ int numEvents = parcel.readInt();
+ for (int i = 0; i < numEvents; i++) {
+ selection.addEvent(new Event(parcel.readInt(), parcel.readString()));
+ }
+
+ return selection;
+ }
+
+ @Override
+ public FillEventHistory[] newArray(int size) {
+ return new FillEventHistory[size];
+ }
+ };
+}
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/core/java/android/service/autofill/FillRequest.aidl
similarity index 67%
copy from packages/SystemUI/res/values/dimens_tv.xml
copy to core/java/android/service/autofill/FillRequest.aidl
index 30355d4..2b1a8fe 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/core/java/android/service/autofill/FillRequest.aidl
@@ -1,7 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 2016, The Android Open Source Project
+/**
+ * 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.
@@ -15,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--->
-<resources>
- <!-- Extra space around the PIP and its outline in PIP onboarding activity -->
- <dimen name="tv_pip_bounds_space">3dp</dimen>
-</resources>
+
+package android.service.autofill;
+
+parcelable FillRequest;
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
new file mode 100644
index 0000000..aa6db4d
--- /dev/null
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.service.autofill;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.assist.AssistStructure;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Parcel;
+import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * This class represents a request to an {@link AutofillService autofill provider}
+ * to interpret the screen and provide information to the system which views are
+ * interesting for saving and what are the possible ways to fill the inputs on
+ * the screen if applicable.
+ *
+ * @see AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)
+ */
+public final class FillRequest implements Parcelable {
+ private static AtomicInteger sIdCounter = new AtomicInteger();
+
+ /**
+ * Indicates autofill was explicitly requested by the user.
+ */
+ public static final int FLAG_MANUAL_REQUEST = 0x1;
+
+ /** @hide */
+ @IntDef(
+ flag = true,
+ value = {FLAG_MANUAL_REQUEST})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface RequestFlags{}
+
+ private final int mId;
+ private final @RequestFlags int mFlags;
+ private final @NonNull AssistStructure mStructure;
+ private final @Nullable Bundle mClientState;
+
+ /** @hide */
+ public FillRequest(@NonNull AssistStructure structure,
+ @Nullable Bundle clientState, @RequestFlags int flags) {
+ this(sIdCounter.incrementAndGet(), structure, clientState, flags);
+ }
+
+ private FillRequest(@NonNull Parcel parcel) {
+ mId = parcel.readInt();
+ mStructure = parcel.readParcelable(null);
+ mClientState = parcel.readBundle();
+ mFlags = parcel.readInt();
+ }
+
+ private FillRequest(int id, @NonNull AssistStructure structure,
+ @Nullable Bundle clientState, @RequestFlags int flags) {
+ mId = id;
+ mFlags = Preconditions.checkFlagsArgument(flags, FLAG_MANUAL_REQUEST);
+ mStructure = Preconditions.checkNotNull(structure, "structure");
+ mClientState = clientState;
+ }
+
+ /**
+ * @return The unique id of this request.
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * @return The flags associated with this request.
+ *
+ * @see #FLAG_MANUAL_REQUEST
+ */
+ public @RequestFlags int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * @return The structure capturing the UI state.
+ */
+ public @NonNull AssistStructure getStructure() {
+ return mStructure;
+ }
+
+ /**
+ * Gets the extra client state returned from the last {@link
+ * AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)
+ * fill request}.
+ * <p>
+ * Once a {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)
+ * save request} is made the client state is cleared.
+ *
+ * @return The client state.
+ */
+ public @Nullable Bundle getClientState() {
+ return mClientState;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mId);
+ parcel.writeParcelable(mStructure, flags);
+ parcel.writeBundle(mClientState);
+ parcel.writeInt(mFlags);
+ }
+
+ public static final Parcelable.Creator<FillRequest> CREATOR =
+ new Parcelable.Creator<FillRequest>() {
+ @Override
+ public FillRequest createFromParcel(Parcel parcel) {
+ return new FillRequest(parcel);
+ }
+
+ @Override
+ public FillRequest[] newArray(int size) {
+ return new FillRequest[size];
+ }
+ };
+}
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index eab0d4c..0025365 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -32,8 +32,7 @@
/**
* Response for a {@link
- * AutofillService#onFillRequest(android.app.assist.AssistStructure,
- * Bundle, int, android.os.CancellationSignal, FillCallback)}.
+ * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}.
*
* <p>The response typically contains one or more {@link Dataset}s, each representing a set of
* fields that can be autofilled together, and the Android system displays a dataset picker UI
@@ -132,25 +131,25 @@
*/
public final class FillResponse implements Parcelable {
- private final ArrayList<Dataset> mDatasets;
- private final SaveInfo mSaveInfo;
- private final Bundle mExtras;
- private final RemoteViews mPresentation;
- private final IntentSender mAuthentication;
- private AutofillId[] mAuthenticationIds;
+ private final @Nullable ArrayList<Dataset> mDatasets;
+ private final @Nullable SaveInfo mSaveInfo;
+ private final @Nullable Bundle mClientState;
+ private final @Nullable RemoteViews mPresentation;
+ private final @Nullable IntentSender mAuthentication;
+ private final @Nullable AutofillId[] mAuthenticationIds;
private FillResponse(@NonNull Builder builder) {
mDatasets = builder.mDatasets;
mSaveInfo = builder.mSaveInfo;
- mExtras = builder.mExtras;
+ mClientState = builder.mCLientState;
mPresentation = builder.mPresentation;
mAuthentication = builder.mAuthentication;
mAuthenticationIds = builder.mAuthenticationIds;
}
/** @hide */
- public @Nullable Bundle getExtras() {
- return mExtras;
+ public @Nullable Bundle getClientState() {
+ return mClientState;
}
/** @hide */
@@ -185,7 +184,7 @@
public static final class Builder {
private ArrayList<Dataset> mDatasets;
private SaveInfo mSaveInfo;
- private Bundle mExtras;
+ private Bundle mCLientState;
private RemoteViews mPresentation;
private IntentSender mAuthentication;
private AutofillId[] mAuthenticationIds;
@@ -258,6 +257,19 @@
}
/**
+ * Specifies views that should not trigger new
+ * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
+ * FillCallback)} requests.
+ *
+ * <p>This is typically used when the service cannot autofill the view; for example, an
+ * {@code EditText} representing a captcha.
+ */
+ public Builder setIgnoredIds(AutofillId...ids) {
+ // TODO: implement
+ return this;
+ }
+
+ /**
* Adds a new {@link Dataset} to this response.
*
* @return This builder.
@@ -290,22 +302,35 @@
}
/**
- * Sets a {@link Bundle} that will be passed to subsequent APIs that
+ * @deprecated Use {@link #setClientState(Bundle)} instead.
+ */
+ @Deprecated
+ public Builder setExtras(@Nullable Bundle extras) {
+ throwIfDestroyed();
+ mCLientState = extras;
+ return this;
+ }
+
+ /**
+ * Sets a {@link Bundle state} that will be passed to subsequent APIs that
* manipulate this response. For example, they are passed to subsequent
- * calls to {@link AutofillService#onFillRequest(
- * android.app.assist.AssistStructure, Bundle, int,
- * android.os.CancellationSignal, FillCallback)} and {@link AutofillService#onSaveRequest(
- * android.app.assist.AssistStructure, Bundle, SaveCallback)}.
+ * calls to {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
+ * FillCallback)} and {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}.
+ * You can use this to store intermediate state that is persistent across multiple
+ * fill requests and the subsequent save request.
*
* <p>If this method is called on multiple {@link FillResponse} objects for the same
* activity, just the latest bundle is passed back to the service.
*
- * @param extras The response extras.
+ * <p>Once a {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)
+ * save request} is made the client state is cleared.
+ *
+ * @param clientState The custom client state.
* @return This builder.
*/
- public Builder setExtras(Bundle extras) {
+ public Builder setClientState(@Nullable Bundle clientState) {
throwIfDestroyed();
- mExtras = extras;
+ mCLientState = clientState;
return this;
}
@@ -344,7 +369,7 @@
return new StringBuilder(
"FillResponse: [datasets=").append(mDatasets)
.append(", saveInfo=").append(mSaveInfo)
- .append(", hasExtras=").append(mExtras != null)
+ .append(", clientState=").append(mClientState != null)
.append(", hasPresentation=").append(mPresentation != null)
.append(", hasAuthentication=").append(mAuthentication != null)
.append(", authenticationSize=").append(mAuthenticationIds != null
@@ -365,7 +390,7 @@
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeTypedArrayList(mDatasets, flags);
parcel.writeParcelable(mSaveInfo, flags);
- parcel.writeParcelable(mExtras, flags);
+ parcel.writeParcelable(mClientState, flags);
parcel.writeParcelableArray(mAuthenticationIds, flags);
parcel.writeParcelable(mAuthentication, flags);
parcel.writeParcelable(mPresentation, flags);
diff --git a/core/java/android/service/autofill/IAutoFillService.aidl b/core/java/android/service/autofill/IAutoFillService.aidl
index a8d86ca..23a1a3f 100644
--- a/core/java/android/service/autofill/IAutoFillService.aidl
+++ b/core/java/android/service/autofill/IAutoFillService.aidl
@@ -16,10 +16,10 @@
package android.service.autofill;
-import android.app.assist.AssistStructure;
-import android.os.Bundle;
+import android.service.autofill.FillRequest;
import android.service.autofill.IFillCallback;
import android.service.autofill.ISaveCallback;
+import android.service.autofill.SaveRequest;
import com.android.internal.os.IResultReceiver;
/**
@@ -29,8 +29,6 @@
*/
oneway interface IAutoFillService {
void onConnectedStateChanged(boolean connected);
- void onFillRequest(in AssistStructure structure, in Bundle extras,
- in IFillCallback callback, int flags);
- void onSaveRequest(in AssistStructure structure, in Bundle extras,
- in ISaveCallback callback);
+ void onFillRequest(in FillRequest request, in IFillCallback callback);
+ void onSaveRequest(in SaveRequest request, in ISaveCallback callback);
}
diff --git a/core/java/android/service/autofill/IFillCallback.aidl b/core/java/android/service/autofill/IFillCallback.aidl
index 2bb3e9a..688ac84 100644
--- a/core/java/android/service/autofill/IFillCallback.aidl
+++ b/core/java/android/service/autofill/IFillCallback.aidl
@@ -27,6 +27,6 @@
*/
interface IFillCallback {
void onCancellable(in ICancellationSignal cancellation);
- void onSuccess(in FillResponse response);
+ void onSuccess(in FillResponse response, int requestId);
void onFailure(CharSequence message);
}
diff --git a/core/java/android/service/autofill/SaveCallback.java b/core/java/android/service/autofill/SaveCallback.java
index 2c4ba6c..3a70138 100644
--- a/core/java/android/service/autofill/SaveCallback.java
+++ b/core/java/android/service/autofill/SaveCallback.java
@@ -17,7 +17,6 @@
package android.service.autofill;
import android.app.Activity;
-import android.os.Bundle;
import android.os.RemoteException;
/**
@@ -35,8 +34,8 @@
/**
* Notifies the Android System that an
- * {@link AutofillService#onSaveRequest (android.app.assist.AssistStructure, Bundle,
- * SaveCallback)} was successfully fulfilled by the service.
+ * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} was successfully fulfilled
+ * by the service.
*
* @throws RuntimeException if an error occurred while calling the Android System.
*/
@@ -52,8 +51,8 @@
/**
* Notifies the Android System that an
- * {@link AutofillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
- * SaveCallback)} could not be fulfilled by the service.
+ * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)} could not be fulfilled
+ * by the service.
*
* @param message error message to be displayed to the user.
*
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index 4ad0f08..f796444 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.assist.AssistStructure;
import android.content.IntentSender;
import android.os.Bundle;
import android.os.Parcel;
@@ -39,7 +40,7 @@
/**
* Information used to indicate that an {@link AutofillService} is interested on saving the
* user-inputed data for future use, through a
- * {@link AutofillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, SaveCallback)}
+ * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}
* call.
*
* <p>A {@link SaveInfo} is always associated with a {@link FillResponse}, and it contains at least
@@ -93,7 +94,7 @@
* </pre>
*
* The
- * {@link AutofillService#onSaveRequest(android.app.assist.AssistStructure, Bundle, SaveCallback)}
+ * {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}
* is triggered after a call to {@link AutofillManager#commit()}, but only when all conditions
* below are met:
*
@@ -140,12 +141,39 @@
*/
public static final int SAVE_DATA_TYPE_EMAIL_ADDRESS = 0x10;
- private final int mType;
+ /** @hide */
+ @IntDef(
+ flag = true,
+ value = {
+ SAVE_DATA_TYPE_GENERIC,
+ SAVE_DATA_TYPE_PASSWORD,
+ SAVE_DATA_TYPE_ADDRESS,
+ SAVE_DATA_TYPE_CREDIT_CARD,
+ SAVE_DATA_TYPE_EMAIL_ADDRESS})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface SaveDataType{}
+
+ /**
+ * Usually {@link AutofillService#onSaveRequest(AssistStructure, Bundle, SaveCallback)}
+ * is called once the activity finishes. If this flag is set it is called once all saved views
+ * become invisible.
+ */
+ public static final int FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE = 0x1;
+
+ /** @hide */
+ @IntDef(
+ flag = true,
+ value = {FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE})
+ @Retention(RetentionPolicy.SOURCE)
+ @interface SaveInfoFlags{}
+
+ private final @SaveDataType int mType;
private final CharSequence mNegativeActionTitle;
private final IntentSender mNegativeActionListener;
private final AutofillId[] mRequiredIds;
private final AutofillId[] mOptionalIds;
private final CharSequence mDescription;
+ private final int mFlags;
private SaveInfo(Builder builder) {
mType = builder.mType;
@@ -154,6 +182,7 @@
mRequiredIds = builder.mRequiredIds;
mOptionalIds = builder.mOptionalIds;
mDescription = builder.mDescription;
+ mFlags = builder.mFlags;
}
/** @hide */
@@ -177,11 +206,16 @@
}
/** @hide */
- public int getType() {
+ public @SaveDataType int getType() {
return mType;
}
/** @hide */
+ public @SaveInfoFlags int getFlags() {
+ return mFlags;
+ }
+
+ /** @hide */
public CharSequence getDescription() {
return mDescription;
}
@@ -191,7 +225,7 @@
*/
public static final class Builder {
- private final int mType;
+ private final @SaveDataType int mType;
private CharSequence mNegativeActionTitle;
private IntentSender mNegativeActionListener;
// TODO(b/33197203): make mRequiredIds final once addSavableIds() is gone
@@ -199,6 +233,7 @@
private AutofillId[] mOptionalIds;
private CharSequence mDescription;
private boolean mDestroyed;
+ private int mFlags;
/**
* Creates a new builder.
@@ -215,7 +250,7 @@
*
* @throws IllegalArgumentException if {@code requiredIds} is {@code null} or empty.
*/
- public Builder(int type, @NonNull AutofillId[] requiredIds) {
+ public Builder(@SaveDataType int type, @NonNull AutofillId[] requiredIds) {
if (false) {// TODO(b/33197203): re-move when clients use it
Preconditions.checkArgument(requiredIds != null && requiredIds.length > 0,
"must have at least one required id: " + Arrays.toString(requiredIds));
@@ -230,7 +265,7 @@
* // TODO(b/33197203): make sure is removed when clients migrated
*/
@Deprecated
- public Builder(int type) {
+ public Builder(@SaveDataType int type) {
this(type, null);
}
@@ -247,6 +282,19 @@
}
/**
+ * Set flags changing the save behavior.
+ *
+ * @param flags {@link #FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE} or 0.
+ * @return This builder.
+ */
+ public @NonNull Builder setFlags(@SaveInfoFlags int flags) {
+ throwIfDestroyed();
+
+ mFlags = Preconditions.checkFlagsArgument(flags, FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE);
+ return this;
+ }
+
+ /**
* Sets the ids of additional, optional views the service would be interested to save.
*
* <p>See {@link SaveInfo} for more info.
@@ -342,6 +390,7 @@
.append(", requiredIds=").append(Arrays.toString(mRequiredIds))
.append(", optionalIds=").append(Arrays.toString(mOptionalIds))
.append(", description=").append(mDescription)
+ .append(", mFlags=").append(mFlags)
.append("]").toString();
}
@@ -362,6 +411,7 @@
parcel.writeParcelable(mNegativeActionListener, flags);
parcel.writeParcelableArray(mOptionalIds, flags);
parcel.writeCharSequence(mDescription);
+ parcel.writeInt(mFlags);
}
public static final Parcelable.Creator<SaveInfo> CREATOR = new Parcelable.Creator<SaveInfo>() {
@@ -375,6 +425,7 @@
builder.setNegativeAction(parcel.readCharSequence(), parcel.readParcelable(null));
builder.setOptionalIds(parcel.readParcelableArray(null, AutofillId.class));
builder.setDescription(parcel.readCharSequence());
+ builder.setFlags(parcel.readInt());
return builder.build();
}
diff --git a/packages/SystemUI/res/values/dimens_tv.xml b/core/java/android/service/autofill/SaveRequest.aidl
similarity index 67%
copy from packages/SystemUI/res/values/dimens_tv.xml
copy to core/java/android/service/autofill/SaveRequest.aidl
index 30355d4..7789b577 100644
--- a/packages/SystemUI/res/values/dimens_tv.xml
+++ b/core/java/android/service/autofill/SaveRequest.aidl
@@ -1,7 +1,5 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright 2016, The Android Open Source Project
+/**
+ * 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.
@@ -15,8 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
--->
-<resources>
- <!-- Extra space around the PIP and its outline in PIP onboarding activity -->
- <dimen name="tv_pip_bounds_space">3dp</dimen>
-</resources>
+
+package android.service.autofill;
+
+parcelable SaveRequest;
diff --git a/core/java/android/service/autofill/SaveRequest.java b/core/java/android/service/autofill/SaveRequest.java
new file mode 100644
index 0000000..9de9315
--- /dev/null
+++ b/core/java/android/service/autofill/SaveRequest.java
@@ -0,0 +1,92 @@
+/*
+ * 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.service.autofill;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Parcel;
+import android.os.Parcelable;
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents a request to an {@link AutofillService
+ * autofill provider} to save applicable data entered by the user.
+ *
+ * @see AutofillService#onSaveRequest(SaveRequest, SaveCallback)
+ */
+public final class SaveRequest implements Parcelable {
+ private final @NonNull ArrayList<FillContext> mFillContexts;
+ private final @Nullable Bundle mClientState;
+
+ /** @hide */
+ public SaveRequest(@NonNull ArrayList<FillContext> fillContexts,
+ @Nullable Bundle clientState) {
+ mFillContexts = Preconditions.checkNotNull(fillContexts, "fillContexts");
+ mClientState = clientState;
+ }
+
+ private SaveRequest(@NonNull Parcel parcel) {
+ this(parcel.readTypedArrayList(null), parcel.readBundle());
+ }
+
+ /**
+ * @return The contexts associated with each previous fill request.
+ */
+ public @NonNull List<FillContext> getFillContexts() {
+ return mFillContexts;
+ }
+
+ /**
+ * Gets the extra client state returned from the last {@link
+ * AutofillService#onFillRequest(FillRequest, CancellationSignal, FillCallback)}
+ * fill request}.
+ *
+ * @return The client state.
+ */
+ public @Nullable Bundle getClientState() {
+ return mClientState;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeTypedArrayList(mFillContexts, flags);
+ parcel.writeBundle(mClientState);
+ }
+
+ public static final Creator<SaveRequest> CREATOR =
+ new Creator<SaveRequest>() {
+ @Override
+ public SaveRequest createFromParcel(Parcel parcel) {
+ return new SaveRequest(parcel);
+ }
+
+ @Override
+ public SaveRequest[] newArray(int size) {
+ return new SaveRequest[size];
+ }
+ };
+}
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index dc1a70d..ed44f25 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -18,6 +18,7 @@
import android.app.NotificationChannel;
import android.app.NotificationChannelGroup;
+import android.os.UserHandle;
import android.service.notification.IStatusBarNotificationHolder;
import android.service.notification.StatusBarNotification;
import android.service.notification.NotificationRankingUpdate;
@@ -36,8 +37,8 @@
void onInterruptionFilterChanged(int interruptionFilter);
// companion device managers only
- void onNotificationChannelModification(String pkgName, in NotificationChannel channel, int modificationType);
- void onNotificationChannelGroupModification(String pkgName, in NotificationChannelGroup group, int modificationType);
+ void onNotificationChannelModification(String pkgName, in UserHandle user, in NotificationChannel channel, int modificationType);
+ void onNotificationChannelGroupModification(String pkgName, in UserHandle user, in NotificationChannelGroup group, int modificationType);
// rankers only
void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder);
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 4833be3..00bd304 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -49,6 +49,7 @@
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -231,25 +232,25 @@
/**
* Channel or group modification reason provided to
- * {@link #onNotificationChannelModified(String, NotificationChannel, int)} or
- * {@link #onNotificationChannelGroupModified(String, NotificationChannelGroup, int)}- the
- * provided object was created.
+ * {@link #onNotificationChannelModified(String, UserHandle,NotificationChannel, int)} or
+ * {@link #onNotificationChannelGroupModified(String, UserHandle, NotificationChannelGroup,
+ * int)}- the provided object was created.
*/
public static final int NOTIFICATION_CHANNEL_OR_GROUP_ADDED = 1;
/**
* Channel or group modification reason provided to
- * {@link #onNotificationChannelModified(String, NotificationChannel, int)} or
- * {@link #onNotificationChannelGroupModified(String, NotificationChannelGroup, int)}- the
- * provided object was updated.
+ * {@link #onNotificationChannelModified(String, UserHandle, NotificationChannel, int)} or
+ * {@link #onNotificationChannelGroupModified(String, UserHandle,NotificationChannelGroup, int)}
+ * - the provided object was updated.
*/
public static final int NOTIFICATION_CHANNEL_OR_GROUP_UPDATED = 2;
/**
* Channel or group modification reason provided to
- * {@link #onNotificationChannelModified(String, NotificationChannel, int)} or
- * {@link #onNotificationChannelGroupModified(String, NotificationChannelGroup, int)}- the
- * provided object was deleted.
+ * {@link #onNotificationChannelModified(String, UserHandle, NotificationChannel, int)} or
+ * {@link #onNotificationChannelGroupModified(String, UserHandle, NotificationChannelGroup,
+ * int)}- the provided object was deleted.
*/
public static final int NOTIFICATION_CHANNEL_OR_GROUP_DELETED = 3;
@@ -432,13 +433,14 @@
* device} in order to receive this callback.
*
* @param pkg The package the channel belongs to.
+ * @param user The user on which the change was made.
* @param channel The channel that has changed.
* @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED},
* {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED},
* {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}.
*/
- public void onNotificationChannelModified(String pkg, NotificationChannel channel,
- @ChannelOrGroupModificationTypes int modificationType) {
+ public void onNotificationChannelModified(String pkg, UserHandle user,
+ NotificationChannel channel, @ChannelOrGroupModificationTypes int modificationType) {
// optional
}
@@ -449,13 +451,14 @@
* device} in order to receive this callback.
*
* @param pkg The package the group belongs to.
+ * @param user The user on which the change was made.
* @param group The group that has changed.
* @param modificationType One of {@link #NOTIFICATION_CHANNEL_OR_GROUP_ADDED},
* {@link #NOTIFICATION_CHANNEL_OR_GROUP_UPDATED},
* {@link #NOTIFICATION_CHANNEL_OR_GROUP_DELETED}.
*/
- public void onNotificationChannelGroupModified(String pkg, NotificationChannelGroup group,
- @ChannelOrGroupModificationTypes int modificationType) {
+ public void onNotificationChannelGroupModified(String pkg, UserHandle user,
+ NotificationChannelGroup group, @ChannelOrGroupModificationTypes int modificationType) {
// optional
}
@@ -661,21 +664,24 @@
/**
- * Updates a notification channel for a given package. This should only be used to reflect
- * changes a user has made to the channel via the listener's user interface.
+ * Updates a notification channel for a given package for a given user. This should only be used
+ * to reflect changes a user has made to the channel via the listener's user interface.
*
+ * <p>This method will throw a security exception if you don't have access to notifications
+ * for the given user.</p>
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
* device} in order to use this method.
*
* @param pkg The package the channel belongs to.
+ * @param user The user the channel belongs to.
* @param channel the channel to update.
*/
- public final void updateNotificationChannel(@NonNull String pkg,
+ public final void updateNotificationChannel(@NonNull String pkg, @NonNull UserHandle user,
@NonNull NotificationChannel channel) {
if (!isBound()) return;
try {
getNotificationInterface().updateNotificationChannelFromPrivilegedListener(
- mWrapper, pkg, channel);
+ mWrapper, pkg, user, channel);
} catch (RemoteException e) {
Log.v(TAG, "Unable to contact notification manager", e);
throw e.rethrowFromSystemServer();
@@ -683,19 +689,22 @@
}
/**
- * Returns all notification channels belonging to the given package.
+ * Returns all notification channels belonging to the given package for a given user.
*
+ * <p>This method will throw a security exception if you don't have access to notifications
+ * for the given user.</p>
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
* device} in order to use this method.
*
* @param pkg The package to retrieve channels for.
*/
- public final List<NotificationChannel> getNotificationChannels(@NonNull String pkg) {
+ public final List<NotificationChannel> getNotificationChannels(@NonNull String pkg,
+ @NonNull UserHandle user) {
if (!isBound()) return null;
try {
return getNotificationInterface().getNotificationChannelsFromPrivilegedListener(
- mWrapper, pkg).getList();
+ mWrapper, pkg, user).getList();
} catch (RemoteException e) {
Log.v(TAG, "Unable to contact notification manager", e);
throw e.rethrowFromSystemServer();
@@ -703,19 +712,22 @@
}
/**
- * Returns all notification channel groups belonging to the given package.
+ * Returns all notification channel groups belonging to the given package for a given user.
*
+ * <p>This method will throw a security exception if you don't have access to notifications
+ * for the given user.</p>
* <p>The caller must have {@link CompanionDeviceManager#getAssociations() an associated
* device} in order to use this method.
*
* @param pkg The package to retrieve channel groups for.
*/
- public final List<NotificationChannelGroup> getNotificationChannelGroups(@NonNull String pkg) {
+ public final List<NotificationChannelGroup> getNotificationChannelGroups(@NonNull String pkg,
+ @NonNull UserHandle user) {
if (!isBound()) return null;
try {
return getNotificationInterface().getNotificationChannelGroupsFromPrivilegedListener(
- mWrapper, pkg).getList();
+ mWrapper, pkg, user).getList();
} catch (RemoteException e) {
Log.v(TAG, "Unable to contact notification manager", e);
throw e.rethrowFromSystemServer();
@@ -1252,24 +1264,27 @@
}
@Override
- public void onNotificationChannelModification(String pkgName, NotificationChannel channel,
+ public void onNotificationChannelModification(String pkgName, UserHandle user,
+ NotificationChannel channel,
@ChannelOrGroupModificationTypes int modificationType) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = pkgName;
- args.arg2 = channel;
- args.arg3 = modificationType;
+ args.arg2 = user;
+ args.arg3 = channel;
+ args.arg4 = modificationType;
mHandler.obtainMessage(
MyHandler.MSG_ON_NOTIFICATION_CHANNEL_MODIFIED, args).sendToTarget();
}
@Override
- public void onNotificationChannelGroupModification(String pkgName,
+ public void onNotificationChannelGroupModification(String pkgName, UserHandle user,
NotificationChannelGroup group,
@ChannelOrGroupModificationTypes int modificationType) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = pkgName;
- args.arg2 = group;
- args.arg3 = modificationType;
+ args.arg2 = user;
+ args.arg3 = group;
+ args.arg4 = modificationType;
mHandler.obtainMessage(
MyHandler.MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED, args).sendToTarget();
}
@@ -1841,17 +1856,19 @@
case MSG_ON_NOTIFICATION_CHANNEL_MODIFIED: {
SomeArgs args = (SomeArgs) msg.obj;
String pkgName = (String) args.arg1;
- NotificationChannel channel = (NotificationChannel) args.arg2;
- int modificationType = (int) args.arg3;
- onNotificationChannelModified(pkgName, channel, modificationType);
+ UserHandle user= (UserHandle) args.arg2;
+ NotificationChannel channel = (NotificationChannel) args.arg3;
+ int modificationType = (int) args.arg4;
+ onNotificationChannelModified(pkgName, user, channel, modificationType);
} break;
case MSG_ON_NOTIFICATION_CHANNEL_GROUP_MODIFIED: {
SomeArgs args = (SomeArgs) msg.obj;
String pkgName = (String) args.arg1;
- NotificationChannelGroup group = (NotificationChannelGroup) args.arg2;
- int modificationType = (int) args.arg3;
- onNotificationChannelGroupModified(pkgName, group, modificationType);
+ UserHandle user = (UserHandle) args.arg2;
+ NotificationChannelGroup group = (NotificationChannelGroup) args.arg3;
+ int modificationType = (int) args.arg4;
+ onNotificationChannelGroupModified(pkgName, user, group, modificationType);
} break;
}
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 6bbb0ff..98780a7 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -16,6 +16,8 @@
package android.service.wallpaper;
+import android.annotation.Nullable;
+import android.app.WallpaperColors;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.util.MergedConfiguration;
@@ -542,6 +544,24 @@
*/
public void onSurfaceDestroyed(SurfaceHolder holder) {
}
+
+ /**
+ * Notifies the engine that wallpaper colors changed significantly.
+ * This will trigger a {@link #onComputeWallpaperColors()} call.
+ */
+ public void invalidateColors() {
+ }
+
+ /**
+ * Notifies the system about what colors the wallpaper is using.
+ * You might return null if no color information is available at the moment. In that case
+ * you might want to call {@link #invalidateColors()} in a near future.
+ *
+ * @return List of wallpaper colors and their weights.
+ */
+ public @Nullable WallpaperColors onComputeWallpaperColors() {
+ return null;
+ }
protected void dump(String prefix, FileDescriptor fd, PrintWriter out, String[] args) {
out.print(prefix); out.print("mInitializing="); out.print(mInitializing);
diff --git a/core/java/android/util/IconDrawableFactory.java b/core/java/android/util/IconDrawableFactory.java
new file mode 100644
index 0000000..b07942f
--- /dev/null
+++ b/core/java/android/util/IconDrawableFactory.java
@@ -0,0 +1,112 @@
+/*
+ * 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.util;
+
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Utility class to load app drawables with appropriate badging.
+ *
+ * @hide
+ */
+public class IconDrawableFactory {
+
+ protected final Context mContext;
+ protected final PackageManager mPm;
+ protected final UserManager mUm;
+ protected final LauncherIcons mLauncherIcons;
+ protected final boolean mEmbedShadow;
+
+ private IconDrawableFactory(Context context, boolean embedShadow) {
+ mContext = context;
+ mPm = context.getPackageManager();
+ mUm = context.getSystemService(UserManager.class);
+ mLauncherIcons = new LauncherIcons(context);
+ mEmbedShadow = embedShadow;
+ }
+
+ protected boolean needsBadging(ApplicationInfo appInfo, @UserIdInt int userId) {
+ return appInfo.isInstantApp() || mUm.isManagedProfile(userId);
+ }
+
+ public Drawable getBadgedIcon(ApplicationInfo appInfo) {
+ return getBadgedIcon(appInfo, UserHandle.getUserId(appInfo.uid));
+ }
+
+ public Drawable getBadgedIcon(ApplicationInfo appInfo, @UserIdInt int userId) {
+ return getBadgedIcon(appInfo, appInfo, userId);
+ }
+
+ public Drawable getBadgedIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo,
+ @UserIdInt int userId) {
+ Drawable icon = mPm.loadUnbadgedItemIcon(itemInfo, appInfo);
+ if (!mEmbedShadow && !needsBadging(appInfo, userId)) {
+ return icon;
+ }
+
+ // Before badging, add shadow to adaptive icon if needed.
+ icon = mLauncherIcons.wrapIconDrawableWithShadow(icon);
+ if (appInfo.isInstantApp()) {
+ int badgeColor = Resources.getSystem().getColor(
+ com.android.internal.R.color.instant_app_badge, null);
+ icon = mLauncherIcons.getBadgedDrawable(icon,
+ com.android.internal.R.drawable.ic_instant_icon_badge_bolt,
+ badgeColor);
+ }
+ if (mUm.isManagedProfile(userId)) {
+ icon = mLauncherIcons.getBadgedDrawable(icon,
+ com.android.internal.R.drawable.ic_corp_icon_badge_case,
+ getUserBadgeColor(mUm, userId));
+ }
+ return icon;
+ }
+
+ // Should have enough colors to cope with UserManagerService.getMaxManagedProfiles()
+ @VisibleForTesting
+ public static final int[] CORP_BADGE_COLORS = new int[] {
+ com.android.internal.R.color.profile_badge_1,
+ com.android.internal.R.color.profile_badge_2,
+ com.android.internal.R.color.profile_badge_3
+ };
+
+ public static int getUserBadgeColor(UserManager um, @UserIdInt int userId) {
+ int badge = um.getManagedProfileBadge(userId);
+ if (badge < 0) {
+ badge = 0;
+ }
+ int resourceId = CORP_BADGE_COLORS[badge % CORP_BADGE_COLORS.length];
+ return Resources.getSystem().getColor(resourceId, null);
+ }
+
+ public static IconDrawableFactory newInstance(Context context) {
+ return new IconDrawableFactory(context, true);
+ }
+
+ public static IconDrawableFactory newInstance(Context context, boolean embedShadow) {
+ return new IconDrawableFactory(context, embedShadow);
+ }
+}
diff --git a/core/java/android/util/LauncherIcons.java b/core/java/android/util/LauncherIcons.java
index 89b9646..402bef9 100644
--- a/core/java/android/util/LauncherIcons.java
+++ b/core/java/android/util/LauncherIcons.java
@@ -21,10 +21,11 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
+import android.graphics.Rect;
import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableWrapper;
+import android.graphics.drawable.LayerDrawable;
/**
* Utility class to handle icon treatments (e.g., shadow generation) for the Launcher icons.
@@ -32,78 +33,152 @@
*/
public final class LauncherIcons {
- private final Paint mPaint = new Paint();
- private final Canvas mCanvas = new Canvas();
+ // Percent of actual icon size
+ private static final float ICON_SIZE_BLUR_FACTOR = 0.5f/48;
+ // Percent of actual icon size
+ private static final float ICON_SIZE_KEY_SHADOW_DELTA_FACTOR = 1f/48;
private static final int KEY_SHADOW_ALPHA = 61;
private static final int AMBIENT_SHADOW_ALPHA = 30;
- private static final float BLUR_FACTOR = 0.5f / 48;
- private int mShadowInset;
- private Bitmap mShadowBitmap;
- private int mIconSize;
- private Resources mRes;
+
+ private final SparseArray<Bitmap> mShadowCache = new SparseArray<>();
+ private final int mIconSize;
+ private final Resources mRes;
public LauncherIcons(Context context) {
mRes = context.getResources();
- DisplayMetrics metrics = mRes.getDisplayMetrics();
- mShadowInset = (int)(2 * metrics.density);
- mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
- Paint.FILTER_BITMAP_FLAG));
- mIconSize = (int) mRes.getDimensionPixelSize(android.R.dimen.app_icon_size);
- }
-
- /**
- * Draw the drawable into a bitmap.
- */
- public Bitmap createIconBitmap(Drawable icon) {
- final Bitmap bitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
- mPaint.setAlpha(255);
- mCanvas.setBitmap(bitmap);
- int iconInset = 0;
- if (mShadowBitmap != null) {
- mCanvas.drawBitmap(mShadowBitmap, 0, 0, mPaint);
- iconInset = mShadowInset;
- }
-
- icon.setBounds(iconInset, iconInset, mIconSize - iconInset,
- mIconSize - iconInset);
- icon.draw(mCanvas);
- mCanvas.setBitmap(null);
- return bitmap;
+ mIconSize = mRes.getDimensionPixelSize(android.R.dimen.app_icon_size);
}
public Drawable wrapIconDrawableWithShadow(Drawable drawable) {
if (!(drawable instanceof AdaptiveIconDrawable)) {
return drawable;
}
- AdaptiveIconDrawable d =
- (AdaptiveIconDrawable) drawable.getConstantState().newDrawable().mutate();
- getShadowBitmap(d);
- Bitmap iconbitmap = createIconBitmap(d);
- return new BitmapDrawable(mRes, iconbitmap);
+ Bitmap shadow = getShadowBitmap((AdaptiveIconDrawable) drawable);
+ return new ShadowDrawable(shadow, drawable);
}
private Bitmap getShadowBitmap(AdaptiveIconDrawable d) {
- if (mShadowBitmap != null) {
- return mShadowBitmap;
+ int shadowSize = Math.max(mIconSize, d.getIntrinsicHeight());
+ synchronized (mShadowCache) {
+ Bitmap shadow = mShadowCache.get(shadowSize);
+ if (shadow != null) {
+ return shadow;
+ }
}
- mShadowBitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ALPHA_8);
- mCanvas.setBitmap(mShadowBitmap);
+ d.setBounds(0, 0, shadowSize, shadowSize);
- // Draw key shadow
- mPaint.setColor(Color.TRANSPARENT);
- float blur = BLUR_FACTOR * mIconSize;
- mPaint.setShadowLayer(blur, 0, mShadowInset, KEY_SHADOW_ALPHA << 24);
- d.setBounds(mShadowInset, mShadowInset, mIconSize - mShadowInset, mIconSize - mShadowInset);
- mCanvas.drawPath(d.getIconMask(), mPaint);
+ float blur = ICON_SIZE_BLUR_FACTOR * shadowSize;
+ float keyShadowDistance = ICON_SIZE_KEY_SHADOW_DELTA_FACTOR * shadowSize;
+
+ int bitmapSize = (int) (shadowSize + 2 * blur + keyShadowDistance);
+ Bitmap shadow = Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888);
+
+ Canvas canvas = new Canvas(shadow);
+ canvas.translate(blur + keyShadowDistance / 2, blur);
+
+ Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ paint.setColor(Color.TRANSPARENT);
// Draw ambient shadow
- mPaint.setShadowLayer(blur, 0, 0, AMBIENT_SHADOW_ALPHA << 24);
- d.setBounds(mShadowInset, 2 * mShadowInset, mIconSize - mShadowInset, mIconSize);
- mCanvas.drawPath(d.getIconMask(), mPaint);
- mPaint.clearShadowLayer();
+ paint.setShadowLayer(blur, 0, 0, AMBIENT_SHADOW_ALPHA << 24);
+ canvas.drawPath(d.getIconMask(), paint);
- return mShadowBitmap;
+ // Draw key shadow
+ canvas.translate(0, keyShadowDistance);
+ paint.setShadowLayer(blur, 0, 0, KEY_SHADOW_ALPHA << 24);
+ canvas.drawPath(d.getIconMask(), paint);
+
+ canvas.setBitmap(null);
+ synchronized (mShadowCache) {
+ mShadowCache.put(shadowSize, shadow);
+ }
+ return shadow;
+ }
+
+ public Drawable getBadgeDrawable(int foregroundRes, int backgroundColor) {
+ return getBadgedDrawable(null, foregroundRes, backgroundColor);
+ }
+
+ public Drawable getBadgedDrawable(Drawable base, int foregroundRes, int backgroundColor) {
+ Resources sysRes = Resources.getSystem();
+
+ Drawable badgeShadow = sysRes.getDrawable(
+ com.android.internal.R.drawable.ic_corp_icon_badge_shadow);
+
+ Drawable badgeColor = sysRes.getDrawable(
+ com.android.internal.R.drawable.ic_corp_icon_badge_color)
+ .getConstantState().newDrawable().mutate();
+ badgeColor.setTint(backgroundColor);
+
+ Drawable badgeForeground = sysRes.getDrawable(foregroundRes);
+
+ Drawable[] drawables = base == null
+ ? new Drawable[] {badgeShadow, badgeColor, badgeForeground }
+ : new Drawable[] {base, badgeShadow, badgeColor, badgeForeground };
+ return new LayerDrawable(drawables);
+ }
+
+ /**
+ * A drawable which draws a shadow bitmap behind a drawable
+ */
+ private static class ShadowDrawable extends DrawableWrapper {
+
+ final MyConstantState mState;
+
+ public ShadowDrawable(Bitmap shadow, Drawable dr) {
+ super(dr);
+ mState = new MyConstantState(shadow, dr.getConstantState());
+ }
+
+ ShadowDrawable(MyConstantState state) {
+ super(state.mChildState.newDrawable());
+ mState = state;
+ }
+
+ @Override
+ public ConstantState getConstantState() {
+ return mState;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ Rect bounds = getBounds();
+ canvas.drawBitmap(mState.mShadow, null, bounds, mState.mPaint);
+ canvas.save();
+ // Ratio of child drawable size to shadow bitmap size
+ float factor = 1 / (1 + 2 * ICON_SIZE_BLUR_FACTOR + ICON_SIZE_KEY_SHADOW_DELTA_FACTOR);
+
+ canvas.translate(
+ bounds.width() * factor *
+ (ICON_SIZE_BLUR_FACTOR + ICON_SIZE_KEY_SHADOW_DELTA_FACTOR / 2),
+ bounds.height() * factor * ICON_SIZE_BLUR_FACTOR);
+ canvas.scale(factor, factor);
+ super.draw(canvas);
+ canvas.restore();
+ }
+
+ private static class MyConstantState extends ConstantState {
+
+ final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+ final Bitmap mShadow;
+ final ConstantState mChildState;
+
+ MyConstantState(Bitmap shadow, ConstantState childState) {
+ mShadow = shadow;
+ mChildState = childState;
+ }
+
+ @Override
+ public Drawable newDrawable() {
+ return new ShadowDrawable(this);
+ }
+
+ @Override
+ public int getChangingConfigurations() {
+ return mChildState.getChangingConfigurations();
+ }
+ }
}
}
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index f47c355..1ccf16a 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -127,20 +127,23 @@
if (focused == null || focused == root) {
return root;
}
- ViewParent effective = focused.getParent();
+ ViewGroup effective = null;
+ ViewParent nextParent = focused.getParent();
do {
- if (effective == root) {
- return root;
+ if (nextParent == root) {
+ return effective != null ? effective : root;
}
- ViewGroup vg = (ViewGroup) effective;
+ ViewGroup vg = (ViewGroup) nextParent;
if (vg.getTouchscreenBlocksFocus()
&& focused.getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TOUCHSCREEN)
&& vg.isKeyboardNavigationCluster()) {
- return vg;
+ // Don't stop and return here because the cluster could be nested and we only
+ // care about the top-most one.
+ effective = vg;
}
- effective = effective.getParent();
- } while (effective != null);
+ nextParent = nextParent.getParent();
+ } while (nextParent instanceof ViewGroup);
return root;
}
diff --git a/core/java/android/view/IPinnedStackController.aidl b/core/java/android/view/IPinnedStackController.aidl
index 2fe98c0..dbeb747 100644
--- a/core/java/android/view/IPinnedStackController.aidl
+++ b/core/java/android/view/IPinnedStackController.aidl
@@ -28,4 +28,9 @@
* Notifies the controller that the PiP is currently minimized.
*/
oneway void setIsMinimized(boolean isMinimized);
+
+ /**
+ * @return what WM considers to be the current device rotation.
+ */
+ int getDisplayRotation();
}
diff --git a/core/java/android/view/IPinnedStackListener.aidl b/core/java/android/view/IPinnedStackListener.aidl
index 782f349..9382741 100644
--- a/core/java/android/view/IPinnedStackListener.aidl
+++ b/core/java/android/view/IPinnedStackListener.aidl
@@ -41,9 +41,13 @@
* current state with the aspect ratio applied. The {@param animatingBounds} are provided
* to indicate the current target bounds of the pinned stack (the final bounds if animating,
* the current bounds if not), which may be helpful in calculating dependent animation bounds.
+ *
+ * The {@param displayRotation} is provided so that the client can verify when making certain
+ * calls that it will not provide stale information based on an old display rotation (ie. if
+ * the WM has changed in the mean time but the client has not received onMovementBoundsChanged).
*/
void onMovementBoundsChanged(in Rect insetBounds, in Rect normalBounds, in Rect animatingBounds,
- boolean fromImeAdjustement);
+ boolean fromImeAdjustement, int displayRotation);
/**
* Called when window manager decides to adjust the pinned stack bounds because of the IME, or
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 1a71679..9d40895 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -295,7 +295,15 @@
@Override
protected void onDetachedFromWindow() {
- getViewRootImpl().removeWindowStoppedCallback(this);
+ ViewRootImpl viewRoot = getViewRootImpl();
+ // It's possible to create a SurfaceView using the default constructor and never
+ // attach it to a view hierarchy, this is a common use case when dealing with
+ // OpenGL. A developer will probably create a new GLSurfaceView, and let it manage
+ // the lifecycle. Instead of attaching it to a view, he/she can just pass
+ // the SurfaceHolder forward, most live wallpapers do it.
+ if (viewRoot != null) {
+ viewRoot.removeWindowStoppedCallback(this);
+ }
mAttachedToWindow = false;
if (mGlobalListenersAdded) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index a522652..4f0aa32 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -66,6 +66,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
@@ -109,7 +110,6 @@
import android.widget.ScrollBarDrawable;
import com.android.internal.R;
-import com.android.internal.util.Preconditions;
import com.android.internal.view.TooltipPopup;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.widget.ScrollBarUtils;
@@ -954,41 +954,6 @@
private static final int[] VISIBILITY_FLAGS = {VISIBLE, INVISIBLE, GONE};
- /** @hide */
- @IntDef({
- AUTOFILL_MODE_INHERIT,
- AUTOFILL_MODE_AUTO,
- AUTOFILL_MODE_MANUAL
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface AutofillMode {}
-
- /**
- * This view inherits the autofill state from it's parent. If there is no parent it is
- * {@link #AUTOFILL_MODE_AUTO}.
- * Use with {@link #setAutofillMode(int)} and <a href="#attr_android:autofillMode">
- * {@code android:autofillMode}.
- */
- public static final int AUTOFILL_MODE_INHERIT = 0;
-
- /**
- * Allows this view to automatically trigger an autofill request when it get focus.
- * Use with {@link #setAutofillMode(int)} and <a href="#attr_android:autofillMode">
- * {@code android:autofillMode}.
- */
- public static final int AUTOFILL_MODE_AUTO = 1;
-
- /**
- * Do not trigger an autofill request if this view is focused. The user can still force
- * an autofill request.
- * <p>This does not prevent this field from being autofilled if an autofill operation is
- * triggered from a different view.</p>
- *
- * Use with {@link #setAutofillMode(int)} and <a href="#attr_android:autofillMode">{@code
- * android:autofillMode}.
- */
- public static final int AUTOFILL_MODE_MANUAL = 2;
-
/**
* This view contains an email address.
*
@@ -1170,27 +1135,39 @@
@IntDef({
IMPORTANT_FOR_AUTOFILL_AUTO,
IMPORTANT_FOR_AUTOFILL_YES,
- IMPORTANT_FOR_AUTOFILL_NO
+ IMPORTANT_FOR_AUTOFILL_NO,
+ IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS,
+ IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS
})
@Retention(RetentionPolicy.SOURCE)
public @interface AutofillImportance {}
/**
- * Automatically determine whether a view is important for auto-fill.
+ * Automatically determine whether a view is important for autofill.
*/
public static final int IMPORTANT_FOR_AUTOFILL_AUTO = 0x0;
/**
- * The view is important for important for auto-fill.
+ * The view is important for autofill, and its children (if any) will be traversed.
*/
public static final int IMPORTANT_FOR_AUTOFILL_YES = 0x1;
/**
- * The view is not important for auto-fill.
+ * The view is not important for autofill, and its children (if any) will be traversed.
*/
public static final int IMPORTANT_FOR_AUTOFILL_NO = 0x2;
/**
+ * The view is important for autofill, but its children (if any) will not be traversed.
+ */
+ public static final int IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS = 0x4;
+
+ /**
+ * The view is not important for autofill, and its children (if any) will not be traversed.
+ */
+ public static final int IMPORTANT_FOR_AUTOFILL_NO_EXCLUDE_DESCENDANTS = 0x8;
+
+ /**
* This view is enabled. Interpretation varies by subclass.
* Use with ENABLED_MASK when calling setFlags.
* {@hide}
@@ -2762,7 +2739,7 @@
* 1 PFLAG3_IS_AUTOFILLED
* 1 PFLAG3_FINGER_DOWN
* 1 PFLAG3_FOCUSED_BY_DEFAULT
- * 11 PFLAG3_AUTO_FILL_MODE_MASK
+ * __ unused
* 11 PFLAG3_IMPORTANT_FOR_AUTOFILL
* 1 PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE
* 1 PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED
@@ -2992,23 +2969,6 @@
private static final int PFLAG3_FOCUSED_BY_DEFAULT = 0x40000;
/**
- * Shift for the place where the autofill mode is stored in the pflags
- *
- * @see #getAutofillMode()
- * @see #setAutofillMode(int)
- */
- private static final int PFLAG3_AUTOFILL_MODE_SHIFT = 19;
-
- /**
- * Mask for autofill modes
- *
- * @see #getAutofillMode()
- * @see #setAutofillMode(int)
- */
- private static final int PFLAG3_AUTOFILL_MODE_MASK = (AUTOFILL_MODE_INHERIT
- | AUTOFILL_MODE_AUTO | AUTOFILL_MODE_MANUAL) << PFLAG3_AUTOFILL_MODE_SHIFT;
-
- /**
* Shift for the bits in {@link #mPrivateFlags3} related to the
* "importantForAutofill" attribute.
*/
@@ -4433,6 +4393,9 @@
@Nullable
private RoundScrollbarRenderer mRoundScrollbarRenderer;
+ /** Used to delay visibility updates sent to the autofill manager */
+ private Handler mVisibilityChangeForAutofillHandler;
+
/**
* Simple constructor to use when creating a view from code.
*
@@ -5055,11 +5018,6 @@
setFocusedByDefault(a.getBoolean(attr, true));
}
break;
- case R.styleable.View_autofillMode:
- if (a.peekValue(attr) != null) {
- setAutofillMode(a.getInt(attr, AUTOFILL_MODE_INHERIT));
- }
- break;
case R.styleable.View_autofillHints:
if (a.peekValue(attr) != null) {
CharSequence[] rawHints = null;
@@ -6849,8 +6807,7 @@
}
private void notifyEnterOrExitForAutoFillIfNeeded(boolean enter) {
- if (isAutofillable() && isAttachedToWindow()
- && getResolvedAutofillMode() == AUTOFILL_MODE_AUTO) {
+ if (isAutofillable() && isAttachedToWindow()) {
AutofillManager afm = getAutofillManager();
if (afm != null) {
if (enter && hasWindowFocus() && isFocused()) {
@@ -7623,7 +7580,7 @@
* should use {@link #setImportantForAutofill(int)} instead.
*
* <p><strong>Note:</strong> returning {@code false} does not guarantee the view will be
- * excluded from the structure; for example, if the user explicitly requested auto-fill, the
+ * excluded from the structure; for example, if the user explicitly requested autofill, the
* View might be always included.
*
* <p>This decision applies just for the view, not its children - if the view children are not
@@ -9159,21 +9116,6 @@
}
/**
- * Set autofill mode for the view.
- *
- * @param autofillMode One of {@link #AUTOFILL_MODE_INHERIT}, {@link #AUTOFILL_MODE_AUTO},
- * or {@link #AUTOFILL_MODE_MANUAL}.
- * @attr ref android.R.styleable#View_autofillMode
- */
- public void setAutofillMode(@AutofillMode int autofillMode) {
- Preconditions.checkArgumentInRange(autofillMode, AUTOFILL_MODE_INHERIT,
- AUTOFILL_MODE_MANUAL, "autofillMode");
-
- mPrivateFlags3 &= ~PFLAG3_AUTOFILL_MODE_MASK;
- mPrivateFlags3 |= autofillMode << PFLAG3_AUTOFILL_MODE_SHIFT;
- }
-
- /**
* Sets the hints that helps the autofill service to select the appropriate data to fill the
* view.
*
@@ -9790,7 +9732,7 @@
@ViewDebug.IntToString(from = NOT_FOCUSABLE, to = "NOT_FOCUSABLE"),
@ViewDebug.IntToString(from = FOCUSABLE, to = "FOCUSABLE"),
@ViewDebug.IntToString(from = FOCUSABLE_AUTO, to = "FOCUSABLE_AUTO")
- })
+ }, category = "focus")
@Focusable
public int getFocusable() {
return (mViewFlags & FOCUSABLE_AUTO) > 0 ? FOCUSABLE_AUTO : mViewFlags & FOCUSABLE;
@@ -9804,54 +9746,12 @@
* @return Whether the view is focusable in touch mode.
* @attr ref android.R.styleable#View_focusableInTouchMode
*/
- @ViewDebug.ExportedProperty
+ @ViewDebug.ExportedProperty(category = "focus")
public final boolean isFocusableInTouchMode() {
return FOCUSABLE_IN_TOUCH_MODE == (mViewFlags & FOCUSABLE_IN_TOUCH_MODE);
}
/**
- * Returns the autofill mode for this view.
- *
- * @return One of {@link #AUTOFILL_MODE_INHERIT}, {@link #AUTOFILL_MODE_AUTO}, or
- * {@link #AUTOFILL_MODE_MANUAL}.
- * @attr ref android.R.styleable#View_autofillMode
- */
- @ViewDebug.ExportedProperty(mapping = {
- @ViewDebug.IntToString(from = AUTOFILL_MODE_INHERIT, to = "AUTOFILL_MODE_INHERIT"),
- @ViewDebug.IntToString(from = AUTOFILL_MODE_AUTO, to = "AUTOFILL_MODE_AUTO"),
- @ViewDebug.IntToString(from = AUTOFILL_MODE_MANUAL, to = "AUTOFILL_MODE_MANUAL")
- })
- @AutofillMode
- public int getAutofillMode() {
- return (mPrivateFlags3 & PFLAG3_AUTOFILL_MODE_MASK) >> PFLAG3_AUTOFILL_MODE_SHIFT;
- }
-
- /**
- * Returns the resolved autofill mode for this view.
- *
- * This is the same as {@link #getAutofillMode()} but if the mode is
- * {@link #AUTOFILL_MODE_INHERIT} the parents autofill mode will be returned.
- *
- * @return One of {@link #AUTOFILL_MODE_AUTO}, or {@link #AUTOFILL_MODE_MANUAL}. If the auto-
- * fill mode can not be resolved e.g. {@link #getAutofillMode()} is
- * {@link #AUTOFILL_MODE_INHERIT} and the {@link View} is detached
- * {@link #AUTOFILL_MODE_AUTO} is returned.
- */
- public @AutofillMode int getResolvedAutofillMode() {
- @AutofillMode int autofillMode = getAutofillMode();
-
- if (autofillMode == AUTOFILL_MODE_INHERIT) {
- if (mParent == null) {
- return AUTOFILL_MODE_AUTO;
- } else {
- return mParent.getResolvedAutofillMode();
- }
- } else {
- return autofillMode;
- }
- }
-
- /**
* Find the nearest view in the specified direction that can take focus.
* This does not actually give focus to that view.
*
@@ -9874,12 +9774,31 @@
* @return True if this view is a root of a cluster, or false otherwise.
* @attr ref android.R.styleable#View_keyboardNavigationCluster
*/
- @ViewDebug.ExportedProperty(category = "keyboardNavigationCluster")
+ @ViewDebug.ExportedProperty(category = "focus")
public final boolean isKeyboardNavigationCluster() {
return (mPrivateFlags3 & PFLAG3_CLUSTER) != 0;
}
/**
+ * Searches up the view hierarchy to find the top-most cluster. All deeper/nested clusters
+ * will be ignored.
+ *
+ * @return the keyboard navigation cluster that this view is in (can be this view)
+ * or {@code null} if not in one
+ */
+ View findKeyboardNavigationCluster() {
+ if (mParent instanceof View) {
+ View cluster = ((View) mParent).findKeyboardNavigationCluster();
+ if (cluster != null) {
+ return cluster;
+ } else if (isKeyboardNavigationCluster()) {
+ return this;
+ }
+ }
+ return null;
+ }
+
+ /**
* Set whether this view is a root of a keyboard navigation cluster.
*
* @param isCluster If true, this view is a root of a cluster.
@@ -9901,9 +9820,20 @@
*
* @hide
*/
- public void setFocusedInCluster() {
- if (mParent instanceof ViewGroup) {
- ((ViewGroup) mParent).setFocusInCluster(this);
+ public final void setFocusedInCluster() {
+ View top = findKeyboardNavigationCluster();
+ if (top == this) {
+ return;
+ }
+ ViewParent parent = mParent;
+ View child = this;
+ while (parent instanceof ViewGroup) {
+ ((ViewGroup) parent).setFocusedInCluster(child);
+ if (parent == top) {
+ return;
+ }
+ child = (View) parent;
+ parent = parent.getParent();
}
}
@@ -9919,7 +9849,7 @@
* @return {@code true} if this view is the default-focus view, {@code false} otherwise
* @attr ref android.R.styleable#View_focusedByDefault
*/
- @ViewDebug.ExportedProperty(category = "focusedByDefault")
+ @ViewDebug.ExportedProperty(category = "focus")
public final boolean isFocusedByDefault() {
return (mPrivateFlags3 & PFLAG3_FOCUSED_BY_DEFAULT) != 0;
}
@@ -10032,7 +9962,7 @@
* @return True if this View should use a default focus highlight.
* @attr ref android.R.styleable#View_defaultFocusHighlightEnabled
*/
- @ViewDebug.ExportedProperty(category = "defaultFocusHighlightEnabled")
+ @ViewDebug.ExportedProperty(category = "focus")
public final boolean getDefaultFocusHighlightEnabled() {
return mDefaultFocusHighlightEnabled;
}
@@ -11812,6 +11742,30 @@
if (fg != null && isVisible != fg.isVisible()) {
fg.setVisible(isVisible, false);
}
+
+ if (isAutofillable()) {
+ AutofillManager afm = getAutofillManager();
+
+ if (afm != null && getAccessibilityViewId() > LAST_APP_ACCESSIBILITY_ID) {
+ if (mVisibilityChangeForAutofillHandler != null) {
+ mVisibilityChangeForAutofillHandler.removeMessages(0);
+ }
+
+ // If the view is in the background but still part of the hierarchy this is called
+ // with isVisible=false. Hence visibility==false requires further checks
+ if (isVisible) {
+ afm.notifyViewVisibilityChange(this, true);
+ } else {
+ if (mVisibilityChangeForAutofillHandler == null) {
+ mVisibilityChangeForAutofillHandler =
+ new VisibilityChangeForAutofillHandler(afm, this);
+ }
+ // Let current operation (e.g. removal of the view from the hierarchy)
+ // finish before checking state
+ mVisibilityChangeForAutofillHandler.obtainMessage(0, this).sendToTarget();
+ }
+ }
+ }
}
/**
@@ -24608,6 +24562,27 @@
}
/**
+ * When a view becomes invisible checks if autofill considers the view invisible too. This
+ * happens after the regular removal operation to make sure the operation is finished by the
+ * time this is called.
+ */
+ private static class VisibilityChangeForAutofillHandler extends Handler {
+ private final AutofillManager mAfm;
+ private final View mView;
+
+ private VisibilityChangeForAutofillHandler(@NonNull AutofillManager afm,
+ @NonNull View view) {
+ mAfm = afm;
+ mView = view;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ mAfm.notifyViewVisibilityChange(mView, mView.isShown());
+ }
+ }
+
+ /**
* Base class for derived classes that want to save and restore their own
* state in {@link android.view.View#onSaveInstanceState()}.
*/
@@ -25810,6 +25785,7 @@
// focus
stream.addProperty("focus:hasFocus", hasFocus());
stream.addProperty("focus:isFocused", isFocused());
+ stream.addProperty("focus:focusable", getFocusable());
stream.addProperty("focus:isFocusable", isFocusable());
stream.addProperty("focus:isFocusableInTouchMode", isFocusableInTouchMode());
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f9eb25d..1977ef5 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -807,33 +807,27 @@
return mDefaultFocus != null || super.hasDefaultFocus();
}
- void setFocusInCluster(View child) {
- // Stop at the root of the cluster
- if (child.isKeyboardNavigationCluster()) {
- return;
- }
-
+ void setFocusedInCluster(View child) {
mFocusedInCluster = child;
-
- if (mParent instanceof ViewGroup) {
- ((ViewGroup) mParent).setFocusInCluster(this);
- }
}
- void clearFocusInCluster(View child) {
+ /**
+ * Removes {@code child} (and associated focusedInCluster chain) from the cluster containing
+ * it.
+ * <br>
+ * This is intended to be run on {@code child}'s immediate parent. This is necessary because
+ * the chain is sometimes cleared after {@code child} has been detached.
+ */
+ void clearFocusedInCluster(View child) {
if (mFocusedInCluster != child) {
return;
}
-
- if (child.isKeyboardNavigationCluster()) {
- return;
- }
-
- mFocusedInCluster = null;
-
- if (mParent instanceof ViewGroup) {
- ((ViewGroup) mParent).clearFocusInCluster(this);
- }
+ View top = findKeyboardNavigationCluster();
+ ViewParent parent = this;
+ do {
+ ((ViewGroup) parent).mFocusedInCluster = null;
+ parent = parent.getParent();
+ } while (parent != top && parent instanceof ViewGroup);
}
@Override
@@ -1281,7 +1275,7 @@
public void setTouchscreenBlocksFocus(boolean touchscreenBlocksFocus) {
if (touchscreenBlocksFocus) {
mGroupFlags |= FLAG_TOUCHSCREEN_BLOCKS_FOCUS;
- if (hasFocus()) {
+ if (hasFocus() && !isKeyboardNavigationCluster()) {
final View focusedChild = getDeepestFocusedChild();
if (!focusedChild.isFocusableInTouchMode()) {
final View newFocus = focusSearch(FOCUS_FORWARD);
@@ -1306,6 +1300,7 @@
/**
* Check whether this ViewGroup should ignore focus requests for itself and its children.
*/
+ @ViewDebug.ExportedProperty(category = "focus")
public boolean getTouchscreenBlocksFocus() {
return (mGroupFlags & FLAG_TOUCHSCREEN_BLOCKS_FOCUS) != 0;
}
@@ -1316,7 +1311,8 @@
// cluster, focus is free to move around within it.
return getTouchscreenBlocksFocus() &&
mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
- && (!hasFocus() || !isKeyboardNavigationCluster());
+ && !(isKeyboardNavigationCluster()
+ && (hasFocus() || (findKeyboardNavigationCluster() != this)));
}
@Override
@@ -3217,8 +3213,7 @@
}
private boolean restoreFocusInClusterInternal(@FocusRealDirection int direction) {
- if (mFocusedInCluster != null && !mFocusedInCluster.isKeyboardNavigationCluster()
- && getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS
+ if (mFocusedInCluster != null && getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS
&& (mFocusedInCluster.mViewFlags & VISIBILITY_MASK) == VISIBLE
&& mFocusedInCluster.restoreFocusInCluster(direction)) {
return true;
@@ -5182,7 +5177,7 @@
clearChildFocus = true;
}
if (view == mFocusedInCluster) {
- clearFocusInCluster(view);
+ clearFocusedInCluster(view);
}
view.clearAccessibilityFocus();
@@ -5302,7 +5297,7 @@
clearDefaultFocus = view;
}
if (view == mFocusedInCluster) {
- clearFocusInCluster(view);
+ clearFocusedInCluster(view);
}
view.clearAccessibilityFocus();
@@ -5458,7 +5453,7 @@
clearDefaultFocus(child);
}
if (child == mFocusedInCluster) {
- clearFocusInCluster(child);
+ clearFocusedInCluster(child);
}
child.clearAccessibilityFocus();
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index d5aab48..cc11cb8 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -659,17 +659,4 @@
* @return true if the action was consumed by this ViewParent
*/
public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle arguments);
-
- /**
- * Return the resolved autofill mode.
- *
- * @return One of {@link View#AUTOFILL_MODE_AUTO}, {@link View#AUTOFILL_MODE_MANUAL} if the
- * autofill mode can be resolved. If the autofill mode cannot be resolved
- * {@link View#AUTOFILL_MODE_AUTO}.
- *
- * @see View#getResolvedAutofillMode()
- */
- default @View.AutofillMode int getResolvedAutofillMode() {
- return View.AUTOFILL_MODE_AUTO;
- }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index a7ececf..9ecced6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1561,6 +1561,16 @@
host.dispatchApplyWindowInsets(getWindowInsets(true /* forceConstruct */));
}
+ /**
+ * @return the last content insets for use in adjusting the source hint rect for the
+ * picture-in-picture transition.
+ *
+ * @hide
+ */
+ public Rect getLastContentInsets() {
+ return mAttachInfo.mContentInsets;
+ }
+
private static boolean shouldUseDisplaySize(final WindowManager.LayoutParams lp) {
return lp.type == TYPE_STATUS_BAR_PANEL
|| lp.type == TYPE_INPUT_METHOD
@@ -4665,7 +4675,8 @@
if (focused == null && mView.restoreDefaultFocus()) {
return true;
}
- View cluster = focused.keyboardNavigationClusterSearch(null, direction);
+ View cluster = focused == null ? keyboardNavigationClusterSearch(null, direction)
+ : focused.keyboardNavigationClusterSearch(null, direction);
// Since requestFocus only takes "real" focus directions (and therefore also
// restoreFocusInCluster), convert forward/backward focus into FOCUS_DOWN.
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index b157709..92b0d98 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -326,8 +326,8 @@
/**
* Sets whether the data on this node is sensitive; if it is, then its content (text, autofill
* value, etc..) is striped before calls to {@link
- * android.service.autofill.AutofillService#onFillRequest(android.app.assist.AssistStructure,
- * Bundle, int, android.os.CancellationSignal, android.service.autofill.FillCallback)}.
+ * android.service.autofill.AutofillService#onFillRequest(android.service.autofill.FillRequest,
+ * android.os.CancellationSignal, android.service.autofill.FillCallback)}.
*
* <p>By default, all nodes are assumed to be sensitive, and only nodes that does not have PII
* (Personally Identifiable Information - sensitive data such as email addresses, credit card
@@ -336,8 +336,8 @@
*
* <p>Notice that the content of even sensitive nodes are sent to the service (through the
* {@link
- * android.service.autofill.AutofillService#onSaveRequest(android.app.assist.AssistStructure,
- * Bundle, android.service.autofill.SaveCallback)} call) when the user consented to save
+ * android.service.autofill.AutofillService#onSaveRequest(android.service.autofill.SaveRequest,
+ * android.service.autofill.SaveCallback)} call) when the user consented to save
* thedata, so it is important to set the content of sensitive nodes as well, but mark them as
* sensitive.
*
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 6dbc09c..bf0e10f 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -303,13 +303,16 @@
* hidden, no matter how WindowManagerService will react / has reacted
* to corresponding API calls. Note that this state is not guaranteed
* to be synchronized with state in WindowManagerService.
+ * @param dismissImeOnBackKeyPressed {@code true} if the software keyboard is shown and the back
+ * key is expected to dismiss the software keyboard.
* @param targetWindowToken token to identify the target window that the IME is associated with.
* {@code null} when application, system, or the IME itself decided to
* change its window visibility before being associated with any target
* window.
*/
public abstract void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
- boolean imeWindowVisible, @Nullable IBinder targetWindowToken);
+ boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed,
+ @Nullable IBinder targetWindowToken);
/**
* Returns true when the hardware keyboard is available.
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index bb6e0ee..030c78b 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1537,6 +1537,18 @@
public void setLastInputMethodWindowLw(WindowState ime, WindowState target);
/**
+ * An internal callback (from InputMethodManagerService) to notify a state change regarding
+ * whether the back key should dismiss the software keyboard (IME) or not.
+ *
+ * @param newValue {@code true} if the software keyboard is shown and the back key is expected
+ * to dismiss the software keyboard.
+ * @hide
+ */
+ default void setDismissImeOnBackKeyPressed(boolean newValue) {
+ // Default implementation does nothing.
+ }
+
+ /**
* Show the recents task list app.
* @hide
*/
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 41c209c..8ed0762 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -31,7 +31,10 @@
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.service.autofill.AutofillService;
+import android.service.autofill.FillEventHistory;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
@@ -101,7 +104,11 @@
// Public flags start from the lowest bit
/**
* Indicates autofill was explicitly requested by the user.
+ *
+ * @deprecated Use {@link android.service.autofill.FillRequest#FLAG_MANUAL_REQUEST}
*/
+ // TODO(b/33197203): remove
+ @Deprecated
public static final int FLAG_MANUAL_REQUEST = 0x1;
// Private flags start from the highest bit
@@ -140,6 +147,10 @@
@GuardedBy("mLock")
@Nullable private ParcelableMap mLastAutofilledData;
+ /** If view tracking is enabled, contains the tracking state */
+ @GuardedBy("mLock")
+ @Nullable private TrackedViews mTrackedViews;
+
/** @hide */
public interface AutofillClient {
/**
@@ -174,6 +185,20 @@
* @return Whether the UI was hidden.
*/
boolean autofillCallbackRequestHideFillUi();
+
+ /**
+ * Checks if the view is currently attached and visible.
+ *
+ * @return {@code true} iff the view is attached or visible
+ */
+ boolean getViewVisibility(int viewId);
+
+ /**
+ * Checks is the client is currently visible as understood by autofill.
+ *
+ * @return {@code true} if the client is currently visible
+ */
+ boolean isVisibleForAutofill();
}
/**
@@ -257,6 +282,21 @@
}
/**
+ * Called once the client becomes visible.
+ *
+ * @see AutofillClient#isVisibleForAutofill()
+ *
+ * {@hide}
+ */
+ public void onVisibleForAutofill() {
+ synchronized (mLock) {
+ if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) {
+ mTrackedViews.onVisibleForAutofill();
+ }
+ }
+ }
+
+ /**
* Save state before activity lifecycle
*
* @param outState Place to store the state
@@ -297,6 +337,20 @@
}
/**
+ * Should always be called from {@link AutofillService#getFillEventHistory()}.
+ *
+ * @hide
+ */
+ @Nullable public FillEventHistory getFillEventHistory() {
+ try {
+ return mService.getFillEventHistory();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return null;
+ }
+ }
+
+ /**
* Explicitly requests a new autofill context.
*
* <p>Normally, the autofill context is automatically started when autofillable views are
@@ -409,6 +463,22 @@
}
/**
+ * Called when a {@link View view's} visibility changes.
+ *
+ * @param view {@link View} that was exited.
+ * @param isVisible visible if the view is visible in the view hierarchy.
+ *
+ * @hide
+ */
+ public void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) {
+ synchronized (mLock) {
+ if (mEnabled && mSessionId != NO_SESSION && mTrackedViews != null) {
+ mTrackedViews.notifyViewVisibilityChange(view, isVisible);
+ }
+ }
+ }
+
+ /**
* Called when a virtual view that supports autofill is entered.
*
* @param view the {@link View} whose descendant is the virtual view.
@@ -666,6 +736,7 @@
throw e.rethrowFromSystemServer();
}
+ mTrackedViews = null;
mSessionId = NO_SESSION;
}
@@ -680,6 +751,7 @@
throw e.rethrowFromSystemServer();
}
+ mTrackedViews = null;
mSessionId = NO_SESSION;
}
@@ -760,8 +832,8 @@
}
}
- private void requestShowFillUi(IBinder windowToken, AutofillId id, int width, int height,
- Rect anchorBounds, IAutofillWindowPresenter presenter) {
+ private void requestShowFillUi(int sessionId, IBinder windowToken, AutofillId id, int width,
+ int height, Rect anchorBounds, IAutofillWindowPresenter presenter) {
final View anchor = findAchorView(windowToken, id);
if (anchor == null) {
return;
@@ -769,9 +841,15 @@
AutofillCallback callback = null;
synchronized (mLock) {
- if (getClientLocked().autofillCallbackRequestShowFillUi(anchor, width, height,
- anchorBounds, presenter) && mCallback != null) {
- callback = mCallback;
+ if (mSessionId == sessionId) {
+ AutofillClient client = getClientLocked();
+
+ if (client != null) {
+ if (client.autofillCallbackRequestShowFillUi(anchor, width, height,
+ anchorBounds, presenter) && mCallback != null) {
+ callback = mCallback;
+ }
+ }
}
}
@@ -785,6 +863,23 @@
}
}
+ private void authenticate(int sessionId, IntentSender intent, Intent fillInIntent) {
+ synchronized (mLock) {
+ if (sessionId == mSessionId) {
+ AutofillClient client = getClientLocked();
+ if (client != null) {
+ client.autofillCallbackAuthenticate(intent, fillInIntent);
+ }
+ }
+ }
+ }
+
+ private void setState(boolean enabled) {
+ synchronized (mLock) {
+ mEnabled = enabled;
+ }
+ }
+
/**
* Sets a view as autofilled if the current value is the {code targetValue}.
*
@@ -804,80 +899,111 @@
}
}
- private void handleAutofill(IBinder windowToken, List<AutofillId> ids,
+ private void autofill(int sessionId, IBinder windowToken, List<AutofillId> ids,
List<AutofillValue> values) {
- final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken);
- if (root == null) {
- return;
- }
-
- final int itemCount = ids.size();
- int numApplied = 0;
- ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
-
- for (int i = 0; i < itemCount; i++) {
- final AutofillId id = ids.get(i);
- final AutofillValue value = values.get(i);
- final int viewId = id.getViewId();
- final View view = root.findViewByAccessibilityIdTraversal(viewId);
- if (view == null) {
- Log.w(TAG, "autofill(): no View with id " + viewId);
- continue;
+ synchronized (mLock) {
+ if (sessionId != mSessionId) {
+ return;
}
- if (id.isVirtual()) {
- if (virtualValues == null) {
- // Most likely there will be just one view with virtual children.
- virtualValues = new ArrayMap<>(1);
+
+ final View root = WindowManagerGlobal.getInstance().getWindowView(windowToken);
+ if (root == null) {
+ return;
+ }
+
+ final int itemCount = ids.size();
+ int numApplied = 0;
+ ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null;
+
+ for (int i = 0; i < itemCount; i++) {
+ final AutofillId id = ids.get(i);
+ final AutofillValue value = values.get(i);
+ final int viewId = id.getViewId();
+ final View view = root.findViewByAccessibilityIdTraversal(viewId);
+ if (view == null) {
+ Log.w(TAG, "autofill(): no View with id " + viewId);
+ continue;
}
- SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
- if (valuesByParent == null) {
- // We don't know the size yet, but usually it will be just a few fields...
- valuesByParent = new SparseArray<>(5);
- virtualValues.put(view, valuesByParent);
- }
- valuesByParent.put(id.getVirtualChildId(), value);
- } else {
- synchronized (mLock) {
+ if (id.isVirtual()) {
+ if (virtualValues == null) {
+ // Most likely there will be just one view with virtual children.
+ virtualValues = new ArrayMap<>(1);
+ }
+ SparseArray<AutofillValue> valuesByParent = virtualValues.get(view);
+ if (valuesByParent == null) {
+ // We don't know the size yet, but usually it will be just a few fields...
+ valuesByParent = new SparseArray<>(5);
+ virtualValues.put(view, valuesByParent);
+ }
+ valuesByParent.put(id.getVirtualChildId(), value);
+ } else {
// Mark the view as to be autofilled with 'value'
if (mLastAutofilledData == null) {
mLastAutofilledData = new ParcelableMap(itemCount - i);
}
mLastAutofilledData.put(id, value);
+
+ view.autofill(value);
+
+ // Set as autofilled if the values match now, e.g. when the value was updated
+ // synchronously.
+ // If autofill happens async, the view is set to autofilled in
+ // notifyValueChanged.
+ setAutofilledIfValuesIs(view, value);
+
+ numApplied++;
}
-
- view.autofill(value);
-
- // Set as autofilled if the values match now, e.g. when the value was updated
- // synchronously.
- // If autofill happens async, the view is set to autofilled in notifyValueChanged.
- setAutofilledIfValuesIs(view, value);
-
- numApplied++;
}
- }
- if (virtualValues != null) {
- for (int i = 0; i < virtualValues.size(); i++) {
- final View parent = virtualValues.keyAt(i);
- final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
- parent.autofill(childrenValues);
- numApplied += childrenValues.size();
+ if (virtualValues != null) {
+ for (int i = 0; i < virtualValues.size(); i++) {
+ final View parent = virtualValues.keyAt(i);
+ final SparseArray<AutofillValue> childrenValues = virtualValues.valueAt(i);
+ parent.autofill(childrenValues);
+ numApplied += childrenValues.size();
+ }
}
- }
- final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED);
- log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount);
- log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED, numApplied);
- mMetricsLogger.write(log);
+ final LogMaker log = new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_DATASET_APPLIED);
+ log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VALUES, itemCount);
+ log.addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_NUM_VIEWS_FILLED,
+ numApplied);
+ mMetricsLogger.write(log);
+ }
}
- private void requestHideFillUi(IBinder windowToken, AutofillId id) {
+ /**
+ * Set the tracked views.
+ *
+ * @param trackedIds The views to be tracked
+ * @param saveOnAllViewsInvisible Finish the session once all tracked views are invisible.
+ */
+ private void setTrackedViews(int sessionId, List<AutofillId> trackedIds,
+ boolean saveOnAllViewsInvisible) {
+ synchronized (mLock) {
+ if (mEnabled && mSessionId == sessionId) {
+ if (saveOnAllViewsInvisible) {
+ mTrackedViews = new TrackedViews(trackedIds);
+ } else {
+ mTrackedViews = null;
+ }
+ }
+ }
+ }
+
+ private void requestHideFillUi(int sessionId, IBinder windowToken, AutofillId id) {
final View anchor = findAchorView(windowToken, id);
AutofillCallback callback = null;
synchronized (mLock) {
- if (getClientLocked().autofillCallbackRequestHideFillUi() && mCallback != null) {
- callback = mCallback;
+ if (mSessionId == sessionId) {
+ AutofillClient client = getClientLocked();
+
+ if (client != null) {
+ if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
+ callback = mCallback;
+ }
+ }
}
}
@@ -891,12 +1017,14 @@
}
}
- private void notifyNoFillUi(IBinder windowToken, AutofillId id) {
+ private void notifyNoFillUi(int sessionId, IBinder windowToken, AutofillId id) {
final View anchor = findAchorView(windowToken, id);
- AutofillCallback callback;
+ AutofillCallback callback = null;
synchronized (mLock) {
- callback = mCallback;
+ if (mSessionId == sessionId && getClientLocked() != null) {
+ callback = mCallback;
+ }
}
if (callback != null) {
@@ -929,6 +1057,195 @@
}
/**
+ * View tracking information. Once all tracked views become invisible the session is finished.
+ */
+ private class TrackedViews {
+ /** Visible tracked views */
+ @Nullable private ArraySet<AutofillId> mVisibleTrackedIds;
+
+ /** Invisible tracked views */
+ @Nullable private ArraySet<AutofillId> mInvisibleTrackedIds;
+
+ /**
+ * Check if set is null or value is in set.
+ *
+ * @param set The set or null (== empty set)
+ * @param value The value that might be in the set
+ *
+ * @return {@code true} iff set is not empty and value is in set
+ */
+ private <T> boolean isInSet(@Nullable ArraySet<T> set, T value) {
+ return set != null && set.contains(value);
+ }
+
+ /**
+ * Add a value to a set. If set is null, create a new set.
+ *
+ * @param set The set or null (== empty set)
+ * @param valueToAdd The value to add
+ *
+ * @return The set including the new value. If set was {@code null}, a set containing only
+ * the new value.
+ */
+ @NonNull
+ private <T> ArraySet<T> addToSet(@Nullable ArraySet<T> set, T valueToAdd) {
+ if (set == null) {
+ set = new ArraySet<>(1);
+ }
+
+ set.add(valueToAdd);
+
+ return set;
+ }
+
+ /**
+ * Remove a value from a set.
+ *
+ * @param set The set or null (== empty set)
+ * @param valueToRemove The value to remove
+ *
+ * @return The set without the removed value. {@code null} if set was null, or is empty
+ * after removal.
+ */
+ @Nullable
+ private <T> ArraySet<T> removeFromSet(@Nullable ArraySet<T> set, T valueToRemove) {
+ if (set == null) {
+ return null;
+ }
+
+ set.remove(valueToRemove);
+
+ if (set.isEmpty()) {
+ return null;
+ }
+
+ return set;
+ }
+
+ /**
+ * Set the tracked views.
+ *
+ * @param trackedIds The views to be tracked
+ */
+ TrackedViews(@NonNull List<AutofillId> trackedIds) {
+ mVisibleTrackedIds = null;
+ mInvisibleTrackedIds = null;
+
+ AutofillClient client = getClientLocked();
+ if (trackedIds != null) {
+ int numIds = trackedIds.size();
+ for (int i = 0; i < numIds; i++) {
+ AutofillId id = trackedIds.get(i);
+
+ boolean isVisible = true;
+ if (client != null && client.isVisibleForAutofill()) {
+ isVisible = client.getViewVisibility(id.getViewId());
+ }
+
+ if (isVisible) {
+ mVisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
+ } else {
+ mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
+ }
+ }
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "TrackedViews(trackedIds=" + trackedIds + "): "
+ + " mVisibleTrackedIds=" + mVisibleTrackedIds
+ + " mInvisibleTrackedIds=" + mInvisibleTrackedIds);
+ }
+
+ if (mVisibleTrackedIds == null) {
+ finishSessionLocked();
+ }
+ }
+
+ /**
+ * Called when a {@link View view's} visibility changes.
+ *
+ * @param view {@link View} that was exited.
+ * @param isVisible visible if the view is visible in the view hierarchy.
+ */
+ void notifyViewVisibilityChange(@NonNull View view, boolean isVisible) {
+ AutofillId id = getAutofillId(view);
+ AutofillClient client = getClientLocked();
+
+ if (DEBUG) {
+ Log.d(TAG, "notifyViewVisibilityChange(): id=" + id + " isVisible="
+ + isVisible);
+ }
+
+ if (client != null && client.isVisibleForAutofill()) {
+ if (isVisible) {
+ if (isInSet(mInvisibleTrackedIds, id)) {
+ mInvisibleTrackedIds = removeFromSet(mInvisibleTrackedIds, id);
+ mVisibleTrackedIds = addToSet(mVisibleTrackedIds, id);
+ }
+ } else {
+ if (isInSet(mVisibleTrackedIds, id)) {
+ mVisibleTrackedIds = removeFromSet(mVisibleTrackedIds, id);
+ mInvisibleTrackedIds = addToSet(mInvisibleTrackedIds, id);
+ }
+ }
+ }
+
+ if (mVisibleTrackedIds == null) {
+ finishSessionLocked();
+ }
+ }
+
+ /**
+ * Called once the client becomes visible.
+ *
+ * @see AutofillClient#isVisibleForAutofill()
+ */
+ void onVisibleForAutofill() {
+ // The visibility of the views might have changed while the client was not started,
+ // hence update the visibility state for all views.
+ AutofillClient client = getClientLocked();
+ ArraySet<AutofillId> updatedVisibleTrackedIds = null;
+ ArraySet<AutofillId> updatedInvisibleTrackedIds = null;
+ if (client != null) {
+ if (mInvisibleTrackedIds != null) {
+ for (AutofillId id : mInvisibleTrackedIds) {
+ if (client.getViewVisibility(id.getViewId())) {
+ updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
+
+ if (DEBUG) {
+ Log.i(TAG, "onVisibleForAutofill() " + id + " became visible");
+ }
+ } else {
+ updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
+ }
+ }
+ }
+
+ if (mVisibleTrackedIds != null) {
+ for (AutofillId id : mVisibleTrackedIds) {
+ if (client.getViewVisibility(id.getViewId())) {
+ updatedVisibleTrackedIds = addToSet(updatedVisibleTrackedIds, id);
+ } else {
+ updatedInvisibleTrackedIds = addToSet(updatedInvisibleTrackedIds, id);
+
+ if (DEBUG) {
+ Log.i(TAG, "onVisibleForAutofill() " + id + " became invisible");
+ }
+ }
+ }
+ }
+
+ mInvisibleTrackedIds = updatedInvisibleTrackedIds;
+ mVisibleTrackedIds = updatedVisibleTrackedIds;
+ }
+
+ if (mVisibleTrackedIds == null) {
+ finishSessionLocked();
+ }
+ }
+ }
+
+ /**
* Callback for auto-fill related events.
*
* <p>Typically used for applications that display their own "auto-complete" views, so they can
@@ -999,73 +1316,57 @@
public void setState(boolean enabled) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.mContext.getMainThreadHandler().post(() -> {
- synchronized (afm.mLock) {
- afm.mEnabled = enabled;
- }
- });
+ afm.mContext.getMainThreadHandler().post(() -> afm.setState(enabled));
}
}
@Override
- public void autofill(IBinder windowToken, List<AutofillId> ids,
+ public void autofill(int sessionId, IBinder windowToken, List<AutofillId> ids,
List<AutofillValue> values) {
// TODO(b/33197203): must keep the dataset so subsequent calls pass the same
// dataset.extras to service
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.mContext.getMainThreadHandler().post(() ->
- afm.handleAutofill(windowToken, ids, values));
+ afm.mContext.getMainThreadHandler().post(
+ () -> afm.autofill(sessionId, windowToken, ids, values));
}
}
@Override
- public void authenticate(IntentSender intent, Intent fillInIntent) {
+ public void authenticate(int sessionId, IntentSender intent, Intent fillInIntent) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.mContext.getMainThreadHandler().post(() -> {
- if (afm.getClientLocked() != null) {
- afm.getClientLocked().autofillCallbackAuthenticate(intent, fillInIntent);
- }
- });
+ afm.mContext.getMainThreadHandler().post(
+ () -> afm.authenticate(sessionId, intent, fillInIntent));
}
}
@Override
- public void requestShowFillUi(IBinder windowToken, AutofillId id,
+ public void requestShowFillUi(int sessionId, IBinder windowToken, AutofillId id,
int width, int height, Rect anchorBounds, IAutofillWindowPresenter presenter) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.mContext.getMainThreadHandler().post(() -> {
- if (afm.getClientLocked() != null) {
- afm.requestShowFillUi(windowToken, id, width,
- height, anchorBounds, presenter);
- }
- });
+ afm.mContext.getMainThreadHandler().post(
+ () -> afm.requestShowFillUi(sessionId, windowToken, id, width, height,
+ anchorBounds, presenter));
}
}
@Override
- public void requestHideFillUi(IBinder windowToken, AutofillId id) {
+ public void requestHideFillUi(int sessionId, IBinder windowToken, AutofillId id) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.mContext.getMainThreadHandler().post(() -> {
- if (afm.getClientLocked() != null) {
- afm.requestHideFillUi(windowToken, id);
- }
- });
+ afm.mContext.getMainThreadHandler().post(
+ () -> afm.requestHideFillUi(sessionId, windowToken, id));
}
}
@Override
- public void notifyNoFillUi(IBinder windowToken, AutofillId id) {
+ public void notifyNoFillUi(int sessionId, IBinder windowToken, AutofillId id) {
final AutofillManager afm = mAfm.get();
if (afm != null) {
- afm.mContext.getMainThreadHandler().post(() -> {
- if (afm.getClientLocked() != null) {
- afm.notifyNoFillUi(windowToken, id);
- }
- });
+ afm.mContext.getMainThreadHandler().post(
+ () -> afm.notifyNoFillUi(sessionId, windowToken, id));
}
}
@@ -1082,5 +1383,16 @@
});
}
}
+
+ @Override
+ public void setTrackedViews(int sessionId, List<AutofillId> ids,
+ boolean saveOnAllViewsInvisible) {
+ final AutofillManager afm = mAfm.get();
+ if (afm != null) {
+ afm.mContext.getMainThreadHandler().post(
+ () -> afm.setTrackedViews(sessionId, ids, saveOnAllViewsInvisible)
+ );
+ }
+ }
}
}
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index 68b3ccabc..df777c4 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -19,6 +19,7 @@
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
+import android.service.autofill.FillEventHistory;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
import android.view.autofill.IAutoFillManagerClient;
@@ -33,6 +34,7 @@
int startSession(IBinder activityToken, IBinder windowToken, in IBinder appCallback,
in AutofillId autoFillId, in Rect bounds, in AutofillValue value, int userId,
boolean hasCallback, int flags, String packageName);
+ FillEventHistory getFillEventHistory();
boolean restoreSession(int sessionId, in IBinder activityToken, in IBinder appCallback);
void setWindow(int sessionId, in IBinder windowToken);
void updateSession(int sessionId, in AutofillId id, in Rect bounds,
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 176eaac..1a6bad2 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -40,28 +40,36 @@
/**
* Autofills the activity with the contents of a dataset.
*/
- void autofill(in IBinder windowToken, in List<AutofillId> ids, in List<AutofillValue> values);
+ void autofill(int sessionId, in IBinder windowToken, in List<AutofillId> ids,
+ in List<AutofillValue> values);
/**
* Authenticates a fill response or a data set.
*/
- void authenticate(in IntentSender intent, in Intent fillInIntent);
+ void authenticate(int sessionId, in IntentSender intent, in Intent fillInIntent);
+
+ /**
+ * Sets the views to track. If saveOnAllViewsInvisible is set and all these view are invisible
+ * the session is finished automatically.
+ */
+ void setTrackedViews(int sessionId, in List<AutofillId> ids,
+ boolean saveOnAllViewsInvisible);
/**
* Requests showing the fill UI.
*/
- void requestShowFillUi(in IBinder windowToken, in AutofillId id, int width,
+ void requestShowFillUi(int sessionId, in IBinder windowToken, in AutofillId id, int width,
int height, in Rect anchorBounds, in IAutofillWindowPresenter presenter);
/**
* Requests hiding the fill UI.
*/
- void requestHideFillUi(in IBinder windowToken, in AutofillId id);
+ void requestHideFillUi(int sessionId, in IBinder windowToken, in AutofillId id);
/**
* Notifies no fill UI will be shown.
*/
- void notifyNoFillUi(in IBinder windowToken, in AutofillId id);
+ void notifyNoFillUi(int sessionId, in IBinder windowToken, in AutofillId id);
/**
* Starts the provided intent sender
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 2f12e9b..28d9fcf 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -155,7 +155,7 @@
public static final int IME_ACTION_PREVIOUS = 0x00000007;
/**
- * Flag of {@link #imeOptions}: used to request that the IME does not update any personalized
+ * Flag of {@link #imeOptions}: used to request that the IME should not update any personalized
* data such as typing history and personalized language model based on what the user typed on
* this text editing object. Typical use cases are:
* <ul>
diff --git a/core/java/android/view/textclassifier/TextClassifierImpl.java b/core/java/android/view/textclassifier/TextClassifierImpl.java
index f634a1b..022c157 100644
--- a/core/java/android/view/textclassifier/TextClassifierImpl.java
+++ b/core/java/android/view/textclassifier/TextClassifierImpl.java
@@ -53,7 +53,6 @@
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
-import java.util.StringJoiner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -89,7 +88,7 @@
@Override
public TextSelection suggestSelection(
@NonNull CharSequence text, int selectionStartIndex, int selectionEndIndex,
- LocaleList defaultLocales) {
+ @Nullable LocaleList defaultLocales) {
validateInput(text, selectionStartIndex, selectionEndIndex);
try {
if (text.length() > 0) {
@@ -128,7 +127,8 @@
@Override
public TextClassificationResult getTextClassificationResult(
- @NonNull CharSequence text, int startIndex, int endIndex, LocaleList defaultLocales) {
+ @NonNull CharSequence text, int startIndex, int endIndex,
+ @Nullable LocaleList defaultLocales) {
validateInput(text, startIndex, endIndex);
try {
if (text.length() > 0) {
@@ -156,7 +156,8 @@
}
@Override
- public LinksInfo getLinks(CharSequence text, int linkMask, LocaleList defaultLocales) {
+ public LinksInfo getLinks(
+ @NonNull CharSequence text, int linkMask, @Nullable LocaleList defaultLocales) {
Preconditions.checkArgument(text != null);
try {
return LinksInfoFactory.create(
@@ -199,12 +200,11 @@
@GuardedBy("mSmartSelectionLock") // Do not call outside this lock.
@Nullable
private Locale findBestSupportedLocaleLocked(LocaleList localeList) {
- final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(
- new StringJoiner(",")
- // Specified localeList takes priority over the system default
- .add(localeList.toLanguageTags())
- .add(LocaleList.getDefault().toLanguageTags())
- .toString());
+ // Specified localeList takes priority over the system default, so it is listed first.
+ final String languages = localeList.isEmpty()
+ ? LocaleList.getDefault().toLanguageTags()
+ : localeList.toLanguageTags() + "," + LocaleList.getDefault().toLanguageTags();
+ final List<Locale.LanguageRange> languageRangeList = Locale.LanguageRange.parse(languages);
return Locale.lookup(languageRangeList, loadModelFilePathsLocked().keySet());
}
diff --git a/core/java/android/widget/Chronometer.java b/core/java/android/widget/Chronometer.java
index 6c6079f..d11c03a 100644
--- a/core/java/android/widget/Chronometer.java
+++ b/core/java/android/widget/Chronometer.java
@@ -17,11 +17,13 @@
package android.widget;
import android.content.Context;
+import android.content.Intent;
import android.content.res.TypedArray;
import android.icu.text.MeasureFormat;
import android.icu.text.MeasureFormat.FormatWidth;
import android.icu.util.Measure;
import android.icu.util.MeasureUnit;
+import android.net.Uri;
import android.os.SystemClock;
import android.text.format.DateUtils;
import android.util.AttributeSet;
@@ -148,6 +150,22 @@
}
/**
+ * @return whether this is the final countdown
+ */
+ public boolean isTheFinalCountDown() {
+ try {
+ getContext().startActivity(
+ new Intent(Intent.ACTION_VIEW, Uri.parse("https://youtu.be/9jK-NcRmVcw"))
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+ | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT));
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
* Set the time that the count-up timer is in reference to.
*
* @param base Use the {@link SystemClock#elapsedRealtime} time base.
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4fb7b19..0d3baa8 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -146,7 +146,7 @@
private static final String UNDO_OWNER_TAG = "Editor";
// Ordering constants used to place the Action Mode or context menu items in their menu.
- private static final int MENU_ITEM_ORDER_ASSIST = 1;
+ private static final int MENU_ITEM_ORDER_ASSIST = 0;
private static final int MENU_ITEM_ORDER_UNDO = 2;
private static final int MENU_ITEM_ORDER_REDO = 3;
private static final int MENU_ITEM_ORDER_CUT = 4;
@@ -156,8 +156,8 @@
private static final int MENU_ITEM_ORDER_PASTE_AS_PLAIN_TEXT = 8;
private static final int MENU_ITEM_ORDER_SELECT_ALL = 9;
private static final int MENU_ITEM_ORDER_REPLACE = 10;
- private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 11;
- private static final int MENU_ITEM_ORDER_AUTOFILL = 12;
+ private static final int MENU_ITEM_ORDER_AUTOFILL = 11;
+ private static final int MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START = 100;
// Each Editor manages its own undo stack.
private final UndoManager mUndoManager = new UndoManager();
@@ -6322,9 +6322,10 @@
* Adds "PROCESS_TEXT" menu items to the specified menu.
*/
public void onInitializeMenu(Menu menu) {
- int i = 0;
+ final int size = mSupportedActivities.size();
loadSupportedActivities();
- for (ResolveInfo resolveInfo : mSupportedActivities) {
+ for (int i = 0; i < size; i++) {
+ final ResolveInfo resolveInfo = mSupportedActivities.get(i);
menu.add(Menu.NONE, Menu.NONE,
Editor.MENU_ITEM_ORDER_PROCESS_TEXT_INTENT_ACTIONS_START + i++,
getLabel(resolveInfo))
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index f9f10af..f1a3ff5 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8196,9 +8196,11 @@
mTempTextPaint.set(getPaint());
mTempTextPaint.setTextSize(suggestedSizeInPx);
+ final int availableWidth = mHorizontallyScrolling
+ ? VERY_WIDE
+ : getMeasuredWidth() - getTotalPaddingLeft() - getTotalPaddingRight();
final StaticLayout.Builder layoutBuilder = StaticLayout.Builder.obtain(
- text, 0, text.length(), mTempTextPaint,
- getMeasuredWidth() - getTotalPaddingLeft() - getTotalPaddingRight());
+ text, 0, text.length(), mTempTextPaint, availableWidth);
layoutBuilder.setAlignment(getLayoutAlignment())
.setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
index 032c775..b9ed963 100644
--- a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -34,6 +34,7 @@
import android.widget.TextView;
import com.android.internal.R;
+import com.android.internal.widget.ResolverDrawerLayout;
import java.util.ArrayList;
import java.util.Collections;
@@ -56,6 +57,11 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.accessibility_button_chooser);
+ final ResolverDrawerLayout rdl = findViewById(R.id.contentPanel);
+ if (rdl != null) {
+ rdl.setOnDismissedListener(this::finish);
+ }
+
String component = Settings.Secure.getString(getContentResolver(),
Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT);
if (TextUtils.isEmpty(component)) {
diff --git a/core/java/com/android/internal/app/AlertActivity.java b/core/java/com/android/internal/app/AlertActivity.java
index 35ffa71..999a908 100644
--- a/core/java/com/android/internal/app/AlertActivity.java
+++ b/core/java/com/android/internal/app/AlertActivity.java
@@ -67,10 +67,15 @@
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
- event.setClassName(Dialog.class.getName());
- event.setPackageName(getPackageName());
+ return dispatchPopulateAccessibilityEvent(this, event);
+ }
- ViewGroup.LayoutParams params = getWindow().getAttributes();
+ public static boolean dispatchPopulateAccessibilityEvent(Activity act,
+ AccessibilityEvent event) {
+ event.setClassName(Dialog.class.getName());
+ event.setPackageName(act.getPackageName());
+
+ ViewGroup.LayoutParams params = act.getWindow().getAttributes();
boolean isFullScreen = (params.width == ViewGroup.LayoutParams.MATCH_PARENT) &&
(params.height == ViewGroup.LayoutParams.MATCH_PARENT);
event.setFullScreen(isFullScreen);
@@ -86,8 +91,7 @@
* @see #mAlertParams
*/
protected void setupAlert() {
- mAlertParams.apply(mAlert);
- mAlert.installContent();
+ mAlert.installContent(mAlertParams);
}
@Override
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 95c291a..46cb546 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -247,6 +247,11 @@
return false;
}
+ public void installContent(AlertParams params) {
+ params.apply(this);
+ installContent();
+ }
+
public void installContent() {
int contentView = selectContentView();
mWindow.setContentView(contentView);
diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
index cb2b019..46f47a3 100644
--- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
+++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java
@@ -16,6 +16,10 @@
package com.android.internal.app;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -57,6 +61,10 @@
private final boolean mCountryMode;
private LayoutInflater mInflater;
+ private Locale mDisplayLocale = null;
+ // used to potentially cache a modified Context that uses mDisplayLocale
+ private Context mContextOverride = null;
+
public SuggestedLocaleAdapter(Set<LocaleStore.LocaleInfo> localeOptions, boolean countryMode) {
mCountryMode = countryMode;
mLocaleOptions = new ArrayList<>(localeOptions.size());
@@ -126,6 +134,31 @@
return position;
}
+ /**
+ * Overrides the locale used to display localized labels. Setting the locale to null will reset
+ * the Adapter to use the default locale for the labels.
+ */
+ public void setDisplayLocale(@NonNull Context context, @Nullable Locale locale) {
+ if (locale == null) {
+ mDisplayLocale = null;
+ mContextOverride = null;
+ } else if (!locale.equals(mDisplayLocale)) {
+ mDisplayLocale = locale;
+ final Configuration configOverride = new Configuration();
+ configOverride.setLocale(locale);
+ mContextOverride = context.createConfigurationContext(configOverride);
+ }
+ }
+
+ private void setTextTo(@NonNull TextView textView, int resId) {
+ if (mContextOverride == null) {
+ textView.setText(resId);
+ } else {
+ textView.setText(mContextOverride.getText(resId));
+ // If mContextOverride is not null, mDisplayLocale can't be null either.
+ }
+ }
+
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null && mInflater == null) {
@@ -143,15 +176,16 @@
}
TextView textView = (TextView) convertView;
if (itemType == TYPE_HEADER_SUGGESTED) {
- textView.setText(R.string.language_picker_section_suggested);
+ setTextTo(textView, R.string.language_picker_section_suggested);
} else {
if (mCountryMode) {
- textView.setText(R.string.region_picker_section_all);
+ setTextTo(textView, R.string.region_picker_section_all);
} else {
- textView.setText(R.string.language_picker_section_all);
+ setTextTo(textView, R.string.language_picker_section_all);
}
}
- textView.setTextLocale(Locale.getDefault());
+ textView.setTextLocale(
+ mDisplayLocale != null ? mDisplayLocale : Locale.getDefault());
break;
default:
// Covers both null, and "reusing" a wrong kind of view
diff --git a/core/java/com/android/internal/graphics/ColorUtils.java b/core/java/com/android/internal/graphics/ColorUtils.java
new file mode 100644
index 0000000..6c1efa4
--- /dev/null
+++ b/core/java/com/android/internal/graphics/ColorUtils.java
@@ -0,0 +1,618 @@
+/*
+ * 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.internal.graphics;
+
+import android.annotation.ColorInt;
+import android.annotation.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.graphics.Color;
+
+/**
+ * Copied from: frameworks/support/core-utils/java/android/support/v4/graphics/ColorUtils.java
+ *
+ * A set of color-related utility methods, building upon those available in {@code Color}.
+ */
+public final class ColorUtils {
+
+ private static final double XYZ_WHITE_REFERENCE_X = 95.047;
+ private static final double XYZ_WHITE_REFERENCE_Y = 100;
+ private static final double XYZ_WHITE_REFERENCE_Z = 108.883;
+ private static final double XYZ_EPSILON = 0.008856;
+ private static final double XYZ_KAPPA = 903.3;
+
+ private static final int MIN_ALPHA_SEARCH_MAX_ITERATIONS = 10;
+ private static final int MIN_ALPHA_SEARCH_PRECISION = 1;
+
+ private static final ThreadLocal<double[]> TEMP_ARRAY = new ThreadLocal<>();
+
+ private ColorUtils() {}
+
+ /**
+ * Composite two potentially translucent colors over each other and returns the result.
+ */
+ public static int compositeColors(@ColorInt int foreground, @ColorInt int background) {
+ int bgAlpha = Color.alpha(background);
+ int fgAlpha = Color.alpha(foreground);
+ int a = compositeAlpha(fgAlpha, bgAlpha);
+
+ int r = compositeComponent(Color.red(foreground), fgAlpha,
+ Color.red(background), bgAlpha, a);
+ int g = compositeComponent(Color.green(foreground), fgAlpha,
+ Color.green(background), bgAlpha, a);
+ int b = compositeComponent(Color.blue(foreground), fgAlpha,
+ Color.blue(background), bgAlpha, a);
+
+ return Color.argb(a, r, g, b);
+ }
+
+ private static int compositeAlpha(int foregroundAlpha, int backgroundAlpha) {
+ return 0xFF - (((0xFF - backgroundAlpha) * (0xFF - foregroundAlpha)) / 0xFF);
+ }
+
+ private static int compositeComponent(int fgC, int fgA, int bgC, int bgA, int a) {
+ if (a == 0) return 0;
+ return ((0xFF * fgC * fgA) + (bgC * bgA * (0xFF - fgA))) / (a * 0xFF);
+ }
+
+ /**
+ * Returns the luminance of a color as a float between {@code 0.0} and {@code 1.0}.
+ * <p>Defined as the Y component in the XYZ representation of {@code color}.</p>
+ */
+ @FloatRange(from = 0.0, to = 1.0)
+ public static double calculateLuminance(@ColorInt int color) {
+ final double[] result = getTempDouble3Array();
+ colorToXYZ(color, result);
+ // Luminance is the Y component
+ return result[1] / 100;
+ }
+
+ /**
+ * Returns the contrast ratio between {@code foreground} and {@code background}.
+ * {@code background} must be opaque.
+ * <p>
+ * Formula defined
+ * <a href="http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef">here</a>.
+ */
+ public static double calculateContrast(@ColorInt int foreground, @ColorInt int background) {
+ if (Color.alpha(background) != 255) {
+ throw new IllegalArgumentException("background can not be translucent: #"
+ + Integer.toHexString(background));
+ }
+ if (Color.alpha(foreground) < 255) {
+ // If the foreground is translucent, composite the foreground over the background
+ foreground = compositeColors(foreground, background);
+ }
+
+ final double luminance1 = calculateLuminance(foreground) + 0.05;
+ final double luminance2 = calculateLuminance(background) + 0.05;
+
+ // Now return the lighter luminance divided by the darker luminance
+ return Math.max(luminance1, luminance2) / Math.min(luminance1, luminance2);
+ }
+
+ /**
+ * Calculates the minimum alpha value which can be applied to {@code foreground} so that would
+ * have a contrast value of at least {@code minContrastRatio} when compared to
+ * {@code background}.
+ *
+ * @param foreground the foreground color
+ * @param background the opaque background color
+ * @param minContrastRatio the minimum contrast ratio
+ * @return the alpha value in the range 0-255, or -1 if no value could be calculated
+ */
+ public static int calculateMinimumAlpha(@ColorInt int foreground, @ColorInt int background,
+ float minContrastRatio) {
+ if (Color.alpha(background) != 255) {
+ throw new IllegalArgumentException("background can not be translucent: #"
+ + Integer.toHexString(background));
+ }
+
+ // First lets check that a fully opaque foreground has sufficient contrast
+ int testForeground = setAlphaComponent(foreground, 255);
+ double testRatio = calculateContrast(testForeground, background);
+ if (testRatio < minContrastRatio) {
+ // Fully opaque foreground does not have sufficient contrast, return error
+ return -1;
+ }
+
+ // Binary search to find a value with the minimum value which provides sufficient contrast
+ int numIterations = 0;
+ int minAlpha = 0;
+ int maxAlpha = 255;
+
+ while (numIterations <= MIN_ALPHA_SEARCH_MAX_ITERATIONS &&
+ (maxAlpha - minAlpha) > MIN_ALPHA_SEARCH_PRECISION) {
+ final int testAlpha = (minAlpha + maxAlpha) / 2;
+
+ testForeground = setAlphaComponent(foreground, testAlpha);
+ testRatio = calculateContrast(testForeground, background);
+
+ if (testRatio < minContrastRatio) {
+ minAlpha = testAlpha;
+ } else {
+ maxAlpha = testAlpha;
+ }
+
+ numIterations++;
+ }
+
+ // Conservatively return the max of the range of possible alphas, which is known to pass.
+ return maxAlpha;
+ }
+
+ /**
+ * Convert RGB components to HSL (hue-saturation-lightness).
+ * <ul>
+ * <li>outHsl[0] is Hue [0 .. 360)</li>
+ * <li>outHsl[1] is Saturation [0...1]</li>
+ * <li>outHsl[2] is Lightness [0...1]</li>
+ * </ul>
+ *
+ * @param r red component value [0..255]
+ * @param g green component value [0..255]
+ * @param b blue component value [0..255]
+ * @param outHsl 3-element array which holds the resulting HSL components
+ */
+ public static void RGBToHSL(@IntRange(from = 0x0, to = 0xFF) int r,
+ @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
+ @NonNull float[] outHsl) {
+ final float rf = r / 255f;
+ final float gf = g / 255f;
+ final float bf = b / 255f;
+
+ final float max = Math.max(rf, Math.max(gf, bf));
+ final float min = Math.min(rf, Math.min(gf, bf));
+ final float deltaMaxMin = max - min;
+
+ float h, s;
+ float l = (max + min) / 2f;
+
+ if (max == min) {
+ // Monochromatic
+ h = s = 0f;
+ } else {
+ if (max == rf) {
+ h = ((gf - bf) / deltaMaxMin) % 6f;
+ } else if (max == gf) {
+ h = ((bf - rf) / deltaMaxMin) + 2f;
+ } else {
+ h = ((rf - gf) / deltaMaxMin) + 4f;
+ }
+
+ s = deltaMaxMin / (1f - Math.abs(2f * l - 1f));
+ }
+
+ h = (h * 60f) % 360f;
+ if (h < 0) {
+ h += 360f;
+ }
+
+ outHsl[0] = constrain(h, 0f, 360f);
+ outHsl[1] = constrain(s, 0f, 1f);
+ outHsl[2] = constrain(l, 0f, 1f);
+ }
+
+ /**
+ * Convert the ARGB color to its HSL (hue-saturation-lightness) components.
+ * <ul>
+ * <li>outHsl[0] is Hue [0 .. 360)</li>
+ * <li>outHsl[1] is Saturation [0...1]</li>
+ * <li>outHsl[2] is Lightness [0...1]</li>
+ * </ul>
+ *
+ * @param color the ARGB color to convert. The alpha component is ignored
+ * @param outHsl 3-element array which holds the resulting HSL components
+ */
+ public static void colorToHSL(@ColorInt int color, @NonNull float[] outHsl) {
+ RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), outHsl);
+ }
+
+ /**
+ * Convert HSL (hue-saturation-lightness) components to a RGB color.
+ * <ul>
+ * <li>hsl[0] is Hue [0 .. 360)</li>
+ * <li>hsl[1] is Saturation [0...1]</li>
+ * <li>hsl[2] is Lightness [0...1]</li>
+ * </ul>
+ * If hsv values are out of range, they are pinned.
+ *
+ * @param hsl 3-element array which holds the input HSL components
+ * @return the resulting RGB color
+ */
+ @ColorInt
+ public static int HSLToColor(@NonNull float[] hsl) {
+ final float h = hsl[0];
+ final float s = hsl[1];
+ final float l = hsl[2];
+
+ final float c = (1f - Math.abs(2 * l - 1f)) * s;
+ final float m = l - 0.5f * c;
+ final float x = c * (1f - Math.abs((h / 60f % 2f) - 1f));
+
+ final int hueSegment = (int) h / 60;
+
+ int r = 0, g = 0, b = 0;
+
+ switch (hueSegment) {
+ case 0:
+ r = Math.round(255 * (c + m));
+ g = Math.round(255 * (x + m));
+ b = Math.round(255 * m);
+ break;
+ case 1:
+ r = Math.round(255 * (x + m));
+ g = Math.round(255 * (c + m));
+ b = Math.round(255 * m);
+ break;
+ case 2:
+ r = Math.round(255 * m);
+ g = Math.round(255 * (c + m));
+ b = Math.round(255 * (x + m));
+ break;
+ case 3:
+ r = Math.round(255 * m);
+ g = Math.round(255 * (x + m));
+ b = Math.round(255 * (c + m));
+ break;
+ case 4:
+ r = Math.round(255 * (x + m));
+ g = Math.round(255 * m);
+ b = Math.round(255 * (c + m));
+ break;
+ case 5:
+ case 6:
+ r = Math.round(255 * (c + m));
+ g = Math.round(255 * m);
+ b = Math.round(255 * (x + m));
+ break;
+ }
+
+ r = constrain(r, 0, 255);
+ g = constrain(g, 0, 255);
+ b = constrain(b, 0, 255);
+
+ return Color.rgb(r, g, b);
+ }
+
+ /**
+ * Set the alpha component of {@code color} to be {@code alpha}.
+ */
+ @ColorInt
+ public static int setAlphaComponent(@ColorInt int color,
+ @IntRange(from = 0x0, to = 0xFF) int alpha) {
+ if (alpha < 0 || alpha > 255) {
+ throw new IllegalArgumentException("alpha must be between 0 and 255.");
+ }
+ return (color & 0x00ffffff) | (alpha << 24);
+ }
+
+ /**
+ * Convert the ARGB color to its CIE Lab representative components.
+ *
+ * @param color the ARGB color to convert. The alpha component is ignored
+ * @param outLab 3-element array which holds the resulting LAB components
+ */
+ public static void colorToLAB(@ColorInt int color, @NonNull double[] outLab) {
+ RGBToLAB(Color.red(color), Color.green(color), Color.blue(color), outLab);
+ }
+
+ /**
+ * Convert RGB components to its CIE Lab representative components.
+ *
+ * <ul>
+ * <li>outLab[0] is L [0 ...1)</li>
+ * <li>outLab[1] is a [-128...127)</li>
+ * <li>outLab[2] is b [-128...127)</li>
+ * </ul>
+ *
+ * @param r red component value [0..255]
+ * @param g green component value [0..255]
+ * @param b blue component value [0..255]
+ * @param outLab 3-element array which holds the resulting LAB components
+ */
+ public static void RGBToLAB(@IntRange(from = 0x0, to = 0xFF) int r,
+ @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
+ @NonNull double[] outLab) {
+ // First we convert RGB to XYZ
+ RGBToXYZ(r, g, b, outLab);
+ // outLab now contains XYZ
+ XYZToLAB(outLab[0], outLab[1], outLab[2], outLab);
+ // outLab now contains LAB representation
+ }
+
+ /**
+ * Convert the ARGB color to its CIE XYZ representative components.
+ *
+ * <p>The resulting XYZ representation will use the D65 illuminant and the CIE
+ * 2° Standard Observer (1931).</p>
+ *
+ * <ul>
+ * <li>outXyz[0] is X [0 ...95.047)</li>
+ * <li>outXyz[1] is Y [0...100)</li>
+ * <li>outXyz[2] is Z [0...108.883)</li>
+ * </ul>
+ *
+ * @param color the ARGB color to convert. The alpha component is ignored
+ * @param outXyz 3-element array which holds the resulting LAB components
+ */
+ public static void colorToXYZ(@ColorInt int color, @NonNull double[] outXyz) {
+ RGBToXYZ(Color.red(color), Color.green(color), Color.blue(color), outXyz);
+ }
+
+ /**
+ * Convert RGB components to its CIE XYZ representative components.
+ *
+ * <p>The resulting XYZ representation will use the D65 illuminant and the CIE
+ * 2° Standard Observer (1931).</p>
+ *
+ * <ul>
+ * <li>outXyz[0] is X [0 ...95.047)</li>
+ * <li>outXyz[1] is Y [0...100)</li>
+ * <li>outXyz[2] is Z [0...108.883)</li>
+ * </ul>
+ *
+ * @param r red component value [0..255]
+ * @param g green component value [0..255]
+ * @param b blue component value [0..255]
+ * @param outXyz 3-element array which holds the resulting XYZ components
+ */
+ public static void RGBToXYZ(@IntRange(from = 0x0, to = 0xFF) int r,
+ @IntRange(from = 0x0, to = 0xFF) int g, @IntRange(from = 0x0, to = 0xFF) int b,
+ @NonNull double[] outXyz) {
+ if (outXyz.length != 3) {
+ throw new IllegalArgumentException("outXyz must have a length of 3.");
+ }
+
+ double sr = r / 255.0;
+ sr = sr < 0.04045 ? sr / 12.92 : Math.pow((sr + 0.055) / 1.055, 2.4);
+ double sg = g / 255.0;
+ sg = sg < 0.04045 ? sg / 12.92 : Math.pow((sg + 0.055) / 1.055, 2.4);
+ double sb = b / 255.0;
+ sb = sb < 0.04045 ? sb / 12.92 : Math.pow((sb + 0.055) / 1.055, 2.4);
+
+ outXyz[0] = 100 * (sr * 0.4124 + sg * 0.3576 + sb * 0.1805);
+ outXyz[1] = 100 * (sr * 0.2126 + sg * 0.7152 + sb * 0.0722);
+ outXyz[2] = 100 * (sr * 0.0193 + sg * 0.1192 + sb * 0.9505);
+ }
+
+ /**
+ * Converts a color from CIE XYZ to CIE Lab representation.
+ *
+ * <p>This method expects the XYZ representation to use the D65 illuminant and the CIE
+ * 2° Standard Observer (1931).</p>
+ *
+ * <ul>
+ * <li>outLab[0] is L [0 ...1)</li>
+ * <li>outLab[1] is a [-128...127)</li>
+ * <li>outLab[2] is b [-128...127)</li>
+ * </ul>
+ *
+ * @param x X component value [0...95.047)
+ * @param y Y component value [0...100)
+ * @param z Z component value [0...108.883)
+ * @param outLab 3-element array which holds the resulting Lab components
+ */
+ public static void XYZToLAB(@FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_X) double x,
+ @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Y) double y,
+ @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Z) double z,
+ @NonNull double[] outLab) {
+ if (outLab.length != 3) {
+ throw new IllegalArgumentException("outLab must have a length of 3.");
+ }
+ x = pivotXyzComponent(x / XYZ_WHITE_REFERENCE_X);
+ y = pivotXyzComponent(y / XYZ_WHITE_REFERENCE_Y);
+ z = pivotXyzComponent(z / XYZ_WHITE_REFERENCE_Z);
+ outLab[0] = Math.max(0, 116 * y - 16);
+ outLab[1] = 500 * (x - y);
+ outLab[2] = 200 * (y - z);
+ }
+
+ /**
+ * Converts a color from CIE Lab to CIE XYZ representation.
+ *
+ * <p>The resulting XYZ representation will use the D65 illuminant and the CIE
+ * 2° Standard Observer (1931).</p>
+ *
+ * <ul>
+ * <li>outXyz[0] is X [0 ...95.047)</li>
+ * <li>outXyz[1] is Y [0...100)</li>
+ * <li>outXyz[2] is Z [0...108.883)</li>
+ * </ul>
+ *
+ * @param l L component value [0...100)
+ * @param a A component value [-128...127)
+ * @param b B component value [-128...127)
+ * @param outXyz 3-element array which holds the resulting XYZ components
+ */
+ public static void LABToXYZ(@FloatRange(from = 0f, to = 100) final double l,
+ @FloatRange(from = -128, to = 127) final double a,
+ @FloatRange(from = -128, to = 127) final double b,
+ @NonNull double[] outXyz) {
+ final double fy = (l + 16) / 116;
+ final double fx = a / 500 + fy;
+ final double fz = fy - b / 200;
+
+ double tmp = Math.pow(fx, 3);
+ final double xr = tmp > XYZ_EPSILON ? tmp : (116 * fx - 16) / XYZ_KAPPA;
+ final double yr = l > XYZ_KAPPA * XYZ_EPSILON ? Math.pow(fy, 3) : l / XYZ_KAPPA;
+
+ tmp = Math.pow(fz, 3);
+ final double zr = tmp > XYZ_EPSILON ? tmp : (116 * fz - 16) / XYZ_KAPPA;
+
+ outXyz[0] = xr * XYZ_WHITE_REFERENCE_X;
+ outXyz[1] = yr * XYZ_WHITE_REFERENCE_Y;
+ outXyz[2] = zr * XYZ_WHITE_REFERENCE_Z;
+ }
+
+ /**
+ * Converts a color from CIE XYZ to its RGB representation.
+ *
+ * <p>This method expects the XYZ representation to use the D65 illuminant and the CIE
+ * 2° Standard Observer (1931).</p>
+ *
+ * @param x X component value [0...95.047)
+ * @param y Y component value [0...100)
+ * @param z Z component value [0...108.883)
+ * @return int containing the RGB representation
+ */
+ @ColorInt
+ public static int XYZToColor(@FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_X) double x,
+ @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Y) double y,
+ @FloatRange(from = 0f, to = XYZ_WHITE_REFERENCE_Z) double z) {
+ double r = (x * 3.2406 + y * -1.5372 + z * -0.4986) / 100;
+ double g = (x * -0.9689 + y * 1.8758 + z * 0.0415) / 100;
+ double b = (x * 0.0557 + y * -0.2040 + z * 1.0570) / 100;
+
+ r = r > 0.0031308 ? 1.055 * Math.pow(r, 1 / 2.4) - 0.055 : 12.92 * r;
+ g = g > 0.0031308 ? 1.055 * Math.pow(g, 1 / 2.4) - 0.055 : 12.92 * g;
+ b = b > 0.0031308 ? 1.055 * Math.pow(b, 1 / 2.4) - 0.055 : 12.92 * b;
+
+ return Color.rgb(
+ constrain((int) Math.round(r * 255), 0, 255),
+ constrain((int) Math.round(g * 255), 0, 255),
+ constrain((int) Math.round(b * 255), 0, 255));
+ }
+
+ /**
+ * Converts a color from CIE Lab to its RGB representation.
+ *
+ * @param l L component value [0...100]
+ * @param a A component value [-128...127]
+ * @param b B component value [-128...127]
+ * @return int containing the RGB representation
+ */
+ @ColorInt
+ public static int LABToColor(@FloatRange(from = 0f, to = 100) final double l,
+ @FloatRange(from = -128, to = 127) final double a,
+ @FloatRange(from = -128, to = 127) final double b) {
+ final double[] result = getTempDouble3Array();
+ LABToXYZ(l, a, b, result);
+ return XYZToColor(result[0], result[1], result[2]);
+ }
+
+ /**
+ * Returns the euclidean distance between two LAB colors.
+ */
+ public static double distanceEuclidean(@NonNull double[] labX, @NonNull double[] labY) {
+ return Math.sqrt(Math.pow(labX[0] - labY[0], 2)
+ + Math.pow(labX[1] - labY[1], 2)
+ + Math.pow(labX[2] - labY[2], 2));
+ }
+
+ private static float constrain(float amount, float low, float high) {
+ return amount < low ? low : (amount > high ? high : amount);
+ }
+
+ private static int constrain(int amount, int low, int high) {
+ return amount < low ? low : (amount > high ? high : amount);
+ }
+
+ private static double pivotXyzComponent(double component) {
+ return component > XYZ_EPSILON
+ ? Math.pow(component, 1 / 3.0)
+ : (XYZ_KAPPA * component + 16) / 116;
+ }
+
+ /**
+ * Blend between two ARGB colors using the given ratio.
+ *
+ * <p>A blend ratio of 0.0 will result in {@code color1}, 0.5 will give an even blend,
+ * 1.0 will result in {@code color2}.</p>
+ *
+ * @param color1 the first ARGB color
+ * @param color2 the second ARGB color
+ * @param ratio the blend ratio of {@code color1} to {@code color2}
+ */
+ @ColorInt
+ public static int blendARGB(@ColorInt int color1, @ColorInt int color2,
+ @FloatRange(from = 0.0, to = 1.0) float ratio) {
+ final float inverseRatio = 1 - ratio;
+ float a = Color.alpha(color1) * inverseRatio + Color.alpha(color2) * ratio;
+ float r = Color.red(color1) * inverseRatio + Color.red(color2) * ratio;
+ float g = Color.green(color1) * inverseRatio + Color.green(color2) * ratio;
+ float b = Color.blue(color1) * inverseRatio + Color.blue(color2) * ratio;
+ return Color.argb((int) a, (int) r, (int) g, (int) b);
+ }
+
+ /**
+ * Blend between {@code hsl1} and {@code hsl2} using the given ratio. This will interpolate
+ * the hue using the shortest angle.
+ *
+ * <p>A blend ratio of 0.0 will result in {@code hsl1}, 0.5 will give an even blend,
+ * 1.0 will result in {@code hsl2}.</p>
+ *
+ * @param hsl1 3-element array which holds the first HSL color
+ * @param hsl2 3-element array which holds the second HSL color
+ * @param ratio the blend ratio of {@code hsl1} to {@code hsl2}
+ * @param outResult 3-element array which holds the resulting HSL components
+ */
+ public static void blendHSL(@NonNull float[] hsl1, @NonNull float[] hsl2,
+ @FloatRange(from = 0.0, to = 1.0) float ratio, @NonNull float[] outResult) {
+ if (outResult.length != 3) {
+ throw new IllegalArgumentException("result must have a length of 3.");
+ }
+ final float inverseRatio = 1 - ratio;
+ // Since hue is circular we will need to interpolate carefully
+ outResult[0] = circularInterpolate(hsl1[0], hsl2[0], ratio);
+ outResult[1] = hsl1[1] * inverseRatio + hsl2[1] * ratio;
+ outResult[2] = hsl1[2] * inverseRatio + hsl2[2] * ratio;
+ }
+
+ /**
+ * Blend between two CIE-LAB colors using the given ratio.
+ *
+ * <p>A blend ratio of 0.0 will result in {@code lab1}, 0.5 will give an even blend,
+ * 1.0 will result in {@code lab2}.</p>
+ *
+ * @param lab1 3-element array which holds the first LAB color
+ * @param lab2 3-element array which holds the second LAB color
+ * @param ratio the blend ratio of {@code lab1} to {@code lab2}
+ * @param outResult 3-element array which holds the resulting LAB components
+ */
+ public static void blendLAB(@NonNull double[] lab1, @NonNull double[] lab2,
+ @FloatRange(from = 0.0, to = 1.0) double ratio, @NonNull double[] outResult) {
+ if (outResult.length != 3) {
+ throw new IllegalArgumentException("outResult must have a length of 3.");
+ }
+ final double inverseRatio = 1 - ratio;
+ outResult[0] = lab1[0] * inverseRatio + lab2[0] * ratio;
+ outResult[1] = lab1[1] * inverseRatio + lab2[1] * ratio;
+ outResult[2] = lab1[2] * inverseRatio + lab2[2] * ratio;
+ }
+
+ static float circularInterpolate(float a, float b, float f) {
+ if (Math.abs(b - a) > 180) {
+ if (b > a) {
+ a += 360;
+ } else {
+ b += 360;
+ }
+ }
+ return (a + ((b - a) * f)) % 360;
+ }
+
+ private static double[] getTempDouble3Array() {
+ double[] result = TEMP_ARRAY.get();
+ if (result == null) {
+ result = new double[3];
+ TEMP_ARRAY.set(result);
+ }
+ return result;
+ }
+
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java b/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java
new file mode 100644
index 0000000..2d0ad66
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/ColorCutQuantizer.java
@@ -0,0 +1,535 @@
+/*
+ * 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.internal.graphics.palette;
+
+/*
+ * Copyright 2014 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.
+ */
+
+import android.graphics.Color;
+import android.util.TimingLogger;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.List;
+import java.util.PriorityQueue;
+
+import com.android.internal.graphics.ColorUtils;
+import com.android.internal.graphics.palette.Palette.Swatch;
+
+/**
+ * Copied from: frameworks/support/v7/palette/src/main/java/android/support/v7/
+ * graphics/ColorCutQuantizer.java
+ *
+ * An color quantizer based on the Median-cut algorithm, but optimized for picking out distinct
+ * colors rather than representation colors.
+ *
+ * The color space is represented as a 3-dimensional cube with each dimension being an RGB
+ * component. The cube is then repeatedly divided until we have reduced the color space to the
+ * requested number of colors. An average color is then generated from each cube.
+ *
+ * What makes this different to median-cut is that median-cut divided cubes so that all of the cubes
+ * have roughly the same population, where this quantizer divides boxes based on their color volume.
+ * This means that the color space is divided into distinct colors, rather than representative
+ * colors.
+ */
+final class ColorCutQuantizer {
+
+ private static final String LOG_TAG = "ColorCutQuantizer";
+ private static final boolean LOG_TIMINGS = false;
+
+ static final int COMPONENT_RED = -3;
+ static final int COMPONENT_GREEN = -2;
+ static final int COMPONENT_BLUE = -1;
+
+ private static final int QUANTIZE_WORD_WIDTH = 5;
+ private static final int QUANTIZE_WORD_MASK = (1 << QUANTIZE_WORD_WIDTH) - 1;
+
+ final int[] mColors;
+ final int[] mHistogram;
+ final List<Swatch> mQuantizedColors;
+ final TimingLogger mTimingLogger;
+ final Palette.Filter[] mFilters;
+
+ private final float[] mTempHsl = new float[3];
+
+ /**
+ * Constructor.
+ *
+ * @param pixels histogram representing an image's pixel data
+ * @param maxColors The maximum number of colors that should be in the result palette.
+ * @param filters Set of filters to use in the quantization stage
+ */
+ ColorCutQuantizer(final int[] pixels, final int maxColors, final Palette.Filter[] filters) {
+ mTimingLogger = LOG_TIMINGS ? new TimingLogger(LOG_TAG, "Creation") : null;
+ mFilters = filters;
+
+ final int[] hist = mHistogram = new int[1 << (QUANTIZE_WORD_WIDTH * 3)];
+ for (int i = 0; i < pixels.length; i++) {
+ final int quantizedColor = quantizeFromRgb888(pixels[i]);
+ // Now update the pixel value to the quantized value
+ pixels[i] = quantizedColor;
+ // And update the histogram
+ hist[quantizedColor]++;
+ }
+
+ if (LOG_TIMINGS) {
+ mTimingLogger.addSplit("Histogram created");
+ }
+
+ // Now let's count the number of distinct colors
+ int distinctColorCount = 0;
+ for (int color = 0; color < hist.length; color++) {
+ if (hist[color] > 0 && shouldIgnoreColor(color)) {
+ // If we should ignore the color, set the population to 0
+ hist[color] = 0;
+ }
+ if (hist[color] > 0) {
+ // If the color has population, increase the distinct color count
+ distinctColorCount++;
+ }
+ }
+
+ if (LOG_TIMINGS) {
+ mTimingLogger.addSplit("Filtered colors and distinct colors counted");
+ }
+
+ // Now lets go through create an array consisting of only distinct colors
+ final int[] colors = mColors = new int[distinctColorCount];
+ int distinctColorIndex = 0;
+ for (int color = 0; color < hist.length; color++) {
+ if (hist[color] > 0) {
+ colors[distinctColorIndex++] = color;
+ }
+ }
+
+ if (LOG_TIMINGS) {
+ mTimingLogger.addSplit("Distinct colors copied into array");
+ }
+
+ if (distinctColorCount <= maxColors) {
+ // The image has fewer colors than the maximum requested, so just return the colors
+ mQuantizedColors = new ArrayList<>();
+ for (int color : colors) {
+ mQuantizedColors.add(new Swatch(approximateToRgb888(color), hist[color]));
+ }
+
+ if (LOG_TIMINGS) {
+ mTimingLogger.addSplit("Too few colors present. Copied to Swatches");
+ mTimingLogger.dumpToLog();
+ }
+ } else {
+ // We need use quantization to reduce the number of colors
+ mQuantizedColors = quantizePixels(maxColors);
+
+ if (LOG_TIMINGS) {
+ mTimingLogger.addSplit("Quantized colors computed");
+ mTimingLogger.dumpToLog();
+ }
+ }
+ }
+
+ /**
+ * @return the list of quantized colors
+ */
+ List<Swatch> getQuantizedColors() {
+ return mQuantizedColors;
+ }
+
+ private List<Swatch> quantizePixels(int maxColors) {
+ // Create the priority queue which is sorted by volume descending. This means we always
+ // split the largest box in the queue
+ final PriorityQueue<Vbox> pq = new PriorityQueue<>(maxColors, VBOX_COMPARATOR_VOLUME);
+
+ // To start, offer a box which contains all of the colors
+ pq.offer(new Vbox(0, mColors.length - 1));
+
+ // Now go through the boxes, splitting them until we have reached maxColors or there are no
+ // more boxes to split
+ splitBoxes(pq, maxColors);
+
+ // Finally, return the average colors of the color boxes
+ return generateAverageColors(pq);
+ }
+
+ /**
+ * Iterate through the {@link java.util.Queue}, popping
+ * {@link ColorCutQuantizer.Vbox} objects from the queue
+ * and splitting them. Once split, the new box and the remaining box are offered back to the
+ * queue.
+ *
+ * @param queue {@link java.util.PriorityQueue} to poll for boxes
+ * @param maxSize Maximum amount of boxes to split
+ */
+ private void splitBoxes(final PriorityQueue<Vbox> queue, final int maxSize) {
+ while (queue.size() < maxSize) {
+ final Vbox vbox = queue.poll();
+
+ if (vbox != null && vbox.canSplit()) {
+ // First split the box, and offer the result
+ queue.offer(vbox.splitBox());
+
+ if (LOG_TIMINGS) {
+ mTimingLogger.addSplit("Box split");
+ }
+ // Then offer the box back
+ queue.offer(vbox);
+ } else {
+ if (LOG_TIMINGS) {
+ mTimingLogger.addSplit("All boxes split");
+ }
+ // If we get here then there are no more boxes to split, so return
+ return;
+ }
+ }
+ }
+
+ private List<Swatch> generateAverageColors(Collection<Vbox> vboxes) {
+ ArrayList<Swatch> colors = new ArrayList<>(vboxes.size());
+ for (Vbox vbox : vboxes) {
+ Swatch swatch = vbox.getAverageColor();
+ if (!shouldIgnoreColor(swatch)) {
+ // As we're averaging a color box, we can still get colors which we do not want, so
+ // we check again here
+ colors.add(swatch);
+ }
+ }
+ return colors;
+ }
+
+ /**
+ * Represents a tightly fitting box around a color space.
+ */
+ private class Vbox {
+ // lower and upper index are inclusive
+ private int mLowerIndex;
+ private int mUpperIndex;
+ // Population of colors within this box
+ private int mPopulation;
+
+ private int mMinRed, mMaxRed;
+ private int mMinGreen, mMaxGreen;
+ private int mMinBlue, mMaxBlue;
+
+ Vbox(int lowerIndex, int upperIndex) {
+ mLowerIndex = lowerIndex;
+ mUpperIndex = upperIndex;
+ fitBox();
+ }
+
+ final int getVolume() {
+ return (mMaxRed - mMinRed + 1) * (mMaxGreen - mMinGreen + 1) *
+ (mMaxBlue - mMinBlue + 1);
+ }
+
+ final boolean canSplit() {
+ return getColorCount() > 1;
+ }
+
+ final int getColorCount() {
+ return 1 + mUpperIndex - mLowerIndex;
+ }
+
+ /**
+ * Recomputes the boundaries of this box to tightly fit the colors within the box.
+ */
+ final void fitBox() {
+ final int[] colors = mColors;
+ final int[] hist = mHistogram;
+
+ // Reset the min and max to opposite values
+ int minRed, minGreen, minBlue;
+ minRed = minGreen = minBlue = Integer.MAX_VALUE;
+ int maxRed, maxGreen, maxBlue;
+ maxRed = maxGreen = maxBlue = Integer.MIN_VALUE;
+ int count = 0;
+
+ for (int i = mLowerIndex; i <= mUpperIndex; i++) {
+ final int color = colors[i];
+ count += hist[color];
+
+ final int r = quantizedRed(color);
+ final int g = quantizedGreen(color);
+ final int b = quantizedBlue(color);
+ if (r > maxRed) {
+ maxRed = r;
+ }
+ if (r < minRed) {
+ minRed = r;
+ }
+ if (g > maxGreen) {
+ maxGreen = g;
+ }
+ if (g < minGreen) {
+ minGreen = g;
+ }
+ if (b > maxBlue) {
+ maxBlue = b;
+ }
+ if (b < minBlue) {
+ minBlue = b;
+ }
+ }
+
+ mMinRed = minRed;
+ mMaxRed = maxRed;
+ mMinGreen = minGreen;
+ mMaxGreen = maxGreen;
+ mMinBlue = minBlue;
+ mMaxBlue = maxBlue;
+ mPopulation = count;
+ }
+
+ /**
+ * Split this color box at the mid-point along its longest dimension
+ *
+ * @return the new ColorBox
+ */
+ final Vbox splitBox() {
+ if (!canSplit()) {
+ throw new IllegalStateException("Can not split a box with only 1 color");
+ }
+
+ // find median along the longest dimension
+ final int splitPoint = findSplitPoint();
+
+ Vbox newBox = new Vbox(splitPoint + 1, mUpperIndex);
+
+ // Now change this box's upperIndex and recompute the color boundaries
+ mUpperIndex = splitPoint;
+ fitBox();
+
+ return newBox;
+ }
+
+ /**
+ * @return the dimension which this box is largest in
+ */
+ final int getLongestColorDimension() {
+ final int redLength = mMaxRed - mMinRed;
+ final int greenLength = mMaxGreen - mMinGreen;
+ final int blueLength = mMaxBlue - mMinBlue;
+
+ if (redLength >= greenLength && redLength >= blueLength) {
+ return COMPONENT_RED;
+ } else if (greenLength >= redLength && greenLength >= blueLength) {
+ return COMPONENT_GREEN;
+ } else {
+ return COMPONENT_BLUE;
+ }
+ }
+
+ /**
+ * Finds the point within this box's lowerIndex and upperIndex index of where to split.
+ *
+ * This is calculated by finding the longest color dimension, and then sorting the
+ * sub-array based on that dimension value in each color. The colors are then iterated over
+ * until a color is found with at least the midpoint of the whole box's dimension midpoint.
+ *
+ * @return the index of the colors array to split from
+ */
+ final int findSplitPoint() {
+ final int longestDimension = getLongestColorDimension();
+ final int[] colors = mColors;
+ final int[] hist = mHistogram;
+
+ // We need to sort the colors in this box based on the longest color dimension.
+ // As we can't use a Comparator to define the sort logic, we modify each color so that
+ // its most significant is the desired dimension
+ modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex);
+
+ // Now sort... Arrays.sort uses a exclusive toIndex so we need to add 1
+ Arrays.sort(colors, mLowerIndex, mUpperIndex + 1);
+
+ // Now revert all of the colors so that they are packed as RGB again
+ modifySignificantOctet(colors, longestDimension, mLowerIndex, mUpperIndex);
+
+ final int midPoint = mPopulation / 2;
+ for (int i = mLowerIndex, count = 0; i <= mUpperIndex; i++) {
+ count += hist[colors[i]];
+ if (count >= midPoint) {
+ return i;
+ }
+ }
+
+ return mLowerIndex;
+ }
+
+ /**
+ * @return the average color of this box.
+ */
+ final Swatch getAverageColor() {
+ final int[] colors = mColors;
+ final int[] hist = mHistogram;
+ int redSum = 0;
+ int greenSum = 0;
+ int blueSum = 0;
+ int totalPopulation = 0;
+
+ for (int i = mLowerIndex; i <= mUpperIndex; i++) {
+ final int color = colors[i];
+ final int colorPopulation = hist[color];
+
+ totalPopulation += colorPopulation;
+ redSum += colorPopulation * quantizedRed(color);
+ greenSum += colorPopulation * quantizedGreen(color);
+ blueSum += colorPopulation * quantizedBlue(color);
+ }
+
+ final int redMean = Math.round(redSum / (float) totalPopulation);
+ final int greenMean = Math.round(greenSum / (float) totalPopulation);
+ final int blueMean = Math.round(blueSum / (float) totalPopulation);
+
+ return new Swatch(approximateToRgb888(redMean, greenMean, blueMean), totalPopulation);
+ }
+ }
+
+ /**
+ * Modify the significant octet in a packed color int. Allows sorting based on the value of a
+ * single color component. This relies on all components being the same word size.
+ *
+ * @see Vbox#findSplitPoint()
+ */
+ static void modifySignificantOctet(final int[] a, final int dimension,
+ final int lower, final int upper) {
+ switch (dimension) {
+ case COMPONENT_RED:
+ // Already in RGB, no need to do anything
+ break;
+ case COMPONENT_GREEN:
+ // We need to do a RGB to GRB swap, or vice-versa
+ for (int i = lower; i <= upper; i++) {
+ final int color = a[i];
+ a[i] = quantizedGreen(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)
+ | quantizedRed(color) << QUANTIZE_WORD_WIDTH
+ | quantizedBlue(color);
+ }
+ break;
+ case COMPONENT_BLUE:
+ // We need to do a RGB to BGR swap, or vice-versa
+ for (int i = lower; i <= upper; i++) {
+ final int color = a[i];
+ a[i] = quantizedBlue(color) << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)
+ | quantizedGreen(color) << QUANTIZE_WORD_WIDTH
+ | quantizedRed(color);
+ }
+ break;
+ }
+ }
+
+ private boolean shouldIgnoreColor(int color565) {
+ final int rgb = approximateToRgb888(color565);
+ ColorUtils.colorToHSL(rgb, mTempHsl);
+ return shouldIgnoreColor(rgb, mTempHsl);
+ }
+
+ private boolean shouldIgnoreColor(Swatch color) {
+ return shouldIgnoreColor(color.getRgb(), color.getHsl());
+ }
+
+ private boolean shouldIgnoreColor(int rgb, float[] hsl) {
+ if (mFilters != null && mFilters.length > 0) {
+ for (int i = 0, count = mFilters.length; i < count; i++) {
+ if (!mFilters[i].isAllowed(rgb, hsl)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Comparator which sorts {@link Vbox} instances based on their volume, in descending order
+ */
+ private static final Comparator<Vbox> VBOX_COMPARATOR_VOLUME = new Comparator<Vbox>() {
+ @Override
+ public int compare(Vbox lhs, Vbox rhs) {
+ return rhs.getVolume() - lhs.getVolume();
+ }
+ };
+
+ /**
+ * Quantized a RGB888 value to have a word width of {@value #QUANTIZE_WORD_WIDTH}.
+ */
+ private static int quantizeFromRgb888(int color) {
+ int r = modifyWordWidth(Color.red(color), 8, QUANTIZE_WORD_WIDTH);
+ int g = modifyWordWidth(Color.green(color), 8, QUANTIZE_WORD_WIDTH);
+ int b = modifyWordWidth(Color.blue(color), 8, QUANTIZE_WORD_WIDTH);
+ return r << (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH) | g << QUANTIZE_WORD_WIDTH | b;
+ }
+
+ /**
+ * Quantized RGB888 values to have a word width of {@value #QUANTIZE_WORD_WIDTH}.
+ */
+ static int approximateToRgb888(int r, int g, int b) {
+ return Color.rgb(modifyWordWidth(r, QUANTIZE_WORD_WIDTH, 8),
+ modifyWordWidth(g, QUANTIZE_WORD_WIDTH, 8),
+ modifyWordWidth(b, QUANTIZE_WORD_WIDTH, 8));
+ }
+
+ private static int approximateToRgb888(int color) {
+ return approximateToRgb888(quantizedRed(color), quantizedGreen(color), quantizedBlue(color));
+ }
+
+ /**
+ * @return red component of the quantized color
+ */
+ static int quantizedRed(int color) {
+ return (color >> (QUANTIZE_WORD_WIDTH + QUANTIZE_WORD_WIDTH)) & QUANTIZE_WORD_MASK;
+ }
+
+ /**
+ * @return green component of a quantized color
+ */
+ static int quantizedGreen(int color) {
+ return (color >> QUANTIZE_WORD_WIDTH) & QUANTIZE_WORD_MASK;
+ }
+
+ /**
+ * @return blue component of a quantized color
+ */
+ static int quantizedBlue(int color) {
+ return color & QUANTIZE_WORD_MASK;
+ }
+
+ private static int modifyWordWidth(int value, int currentWidth, int targetWidth) {
+ final int newValue;
+ if (targetWidth > currentWidth) {
+ // If we're approximating up in word width, we'll shift up
+ newValue = value << (targetWidth - currentWidth);
+ } else {
+ // Else, we will just shift and keep the MSB
+ newValue = value >> (currentWidth - targetWidth);
+ }
+ return newValue & ((1 << targetWidth) - 1);
+ }
+
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/graphics/palette/Palette.java b/core/java/com/android/internal/graphics/palette/Palette.java
new file mode 100644
index 0000000..9f1504a
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/Palette.java
@@ -0,0 +1,990 @@
+/*
+ * 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.internal.graphics.palette;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.os.AsyncTask;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.SparseBooleanArray;
+import android.util.TimingLogger;
+
+import com.android.internal.graphics.ColorUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * Copied from: /frameworks/support/v7/palette/src/main/java/android/support/v7/
+ * graphics/Palette.java
+ *
+ * A helper class to extract prominent colors from an image.
+ * <p>
+ * A number of colors with different profiles are extracted from the image:
+ * <ul>
+ * <li>Vibrant</li>
+ * <li>Vibrant Dark</li>
+ * <li>Vibrant Light</li>
+ * <li>Muted</li>
+ * <li>Muted Dark</li>
+ * <li>Muted Light</li>
+ * </ul>
+ * These can be retrieved from the appropriate getter method.
+ *
+ * <p>
+ * Instances are created with a {@link Palette.Builder} which supports several options to tweak the
+ * generated Palette. See that class' documentation for more information.
+ * <p>
+ * Generation should always be completed on a background thread, ideally the one in
+ * which you load your image on. {@link Palette.Builder} supports both synchronous and asynchronous
+ * generation:
+ *
+ * <pre>
+ * // Synchronous
+ * Palette p = Palette.from(bitmap).generate();
+ *
+ * // Asynchronous
+ * Palette.from(bitmap).generate(new PaletteAsyncListener() {
+ * public void onGenerated(Palette p) {
+ * // Use generated instance
+ * }
+ * });
+ * </pre>
+ */
+public final class Palette {
+
+ /**
+ * Listener to be used with {@link #generateAsync(Bitmap, Palette.PaletteAsyncListener)} or
+ * {@link #generateAsync(Bitmap, int, Palette.PaletteAsyncListener)}
+ */
+ public interface PaletteAsyncListener {
+
+ /**
+ * Called when the {@link Palette} has been generated.
+ */
+ void onGenerated(Palette palette);
+ }
+
+ static final int DEFAULT_RESIZE_BITMAP_AREA = 112 * 112;
+ static final int DEFAULT_CALCULATE_NUMBER_COLORS = 16;
+
+ static final float MIN_CONTRAST_TITLE_TEXT = 3.0f;
+ static final float MIN_CONTRAST_BODY_TEXT = 4.5f;
+
+ static final String LOG_TAG = "Palette";
+ static final boolean LOG_TIMINGS = false;
+
+ /**
+ * Start generating a {@link Palette} with the returned {@link Palette.Builder} instance.
+ */
+ public static Palette.Builder from(Bitmap bitmap) {
+ return new Palette.Builder(bitmap);
+ }
+
+ /**
+ * Generate a {@link Palette} from the pre-generated list of {@link Palette.Swatch} swatches.
+ * This is useful for testing, or if you want to resurrect a {@link Palette} instance from a
+ * list of swatches. Will return null if the {@code swatches} is null.
+ */
+ public static Palette from(List<Palette.Swatch> swatches) {
+ return new Palette.Builder(swatches).generate();
+ }
+
+ /**
+ * @deprecated Use {@link Palette.Builder} to generate the Palette.
+ */
+ @Deprecated
+ public static Palette generate(Bitmap bitmap) {
+ return from(bitmap).generate();
+ }
+
+ /**
+ * @deprecated Use {@link Palette.Builder} to generate the Palette.
+ */
+ @Deprecated
+ public static Palette generate(Bitmap bitmap, int numColors) {
+ return from(bitmap).maximumColorCount(numColors).generate();
+ }
+
+ /**
+ * @deprecated Use {@link Palette.Builder} to generate the Palette.
+ */
+ @Deprecated
+ public static AsyncTask<Bitmap, Void, Palette> generateAsync(
+ Bitmap bitmap, Palette.PaletteAsyncListener listener) {
+ return from(bitmap).generate(listener);
+ }
+
+ /**
+ * @deprecated Use {@link Palette.Builder} to generate the Palette.
+ */
+ @Deprecated
+ public static AsyncTask<Bitmap, Void, Palette> generateAsync(
+ final Bitmap bitmap, final int numColors, final Palette.PaletteAsyncListener listener) {
+ return from(bitmap).maximumColorCount(numColors).generate(listener);
+ }
+
+ private final List<Palette.Swatch> mSwatches;
+ private final List<Target> mTargets;
+
+ private final Map<Target, Palette.Swatch> mSelectedSwatches;
+ private final SparseBooleanArray mUsedColors;
+
+ private final Palette.Swatch mDominantSwatch;
+
+ Palette(List<Palette.Swatch> swatches, List<Target> targets) {
+ mSwatches = swatches;
+ mTargets = targets;
+
+ mUsedColors = new SparseBooleanArray();
+ mSelectedSwatches = new ArrayMap<>();
+
+ mDominantSwatch = findDominantSwatch();
+ }
+
+ /**
+ * Returns all of the swatches which make up the palette.
+ */
+ @NonNull
+ public List<Palette.Swatch> getSwatches() {
+ return Collections.unmodifiableList(mSwatches);
+ }
+
+ /**
+ * Returns the targets used to generate this palette.
+ */
+ @NonNull
+ public List<Target> getTargets() {
+ return Collections.unmodifiableList(mTargets);
+ }
+
+ /**
+ * Returns the most vibrant swatch in the palette. Might be null.
+ *
+ * @see Target#VIBRANT
+ */
+ @Nullable
+ public Palette.Swatch getVibrantSwatch() {
+ return getSwatchForTarget(Target.VIBRANT);
+ }
+
+ /**
+ * Returns a light and vibrant swatch from the palette. Might be null.
+ *
+ * @see Target#LIGHT_VIBRANT
+ */
+ @Nullable
+ public Palette.Swatch getLightVibrantSwatch() {
+ return getSwatchForTarget(Target.LIGHT_VIBRANT);
+ }
+
+ /**
+ * Returns a dark and vibrant swatch from the palette. Might be null.
+ *
+ * @see Target#DARK_VIBRANT
+ */
+ @Nullable
+ public Palette.Swatch getDarkVibrantSwatch() {
+ return getSwatchForTarget(Target.DARK_VIBRANT);
+ }
+
+ /**
+ * Returns a muted swatch from the palette. Might be null.
+ *
+ * @see Target#MUTED
+ */
+ @Nullable
+ public Palette.Swatch getMutedSwatch() {
+ return getSwatchForTarget(Target.MUTED);
+ }
+
+ /**
+ * Returns a muted and light swatch from the palette. Might be null.
+ *
+ * @see Target#LIGHT_MUTED
+ */
+ @Nullable
+ public Palette.Swatch getLightMutedSwatch() {
+ return getSwatchForTarget(Target.LIGHT_MUTED);
+ }
+
+ /**
+ * Returns a muted and dark swatch from the palette. Might be null.
+ *
+ * @see Target#DARK_MUTED
+ */
+ @Nullable
+ public Palette.Swatch getDarkMutedSwatch() {
+ return getSwatchForTarget(Target.DARK_MUTED);
+ }
+
+ /**
+ * Returns the most vibrant color in the palette as an RGB packed int.
+ *
+ * @param defaultColor value to return if the swatch isn't available
+ * @see #getVibrantSwatch()
+ */
+ @ColorInt
+ public int getVibrantColor(@ColorInt final int defaultColor) {
+ return getColorForTarget(Target.VIBRANT, defaultColor);
+ }
+
+ /**
+ * Returns a light and vibrant color from the palette as an RGB packed int.
+ *
+ * @param defaultColor value to return if the swatch isn't available
+ * @see #getLightVibrantSwatch()
+ */
+ @ColorInt
+ public int getLightVibrantColor(@ColorInt final int defaultColor) {
+ return getColorForTarget(Target.LIGHT_VIBRANT, defaultColor);
+ }
+
+ /**
+ * Returns a dark and vibrant color from the palette as an RGB packed int.
+ *
+ * @param defaultColor value to return if the swatch isn't available
+ * @see #getDarkVibrantSwatch()
+ */
+ @ColorInt
+ public int getDarkVibrantColor(@ColorInt final int defaultColor) {
+ return getColorForTarget(Target.DARK_VIBRANT, defaultColor);
+ }
+
+ /**
+ * Returns a muted color from the palette as an RGB packed int.
+ *
+ * @param defaultColor value to return if the swatch isn't available
+ * @see #getMutedSwatch()
+ */
+ @ColorInt
+ public int getMutedColor(@ColorInt final int defaultColor) {
+ return getColorForTarget(Target.MUTED, defaultColor);
+ }
+
+ /**
+ * Returns a muted and light color from the palette as an RGB packed int.
+ *
+ * @param defaultColor value to return if the swatch isn't available
+ * @see #getLightMutedSwatch()
+ */
+ @ColorInt
+ public int getLightMutedColor(@ColorInt final int defaultColor) {
+ return getColorForTarget(Target.LIGHT_MUTED, defaultColor);
+ }
+
+ /**
+ * Returns a muted and dark color from the palette as an RGB packed int.
+ *
+ * @param defaultColor value to return if the swatch isn't available
+ * @see #getDarkMutedSwatch()
+ */
+ @ColorInt
+ public int getDarkMutedColor(@ColorInt final int defaultColor) {
+ return getColorForTarget(Target.DARK_MUTED, defaultColor);
+ }
+
+ /**
+ * Returns the selected swatch for the given target from the palette, or {@code null} if one
+ * could not be found.
+ */
+ @Nullable
+ public Palette.Swatch getSwatchForTarget(@NonNull final Target target) {
+ return mSelectedSwatches.get(target);
+ }
+
+ /**
+ * Returns the selected color for the given target from the palette as an RGB packed int.
+ *
+ * @param defaultColor value to return if the swatch isn't available
+ */
+ @ColorInt
+ public int getColorForTarget(@NonNull final Target target, @ColorInt final int defaultColor) {
+ Palette.Swatch swatch = getSwatchForTarget(target);
+ return swatch != null ? swatch.getRgb() : defaultColor;
+ }
+
+ /**
+ * Returns the dominant swatch from the palette.
+ *
+ * <p>The dominant swatch is defined as the swatch with the greatest population (frequency)
+ * within the palette.</p>
+ */
+ @Nullable
+ public Palette.Swatch getDominantSwatch() {
+ return mDominantSwatch;
+ }
+
+ /**
+ * Returns the color of the dominant swatch from the palette, as an RGB packed int.
+ *
+ * @param defaultColor value to return if the swatch isn't available
+ * @see #getDominantSwatch()
+ */
+ @ColorInt
+ public int getDominantColor(@ColorInt int defaultColor) {
+ return mDominantSwatch != null ? mDominantSwatch.getRgb() : defaultColor;
+ }
+
+ void generate() {
+ // We need to make sure that the scored targets are generated first. This is so that
+ // inherited targets have something to inherit from
+ for (int i = 0, count = mTargets.size(); i < count; i++) {
+ final Target target = mTargets.get(i);
+ target.normalizeWeights();
+ mSelectedSwatches.put(target, generateScoredTarget(target));
+ }
+ // We now clear out the used colors
+ mUsedColors.clear();
+ }
+
+ private Palette.Swatch generateScoredTarget(final Target target) {
+ final Palette.Swatch maxScoreSwatch = getMaxScoredSwatchForTarget(target);
+ if (maxScoreSwatch != null && target.isExclusive()) {
+ // If we have a swatch, and the target is exclusive, add the color to the used list
+ mUsedColors.append(maxScoreSwatch.getRgb(), true);
+ }
+ return maxScoreSwatch;
+ }
+
+ private Palette.Swatch getMaxScoredSwatchForTarget(final Target target) {
+ float maxScore = 0;
+ Palette.Swatch maxScoreSwatch = null;
+ for (int i = 0, count = mSwatches.size(); i < count; i++) {
+ final Palette.Swatch swatch = mSwatches.get(i);
+ if (shouldBeScoredForTarget(swatch, target)) {
+ final float score = generateScore(swatch, target);
+ if (maxScoreSwatch == null || score > maxScore) {
+ maxScoreSwatch = swatch;
+ maxScore = score;
+ }
+ }
+ }
+ return maxScoreSwatch;
+ }
+
+ private boolean shouldBeScoredForTarget(final Palette.Swatch swatch, final Target target) {
+ // Check whether the HSL values are within the correct ranges, and this color hasn't
+ // been used yet.
+ final float hsl[] = swatch.getHsl();
+ return hsl[1] >= target.getMinimumSaturation() && hsl[1] <= target.getMaximumSaturation()
+ && hsl[2] >= target.getMinimumLightness() && hsl[2] <= target.getMaximumLightness()
+ && !mUsedColors.get(swatch.getRgb());
+ }
+
+ private float generateScore(Palette.Swatch swatch, Target target) {
+ final float[] hsl = swatch.getHsl();
+
+ float saturationScore = 0;
+ float luminanceScore = 0;
+ float populationScore = 0;
+
+ final int maxPopulation = mDominantSwatch != null ? mDominantSwatch.getPopulation() : 1;
+
+ if (target.getSaturationWeight() > 0) {
+ saturationScore = target.getSaturationWeight()
+ * (1f - Math.abs(hsl[1] - target.getTargetSaturation()));
+ }
+ if (target.getLightnessWeight() > 0) {
+ luminanceScore = target.getLightnessWeight()
+ * (1f - Math.abs(hsl[2] - target.getTargetLightness()));
+ }
+ if (target.getPopulationWeight() > 0) {
+ populationScore = target.getPopulationWeight()
+ * (swatch.getPopulation() / (float) maxPopulation);
+ }
+
+ return saturationScore + luminanceScore + populationScore;
+ }
+
+ private Palette.Swatch findDominantSwatch() {
+ int maxPop = Integer.MIN_VALUE;
+ Palette.Swatch maxSwatch = null;
+ for (int i = 0, count = mSwatches.size(); i < count; i++) {
+ Palette.Swatch swatch = mSwatches.get(i);
+ if (swatch.getPopulation() > maxPop) {
+ maxSwatch = swatch;
+ maxPop = swatch.getPopulation();
+ }
+ }
+ return maxSwatch;
+ }
+
+ private static float[] copyHslValues(Palette.Swatch color) {
+ final float[] newHsl = new float[3];
+ System.arraycopy(color.getHsl(), 0, newHsl, 0, 3);
+ return newHsl;
+ }
+
+ /**
+ * Represents a color swatch generated from an image's palette. The RGB color can be retrieved
+ * by calling {@link #getRgb()}.
+ */
+ public static final class Swatch {
+ private final int mRed, mGreen, mBlue;
+ private final int mRgb;
+ private final int mPopulation;
+
+ private boolean mGeneratedTextColors;
+ private int mTitleTextColor;
+ private int mBodyTextColor;
+
+ private float[] mHsl;
+
+ public Swatch(@ColorInt int color, int population) {
+ mRed = Color.red(color);
+ mGreen = Color.green(color);
+ mBlue = Color.blue(color);
+ mRgb = color;
+ mPopulation = population;
+ }
+
+ Swatch(int red, int green, int blue, int population) {
+ mRed = red;
+ mGreen = green;
+ mBlue = blue;
+ mRgb = Color.rgb(red, green, blue);
+ mPopulation = population;
+ }
+
+ Swatch(float[] hsl, int population) {
+ this(ColorUtils.HSLToColor(hsl), population);
+ mHsl = hsl;
+ }
+
+ /**
+ * @return this swatch's RGB color value
+ */
+ @ColorInt
+ public int getRgb() {
+ return mRgb;
+ }
+
+ /**
+ * Return this swatch's HSL values.
+ * hsv[0] is Hue [0 .. 360)
+ * hsv[1] is Saturation [0...1]
+ * hsv[2] is Lightness [0...1]
+ */
+ public float[] getHsl() {
+ if (mHsl == null) {
+ mHsl = new float[3];
+ }
+ ColorUtils.RGBToHSL(mRed, mGreen, mBlue, mHsl);
+ return mHsl;
+ }
+
+ /**
+ * @return the number of pixels represented by this swatch
+ */
+ public int getPopulation() {
+ return mPopulation;
+ }
+
+ /**
+ * Returns an appropriate color to use for any 'title' text which is displayed over this
+ * {@link Palette.Swatch}'s color. This color is guaranteed to have sufficient contrast.
+ */
+ @ColorInt
+ public int getTitleTextColor() {
+ ensureTextColorsGenerated();
+ return mTitleTextColor;
+ }
+
+ /**
+ * Returns an appropriate color to use for any 'body' text which is displayed over this
+ * {@link Palette.Swatch}'s color. This color is guaranteed to have sufficient contrast.
+ */
+ @ColorInt
+ public int getBodyTextColor() {
+ ensureTextColorsGenerated();
+ return mBodyTextColor;
+ }
+
+ private void ensureTextColorsGenerated() {
+ if (!mGeneratedTextColors) {
+ // First check white, as most colors will be dark
+ final int lightBodyAlpha = ColorUtils.calculateMinimumAlpha(
+ Color.WHITE, mRgb, MIN_CONTRAST_BODY_TEXT);
+ final int lightTitleAlpha = ColorUtils.calculateMinimumAlpha(
+ Color.WHITE, mRgb, MIN_CONTRAST_TITLE_TEXT);
+
+ if (lightBodyAlpha != -1 && lightTitleAlpha != -1) {
+ // If we found valid light values, use them and return
+ mBodyTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha);
+ mTitleTextColor = ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha);
+ mGeneratedTextColors = true;
+ return;
+ }
+
+ final int darkBodyAlpha = ColorUtils.calculateMinimumAlpha(
+ Color.BLACK, mRgb, MIN_CONTRAST_BODY_TEXT);
+ final int darkTitleAlpha = ColorUtils.calculateMinimumAlpha(
+ Color.BLACK, mRgb, MIN_CONTRAST_TITLE_TEXT);
+
+ if (darkBodyAlpha != -1 && darkTitleAlpha != -1) {
+ // If we found valid dark values, use them and return
+ mBodyTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha);
+ mTitleTextColor = ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha);
+ mGeneratedTextColors = true;
+ return;
+ }
+
+ // If we reach here then we can not find title and body values which use the same
+ // lightness, we need to use mismatched values
+ mBodyTextColor = lightBodyAlpha != -1
+ ? ColorUtils.setAlphaComponent(Color.WHITE, lightBodyAlpha)
+ : ColorUtils.setAlphaComponent(Color.BLACK, darkBodyAlpha);
+ mTitleTextColor = lightTitleAlpha != -1
+ ? ColorUtils.setAlphaComponent(Color.WHITE, lightTitleAlpha)
+ : ColorUtils.setAlphaComponent(Color.BLACK, darkTitleAlpha);
+ mGeneratedTextColors = true;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return new StringBuilder(getClass().getSimpleName())
+ .append(" [RGB: #").append(Integer.toHexString(getRgb())).append(']')
+ .append(" [HSL: ").append(Arrays.toString(getHsl())).append(']')
+ .append(" [Population: ").append(mPopulation).append(']')
+ .append(" [Title Text: #").append(Integer.toHexString(getTitleTextColor()))
+ .append(']')
+ .append(" [Body Text: #").append(Integer.toHexString(getBodyTextColor()))
+ .append(']').toString();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ Palette.Swatch
+ swatch = (Palette.Swatch) o;
+ return mPopulation == swatch.mPopulation && mRgb == swatch.mRgb;
+ }
+
+ @Override
+ public int hashCode() {
+ return 31 * mRgb + mPopulation;
+ }
+ }
+
+ /**
+ * Builder class for generating {@link Palette} instances.
+ */
+ public static final class Builder {
+ private final List<Palette.Swatch> mSwatches;
+ private final Bitmap mBitmap;
+
+ private final List<Target> mTargets = new ArrayList<>();
+
+ private int mMaxColors = DEFAULT_CALCULATE_NUMBER_COLORS;
+ private int mResizeArea = DEFAULT_RESIZE_BITMAP_AREA;
+ private int mResizeMaxDimension = -1;
+
+ private final List<Palette.Filter> mFilters = new ArrayList<>();
+ private Rect mRegion;
+
+ /**
+ * Construct a new {@link Palette.Builder} using a source {@link Bitmap}
+ */
+ public Builder(Bitmap bitmap) {
+ if (bitmap == null || bitmap.isRecycled()) {
+ throw new IllegalArgumentException("Bitmap is not valid");
+ }
+ mFilters.add(DEFAULT_FILTER);
+ mBitmap = bitmap;
+ mSwatches = null;
+
+ // Add the default targets
+ mTargets.add(Target.LIGHT_VIBRANT);
+ mTargets.add(Target.VIBRANT);
+ mTargets.add(Target.DARK_VIBRANT);
+ mTargets.add(Target.LIGHT_MUTED);
+ mTargets.add(Target.MUTED);
+ mTargets.add(Target.DARK_MUTED);
+ }
+
+ /**
+ * Construct a new {@link Palette.Builder} using a list of {@link Palette.Swatch} instances.
+ * Typically only used for testing.
+ */
+ public Builder(List<Palette.Swatch> swatches) {
+ if (swatches == null || swatches.isEmpty()) {
+ throw new IllegalArgumentException("List of Swatches is not valid");
+ }
+ mFilters.add(DEFAULT_FILTER);
+ mSwatches = swatches;
+ mBitmap = null;
+ }
+
+ /**
+ * Set the maximum number of colors to use in the quantization step when using a
+ * {@link android.graphics.Bitmap} as the source.
+ * <p>
+ * Good values for depend on the source image type. For landscapes, good values are in
+ * the range 10-16. For images which are largely made up of people's faces then this
+ * value should be increased to ~24.
+ */
+ @NonNull
+ public Palette.Builder maximumColorCount(int colors) {
+ mMaxColors = colors;
+ return this;
+ }
+
+ /**
+ * Set the resize value when using a {@link android.graphics.Bitmap} as the source.
+ * If the bitmap's largest dimension is greater than the value specified, then the bitmap
+ * will be resized so that its largest dimension matches {@code maxDimension}. If the
+ * bitmap is smaller or equal, the original is used as-is.
+ *
+ * @deprecated Using {@link #resizeBitmapArea(int)} is preferred since it can handle
+ * abnormal aspect ratios more gracefully.
+ *
+ * @param maxDimension the number of pixels that the max dimension should be scaled down to,
+ * or any value <= 0 to disable resizing.
+ */
+ @NonNull
+ @Deprecated
+ public Palette.Builder resizeBitmapSize(final int maxDimension) {
+ mResizeMaxDimension = maxDimension;
+ mResizeArea = -1;
+ return this;
+ }
+
+ /**
+ * Set the resize value when using a {@link android.graphics.Bitmap} as the source.
+ * If the bitmap's area is greater than the value specified, then the bitmap
+ * will be resized so that its area matches {@code area}. If the
+ * bitmap is smaller or equal, the original is used as-is.
+ * <p>
+ * This value has a large effect on the processing time. The larger the resized image is,
+ * the greater time it will take to generate the palette. The smaller the image is, the
+ * more detail is lost in the resulting image and thus less precision for color selection.
+ *
+ * @param area the number of pixels that the intermediary scaled down Bitmap should cover,
+ * or any value <= 0 to disable resizing.
+ */
+ @NonNull
+ public Palette.Builder resizeBitmapArea(final int area) {
+ mResizeArea = area;
+ mResizeMaxDimension = -1;
+ return this;
+ }
+
+ /**
+ * Clear all added filters. This includes any default filters added automatically by
+ * {@link Palette}.
+ */
+ @NonNull
+ public Palette.Builder clearFilters() {
+ mFilters.clear();
+ return this;
+ }
+
+ /**
+ * Add a filter to be able to have fine grained control over which colors are
+ * allowed in the resulting palette.
+ *
+ * @param filter filter to add.
+ */
+ @NonNull
+ public Palette.Builder addFilter(
+ Palette.Filter filter) {
+ if (filter != null) {
+ mFilters.add(filter);
+ }
+ return this;
+ }
+
+ /**
+ * Set a region of the bitmap to be used exclusively when calculating the palette.
+ * <p>This only works when the original input is a {@link Bitmap}.</p>
+ *
+ * @param left The left side of the rectangle used for the region.
+ * @param top The top of the rectangle used for the region.
+ * @param right The right side of the rectangle used for the region.
+ * @param bottom The bottom of the rectangle used for the region.
+ */
+ @NonNull
+ public Palette.Builder setRegion(int left, int top, int right, int bottom) {
+ if (mBitmap != null) {
+ if (mRegion == null) mRegion = new Rect();
+ // Set the Rect to be initially the whole Bitmap
+ mRegion.set(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+ // Now just get the intersection with the region
+ if (!mRegion.intersect(left, top, right, bottom)) {
+ throw new IllegalArgumentException("The given region must intersect with "
+ + "the Bitmap's dimensions.");
+ }
+ }
+ return this;
+ }
+
+ /**
+ * Clear any previously region set via {@link #setRegion(int, int, int, int)}.
+ */
+ @NonNull
+ public Palette.Builder clearRegion() {
+ mRegion = null;
+ return this;
+ }
+
+ /**
+ * Add a target profile to be generated in the palette.
+ *
+ * <p>You can retrieve the result via {@link Palette#getSwatchForTarget(Target)}.</p>
+ */
+ @NonNull
+ public Palette.Builder addTarget(@NonNull final Target target) {
+ if (!mTargets.contains(target)) {
+ mTargets.add(target);
+ }
+ return this;
+ }
+
+ /**
+ * Clear all added targets. This includes any default targets added automatically by
+ * {@link Palette}.
+ */
+ @NonNull
+ public Palette.Builder clearTargets() {
+ if (mTargets != null) {
+ mTargets.clear();
+ }
+ return this;
+ }
+
+ /**
+ * Generate and return the {@link Palette} synchronously.
+ */
+ @NonNull
+ public Palette generate() {
+ final TimingLogger logger = LOG_TIMINGS
+ ? new TimingLogger(LOG_TAG, "Generation")
+ : null;
+
+ List<Palette.Swatch> swatches;
+
+ if (mBitmap != null) {
+ // We have a Bitmap so we need to use quantization to reduce the number of colors
+
+ // First we'll scale down the bitmap if needed
+ final Bitmap bitmap = scaleBitmapDown(mBitmap);
+
+ if (logger != null) {
+ logger.addSplit("Processed Bitmap");
+ }
+
+ final Rect region = mRegion;
+ if (bitmap != mBitmap && region != null) {
+ // If we have a scaled bitmap and a selected region, we need to scale down the
+ // region to match the new scale
+ final double scale = bitmap.getWidth() / (double) mBitmap.getWidth();
+ region.left = (int) Math.floor(region.left * scale);
+ region.top = (int) Math.floor(region.top * scale);
+ region.right = Math.min((int) Math.ceil(region.right * scale),
+ bitmap.getWidth());
+ region.bottom = Math.min((int) Math.ceil(region.bottom * scale),
+ bitmap.getHeight());
+ }
+
+ // Now generate a quantizer from the Bitmap
+ final ColorCutQuantizer quantizer = new ColorCutQuantizer(
+ getPixelsFromBitmap(bitmap),
+ mMaxColors,
+ mFilters.isEmpty() ? null : mFilters.toArray(new Palette.Filter[mFilters.size()]));
+
+ // If created a new bitmap, recycle it
+ if (bitmap != mBitmap) {
+ bitmap.recycle();
+ }
+
+ swatches = quantizer.getQuantizedColors();
+
+ if (logger != null) {
+ logger.addSplit("Color quantization completed");
+ }
+ } else {
+ // Else we're using the provided swatches
+ swatches = mSwatches;
+ }
+
+ // Now create a Palette instance
+ final Palette p = new Palette(swatches, mTargets);
+ // And make it generate itself
+ p.generate();
+
+ if (logger != null) {
+ logger.addSplit("Created Palette");
+ logger.dumpToLog();
+ }
+
+ return p;
+ }
+
+ /**
+ * Generate the {@link Palette} asynchronously. The provided listener's
+ * {@link Palette.PaletteAsyncListener#onGenerated} method will be called with the palette when
+ * generated.
+ */
+ @NonNull
+ public AsyncTask<Bitmap, Void, Palette> generate(final Palette.PaletteAsyncListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("listener can not be null");
+ }
+
+ return new AsyncTask<Bitmap, Void, Palette>() {
+ @Override
+ protected Palette doInBackground(Bitmap... params) {
+ try {
+ return generate();
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Exception thrown during async generate", e);
+ return null;
+ }
+ }
+
+ @Override
+ protected void onPostExecute(Palette colorExtractor) {
+ listener.onGenerated(colorExtractor);
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, mBitmap);
+ }
+
+ private int[] getPixelsFromBitmap(Bitmap bitmap) {
+ final int bitmapWidth = bitmap.getWidth();
+ final int bitmapHeight = bitmap.getHeight();
+ final int[] pixels = new int[bitmapWidth * bitmapHeight];
+ bitmap.getPixels(pixels, 0, bitmapWidth, 0, 0, bitmapWidth, bitmapHeight);
+
+ if (mRegion == null) {
+ // If we don't have a region, return all of the pixels
+ return pixels;
+ } else {
+ // If we do have a region, lets create a subset array containing only the region's
+ // pixels
+ final int regionWidth = mRegion.width();
+ final int regionHeight = mRegion.height();
+ // pixels contains all of the pixels, so we need to iterate through each row and
+ // copy the regions pixels into a new smaller array
+ final int[] subsetPixels = new int[regionWidth * regionHeight];
+ for (int row = 0; row < regionHeight; row++) {
+ System.arraycopy(pixels, ((row + mRegion.top) * bitmapWidth) + mRegion.left,
+ subsetPixels, row * regionWidth, regionWidth);
+ }
+ return subsetPixels;
+ }
+ }
+
+ /**
+ * Scale the bitmap down as needed.
+ */
+ private Bitmap scaleBitmapDown(final Bitmap bitmap) {
+ double scaleRatio = -1;
+
+ if (mResizeArea > 0) {
+ final int bitmapArea = bitmap.getWidth() * bitmap.getHeight();
+ if (bitmapArea > mResizeArea) {
+ scaleRatio = Math.sqrt(mResizeArea / (double) bitmapArea);
+ }
+ } else if (mResizeMaxDimension > 0) {
+ final int maxDimension = Math.max(bitmap.getWidth(), bitmap.getHeight());
+ if (maxDimension > mResizeMaxDimension) {
+ scaleRatio = mResizeMaxDimension / (double) maxDimension;
+ }
+ }
+
+ if (scaleRatio <= 0) {
+ // Scaling has been disabled or not needed so just return the Bitmap
+ return bitmap;
+ }
+
+ return Bitmap.createScaledBitmap(bitmap,
+ (int) Math.ceil(bitmap.getWidth() * scaleRatio),
+ (int) Math.ceil(bitmap.getHeight() * scaleRatio),
+ false);
+ }
+ }
+
+ /**
+ * A Filter provides a mechanism for exercising fine-grained control over which colors
+ * are valid within a resulting {@link Palette}.
+ */
+ public interface Filter {
+ /**
+ * Hook to allow clients to be able filter colors from resulting palette.
+ *
+ * @param rgb the color in RGB888.
+ * @param hsl HSL representation of the color.
+ *
+ * @return true if the color is allowed, false if not.
+ *
+ * @see Palette.Builder#addFilter(Palette.Filter)
+ */
+ boolean isAllowed(int rgb, float[] hsl);
+ }
+
+ /**
+ * The default filter.
+ */
+ static final Palette.Filter
+ DEFAULT_FILTER = new Palette.Filter() {
+ private static final float BLACK_MAX_LIGHTNESS = 0.05f;
+ private static final float WHITE_MIN_LIGHTNESS = 0.95f;
+
+ @Override
+ public boolean isAllowed(int rgb, float[] hsl) {
+ return !isWhite(hsl) && !isBlack(hsl) && !isNearRedILine(hsl);
+ }
+
+ /**
+ * @return true if the color represents a color which is close to black.
+ */
+ private boolean isBlack(float[] hslColor) {
+ return hslColor[2] <= BLACK_MAX_LIGHTNESS;
+ }
+
+ /**
+ * @return true if the color represents a color which is close to white.
+ */
+ private boolean isWhite(float[] hslColor) {
+ return hslColor[2] >= WHITE_MIN_LIGHTNESS;
+ }
+
+ /**
+ * @return true if the color lies close to the red side of the I line.
+ */
+ private boolean isNearRedILine(float[] hslColor) {
+ return hslColor[0] >= 10f && hslColor[0] <= 37f && hslColor[1] <= 0.82f;
+ }
+ };
+}
diff --git a/core/java/com/android/internal/graphics/palette/Target.java b/core/java/com/android/internal/graphics/palette/Target.java
new file mode 100644
index 0000000..0540d80
--- /dev/null
+++ b/core/java/com/android/internal/graphics/palette/Target.java
@@ -0,0 +1,435 @@
+/*
+ * 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.internal.graphics.palette;
+
+/*
+ * Copyright 2015 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.
+ */
+
+import android.annotation.FloatRange;
+
+/**
+ * Copied from: frameworks/support/v7/palette/src/main/java/android/support/v7/graphics/Target.java
+ *
+ * A class which allows custom selection of colors in a {@link Palette}'s generation. Instances
+ * can be created via the {@link android.support.v7.graphics.Target.Builder} class.
+ *
+ * <p>To use the target, use the {@link Palette.Builder#addTarget(Target)} API when building a
+ * Palette.</p>
+ */
+public final class Target {
+
+ private static final float TARGET_DARK_LUMA = 0.26f;
+ private static final float MAX_DARK_LUMA = 0.45f;
+
+ private static final float MIN_LIGHT_LUMA = 0.55f;
+ private static final float TARGET_LIGHT_LUMA = 0.74f;
+
+ private static final float MIN_NORMAL_LUMA = 0.3f;
+ private static final float TARGET_NORMAL_LUMA = 0.5f;
+ private static final float MAX_NORMAL_LUMA = 0.7f;
+
+ private static final float TARGET_MUTED_SATURATION = 0.3f;
+ private static final float MAX_MUTED_SATURATION = 0.4f;
+
+ private static final float TARGET_VIBRANT_SATURATION = 1f;
+ private static final float MIN_VIBRANT_SATURATION = 0.35f;
+
+ private static final float WEIGHT_SATURATION = 0.24f;
+ private static final float WEIGHT_LUMA = 0.52f;
+ private static final float WEIGHT_POPULATION = 0.24f;
+
+ static final int INDEX_MIN = 0;
+ static final int INDEX_TARGET = 1;
+ static final int INDEX_MAX = 2;
+
+ static final int INDEX_WEIGHT_SAT = 0;
+ static final int INDEX_WEIGHT_LUMA = 1;
+ static final int INDEX_WEIGHT_POP = 2;
+
+ /**
+ * A target which has the characteristics of a vibrant color which is light in luminance.
+ */
+ public static final Target LIGHT_VIBRANT;
+
+ /**
+ * A target which has the characteristics of a vibrant color which is neither light or dark.
+ */
+ public static final Target VIBRANT;
+
+ /**
+ * A target which has the characteristics of a vibrant color which is dark in luminance.
+ */
+ public static final Target DARK_VIBRANT;
+
+ /**
+ * A target which has the characteristics of a muted color which is light in luminance.
+ */
+ public static final Target LIGHT_MUTED;
+
+ /**
+ * A target which has the characteristics of a muted color which is neither light or dark.
+ */
+ public static final Target MUTED;
+
+ /**
+ * A target which has the characteristics of a muted color which is dark in luminance.
+ */
+ public static final Target DARK_MUTED;
+
+ static {
+ LIGHT_VIBRANT = new Target();
+ setDefaultLightLightnessValues(LIGHT_VIBRANT);
+ setDefaultVibrantSaturationValues(LIGHT_VIBRANT);
+
+ VIBRANT = new Target();
+ setDefaultNormalLightnessValues(VIBRANT);
+ setDefaultVibrantSaturationValues(VIBRANT);
+
+ DARK_VIBRANT = new Target();
+ setDefaultDarkLightnessValues(DARK_VIBRANT);
+ setDefaultVibrantSaturationValues(DARK_VIBRANT);
+
+ LIGHT_MUTED = new Target();
+ setDefaultLightLightnessValues(LIGHT_MUTED);
+ setDefaultMutedSaturationValues(LIGHT_MUTED);
+
+ MUTED = new Target();
+ setDefaultNormalLightnessValues(MUTED);
+ setDefaultMutedSaturationValues(MUTED);
+
+ DARK_MUTED = new Target();
+ setDefaultDarkLightnessValues(DARK_MUTED);
+ setDefaultMutedSaturationValues(DARK_MUTED);
+ }
+
+ final float[] mSaturationTargets = new float[3];
+ final float[] mLightnessTargets = new float[3];
+ final float[] mWeights = new float[3];
+ boolean mIsExclusive = true; // default to true
+
+ Target() {
+ setTargetDefaultValues(mSaturationTargets);
+ setTargetDefaultValues(mLightnessTargets);
+ setDefaultWeights();
+ }
+
+ Target(Target from) {
+ System.arraycopy(from.mSaturationTargets, 0, mSaturationTargets, 0,
+ mSaturationTargets.length);
+ System.arraycopy(from.mLightnessTargets, 0, mLightnessTargets, 0,
+ mLightnessTargets.length);
+ System.arraycopy(from.mWeights, 0, mWeights, 0, mWeights.length);
+ }
+
+ /**
+ * The minimum saturation value for this target.
+ */
+ @FloatRange(from = 0, to = 1)
+ public float getMinimumSaturation() {
+ return mSaturationTargets[INDEX_MIN];
+ }
+
+ /**
+ * The target saturation value for this target.
+ */
+ @FloatRange(from = 0, to = 1)
+ public float getTargetSaturation() {
+ return mSaturationTargets[INDEX_TARGET];
+ }
+
+ /**
+ * The maximum saturation value for this target.
+ */
+ @FloatRange(from = 0, to = 1)
+ public float getMaximumSaturation() {
+ return mSaturationTargets[INDEX_MAX];
+ }
+
+ /**
+ * The minimum lightness value for this target.
+ */
+ @FloatRange(from = 0, to = 1)
+ public float getMinimumLightness() {
+ return mLightnessTargets[INDEX_MIN];
+ }
+
+ /**
+ * The target lightness value for this target.
+ */
+ @FloatRange(from = 0, to = 1)
+ public float getTargetLightness() {
+ return mLightnessTargets[INDEX_TARGET];
+ }
+
+ /**
+ * The maximum lightness value for this target.
+ */
+ @FloatRange(from = 0, to = 1)
+ public float getMaximumLightness() {
+ return mLightnessTargets[INDEX_MAX];
+ }
+
+ /**
+ * Returns the weight of importance that this target places on a color's saturation within
+ * the image.
+ *
+ * <p>The larger the weight, relative to the other weights, the more important that a color
+ * being close to the target value has on selection.</p>
+ *
+ * @see #getTargetSaturation()
+ */
+ public float getSaturationWeight() {
+ return mWeights[INDEX_WEIGHT_SAT];
+ }
+
+ /**
+ * Returns the weight of importance that this target places on a color's lightness within
+ * the image.
+ *
+ * <p>The larger the weight, relative to the other weights, the more important that a color
+ * being close to the target value has on selection.</p>
+ *
+ * @see #getTargetLightness()
+ */
+ public float getLightnessWeight() {
+ return mWeights[INDEX_WEIGHT_LUMA];
+ }
+
+ /**
+ * Returns the weight of importance that this target places on a color's population within
+ * the image.
+ *
+ * <p>The larger the weight, relative to the other weights, the more important that a
+ * color's population being close to the most populous has on selection.</p>
+ */
+ public float getPopulationWeight() {
+ return mWeights[INDEX_WEIGHT_POP];
+ }
+
+ /**
+ * Returns whether any color selected for this target is exclusive for this target only.
+ *
+ * <p>If false, then the color can be selected for other targets.</p>
+ */
+ public boolean isExclusive() {
+ return mIsExclusive;
+ }
+
+ private static void setTargetDefaultValues(final float[] values) {
+ values[INDEX_MIN] = 0f;
+ values[INDEX_TARGET] = 0.5f;
+ values[INDEX_MAX] = 1f;
+ }
+
+ private void setDefaultWeights() {
+ mWeights[INDEX_WEIGHT_SAT] = WEIGHT_SATURATION;
+ mWeights[INDEX_WEIGHT_LUMA] = WEIGHT_LUMA;
+ mWeights[INDEX_WEIGHT_POP] = WEIGHT_POPULATION;
+ }
+
+ void normalizeWeights() {
+ float sum = 0;
+ for (int i = 0, z = mWeights.length; i < z; i++) {
+ float weight = mWeights[i];
+ if (weight > 0) {
+ sum += weight;
+ }
+ }
+ if (sum != 0) {
+ for (int i = 0, z = mWeights.length; i < z; i++) {
+ if (mWeights[i] > 0) {
+ mWeights[i] /= sum;
+ }
+ }
+ }
+ }
+
+ private static void setDefaultDarkLightnessValues(Target target) {
+ target.mLightnessTargets[INDEX_TARGET] = TARGET_DARK_LUMA;
+ target.mLightnessTargets[INDEX_MAX] = MAX_DARK_LUMA;
+ }
+
+ private static void setDefaultNormalLightnessValues(Target target) {
+ target.mLightnessTargets[INDEX_MIN] = MIN_NORMAL_LUMA;
+ target.mLightnessTargets[INDEX_TARGET] = TARGET_NORMAL_LUMA;
+ target.mLightnessTargets[INDEX_MAX] = MAX_NORMAL_LUMA;
+ }
+
+ private static void setDefaultLightLightnessValues(Target target) {
+ target.mLightnessTargets[INDEX_MIN] = MIN_LIGHT_LUMA;
+ target.mLightnessTargets[INDEX_TARGET] = TARGET_LIGHT_LUMA;
+ }
+
+ private static void setDefaultVibrantSaturationValues(Target target) {
+ target.mSaturationTargets[INDEX_MIN] = MIN_VIBRANT_SATURATION;
+ target.mSaturationTargets[INDEX_TARGET] = TARGET_VIBRANT_SATURATION;
+ }
+
+ private static void setDefaultMutedSaturationValues(Target target) {
+ target.mSaturationTargets[INDEX_TARGET] = TARGET_MUTED_SATURATION;
+ target.mSaturationTargets[INDEX_MAX] = MAX_MUTED_SATURATION;
+ }
+
+ /**
+ * Builder class for generating custom {@link Target} instances.
+ */
+ public final static class Builder {
+ private final Target mTarget;
+
+ /**
+ * Create a new {@link Target} builder from scratch.
+ */
+ public Builder() {
+ mTarget = new Target();
+ }
+
+ /**
+ * Create a new builder based on an existing {@link Target}.
+ */
+ public Builder(Target target) {
+ mTarget = new Target(target);
+ }
+
+ /**
+ * Set the minimum saturation value for this target.
+ */
+ public Target.Builder setMinimumSaturation(@FloatRange(from = 0, to = 1) float value) {
+ mTarget.mSaturationTargets[INDEX_MIN] = value;
+ return this;
+ }
+
+ /**
+ * Set the target/ideal saturation value for this target.
+ */
+ public Target.Builder setTargetSaturation(@FloatRange(from = 0, to = 1) float value) {
+ mTarget.mSaturationTargets[INDEX_TARGET] = value;
+ return this;
+ }
+
+ /**
+ * Set the maximum saturation value for this target.
+ */
+ public Target.Builder setMaximumSaturation(@FloatRange(from = 0, to = 1) float value) {
+ mTarget.mSaturationTargets[INDEX_MAX] = value;
+ return this;
+ }
+
+ /**
+ * Set the minimum lightness value for this target.
+ */
+ public Target.Builder setMinimumLightness(@FloatRange(from = 0, to = 1) float value) {
+ mTarget.mLightnessTargets[INDEX_MIN] = value;
+ return this;
+ }
+
+ /**
+ * Set the target/ideal lightness value for this target.
+ */
+ public Target.Builder setTargetLightness(@FloatRange(from = 0, to = 1) float value) {
+ mTarget.mLightnessTargets[INDEX_TARGET] = value;
+ return this;
+ }
+
+ /**
+ * Set the maximum lightness value for this target.
+ */
+ public Target.Builder setMaximumLightness(@FloatRange(from = 0, to = 1) float value) {
+ mTarget.mLightnessTargets[INDEX_MAX] = value;
+ return this;
+ }
+
+ /**
+ * Set the weight of importance that this target will place on saturation values.
+ *
+ * <p>The larger the weight, relative to the other weights, the more important that a color
+ * being close to the target value has on selection.</p>
+ *
+ * <p>A weight of 0 means that it has no weight, and thus has no
+ * bearing on the selection.</p>
+ *
+ * @see #setTargetSaturation(float)
+ */
+ public Target.Builder setSaturationWeight(@FloatRange(from = 0) float weight) {
+ mTarget.mWeights[INDEX_WEIGHT_SAT] = weight;
+ return this;
+ }
+
+ /**
+ * Set the weight of importance that this target will place on lightness values.
+ *
+ * <p>The larger the weight, relative to the other weights, the more important that a color
+ * being close to the target value has on selection.</p>
+ *
+ * <p>A weight of 0 means that it has no weight, and thus has no
+ * bearing on the selection.</p>
+ *
+ * @see #setTargetLightness(float)
+ */
+ public Target.Builder setLightnessWeight(@FloatRange(from = 0) float weight) {
+ mTarget.mWeights[INDEX_WEIGHT_LUMA] = weight;
+ return this;
+ }
+
+ /**
+ * Set the weight of importance that this target will place on a color's population within
+ * the image.
+ *
+ * <p>The larger the weight, relative to the other weights, the more important that a
+ * color's population being close to the most populous has on selection.</p>
+ *
+ * <p>A weight of 0 means that it has no weight, and thus has no
+ * bearing on the selection.</p>
+ */
+ public Target.Builder setPopulationWeight(@FloatRange(from = 0) float weight) {
+ mTarget.mWeights[INDEX_WEIGHT_POP] = weight;
+ return this;
+ }
+
+ /**
+ * Set whether any color selected for this target is exclusive to this target only.
+ * Defaults to true.
+ *
+ * @param exclusive true if any the color is exclusive to this target, or false is the
+ * color can be selected for other targets.
+ */
+ public Target.Builder setExclusive(boolean exclusive) {
+ mTarget.mIsExclusive = exclusive;
+ return this;
+ }
+
+ /**
+ * Builds and returns the resulting {@link Target}.
+ */
+ public Target build() {
+ return mTarget;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java
new file mode 100644
index 0000000..4ce6f60
--- /dev/null
+++ b/core/java/com/android/internal/notification/NotificationAccessConfirmationActivityContract.java
@@ -0,0 +1,37 @@
+/*
+ * 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.internal.notification;
+
+import android.content.ComponentName;
+import android.content.Intent;
+
+public final class NotificationAccessConfirmationActivityContract {
+ private static final ComponentName COMPONENT_NAME = new ComponentName(
+ "com.android.settings",
+ "com.android.settings.notification.NotificationAccessConfirmationActivity");
+ public static final String EXTRA_USER_ID = "user_id";
+ public static final String EXTRA_COMPONENT_NAME = "component_name";
+ public static final String EXTRA_PACKAGE_TITLE = "package_title";
+
+ public static Intent launcherIntent(int userId, ComponentName component, String packageTitle) {
+ return new Intent()
+ .setComponent(COMPONENT_NAME)
+ .putExtra(EXTRA_USER_ID, userId)
+ .putExtra(EXTRA_COMPONENT_NAME, component)
+ .putExtra(EXTRA_PACKAGE_TITLE, packageTitle);
+ }
+}
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index cc3f58c..e28079f 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -70,6 +70,7 @@
@Override
protected boolean handlePreloadPackage(String packagePath, String libsPath,
String cacheKey) {
+ Log.i(TAG, "Beginning package preload");
// Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that
// our children will reuse the same classloader instead of creating their own.
// This enables us to preload Java and native code in the webview zygote process and
@@ -97,6 +98,7 @@
IllegalAccessException | InvocationTargetException e) {
Log.e(TAG, "Exception while preloading package", e);
}
+ Log.i(TAG, "Package preload done");
return false;
}
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index baf6db9..80b6b08 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -82,6 +82,7 @@
import static android.app.ActivityManager.StackId;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
@@ -2182,19 +2183,22 @@
final boolean wasAdjustedForStack = mElevationAdjustedForStack;
// Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
// since the shadow is bound to the content size and not the target size.
- if (StackId.hasWindowShadow(mStackId) && !isResizing()) {
+ if ((mStackId == FREEFORM_WORKSPACE_STACK_ID) && !isResizing()) {
elevation = hasWindowFocus() ?
DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
// Add a maximum shadow height value to the top level view.
// Note that pinned stack doesn't have focus
// so maximum shadow height adjustment isn't needed.
// TODO(skuhne): Remove this if clause once b/22668382 got fixed.
- if (!mAllowUpdateElevation && mStackId != PINNED_STACK_ID) {
+ if (!mAllowUpdateElevation) {
elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
}
// Convert the DP elevation into physical pixels.
elevation = dipToPx(elevation);
mElevationAdjustedForStack = true;
+ } else if (mStackId == PINNED_STACK_ID) {
+ elevation = dipToPx(DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP);
+ mElevationAdjustedForStack = true;
} else {
mElevationAdjustedForStack = false;
}
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 287f68c..96b443d 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -16,6 +16,8 @@
package com.android.internal.util;
+import static com.android.internal.util.ArrayUtils.isEmpty;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -64,7 +66,7 @@
*/
public static @NonNull <I, O> List<O> map(@Nullable List<I> cur,
Function<? super I, ? extends O> f) {
- if (cur == null || cur.isEmpty()) return Collections.emptyList();
+ if (isEmpty(cur)) return Collections.emptyList();
final ArrayList<O> result = new ArrayList<>();
for (int i = 0; i < cur.size(); i++) {
result.add(f.apply(cur.get(i)));
@@ -73,6 +75,30 @@
}
/**
+ * {@link #map(List, Function)} + {@link #filter(List, java.util.function.Predicate)}
+ *
+ * Calling this is equivalent (but more memory efficient) to:
+ *
+ * {@code
+ * filter(
+ * map(cur, f),
+ * i -> { i != null })
+ * }
+ */
+ public static @NonNull <I, O> List<O> mapNotNull(@Nullable List<I> cur,
+ Function<? super I, ? extends O> f) {
+ if (isEmpty(cur)) return Collections.emptyList();
+ final ArrayList<O> result = new ArrayList<>();
+ for (int i = 0; i < cur.size(); i++) {
+ O transformed = f.apply(cur.get(i));
+ if (transformed != null) {
+ result.add(transformed);
+ }
+ }
+ return result;
+ }
+
+ /**
* Returns the given list, or an immutable empty list if the provided list is null
*
* This can be used to guaranty null-safety without paying the price of extra allocations
@@ -94,7 +120,7 @@
* Returns the elements of the given list that are of type {@code c}
*/
public static @NonNull <T> List<T> filter(@Nullable List<?> list, Class<T> c) {
- if (ArrayUtils.isEmpty(list)) return Collections.emptyList();
+ if (isEmpty(list)) return Collections.emptyList();
ArrayList<T> result = null;
for (int i = 0; i < list.size(); i++) {
final Object item = list.get(i);
@@ -120,11 +146,42 @@
*/
public static @Nullable <T> T find(@Nullable List<T> items,
java.util.function.Predicate<T> predicate) {
- if (ArrayUtils.isEmpty(items)) return null;
+ if (isEmpty(items)) return null;
for (int i = 0; i < items.size(); i++) {
final T item = items.get(i);
if (predicate.test(item)) return item;
}
return null;
}
+
+ /**
+ * Similar to {@link List#add}, but with support for list values of {@code null} and
+ * {@link Collections#emptyList}
+ */
+ public static @NonNull <T> List<T> add(@Nullable List<T> cur, T val) {
+ if (cur == null || cur == Collections.emptyList()) {
+ cur = new ArrayList<>();
+ }
+ cur.add(val);
+ return cur;
+ }
+
+ /**
+ * Similar to {@link List#remove}, but with support for list values of {@code null} and
+ * {@link Collections#emptyList}
+ */
+ public static @NonNull <T> List<T> remove(@Nullable List<T> cur, T val) {
+ if (isEmpty(cur)) {
+ return emptyIfNull(cur);
+ }
+ cur.remove(val);
+ return cur;
+ }
+
+ /**
+ * @return a list that will not be affected by mutations to the given original list.
+ */
+ public static @NonNull <T> List<T> copyOf(@Nullable List<T> cur) {
+ return isEmpty(cur) ? Collections.emptyList() : new ArrayList<>(cur);
+ }
}
diff --git a/core/java/com/android/internal/util/FunctionalUtils.java b/core/java/com/android/internal/util/FunctionalUtils.java
new file mode 100644
index 0000000..9aeb0415
--- /dev/null
+++ b/core/java/com/android/internal/util/FunctionalUtils.java
@@ -0,0 +1,48 @@
+/*
+ * 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.internal.util;
+
+import java.util.function.Supplier;
+
+/**
+ * Utilities specific to functional programming
+ */
+public class FunctionalUtils {
+ private FunctionalUtils() {}
+
+ /**
+ * An equivalent of {@link Runnable} that allows throwing checked exceptions
+ *
+ * This can be used to specify a lambda argument without forcing all the checked exceptions
+ * to be handled within it
+ */
+ @FunctionalInterface
+ public interface ThrowingRunnable {
+ void run() throws Exception;
+ }
+
+ /**
+ * An equivalent of {@link Supplier} that allows throwing checked exceptions
+ *
+ * This can be used to specify a lambda argument without forcing all the checked exceptions
+ * to be handled within it
+ */
+ @FunctionalInterface
+ public interface ThrowingSupplier<T> {
+ T get() throws Exception;
+ }
+}
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 4e6857a..e5d5716 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -49,6 +49,23 @@
}
/**
+ * Ensures that an expression checking an argument is true.
+ *
+ * @param expression the expression to check
+ * @param messageTemplate a printf-style message template to use if the check fails; will
+ * be converted to a string using {@link String#format(String, Object...)}
+ * @param messageArgs arguments for {@code messageTemplate}
+ * @throws IllegalArgumentException if {@code expression} is false
+ */
+ public static void checkArgument(boolean expression,
+ final String messageTemplate,
+ final Object... messageArgs) {
+ if (!expression) {
+ throw new IllegalArgumentException(String.format(messageTemplate, messageArgs));
+ }
+ }
+
+ /**
* Ensures that an string reference passed as a parameter to the calling
* method is not empty.
*
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 818cc2c..8c71cf7 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -233,6 +233,7 @@
private void doShow() {
List<MenuItem> menuItems = getVisibleAndEnabledMenuItems(mMenu);
+ tidy(menuItems);
if (!isCurrentlyShowing(menuItems) || mWidthChanged) {
mPopup.dismiss();
mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth);
@@ -274,6 +275,36 @@
return menuItems;
}
+ /**
+ * Update the list of menu items to conform to certain requirements.
+ */
+ private void tidy(List<MenuItem> menuItems) {
+ int assistItemIndex = -1;
+ Drawable assistItemDrawable = null;
+
+ final int size = menuItems.size();
+ for (int i = 0; i < size; i++) {
+ final MenuItem menuItem = menuItems.get(i);
+
+ if (menuItem.getItemId() == android.R.id.textAssist) {
+ assistItemIndex = i;
+ assistItemDrawable = menuItem.getIcon();
+ }
+
+ // Remove icons for all menu items with text.
+ if (!TextUtils.isEmpty(menuItem.getTitle())) {
+ menuItem.setIcon(null);
+ }
+ }
+ if (assistItemIndex > -1) {
+ final MenuItem assistMenuItem = menuItems.remove(assistItemIndex);
+ // Ensure the assist menu item preserves its icon.
+ assistMenuItem.setIcon(assistItemDrawable);
+ // Ensure the assist menu item is always the first item.
+ menuItems.add(0, assistMenuItem);
+ }
+ }
+
private List<Object> getShowingMenuItemsReferences(List<MenuItem> menuItems) {
List<Object> references = new ArrayList<Object>();
for (MenuItem menuItem : menuItems) {
diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java
index 4a9a2c5..c1f443f 100644
--- a/core/java/com/android/server/BootReceiver.java
+++ b/core/java/com/android/server/BootReceiver.java
@@ -35,6 +35,7 @@
import android.util.Slog;
import android.util.Xml;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
@@ -89,6 +90,11 @@
// example: fs_stat,/dev/block/platform/soc/by-name/userdata,0x5
private static final String FS_STAT_PATTERN = "fs_stat,[^,]*/([^/,]+),(0x[0-9a-fA-F]+)";
+ private static final int FS_STAT_FS_FIXED = 0x400; // should match with fs_mgr.cpp:FsStatFlags
+ private static final String FSCK_PASS_PATTERN = "Pass ([1-9]E?):";
+ private static final String FSCK_TREE_OPTIMIZATION_PATTERN =
+ "Inode [0-9]+ extent tree.*could be shorter";
+ private static final String FSCK_FS_MODIFIED = "FILE SYSTEM WAS MODIFIED";
// ro.boottime.init.mount_all. + postfix for mount_all duration
private static final String[] MOUNT_DURATION_PROPS_POSTFIX =
new String[] { "early", "default", "late" };
@@ -334,17 +340,22 @@
String log = FileUtils.readTextFile(file, maxSize, "[[TRUNCATED]]\n");
Pattern pattern = Pattern.compile(FS_STAT_PATTERN);
- for (String line : log.split("\n")) { // should check all lines
- if (line.contains("FILE SYSTEM WAS MODIFIED")) {
+ String lines[] = log.split("\n");
+ int lineNumber = 0;
+ int lastFsStatLineNumber = 0;
+ for (String line : lines) { // should check all lines
+ if (line.contains(FSCK_FS_MODIFIED)) {
uploadNeeded = true;
} else if (line.contains("fs_stat")){
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
- handleFsckFsStat(matcher);
+ handleFsckFsStat(matcher, lines, lastFsStatLineNumber, lineNumber);
+ lastFsStatLineNumber = lineNumber;
} else {
Slog.w(TAG, "cannot parse fs_stat:" + line);
}
}
+ lineNumber++;
}
if (uploadEnabled && uploadNeeded ) {
@@ -403,7 +414,88 @@
}
}
- private static void handleFsckFsStat(Matcher match) {
+ /**
+ * Fix fs_stat from e2fsck.
+ * For now, only handle the case of quota warning caused by tree optimization. Clear fs fix
+ * flag (=0x400) caused by that.
+ *
+ * @param partition partition name
+ * @param statOrg original stat reported from e2fsck log
+ * @param lines e2fsck logs broken down into lines
+ * @param startLineNumber start line to parse
+ * @param endLineNumber end line. exclusive.
+ * @return updated fs_stat. For tree optimization, will clear bit 0x400.
+ */
+ @VisibleForTesting
+ public static int fixFsckFsStat(String partition, int statOrg, String[] lines,
+ int startLineNumber, int endLineNumber) {
+ int stat = statOrg;
+ if ((stat & FS_STAT_FS_FIXED) != 0) {
+ // fs was fixed. should check if quota warning was caused by tree optimization.
+ // This is not a real fix but optimization, so should not be counted as a fs fix.
+ Pattern passPattern = Pattern.compile(FSCK_PASS_PATTERN);
+ Pattern treeOptPattern = Pattern.compile(FSCK_TREE_OPTIMIZATION_PATTERN);
+ String currentPass = "";
+ boolean foundTreeOptimization = false;
+ boolean foundQuotaFix = false;
+ boolean foundOtherFix = false;
+ String otherFixLine = null;
+ for (int i = startLineNumber; i < endLineNumber; i++) {
+ String line = lines[i];
+ if (line.contains(FSCK_FS_MODIFIED)) { // no need to parse above this
+ break;
+ } else if (line.startsWith("Pass ")) {
+ Matcher matcher = passPattern.matcher(line);
+ if (matcher.find()) {
+ currentPass = matcher.group(1);
+ }
+ } else if (line.startsWith("Inode ")) {
+ Matcher matcher = treeOptPattern.matcher(line);
+ if (matcher.find() && currentPass.equals("1")) {
+ foundTreeOptimization = true;
+ Slog.i(TAG, "fs_stat, partition:" + partition + " found tree optimization:"
+ + line);
+ } else {
+ foundOtherFix = true;
+ otherFixLine = line;
+ break;
+ }
+ } else if (line.startsWith("[QUOTA WARNING]") && currentPass.equals("5")) {
+ Slog.i(TAG, "fs_stat, partition:" + partition + " found quota warning:"
+ + line);
+ foundQuotaFix = true;
+ if (!foundTreeOptimization) { // only quota warning, this is real fix.
+ otherFixLine = line;
+ break;
+ }
+ } else if (line.startsWith("Update quota info") && currentPass.equals("5")) {
+ // follows "[QUOTA WARNING]", ignore
+ } else {
+ line = line.trim();
+ // ignore empty msg or any msg before Pass 1
+ if (!line.isEmpty() && !currentPass.isEmpty()) {
+ foundOtherFix = true;
+ otherFixLine = line;
+ break;
+ }
+ }
+ }
+ if (!foundOtherFix && foundTreeOptimization && foundQuotaFix) {
+ // not a real fix, so clear it.
+ Slog.i(TAG, "fs_stat, partition:" + partition +
+ " quota fix due to tree optimization");
+ stat &= ~FS_STAT_FS_FIXED;
+ } else {
+ if (otherFixLine != null) {
+ Slog.i(TAG, "fs_stat, partition:" + partition + " fix:" + otherFixLine);
+ }
+ }
+ }
+ return stat;
+ }
+
+ private static void handleFsckFsStat(Matcher match, String[] lines, int startLineNumber,
+ int endLineNumber) {
String partition = match.group(1);
int stat;
try {
@@ -412,9 +504,9 @@
Slog.w(TAG, "cannot parse fs_stat: partition:" + partition + " stat:" + match.group(2));
return;
}
-
+ stat = fixFsckFsStat(partition, stat, lines, startLineNumber, endLineNumber);
MetricsLogger.histogram(null, "boot_fs_stat_" + partition, stat);
- Slog.i(TAG, "fs_stat, partition:" + partition + " stat:" + match.group(2));
+ Slog.i(TAG, "fs_stat, partition:" + partition + " stat:0x" + Integer.toHexString(stat));
}
private static HashMap<String, Long> readTimestamps() {
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 3a03af6..0e67d304 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -44,14 +44,6 @@
static jmethodID gBitmap_reinitMethodID;
static jmethodID gBitmap_getAllocationByteCountMethodID;
-static jfieldID gTransferParams_aFieldID;
-static jfieldID gTransferParams_bFieldID;
-static jfieldID gTransferParams_cFieldID;
-static jfieldID gTransferParams_dFieldID;
-static jfieldID gTransferParams_eFieldID;
-static jfieldID gTransferParams_fFieldID;
-static jfieldID gTransferParams_gFieldID;
-
namespace android {
class BitmapWrapper {
@@ -742,28 +734,8 @@
if (colorType != kN32_SkColorType || xyzD50 == nullptr || transferParameters == nullptr) {
colorSpace = GraphicsJNI::colorSpaceForType(colorType);
} else {
- SkColorSpaceTransferFn p;
- p.fA = (float) env->GetDoubleField(transferParameters, gTransferParams_aFieldID);
- p.fB = (float) env->GetDoubleField(transferParameters, gTransferParams_bFieldID);
- p.fC = (float) env->GetDoubleField(transferParameters, gTransferParams_cFieldID);
- p.fD = (float) env->GetDoubleField(transferParameters, gTransferParams_dFieldID);
- p.fE = (float) env->GetDoubleField(transferParameters, gTransferParams_eFieldID);
- p.fF = (float) env->GetDoubleField(transferParameters, gTransferParams_fFieldID);
- p.fG = (float) env->GetDoubleField(transferParameters, gTransferParams_gFieldID);
-
- SkMatrix44 xyzMatrix(SkMatrix44::kIdentity_Constructor);
- jfloat* array = env->GetFloatArrayElements(xyzD50, NULL);
- xyzMatrix.setFloat(0, 0, array[0]);
- xyzMatrix.setFloat(1, 0, array[1]);
- xyzMatrix.setFloat(2, 0, array[2]);
- xyzMatrix.setFloat(0, 1, array[3]);
- xyzMatrix.setFloat(1, 1, array[4]);
- xyzMatrix.setFloat(2, 1, array[5]);
- xyzMatrix.setFloat(0, 2, array[6]);
- xyzMatrix.setFloat(1, 2, array[7]);
- xyzMatrix.setFloat(2, 2, array[8]);
- env->ReleaseFloatArrayElements(xyzD50, array, 0);
-
+ SkColorSpaceTransferFn p = GraphicsJNI::getNativeTransferParameters(env, transferParameters);
+ SkMatrix44 xyzMatrix = GraphicsJNI::getNativeXYZMatrix(env, xyzD50);
colorSpace = SkColorSpace::MakeRGB(p, xyzMatrix);
}
@@ -1635,20 +1607,6 @@
}
///////////////////////////////////////////////////////////////////////////////
-static jclass make_globalref(JNIEnv* env, const char classname[])
-{
- jclass c = env->FindClass(classname);
- SkASSERT(c);
- return (jclass) env->NewGlobalRef(c);
-}
-
-static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
- const char fieldname[], const char type[])
-{
- jfieldID id = env->GetFieldID(clazz, fieldname, type);
- SkASSERT(id);
- return id;
-}
static const JNINativeMethod gBitmapMethods[] = {
{ "nativeCreate", "([IIIIIIZ[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/Bitmap;",
@@ -1706,20 +1664,11 @@
int register_android_graphics_Bitmap(JNIEnv* env)
{
- jclass transfer_params_class = FindClassOrDie(env, "android/graphics/ColorSpace$Rgb$TransferParameters");
- gTransferParams_aFieldID = GetFieldIDOrDie(env, transfer_params_class, "a", "D");
- gTransferParams_bFieldID = GetFieldIDOrDie(env, transfer_params_class, "b", "D");
- gTransferParams_cFieldID = GetFieldIDOrDie(env, transfer_params_class, "c", "D");
- gTransferParams_dFieldID = GetFieldIDOrDie(env, transfer_params_class, "d", "D");
- gTransferParams_eFieldID = GetFieldIDOrDie(env, transfer_params_class, "e", "D");
- gTransferParams_fFieldID = GetFieldIDOrDie(env, transfer_params_class, "f", "D");
- gTransferParams_gFieldID = GetFieldIDOrDie(env, transfer_params_class, "g", "D");
-
- gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
- gBitmap_nativePtr = getFieldIDCheck(env, gBitmap_class, "mNativePtr", "J");
- gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(JIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V");
- gBitmap_reinitMethodID = env->GetMethodID(gBitmap_class, "reinit", "(IIZ)V");
- gBitmap_getAllocationByteCountMethodID = env->GetMethodID(gBitmap_class, "getAllocationByteCount", "()I");
+ gBitmap_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap"));
+ gBitmap_nativePtr = GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J");
+ gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "<init>", "(JIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V");
+ gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V");
+ gBitmap_getAllocationByteCountMethodID = GetMethodIDOrDie(env, gBitmap_class, "getAllocationByteCount", "()I");
return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods,
NELEM(gBitmapMethods));
}
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index a38acd3..e714671 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -27,6 +27,7 @@
jfieldID gOptions_justBoundsFieldID;
jfieldID gOptions_sampleSizeFieldID;
jfieldID gOptions_configFieldID;
+jfieldID gOptions_colorSpaceFieldID;
jfieldID gOptions_premultipliedFieldID;
jfieldID gOptions_mutableFieldID;
jfieldID gOptions_ditherFieldID;
@@ -51,20 +52,6 @@
jclass gBitmapConfig_class;
jmethodID gBitmapConfig_nativeToConfigMethodID;
-jclass gColorSpace_class;
-jmethodID gColorSpace_getMethodID;
-jmethodID gColorSpace_matchMethodID;
-
-jclass gColorSpaceRGB_class;
-jmethodID gColorSpaceRGB_constructorMethodID;
-
-jclass gColorSpace_Named_class;
-jfieldID gColorSpace_Named_sRGBFieldID;
-jfieldID gColorSpace_Named_LinearExtendedSRGBFieldID;
-
-jclass gTransferParameters_class;
-jmethodID gTransferParameters_constructorMethodID;
-
using namespace android;
jstring encodedFormatToString(JNIEnv* env, SkEncodedImageFormat format) {
@@ -243,70 +230,6 @@
needsFineScale(fullSize.height(), decodedSize.height(), sampleSize);
}
-static jobject getColorSpace(JNIEnv* env,
- sk_sp<SkColorSpace>& decodeColorSpace, SkColorType decodeColorType) {
- jobject colorSpace = nullptr;
-
- // No need to match, we know what the output color space will be
- if (decodeColorType == kRGBA_F16_SkColorType) {
- jobject linearExtendedSRGB = env->GetStaticObjectField(
- gColorSpace_Named_class, gColorSpace_Named_LinearExtendedSRGBFieldID);
- colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
- gColorSpace_getMethodID, linearExtendedSRGB);
- } else {
- // Same here, no need to match
- if (decodeColorSpace->isSRGB()) {
- jobject sRGB = env->GetStaticObjectField(
- gColorSpace_Named_class, gColorSpace_Named_sRGBFieldID);
- colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
- gColorSpace_getMethodID, sRGB);
- } else if (decodeColorSpace.get() != nullptr) {
- // Try to match against known RGB color spaces using the CIE XYZ D50
- // conversion matrix and numerical transfer function parameters
- SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor);
- LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix));
-
- SkColorSpaceTransferFn transferParams;
- // We can only handle numerical transfer functions at the moment
- LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams));
-
- jobject params = env->NewObject(gTransferParameters_class,
- gTransferParameters_constructorMethodID,
- transferParams.fA, transferParams.fB, transferParams.fC,
- transferParams.fD, transferParams.fE, transferParams.fF,
- transferParams.fG);
-
- jfloatArray xyzArray = env->NewFloatArray(9);
- jfloat xyz[9] = {
- xyzMatrix.getFloat(0, 0),
- xyzMatrix.getFloat(1, 0),
- xyzMatrix.getFloat(2, 0),
- xyzMatrix.getFloat(0, 1),
- xyzMatrix.getFloat(1, 1),
- xyzMatrix.getFloat(2, 1),
- xyzMatrix.getFloat(0, 2),
- xyzMatrix.getFloat(1, 2),
- xyzMatrix.getFloat(2, 2)
- };
- env->SetFloatArrayRegion(xyzArray, 0, 9, xyz);
-
- colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
- gColorSpace_matchMethodID, xyzArray, params);
-
- if (colorSpace == nullptr) {
- // We couldn't find an exact match, let's create a new color space
- // instance with the 3x3 conversion matrix and transfer function
- colorSpace = env->NewObject(gColorSpaceRGB_class,
- gColorSpaceRGB_constructorMethodID,
- env->NewStringUTF("Unknown"), xyzArray, params);
- }
-
- env->DeleteLocalRef(xyzArray);
- }
- }
- return colorSpace;
-}
-
static jobject doDecode(JNIEnv* env, SkStreamRewindable* stream, jobject padding, jobject options) {
// This function takes ownership of the input stream. Since the SkAndroidCodec
// will take ownership of the stream, we don't necessarily need to take ownership
@@ -323,6 +246,7 @@
float scale = 1.0f;
bool requireUnpremultiplied = false;
jobject javaBitmap = NULL;
+ sk_sp<SkColorSpace> prefColorSpace = nullptr;
// Update with options supplied by the client.
if (options != NULL) {
@@ -346,6 +270,8 @@
jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
prefColorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
+ jobject jcolorSpace = env->GetObjectField(options, gOptions_colorSpaceFieldID);
+ prefColorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace);
isHardware = GraphicsJNI::isHardwareConfig(env, jconfig);
isMutable = env->GetBooleanField(options, gOptions_mutableFieldID);
requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
@@ -399,7 +325,8 @@
// Set the decode colorType
SkColorType decodeColorType = codec->computeOutputColorType(prefColorType);
- sk_sp<SkColorSpace> decodeColorSpace = codec->computeOutputColorSpace(decodeColorType);
+ sk_sp<SkColorSpace> decodeColorSpace = codec->computeOutputColorSpace(
+ decodeColorType, prefColorSpace);
// Set the options and return if the client only wants the size.
if (options != NULL) {
@@ -427,7 +354,7 @@
env->SetObjectField(options, gOptions_outConfigFieldID, config);
env->SetObjectField(options, gOptions_outColorSpaceFieldID,
- getColorSpace(env, decodeColorSpace, decodeColorType));
+ GraphicsJNI::getColorSpace(env, decodeColorSpace, decodeColorType));
if (onlyDecodeSize) {
return nullptr;
@@ -795,6 +722,8 @@
gOptions_sampleSizeFieldID = GetFieldIDOrDie(env, options_class, "inSampleSize", "I");
gOptions_configFieldID = GetFieldIDOrDie(env, options_class, "inPreferredConfig",
"Landroid/graphics/Bitmap$Config;");
+ gOptions_colorSpaceFieldID = GetFieldIDOrDie(env, options_class, "inPreferredColorSpace",
+ "Landroid/graphics/ColorSpace;");
gOptions_premultipliedFieldID = GetFieldIDOrDie(env, options_class, "inPremultiplied", "Z");
gOptions_mutableFieldID = GetFieldIDOrDie(env, options_class, "inMutable", "Z");
gOptions_ditherFieldID = GetFieldIDOrDie(env, options_class, "inDither", "Z");
@@ -827,29 +756,6 @@
gBitmapConfig_nativeToConfigMethodID = GetStaticMethodIDOrDie(env, gBitmapConfig_class,
"nativeToConfig", "(I)Landroid/graphics/Bitmap$Config;");
- gColorSpace_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ColorSpace"));
- gColorSpace_getMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class,
- "get", "(Landroid/graphics/ColorSpace$Named;)Landroid/graphics/ColorSpace;");
- gColorSpace_matchMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, "match",
- "([FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/ColorSpace;");
-
- gColorSpaceRGB_class = MakeGlobalRefOrDie(env,
- FindClassOrDie(env, "android/graphics/ColorSpace$Rgb"));
- gColorSpaceRGB_constructorMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class,
- "<init>", "(Ljava/lang/String;[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)V");
-
- gColorSpace_Named_class = MakeGlobalRefOrDie(env,
- FindClassOrDie(env, "android/graphics/ColorSpace$Named"));
- gColorSpace_Named_sRGBFieldID = GetStaticFieldIDOrDie(env,
- gColorSpace_Named_class, "SRGB", "Landroid/graphics/ColorSpace$Named;");
- gColorSpace_Named_LinearExtendedSRGBFieldID = GetStaticFieldIDOrDie(env,
- gColorSpace_Named_class, "LINEAR_EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;");
-
- gTransferParameters_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
- "android/graphics/ColorSpace$Rgb$TransferParameters"));
- gTransferParameters_constructorMethodID = GetMethodIDOrDie(env, gTransferParameters_class,
- "<init>", "(DDDDDDD)V");
-
return android::RegisterMethodsOrDie(env, "android/graphics/BitmapFactory",
gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android/graphics/BitmapFactory.h b/core/jni/android/graphics/BitmapFactory.h
index 76db41d..1ee49fa 100644
--- a/core/jni/android/graphics/BitmapFactory.h
+++ b/core/jni/android/graphics/BitmapFactory.h
@@ -8,6 +8,7 @@
extern jfieldID gOptions_justBoundsFieldID;
extern jfieldID gOptions_sampleSizeFieldID;
extern jfieldID gOptions_configFieldID;
+extern jfieldID gOptions_colorSpaceFieldID;
extern jfieldID gOptions_premultipliedFieldID;
extern jfieldID gOptions_ditherFieldID;
extern jfieldID gOptions_purgeableFieldID;
@@ -17,9 +18,14 @@
extern jfieldID gOptions_widthFieldID;
extern jfieldID gOptions_heightFieldID;
extern jfieldID gOptions_mimeFieldID;
+extern jfieldID gOptions_outConfigFieldID;
+extern jfieldID gOptions_outColorSpaceFieldID;
extern jfieldID gOptions_mCancelID;
extern jfieldID gOptions_bitmapFieldID;
+extern jclass gBitmapConfig_class;
+extern jmethodID gBitmapConfig_nativeToConfigMethodID;
+
jstring encodedFormatToString(JNIEnv* env, SkEncodedImageFormat format);
jobject decodeBitmap(JNIEnv* env, void* data, size_t size);
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 3247851..9355cfc 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -132,11 +132,14 @@
bool requireUnpremul = false;
jobject javaBitmap = NULL;
bool isHardware = false;
+ sk_sp<SkColorSpace> colorSpace = nullptr;
// Update the default options with any options supplied by the client.
if (NULL != options) {
sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
jobject jconfig = env->GetObjectField(options, gOptions_configFieldID);
colorType = GraphicsJNI::getNativeBitmapColorType(env, jconfig);
+ jobject jcolorSpace = env->GetObjectField(options, gOptions_colorSpaceFieldID);
+ colorSpace = GraphicsJNI::getNativeColorSpace(env, jcolorSpace);
isHardware = GraphicsJNI::isHardwareConfig(env, jconfig);
requireUnpremul = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
@@ -148,8 +151,16 @@
env->SetIntField(options, gOptions_widthFieldID, -1);
env->SetIntField(options, gOptions_heightFieldID, -1);
env->SetObjectField(options, gOptions_mimeFieldID, 0);
+ env->SetObjectField(options, gOptions_outConfigFieldID, 0);
+ env->SetObjectField(options, gOptions_outColorSpaceFieldID, 0);
}
+ SkBitmapRegionDecoder* brd = reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
+
+ SkColorType decodeColorType = brd->computeOutputColorType(colorType);
+ sk_sp<SkColorSpace> decodeColorSpace = brd->computeOutputColorSpace(
+ decodeColorType, colorSpace);
+
// Recycle a bitmap if possible.
android::Bitmap* recycledBitmap = nullptr;
size_t recycledBytes = 0;
@@ -168,17 +179,16 @@
if (javaBitmap) {
allocator = &recycleAlloc;
// We are required to match the color type of the recycled bitmap.
- colorType = recycledBitmap->info().colorType();
+ decodeColorType = recycledBitmap->info().colorType();
} else {
allocator = &heapAlloc;
}
// Decode the region.
SkIRect subset = SkIRect::MakeXYWH(inputX, inputY, inputWidth, inputHeight);
- SkBitmapRegionDecoder* brd =
- reinterpret_cast<SkBitmapRegionDecoder*>(brdHandle);
SkBitmap bitmap;
- if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize, colorType, requireUnpremul)) {
+ if (!brd->decodeRegion(&bitmap, allocator, subset, sampleSize,
+ decodeColorType, requireUnpremul, decodeColorSpace)) {
return nullObjectReturn("Failed to decode region.");
}
@@ -186,16 +196,29 @@
if (NULL != options) {
env->SetIntField(options, gOptions_widthFieldID, bitmap.width());
env->SetIntField(options, gOptions_heightFieldID, bitmap.height());
+
env->SetObjectField(options, gOptions_mimeFieldID,
encodedFormatToString(env, (SkEncodedImageFormat)brd->getEncodedFormat()));
if (env->ExceptionCheck()) {
return nullObjectReturn("OOM in encodedFormatToString()");
}
+
+ jint configID = GraphicsJNI::colorTypeToLegacyBitmapConfig(decodeColorType);
+ if (isHardware) {
+ configID = GraphicsJNI::kHardware_LegacyBitmapConfig;
+ }
+ jobject config = env->CallStaticObjectMethod(gBitmapConfig_class,
+ gBitmapConfig_nativeToConfigMethodID, configID);
+ env->SetObjectField(options, gOptions_outConfigFieldID, config);
+
+ env->SetObjectField(options, gOptions_outColorSpaceFieldID,
+ GraphicsJNI::getColorSpace(env, decodeColorSpace, decodeColorType));
}
// If we may have reused a bitmap, we need to indicate that the pixels have changed.
if (javaBitmap) {
recycleAlloc.copyIfNecessary();
+ bitmap::reinitBitmap(env, javaBitmap, recycledBitmap->info(), !requireUnpremul);
return javaBitmap;
}
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index e66587a..b11fd4f 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -6,6 +6,7 @@
#include "jni.h"
#include "JNIHelp.h"
#include "GraphicsJNI.h"
+#include "core_jni_helpers.h"
#include "SkCanvas.h"
#include "SkMath.h"
@@ -17,6 +18,8 @@
#include <Caches.h>
#include <TextureCache.h>
+using namespace android;
+
void doThrowNPE(JNIEnv* env) {
jniThrowNullPointerException(env, NULL);
}
@@ -178,6 +181,32 @@
static jmethodID gVMRuntime_newNonMovableArray;
static jmethodID gVMRuntime_addressOf;
+static jfieldID gTransferParams_aFieldID;
+static jfieldID gTransferParams_bFieldID;
+static jfieldID gTransferParams_cFieldID;
+static jfieldID gTransferParams_dFieldID;
+static jfieldID gTransferParams_eFieldID;
+static jfieldID gTransferParams_fFieldID;
+static jfieldID gTransferParams_gFieldID;
+
+static jclass gColorSpace_class;
+static jfieldID gColorSpace_IlluminantD50FieldID;
+static jmethodID gColorSpace_adaptMethodID;
+static jmethodID gColorSpace_getMethodID;
+static jmethodID gColorSpace_matchMethodID;
+
+static jclass gColorSpaceRGB_class;
+static jmethodID gColorSpaceRGB_getTransferParametersMethodID;
+static jmethodID gColorSpaceRGB_getTransformMethodID;
+static jmethodID gColorSpaceRGB_constructorMethodID;
+
+static jclass gColorSpace_Named_class;
+static jfieldID gColorSpace_Named_sRGBFieldID;
+static jfieldID gColorSpace_Named_LinearExtendedSRGBFieldID;
+
+static jclass gTransferParameters_class;
+static jmethodID gTransferParameters_constructorMethodID;
+
///////////////////////////////////////////////////////////////////////////////
void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B)
@@ -328,7 +357,7 @@
}
void GraphicsJNI::getSkBitmap(JNIEnv* env, jobject bitmap, SkBitmap* outBitmap) {
- android::bitmap::toBitmap(env, bitmap).getSkBitmap(outBitmap);
+ bitmap::toBitmap(env, bitmap).getSkBitmap(outBitmap);
}
SkPixelRef* GraphicsJNI::refSkPixelRef(JNIEnv* env, jobject jbitmap) {
@@ -464,6 +493,125 @@
return colorSpace == nullptr || colorSpace->isSRGB();
}
+SkColorSpaceTransferFn GraphicsJNI::getNativeTransferParameters(JNIEnv* env, jobject transferParams) {
+ SkColorSpaceTransferFn p;
+ p.fA = (float) env->GetDoubleField(transferParams, gTransferParams_aFieldID);
+ p.fB = (float) env->GetDoubleField(transferParams, gTransferParams_bFieldID);
+ p.fC = (float) env->GetDoubleField(transferParams, gTransferParams_cFieldID);
+ p.fD = (float) env->GetDoubleField(transferParams, gTransferParams_dFieldID);
+ p.fE = (float) env->GetDoubleField(transferParams, gTransferParams_eFieldID);
+ p.fF = (float) env->GetDoubleField(transferParams, gTransferParams_fFieldID);
+ p.fG = (float) env->GetDoubleField(transferParams, gTransferParams_gFieldID);
+ return p;
+}
+
+SkMatrix44 GraphicsJNI::getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50) {
+ SkMatrix44 xyzMatrix(SkMatrix44::kIdentity_Constructor);
+ jfloat* array = env->GetFloatArrayElements(xyzD50, NULL);
+ xyzMatrix.setFloat(0, 0, array[0]);
+ xyzMatrix.setFloat(1, 0, array[1]);
+ xyzMatrix.setFloat(2, 0, array[2]);
+ xyzMatrix.setFloat(0, 1, array[3]);
+ xyzMatrix.setFloat(1, 1, array[4]);
+ xyzMatrix.setFloat(2, 1, array[5]);
+ xyzMatrix.setFloat(0, 2, array[6]);
+ xyzMatrix.setFloat(1, 2, array[7]);
+ xyzMatrix.setFloat(2, 2, array[8]);
+ env->ReleaseFloatArrayElements(xyzD50, array, 0);
+ return xyzMatrix;
+}
+
+sk_sp<SkColorSpace> GraphicsJNI::getNativeColorSpace(JNIEnv* env, jobject colorSpace) {
+ if (colorSpace == nullptr) return nullptr;
+ if (!env->IsInstanceOf(colorSpace, gColorSpaceRGB_class)) {
+ doThrowIAE(env, "The color space must be an RGB color space");
+ }
+
+ jobject transferParams = env->CallObjectMethod(colorSpace,
+ gColorSpaceRGB_getTransferParametersMethodID);
+ if (transferParams == nullptr) {
+ doThrowIAE(env, "The color space must use an ICC parametric transfer function");
+ }
+
+ jfloatArray illuminantD50 = (jfloatArray) env->GetStaticObjectField(gColorSpace_class,
+ gColorSpace_IlluminantD50FieldID);
+ jobject colorSpaceD50 = env->CallStaticObjectMethod(gColorSpace_class,
+ gColorSpace_adaptMethodID, colorSpace, illuminantD50);
+
+ jfloatArray xyzD50 = (jfloatArray) env->CallObjectMethod(colorSpaceD50,
+ gColorSpaceRGB_getTransformMethodID);
+
+ SkMatrix44 xyzMatrix = getNativeXYZMatrix(env, xyzD50);
+ SkColorSpaceTransferFn transferFunction = getNativeTransferParameters(env, transferParams);
+
+ return SkColorSpace::MakeRGB(transferFunction, xyzMatrix);
+}
+
+
+jobject GraphicsJNI::getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace,
+ SkColorType decodeColorType) {
+ jobject colorSpace = nullptr;
+
+ // No need to match, we know what the output color space will be
+ if (decodeColorType == kRGBA_F16_SkColorType) {
+ jobject linearExtendedSRGB = env->GetStaticObjectField(
+ gColorSpace_Named_class, gColorSpace_Named_LinearExtendedSRGBFieldID);
+ colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
+ gColorSpace_getMethodID, linearExtendedSRGB);
+ } else {
+ // Same here, no need to match
+ if (decodeColorSpace->isSRGB()) {
+ jobject sRGB = env->GetStaticObjectField(
+ gColorSpace_Named_class, gColorSpace_Named_sRGBFieldID);
+ colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
+ gColorSpace_getMethodID, sRGB);
+ } else if (decodeColorSpace.get() != nullptr) {
+ // Try to match against known RGB color spaces using the CIE XYZ D50
+ // conversion matrix and numerical transfer function parameters
+ SkMatrix44 xyzMatrix(SkMatrix44::kUninitialized_Constructor);
+ LOG_ALWAYS_FATAL_IF(!decodeColorSpace->toXYZD50(&xyzMatrix));
+
+ SkColorSpaceTransferFn transferParams;
+ // We can only handle numerical transfer functions at the moment
+ LOG_ALWAYS_FATAL_IF(!decodeColorSpace->isNumericalTransferFn(&transferParams));
+
+ jobject params = env->NewObject(gTransferParameters_class,
+ gTransferParameters_constructorMethodID,
+ transferParams.fA, transferParams.fB, transferParams.fC,
+ transferParams.fD, transferParams.fE, transferParams.fF,
+ transferParams.fG);
+
+ jfloatArray xyzArray = env->NewFloatArray(9);
+ jfloat xyz[9] = {
+ xyzMatrix.getFloat(0, 0),
+ xyzMatrix.getFloat(1, 0),
+ xyzMatrix.getFloat(2, 0),
+ xyzMatrix.getFloat(0, 1),
+ xyzMatrix.getFloat(1, 1),
+ xyzMatrix.getFloat(2, 1),
+ xyzMatrix.getFloat(0, 2),
+ xyzMatrix.getFloat(1, 2),
+ xyzMatrix.getFloat(2, 2)
+ };
+ env->SetFloatArrayRegion(xyzArray, 0, 9, xyz);
+
+ colorSpace = env->CallStaticObjectMethod(gColorSpace_class,
+ gColorSpace_matchMethodID, xyzArray, params);
+
+ if (colorSpace == nullptr) {
+ // We couldn't find an exact match, let's create a new color space
+ // instance with the 3x3 conversion matrix and transfer function
+ colorSpace = env->NewObject(gColorSpaceRGB_class,
+ gColorSpaceRGB_constructorMethodID,
+ env->NewStringUTF("Unknown"), xyzArray, params);
+ }
+
+ env->DeleteLocalRef(xyzArray);
+ }
+ }
+ return colorSpace;
+}
+
///////////////////////////////////////////////////////////////////////////////
bool HeapAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
mStorage = android::Bitmap::allocateHeapBitmap(bitmap, ctable);
@@ -510,7 +658,11 @@
// mRecycledBitmap->info() for the SkImageInfo. According to the
// specification for BitmapRegionDecoder, we are not allowed to change
// the SkImageInfo.
- mRecycledBitmap->reconfigure(mRecycledBitmap->info(), rowBytes, ctable);
+ // We can (must) preserve the color space since it doesn't affect the
+ // storage needs
+ mRecycledBitmap->reconfigure(
+ mRecycledBitmap->info().makeColorSpace(bitmap->refColorSpace()),
+ rowBytes, ctable);
// Give the bitmap the same pixelRef as mRecycledBitmap.
// skbug.com/4538: We also need to make sure that the rowBytes on the pixel ref
@@ -577,74 +729,97 @@
////////////////////////////////////////////////////////////////////////////////
-static jclass make_globalref(JNIEnv* env, const char classname[])
-{
- jclass c = env->FindClass(classname);
- SkASSERT(c);
- return (jclass) env->NewGlobalRef(c);
-}
-
-static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
- const char fieldname[], const char type[])
-{
- jfieldID id = env->GetFieldID(clazz, fieldname, type);
- SkASSERT(id);
- return id;
-}
-
int register_android_graphics_Graphics(JNIEnv* env)
{
jmethodID m;
jclass c;
- gRect_class = make_globalref(env, "android/graphics/Rect");
- gRect_leftFieldID = getFieldIDCheck(env, gRect_class, "left", "I");
- gRect_topFieldID = getFieldIDCheck(env, gRect_class, "top", "I");
- gRect_rightFieldID = getFieldIDCheck(env, gRect_class, "right", "I");
- gRect_bottomFieldID = getFieldIDCheck(env, gRect_class, "bottom", "I");
+ gRect_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Rect"));
+ gRect_leftFieldID = GetFieldIDOrDie(env, gRect_class, "left", "I");
+ gRect_topFieldID = GetFieldIDOrDie(env, gRect_class, "top", "I");
+ gRect_rightFieldID = GetFieldIDOrDie(env, gRect_class, "right", "I");
+ gRect_bottomFieldID = GetFieldIDOrDie(env, gRect_class, "bottom", "I");
- gRectF_class = make_globalref(env, "android/graphics/RectF");
- gRectF_leftFieldID = getFieldIDCheck(env, gRectF_class, "left", "F");
- gRectF_topFieldID = getFieldIDCheck(env, gRectF_class, "top", "F");
- gRectF_rightFieldID = getFieldIDCheck(env, gRectF_class, "right", "F");
- gRectF_bottomFieldID = getFieldIDCheck(env, gRectF_class, "bottom", "F");
+ gRectF_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/RectF"));
+ gRectF_leftFieldID = GetFieldIDOrDie(env, gRectF_class, "left", "F");
+ gRectF_topFieldID = GetFieldIDOrDie(env, gRectF_class, "top", "F");
+ gRectF_rightFieldID = GetFieldIDOrDie(env, gRectF_class, "right", "F");
+ gRectF_bottomFieldID = GetFieldIDOrDie(env, gRectF_class, "bottom", "F");
- gPoint_class = make_globalref(env, "android/graphics/Point");
- gPoint_xFieldID = getFieldIDCheck(env, gPoint_class, "x", "I");
- gPoint_yFieldID = getFieldIDCheck(env, gPoint_class, "y", "I");
+ gPoint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Point"));
+ gPoint_xFieldID = GetFieldIDOrDie(env, gPoint_class, "x", "I");
+ gPoint_yFieldID = GetFieldIDOrDie(env, gPoint_class, "y", "I");
- gPointF_class = make_globalref(env, "android/graphics/PointF");
- gPointF_xFieldID = getFieldIDCheck(env, gPointF_class, "x", "F");
- gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F");
+ gPointF_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/PointF"));
+ gPointF_xFieldID = GetFieldIDOrDie(env, gPointF_class, "x", "F");
+ gPointF_yFieldID = GetFieldIDOrDie(env, gPointF_class, "y", "F");
- gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
- gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(J)V");
+ gBitmapRegionDecoder_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/BitmapRegionDecoder"));
+ gBitmapRegionDecoder_constructorMethodID = GetMethodIDOrDie(env, gBitmapRegionDecoder_class, "<init>", "(J)V");
- gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
- gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
- "nativeInt", "I");
+ gBitmapConfig_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Bitmap$Config"));
+ gBitmapConfig_nativeInstanceID = GetFieldIDOrDie(env, gBitmapConfig_class, "nativeInt", "I");
- gCanvas_class = make_globalref(env, "android/graphics/Canvas");
- gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvasWrapper", "J");
+ gCanvas_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Canvas"));
+ gCanvas_nativeInstanceID = GetFieldIDOrDie(env, gCanvas_class, "mNativeCanvasWrapper", "J");
- gPicture_class = make_globalref(env, "android/graphics/Picture");
- gPicture_nativeInstanceID = getFieldIDCheck(env, gPicture_class, "mNativePicture", "J");
+ gPicture_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Picture"));
+ gPicture_nativeInstanceID = GetFieldIDOrDie(env, gPicture_class, "mNativePicture", "J");
- gRegion_class = make_globalref(env, "android/graphics/Region");
- gRegion_nativeInstanceID = getFieldIDCheck(env, gRegion_class, "mNativeRegion", "J");
- gRegion_constructorMethodID = env->GetMethodID(gRegion_class, "<init>",
- "(JI)V");
+ gRegion_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Region"));
+ gRegion_nativeInstanceID = GetFieldIDOrDie(env, gRegion_class, "mNativeRegion", "J");
+ gRegion_constructorMethodID = GetMethodIDOrDie(env, gRegion_class, "<init>", "(JI)V");
c = env->FindClass("java/lang/Byte");
gByte_class = (jclass) env->NewGlobalRef(
env->GetStaticObjectField(c, env->GetStaticFieldID(c, "TYPE", "Ljava/lang/Class;")));
- gVMRuntime_class = make_globalref(env, "dalvik/system/VMRuntime");
+ gVMRuntime_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "dalvik/system/VMRuntime"));
m = env->GetStaticMethodID(gVMRuntime_class, "getRuntime", "()Ldalvik/system/VMRuntime;");
gVMRuntime = env->NewGlobalRef(env->CallStaticObjectMethod(gVMRuntime_class, m));
- gVMRuntime_newNonMovableArray = env->GetMethodID(gVMRuntime_class, "newNonMovableArray",
+ gVMRuntime_newNonMovableArray = GetMethodIDOrDie(env, gVMRuntime_class, "newNonMovableArray",
"(Ljava/lang/Class;I)Ljava/lang/Object;");
- gVMRuntime_addressOf = env->GetMethodID(gVMRuntime_class, "addressOf", "(Ljava/lang/Object;)J");
+ gVMRuntime_addressOf = GetMethodIDOrDie(env, gVMRuntime_class, "addressOf", "(Ljava/lang/Object;)J");
+
+ jclass transfer_params_class = FindClassOrDie(env, "android/graphics/ColorSpace$Rgb$TransferParameters");
+ gTransferParams_aFieldID = GetFieldIDOrDie(env, transfer_params_class, "a", "D");
+ gTransferParams_bFieldID = GetFieldIDOrDie(env, transfer_params_class, "b", "D");
+ gTransferParams_cFieldID = GetFieldIDOrDie(env, transfer_params_class, "c", "D");
+ gTransferParams_dFieldID = GetFieldIDOrDie(env, transfer_params_class, "d", "D");
+ gTransferParams_eFieldID = GetFieldIDOrDie(env, transfer_params_class, "e", "D");
+ gTransferParams_fFieldID = GetFieldIDOrDie(env, transfer_params_class, "f", "D");
+ gTransferParams_gFieldID = GetFieldIDOrDie(env, transfer_params_class, "g", "D");
+
+ gColorSpace_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/ColorSpace"));
+ gColorSpace_IlluminantD50FieldID = GetStaticFieldIDOrDie(env,
+ gColorSpace_class, "ILLUMINANT_D50", "[F");
+ gColorSpace_adaptMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, "adapt",
+ "(Landroid/graphics/ColorSpace;[F)Landroid/graphics/ColorSpace;");
+ gColorSpace_getMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class,
+ "get", "(Landroid/graphics/ColorSpace$Named;)Landroid/graphics/ColorSpace;");
+ gColorSpace_matchMethodID = GetStaticMethodIDOrDie(env, gColorSpace_class, "match",
+ "([FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)Landroid/graphics/ColorSpace;");
+
+ gColorSpaceRGB_class = MakeGlobalRefOrDie(env,
+ FindClassOrDie(env, "android/graphics/ColorSpace$Rgb"));
+ gColorSpaceRGB_constructorMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class,
+ "<init>", "(Ljava/lang/String;[FLandroid/graphics/ColorSpace$Rgb$TransferParameters;)V");
+ gColorSpaceRGB_getTransferParametersMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class,
+ "getTransferParameters", "()Landroid/graphics/ColorSpace$Rgb$TransferParameters;");
+ gColorSpaceRGB_getTransformMethodID = GetMethodIDOrDie(env, gColorSpaceRGB_class,
+ "getTransform", "()[F");
+
+ gColorSpace_Named_class = MakeGlobalRefOrDie(env,
+ FindClassOrDie(env, "android/graphics/ColorSpace$Named"));
+ gColorSpace_Named_sRGBFieldID = GetStaticFieldIDOrDie(env,
+ gColorSpace_Named_class, "SRGB", "Landroid/graphics/ColorSpace$Named;");
+ gColorSpace_Named_LinearExtendedSRGBFieldID = GetStaticFieldIDOrDie(env,
+ gColorSpace_Named_class, "LINEAR_EXTENDED_SRGB", "Landroid/graphics/ColorSpace$Named;");
+
+ gTransferParameters_class = MakeGlobalRefOrDie(env, FindClassOrDie(env,
+ "android/graphics/ColorSpace$Rgb$TransferParameters"));
+ gTransferParameters_constructorMethodID = GetMethodIDOrDie(env, gTransferParameters_class,
+ "<init>", "(DDDDDDD)V");
return 0;
}
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 7d7c881..7fbea25 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -10,6 +10,7 @@
#include "SkPoint.h"
#include "SkRect.h"
#include "SkColorSpace.h"
+#include "SkMatrix44.h"
#include <jni.h>
#include <hwui/Canvas.h>
#include <hwui/Bitmap.h>
@@ -112,6 +113,13 @@
static sk_sp<SkColorSpace> linearColorSpace();
static sk_sp<SkColorSpace> colorSpaceForType(SkColorType type);
static bool isColorSpaceSRGB(SkColorSpace* colorSpace);
+
+ static SkColorSpaceTransferFn getNativeTransferParameters(JNIEnv* env, jobject transferParams);
+ static SkMatrix44 getNativeXYZMatrix(JNIEnv* env, jfloatArray xyzD50);
+ static sk_sp<SkColorSpace> getNativeColorSpace(JNIEnv* env, jobject colorSpace);
+
+ static jobject getColorSpace(JNIEnv* env, sk_sp<SkColorSpace>& decodeColorSpace,
+ SkColorType decodeColorType);
};
class HeapAllocator : public SkBRDAllocator {
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index 520302e..2c7174d 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -24,6 +24,7 @@
#include <ScopedLocalRef.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/android_hardware_HardwareBuffer.h>
+#include <vndk/hardware_buffer.h>
#include <sensor/Sensor.h>
#include <sensor/SensorEventQueue.h>
#include <sensor/SensorManager.h>
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 1883ecb..9491a1e 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -59,7 +59,7 @@
cPackageInfo[i] = cString;
env->ReleaseStringUTFChars(element, cString);
}
- int32_t status = VintfObject::CheckCompatibility(cPackageInfo, false /* mount */);
+ int32_t status = VintfObject::CheckCompatibility(cPackageInfo);
return status;
}
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 1aed501..de67c50 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -193,10 +193,10 @@
if (env->IsInstanceOf(excep, gErrorOffsets.mClass)) {
/*
* It's an Error: Reraise the exception and ask the runtime to abort.
- * This will dump the pending exception as well as all thread traces
- * to the log.
*/
env->Throw(excep);
+ ALOGE("java.lang.Error thrown during binder transaction (stack trace follows) : ");
+ env->ExceptionDescribe();
env->FatalError("java.lang.Error thrown during binder transaction.");
}
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index a2f07d9..2042bf62 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -21,10 +21,14 @@
import "frameworks/base/libs/incident/proto/android/privacy.proto";
import "frameworks/base/core/proto/android/service/appwidget.proto";
+import "frameworks/base/core/proto/android/service/battery.proto";
import "frameworks/base/core/proto/android/service/graphicsstats.proto";
import "frameworks/base/core/proto/android/service/fingerprint.proto";
+import "frameworks/base/core/proto/android/service/diskstats.proto";
import "frameworks/base/core/proto/android/service/netstats.proto";
import "frameworks/base/core/proto/android/service/notification.proto";
+import "frameworks/base/core/proto/android/service/package.proto";
+import "frameworks/base/core/proto/android/service/power.proto";
import "frameworks/base/core/proto/android/providers/settings.proto";
package android.os;
@@ -57,6 +61,10 @@
android.service.NetworkStatsServiceDumpProto netstats = 3001;
android.providers.settings.SettingsServiceDumpProto settings = 3002;
android.service.appwidget.AppWidgetServiceDumpProto appwidget = 3003;
+ android.service.battery.BatteryServiceDumpProto battery = 3006;
+ android.service.diskstats.DiskStatsServiceDumpProto diskstats = 3007;
android.service.notification.NotificationServiceDumpProto notification = 3004;
+ android.service.pm.PackageServiceDumpProto package = 3008;
+ android.service.power.PowerServiceDumpProto power = 3009;
android.service.GraphicsStatsServiceDumpProto graphicsstats = 3005;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 000c8c4..8869593 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3556,6 +3556,11 @@
android:process=":ui">
</activity>
+ <activity android:name="com.android.settings.notification.NotificationAccessConfirmationActivity"
+ android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert"
+ android:excludeFromRecents="true">
+ </activity>
+
<receiver android:name="com.android.server.BootReceiver"
android:systemUserOnly="true">
<intent-filter android:priority="1000">
diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml
deleted file mode 100644
index 8917431..0000000
--- a/core/res/res/drawable/ic_corp_badge.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="20.0dp"
- android:height="20.0dp"
- android:viewportWidth="20.0"
- android:viewportHeight="20.0">
- <path
- android:pathData="M10.0,10.0m-10.0,0.0a10.0,10.0 0.0,1.0 1.0,20.0 0.0a10.0,10.0 0.0,1.0 1.0,-20.0 0.0"
- android:fillColor="#FF5722"/>
- <path
- android:pathData="M15.2,6.2L4.8,6.2c-0.5,0.0 -0.9,0.4 -0.9,1.0L3.9,10.0c0.0,0.5 0.4,1.0 0.9,1.0l3.8,0.0l0.0,-1.0l2.9,0.0l0.0,1.0l3.8,0.0c0.5,0.0 1.0,-0.4 1.0,-1.0L16.3,7.1C16.2,6.6 15.8,6.2 15.2,6.2z"
- android:fillColor="#FFFFFF"/>
- <path
- android:pathData="M8.6,12.9l0.0,-1.0L4.3,11.9l0.0,2.4c0.0,0.5 0.4,0.9 0.9,0.9l9.5,0.0c0.5,0.0 0.9,-0.4 0.9,-0.9l0.0,-2.4l-4.3,0.0l0.0,1.0L8.6,12.9z"
- android:fillColor="#FFFFFF"/>
- <path
- android:pathData="M7.1,5.2l0.0,1.0 1.0,0.0 0.0,-1.0 3.799999,0.0 0.0,1.0 1.0,0.0 0.0,-1.0 -1.0,-0.9 -3.799999,0.0z"
- android:fillColor="#FFFFFF"/>
-</vector>
diff --git a/core/res/res/drawable/ic_instant_icon_badge_bolt.xml b/core/res/res/drawable/ic_instant_icon_badge_bolt.xml
new file mode 100644
index 0000000..08512b7
--- /dev/null
+++ b/core/res/res/drawable/ic_instant_icon_badge_bolt.xml
@@ -0,0 +1,29 @@
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="64.0dp"
+ android:height="64.0dp"
+ android:viewportWidth="64.0"
+ android:viewportHeight="64.0">
+
+ <!--
+ The path is similar to ic_corp_icon_badge_case, such that it positions on top of
+ ic_corp_icon_badge_shadow.
+ -->
+ <path
+ android:pathData="M43.9 50.9h4v8l6-12h-4v-8l-6 12z"
+ android:fillColor="#757575"/>
+</vector>
diff --git a/core/res/res/drawable/ic_picture_in_picture.xml b/core/res/res/drawable/ic_picture_in_picture.xml
new file mode 100644
index 0000000..e2dda33
--- /dev/null
+++ b/core/res/res/drawable/ic_picture_in_picture.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M19 11h-8v6h8v-6zm4 8V4.98C23 3.88 22.1 3 21 3H3c-1.1 0-2 .88-2 1.98V19c0 1.1 .9
+2 2 2h18c1.1 0 2-.9 2-2zm-2 .02H3V4.97h18v14.05z" />
+ <path
+ android:pathData="M0 0h24v24H0V0z" />
+</vector>
\ No newline at end of file
diff --git a/core/res/res/layout/accessibility_button_chooser.xml b/core/res/res/layout/accessibility_button_chooser.xml
index 0ef785f..480defb 100644
--- a/core/res/res/layout/accessibility_button_chooser.xml
+++ b/core/res/res/layout/accessibility_button_chooser.xml
@@ -19,7 +19,7 @@
<com.android.internal.widget.ResolverDrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:maxWidth="@dimen/resolver_max_width"
android:maxCollapsedHeight="256dp"
android:maxCollapsedHeightSmall="56dp"
@@ -27,11 +27,14 @@
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alwaysShow="true"
android:orientation="vertical"
android:background="?attr/colorBackground"
android:paddingTop="8dp"
- android:paddingBottom="8dp">
+ android:paddingBottom="8dp"
+ android:paddingStart="?attr/dialogPreferredPadding"
+ android:paddingEnd="?attr/dialogPreferredPadding">
<TextView
android:layout_width="match_parent"
@@ -41,8 +44,6 @@
android:text="@string/accessibility_button_prompt_text"
android:gravity="start|center_vertical"
android:layout_alignParentStart="true"
- android:paddingStart="?attr/dialogPreferredPadding"
- android:paddingEnd="?attr/dialogPreferredPadding"
android:paddingTop="8dp"
android:paddingBottom="8dp"/>
@@ -55,20 +56,15 @@
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
- android:paddingStart="?attr/dialogPreferredPadding"
- android:paddingEnd="?attr/dialogPreferredPadding"
android:gravity="center"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/accessibility_button_prompt"
- android:layout_alwaysShow="true"
android:textAppearance="?attr/textAppearanceMedium"
android:text="@string/accessibility_button_instructional_text"
android:gravity="start|center_vertical"
- android:paddingStart="?attr/dialogPreferredPadding"
- android:paddingEnd="?attr/dialogPreferredPadding"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:visibility="gone"/>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 67f6d19..4fb21fa 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1371,6 +1371,22 @@
Corresponds to
{@link android.view.inputmethod.EditorInfo#IME_ACTION_PREVIOUS}. -->
<flag name="actionPrevious" value="0x00000007" />
+ <!-- Used to request that the IME should not update any personalized data such as typing
+ history and personalized language model based on what the user typed on this text
+ editing object. Typical use cases are:
+ <ul>
+ <li>When the application is in a special mode, where user's activities are expected
+ to be not recorded in the application's history. Some web browsers and chat
+ applications may have this kind of modes.</li>
+ <li>When storing typing history does not make much sense. Specifying this flag in
+ typing games may help to avoid typing history from being filled up with words that
+ the user is less likely to type in their daily life. Another example is that when
+ the application already knows that the expected input is not a valid word (e.g. a
+ promotion code that is not a valid word in any natural language).</li>
+ </ul>
+ <p>Applications need to be aware that the flag is not a guarantee, and some IMEs may
+ not respect it.</p> -->
+ <flag name="flagNoPersonalizedLearning" value="0x1000000" />
<!-- Used to request that the IME never go
into fullscreen mode. Applications need to be aware that the flag is not
a guarantee, and not all IMEs will respect it.
@@ -2304,18 +2320,7 @@
<enum name="auto" value="0x00000010" />
</attr>
- <!-- Controls the autofill behavior for this view. -->
- <attr name="autofillMode">
- <!-- Inherit the behavior from the parent. If there is no parent it is auto. This is the
- default value for this attribute.-->
- <enum name="inherit" value="0" />
- <!-- Allows this view to automatically trigger an autofill request when it get focus.
- -->
- <enum name="auto" value="1" />
- <!-- Do not trigger an autofill request when this view is focused. The user can still
- manually force an autofill request for this view. -->
- <enum name="manual" value="2" />
- </attr>
+ <attr name="__removed3" />
<!-- Describes the content of a view so that a autofill service can fill in the appropriate
data. Multiple hints can be combined in a comma separated list or an array of strings
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 5696468..95ba942 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2439,6 +2439,14 @@
<!-- Whether the given RRO is static or not. -->
<attr name="isStatic" format="boolean" />
+ <!-- Required property name/value pair used to enable this overlay.
+ e.g. name=ro.oem.sku value=MKT210.
+ Overlay will be ignored unless system property exists and is
+ set to specified value -->
+ <!-- @hide @SystemApi This shouldn't be public. -->
+ <attr name="requiredSystemPropertyName" format="string" />
+ <!-- @hide @SystemApi This shouldn't be public. -->
+ <attr name="requiredSystemPropertyValue" format="string" />
</declare-styleable>
<!-- Declaration of an {@link android.content.Intent} object in XML. May
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 937fc6f..536906c 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -171,6 +171,9 @@
<color name="profile_badge_2">#ff000000</color><!-- Black -->
<color name="profile_badge_3">#ff22f033</color><!-- Green -->
+ <!-- Default instant app badge color -->
+ <color name="instant_app_badge">#FFFFFFFF</color><!-- White -->
+
<!-- Multi-sim sim colors -->
<color name="Teal_700">#ff00796b</color>
<color name="Teal_800">#ff00695c</color>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index e0cc5b5..8c37d4b 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -74,10 +74,10 @@
<item name="disabled_alpha_material_light" format="float" type="dimen">0.26</item>
<item name="disabled_alpha_material_dark" format="float" type="dimen">0.30</item>
- <item name="primary_content_alpha_material_light" format="float" type="dimen">1</item>
- <item name="primary_content_alpha_material_dark" format="float" type="dimen">0.87</item>
- <item name="secondary_content_alpha_material_light" format="float" type="dimen">.7</item>
- <item name="secondary_content_alpha_material_dark" format="float" type="dimen">0.54</item>
+ <item name="primary_content_alpha_material_dark" format="float" type="dimen">1</item>
+ <item name="primary_content_alpha_material_light" format="float" type="dimen">0.87</item>
+ <item name="secondary_content_alpha_material_dark" format="float" type="dimen">.7</item>
+ <item name="secondary_content_alpha_material_light" format="float" type="dimen">0.54</item>
<item name="highlight_alpha_material_light" format="float" type="dimen">0.12</item>
<item name="highlight_alpha_material_dark" format="float" type="dimen">0.20</item>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 45dccb6..6a31e16 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -852,6 +852,8 @@
1 - Go to sleep (doze)
2 - Really go to sleep (don't doze)
3 - Really go to sleep and go home (don't doze)
+ 4 - Go to home
+ 5 - Dismiss IME if shown. Otherwise go to home
-->
<integer name="config_shortPressOnPowerBehavior">1</integer>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index d6ed178..89c912fd 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2797,7 +2797,7 @@
<public name="numericModifiers" />
<public name="fontProviderAuthority" />
<public name="fontProviderQuery" />
- <public name="autofillMode" />
+ <public name="__removed3" />
<public name="primaryContentAlpha" />
<public name="secondaryContentAlpha" />
<public name="requiredFeature" />
@@ -2817,6 +2817,10 @@
<public name="defaultFocusHighlightEnabled" />
<public name="persistentFeature"/>
<public name="windowSplashscreenContent" />
+ <!-- @hide @SystemApi -->
+ <public name="requiredSystemPropertyName" />
+ <!-- @hide @SystemApi -->
+ <public name="requiredSystemPropertyValue" />
</public-group>
<public-group type="style" first-id="0x010302e0">
@@ -2830,6 +2834,7 @@
<public-group type="drawable" first-id="0x010800b4">
<public name="autofilled_highlight" />
+ <public name="ic_picture_in_picture" />
</public-group>
<public-group type="string" first-id="0x01040019">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0821674..bd35073 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3000,6 +3000,8 @@
<!-- Do not translate. Default access point SSID used for tethering -->
<string name="wifi_tether_configure_ssid_default" translatable="false">AndroidAP</string>
+ <!-- Do not translate. Default access point SSID used for local only hotspot -->
+ <string name="wifi_localhotspot_configure_ssid_default" translatable="false">AndroidShare</string>
<!-- A notification is shown the first time a connection is attempted on an app owned AP -->
<!-- title for this message -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 603e376..907294d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -19,6 +19,9 @@
<!-- Private symbols that we need to reference from framework code. See
frameworks/base/core/res/MakeJavaSymbols.sed for how to easily generate
this.
+
+ Can be referenced in java code as: com.android.internal.R.<type>.<name>
+ and in layout xml as: "@*android:<type>/<name>"
-->
<java-symbol type="id" name="account_name" />
<java-symbol type="id" name="account_row_icon" />
@@ -1003,6 +1006,7 @@
<java-symbol type="string" name="wifi_p2p_turnon_message" />
<java-symbol type="string" name="wifi_p2p_frequency_conflict_message" />
<java-symbol type="string" name="wifi_tether_configure_ssid_default" />
+ <java-symbol type="string" name="wifi_localhotspot_configure_ssid_default" />
<java-symbol type="string" name="wifi_watchdog_network_disabled" />
<java-symbol type="string" name="wifi_watchdog_network_disabled_detailed" />
<java-symbol type="string" name="imei" />
@@ -1318,6 +1322,7 @@
<java-symbol type="drawable" name="ic_corp_user_badge" />
<java-symbol type="drawable" name="ic_corp_badge_no_background" />
<java-symbol type="drawable" name="ic_corp_statusbar_icon" />
+ <java-symbol type="drawable" name="ic_instant_icon_badge_bolt" />
<java-symbol type="drawable" name="emulator_circular_window_overlay" />
<java-symbol type="drawable" name="sim_light_blue" />
@@ -1349,6 +1354,7 @@
<java-symbol type="color" name="profile_badge_1" />
<java-symbol type="color" name="profile_badge_2" />
<java-symbol type="color" name="profile_badge_3" />
+ <java-symbol type="color" name="instant_app_badge" />
<java-symbol type="layout" name="action_bar_home" />
<java-symbol type="layout" name="action_bar_title_item" />
diff --git a/core/tests/coretests/src/android/metrics/LogMakerTest.java b/core/tests/coretests/src/android/metrics/LogMakerTest.java
index bab9f63..63c1f87 100644
--- a/core/tests/coretests/src/android/metrics/LogMakerTest.java
+++ b/core/tests/coretests/src/android/metrics/LogMakerTest.java
@@ -263,4 +263,32 @@
assertFalse(a.isSubsetOf(b));
assertFalse(b.isSubsetOf(a));
}
+
+ public void testConstructFromNull() {
+ new LogMaker(null);
+ // no promises, just don't throw
+ }
+
+ public void testConstructFromNullKey() {
+ Object[] items = new Object[2];
+ items[0] = null;
+ items[1] = "foo";
+ new LogMaker(items);
+ // no promises, just don't throw
+ }
+
+ public void testConstructFromNullField() {
+ Object[] items = new Object[2];
+ items[0] = 10;
+ items[1] = null;
+ new LogMaker(items);
+ // no promises, just don't throw
+ }
+
+ public void testConstructFromTruncatedArray() {
+ Object[] items = new Object[1];
+ items[0] = 10;
+ new LogMaker(items);
+ // no promises, just don't throw
+ }
}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
new file mode 100644
index 0000000..39b999d
--- /dev/null
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -0,0 +1,237 @@
+/*
+ * 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.view.textclassifier;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+
+import android.os.LocaleList;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Locale;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextClassificationManagerTest {
+
+ private static final LocaleList LOCALES = LocaleList.forLanguageTags("en");
+
+ private TextClassificationManager mTcm;
+ private TextClassifier mClassifier;
+
+ @Before
+ public void setup() {
+ mTcm = InstrumentationRegistry.getTargetContext()
+ .getSystemService(TextClassificationManager.class);
+ mTcm.setTextClassifier(null);
+ mClassifier = mTcm.getTextClassifier();
+ }
+
+ @Test
+ public void testSmartSelection() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "Contact me at droid@android.com";
+ String selected = "droid";
+ String suggested = "droid@android.com";
+ int startIndex = text.indexOf(selected);
+ int endIndex = startIndex + selected.length();
+ int smartStartIndex = text.indexOf(suggested);
+ int smartEndIndex = smartStartIndex + suggested.length();
+
+ assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES),
+ isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
+ }
+
+ @Test
+ public void testSmartSelection_nullLocaleList() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "Contact me at droid@android.com";
+ String selected = "droid";
+ String suggested = "droid@android.com";
+ int startIndex = text.indexOf(selected);
+ int endIndex = startIndex + selected.length();
+ int smartStartIndex = text.indexOf(suggested);
+ int smartEndIndex = smartStartIndex + suggested.length();
+ LocaleList nullLocales = null;
+
+ assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, nullLocales),
+ isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
+ }
+
+ @Test
+ public void testSmartSelection_url() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "Visit http://www.android.com for more information";
+ String selected = "http";
+ String suggested = "http://www.android.com";
+ int startIndex = text.indexOf(selected);
+ int endIndex = startIndex + selected.length();
+ int smartStartIndex = text.indexOf(suggested);
+ int smartEndIndex = smartStartIndex + suggested.length();
+
+ assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES),
+ isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL));
+ }
+
+ @Test
+ public void testTextClassificationResult() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "Contact me at droid@android.com";
+ String classifiedText = "droid@android.com";
+ int startIndex = text.indexOf(classifiedText);
+ int endIndex = startIndex + classifiedText.length();
+ assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex, LOCALES),
+ isTextClassificationResult(classifiedText, TextClassifier.TYPE_EMAIL));
+ }
+
+ @Test
+ public void testTextClassificationResult_url() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "Visit http://www.android.com for more information";
+ String classifiedText = "http://www.android.com";
+ int startIndex = text.indexOf(classifiedText);
+ int endIndex = startIndex + classifiedText.length();
+ assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex, LOCALES),
+ isTextClassificationResult(classifiedText, TextClassifier.TYPE_URL));
+ }
+
+ @Test
+ public void testTextClassificationResult_nullLocaleList() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "Contact me at droid@android.com";
+ String classifiedText = "droid@android.com";
+ int startIndex = text.indexOf(classifiedText);
+ int endIndex = startIndex + classifiedText.length();
+ LocaleList nullLocales = null;
+ assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex, nullLocales),
+ isTextClassificationResult(classifiedText, TextClassifier.TYPE_EMAIL));
+ }
+
+ @Test
+ public void testLanguageDetection() {
+ if (isTextClassifierDisabled()) return;
+
+ String text = "This is a piece of English text";
+ assertThat(mTcm.detectLanguages(text), isDetectedLanguage("en"));
+
+ text = "Das ist ein deutscher Text";
+ assertThat(mTcm.detectLanguages(text), isDetectedLanguage("de"));
+
+ text = "これは日本語のテキストです";
+ assertThat(mTcm.detectLanguages(text), isDetectedLanguage("ja"));
+ }
+
+ @Test
+ public void testSetTextClassifier() {
+ TextClassifier classifier = mock(TextClassifier.class);
+ mTcm.setTextClassifier(classifier);
+ assertEquals(classifier, mTcm.getTextClassifier());
+ }
+
+ private boolean isTextClassifierDisabled() {
+ return mClassifier == TextClassifier.NO_OP;
+ }
+
+ private static Matcher<TextSelection> isTextSelection(
+ final int startIndex, final int endIndex, final String type) {
+ return new BaseMatcher<TextSelection>() {
+ @Override
+ public boolean matches(Object o) {
+ if (o instanceof TextSelection) {
+ TextSelection selection = (TextSelection) o;
+ return startIndex == selection.getSelectionStartIndex()
+ && endIndex == selection.getSelectionEndIndex()
+ && selection.getEntityCount() > 0
+ && type.equals(selection.getEntity(0));
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendValue(
+ String.format("%d, %d, %s", startIndex, endIndex, type));
+ }
+ };
+ }
+
+ private static Matcher<TextClassificationResult> isTextClassificationResult(
+ final String text, final String type) {
+ return new BaseMatcher<TextClassificationResult>() {
+ @Override
+ public boolean matches(Object o) {
+ if (o instanceof TextClassificationResult) {
+ TextClassificationResult result = (TextClassificationResult) o;
+ return text.equals(result.getText())
+ && result.getEntityCount() > 0
+ && type.equals(result.getEntity(0));
+ // TODO: Include other properties.
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("text=").appendValue(text)
+ .appendText(", type=").appendValue(type);
+ }
+ };
+ }
+
+ private static Matcher<List<TextLanguage>> isDetectedLanguage(final String language) {
+ return new BaseMatcher<List<TextLanguage>>() {
+ @Override
+ public boolean matches(Object o) {
+ if (o instanceof List) {
+ List languages = (List) o;
+ if (!languages.isEmpty()) {
+ Object o1 = languages.get(0);
+ if (o1 instanceof TextLanguage) {
+ TextLanguage lang = (TextLanguage) o1;
+ return lang.getLanguageCount() > 0
+ && new Locale(language).getLanguage()
+ .equals(lang.getLanguage(0).getLanguage());
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendValue(String.format("%s", language));
+ }
+ };
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityTest.java b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
index 3029134..2203b6a 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityTest.java
@@ -28,6 +28,7 @@
import static android.widget.espresso.TextViewActions.longPressOnTextAtIndex;
import static android.widget.espresso.TextViewAssertions.hasInsertionPointerAtIndex;
import static android.widget.espresso.TextViewAssertions.hasSelection;
+import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarItemIndex;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsDisplayed;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarIsNotDisplayed;
import static android.widget.espresso.FloatingToolbarEspressoUtils.assertFloatingToolbarContainsItem;
@@ -46,6 +47,11 @@
import static org.hamcrest.Matchers.anyOf;
import static org.hamcrest.Matchers.is;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassifier;
import android.widget.espresso.CustomViewActions.RelativeCoordinatesProvider;
import android.support.test.espresso.action.EspressoKey;
@@ -71,7 +77,8 @@
@Override
public void setUp() throws Exception {
super.setUp();
- getActivity();
+ getActivity().getSystemService(TextClassificationManager.class)
+ .setTextClassifier(TextClassifier.NO_OP);
}
public void testTypedTextIsOnScreen() throws Exception {
@@ -676,4 +683,38 @@
// hasTransientState should return false when selection is created by API.
assertFalse(textView.hasTransientState());
}
+
+ public void testAssistItemIsAtIndexZero() throws Exception {
+ getActivity().getSystemService(TextClassificationManager.class).setTextClassifier(null);
+ final TextView textView = (TextView) getActivity().findViewById(R.id.textview);
+ textView.post(() -> textView.setCustomSelectionActionModeCallback(
+ new ActionMode.Callback() {
+ @Override
+ public boolean onCreateActionMode(ActionMode actionMode, Menu menu) {
+ // Create another item at order position 0 to confirm that it will never be
+ // placed before the textAssist item.
+ menu.add(Menu.NONE, 0 /* id */, 0 /* order */, "Test");
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) {
+ return true;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) {
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode actionMode) {}
+ }));
+ final String text = "droid@android.com";
+
+ onView(withId(R.id.textview)).perform(replaceText(text));
+ onView(withId(R.id.textview)).perform(longPressOnTextAtIndex(text.indexOf('@')));
+ sleepForFloatingToolbarPopup();
+ assertFloatingToolbarItemIndex(android.R.id.textAssist, 0);
+ }
}
diff --git a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
index 838f4db..5206c9b 100644
--- a/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
+++ b/core/tests/coretests/src/android/widget/espresso/FloatingToolbarEspressoUtils.java
@@ -29,7 +29,13 @@
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.is;
+import android.view.MenuItem;
+import android.view.ViewGroup;
+import java.util.ArrayList;
+import java.util.List;
+import org.hamcrest.Description;
import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
import android.support.test.espresso.NoMatchingRootException;
import android.support.test.espresso.NoMatchingViewException;
@@ -123,6 +129,39 @@
}
/**
+ * Asserts that the floating toolbar contains a specified item at a specified index.
+ *
+ * @param menuItemId id of the menu item
+ * @param index expected index of the menu item in the floating toolbar
+ * @throws AssertionError if the assertion fails
+ */
+ public static void assertFloatingToolbarItemIndex(final int menuItemId, final int index) {
+ onFloatingToolBar().check(matches(new TypeSafeMatcher<View>() {
+ private List<Integer> menuItemIds = new ArrayList<>();
+
+ @Override
+ public boolean matchesSafely(View view) {
+ collectMenuItemIds(view);
+ return menuItemIds.size() > index && menuItemIds.get(index) == menuItemId;
+ }
+
+ @Override
+ public void describeTo(Description description) {}
+
+ private void collectMenuItemIds(View view) {
+ if (view.getTag() instanceof MenuItem) {
+ menuItemIds.add(((MenuItem) view.getTag()).getItemId());
+ } else if (view instanceof ViewGroup) {
+ ViewGroup viewGroup = (ViewGroup) view;
+ for (int i = 0; i < viewGroup.getChildCount(); i++) {
+ collectMenuItemIds(viewGroup.getChildAt(i));
+ }
+ }
+ }
+ }));
+ }
+
+ /**
* Asserts that the floating toolbar doesn't contain the specified item.
*
* @param itemLabel label of the item.
diff --git a/core/tests/overlaytests/OverlayAppFiltered/Android.mk b/core/tests/overlaytests/OverlayAppFiltered/Android.mk
new file mode 100644
index 0000000..8ba21df
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppFiltered/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JAVA_LIBRARIES += legacy-test
+
+LOCAL_SDK_VERSION := system_current
+
+LOCAL_PACKAGE_NAME := com.android.overlaytest.filtered_app_overlay
+
+include $(BUILD_PACKAGE)
diff --git a/core/tests/overlaytests/OverlayAppFiltered/AndroidManifest.xml b/core/tests/overlaytests/OverlayAppFiltered/AndroidManifest.xml
new file mode 100644
index 0000000..5b7950a
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppFiltered/AndroidManifest.xml
@@ -0,0 +1,9 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.overlaytest.filtered_app_overlay"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <overlay android:targetPackage="com.android.overlaytest"
+ android:requiredSystemPropertyName="persist.oem.overlay.test"
+ android:requiredSystemPropertyValue="foo"
+ android:priority="3"/>
+</manifest>
diff --git a/core/tests/overlaytests/OverlayAppFiltered/res/raw/lorem_ipsum.txt b/core/tests/overlaytests/OverlayAppFiltered/res/raw/lorem_ipsum.txt
new file mode 100644
index 0000000..0954ced
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppFiltered/res/raw/lorem_ipsum.txt
@@ -0,0 +1 @@
+Lorem ipsum: filtered overlays.
diff --git a/core/tests/overlaytests/OverlayAppFiltered/res/values/config.xml b/core/tests/overlaytests/OverlayAppFiltered/res/values/config.xml
new file mode 100644
index 0000000..60b94ee
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppFiltered/res/values/config.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="str">filtered</string>
+</resources>
diff --git a/core/tests/overlaytests/OverlayAppFiltered/res/xml/integer.xml b/core/tests/overlaytests/OverlayAppFiltered/res/xml/integer.xml
new file mode 100644
index 0000000..e2652b7
--- /dev/null
+++ b/core/tests/overlaytests/OverlayAppFiltered/res/xml/integer.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8"?>
+<integer value="3"/>
diff --git a/core/tests/overlaytests/OverlayAppFirst/Android.mk b/core/tests/overlaytests/OverlayAppFirst/Android.mk
index ee991fc..51f4487 100644
--- a/core/tests/overlaytests/OverlayAppFirst/Android.mk
+++ b/core/tests/overlaytests/OverlayAppFirst/Android.mk
@@ -3,7 +3,7 @@
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_LIBRARIES += legacy-test
LOCAL_SDK_VERSION := current
diff --git a/core/tests/overlaytests/OverlayAppSecond/Android.mk b/core/tests/overlaytests/OverlayAppSecond/Android.mk
index 87402c43..b3cfd18 100644
--- a/core/tests/overlaytests/OverlayAppSecond/Android.mk
+++ b/core/tests/overlaytests/OverlayAppSecond/Android.mk
@@ -3,7 +3,7 @@
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_LIBRARIES += legacy-test
LOCAL_SDK_VERSION := current
diff --git a/core/tests/overlaytests/OverlayTest/Android.mk b/core/tests/overlaytests/OverlayTest/Android.mk
index 4767e52..964348f 100644
--- a/core/tests/overlaytests/OverlayTest/Android.mk
+++ b/core/tests/overlaytests/OverlayTest/Android.mk
@@ -7,6 +7,8 @@
LOCAL_DEX_PREOPT := false
+LOCAL_JAVA_LIBRARIES += legacy-test
+
LOCAL_MODULE_PATH := $(TARGET_OUT)/app
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/core/tests/overlaytests/OverlayTestOverlay/Android.mk b/core/tests/overlaytests/OverlayTestOverlay/Android.mk
index b1327f71..5265d91 100644
--- a/core/tests/overlaytests/OverlayTestOverlay/Android.mk
+++ b/core/tests/overlaytests/OverlayTestOverlay/Android.mk
@@ -3,7 +3,7 @@
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAVA_LIBRARIES += legacy-test
LOCAL_SDK_VERSION := current
diff --git a/core/tests/overlaytests/testrunner.py b/core/tests/overlaytests/testrunner.py
index 2aa25ad..e88805e 100755
--- a/core/tests/overlaytests/testrunner.py
+++ b/core/tests/overlaytests/testrunner.py
@@ -13,6 +13,7 @@
TASK_DISABLE_OVERLAYS = 'disable overlays'
TASK_ENABLE_MULTIPLE_OVERLAYS = 'enable multiple overlays'
TASK_ENABLE_SINGLE_OVERLAY = 'enable single overlay'
+TASK_ENABLE_FILTERED_OVERLAYS = 'enable filtered overlays'
TASK_FILE_EXISTS_TEST = 'test (file exists)'
TASK_GREP_IDMAP_TEST = 'test (grep idmap)'
TASK_MD5_TEST = 'test (md5)'
@@ -25,6 +26,7 @@
TASK_ROOT = 'root'
TASK_REMOUNT = 'remount'
TASK_RM = 'rm'
+TASK_SETPROP = 'setprop'
TASK_SETUP_IDMAP_PATH = 'setup idmap --path'
TASK_SETUP_IDMAP_SCAN = 'setup idmap --scan'
TASK_START = 'start'
@@ -188,7 +190,10 @@
return "%s -> %s" % (self.src, self.dest)
def execute(self):
- src = os.getenv('OUT') + "/" + self.src
+ src = os.getenv('OUT')
+ if (src is None):
+ return 1, "", "Unable to proceed - $OUT environment var not set\n"
+ src += "/" + self.src
argv = shlex.split(adb + ' push %s %s' % (src, self.dest))
proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdout, stderr) = proc.communicate()
@@ -219,10 +224,24 @@
def execute(self):
returncode, stdout, stderr = _adb_shell('ls %s' % self.path)
- if returncode != 0 and stdout.endswith(': No such file or directory\n'):
+ if returncode != 0 and stderr.endswith(': No such file or directory\n'):
return 0, "", ""
return _adb_shell('rm -r %s' % self.path)
+class SetPropTask:
+ def __init__(self, prop, value):
+ self.prop = prop
+ self.value = value
+
+ def get_type(self):
+ return TASK_SETPROP
+
+ def get_name(self):
+ return self.prop
+
+ def execute(self):
+ return _adb_shell('setprop %s %s' % (self.prop, self.value))
+
class IdmapPathTask:
def __init__(self, path_target_apk, path_overlay_apk, path_idmap):
self.path_target_apk = path_target_apk
@@ -236,7 +255,7 @@
return self.path_idmap
def execute(self):
- return _adb_shell('su system idmap --path "%s" "%s" "%s"' % (self.path_target_apk, self.path_overlay_apk, self.path_idmap))
+ return _adb_shell('su system idmap --scan "%s" "%s" "%s" "%s"' % (self.target_pkg_name, self.target_pkg, self.idmap_dir, self.overlay_dir))
class IdmapScanTask:
def __init__(self, overlay_dir, target_pkg_name, target_pkg, idmap_dir, symlink_dir):
@@ -411,8 +430,12 @@
RmTask("/data/resource-cache/vendor@overlay@framework_b.apk@idmap"),
RmTask("/vendor/overlay/app_a.apk"),
RmTask("/vendor/overlay/app_b.apk"),
+ RmTask("/vendor/overlay/app_c.apk"),
RmTask("/data/resource-cache/vendor@overlay@app_a.apk@idmap"),
RmTask("/data/resource-cache/vendor@overlay@app_b.apk@idmap"),
+ RmTask("/data/resource-cache/vendor@overlay@app_c.apk@idmap"),
+ SetPropTask('persist.oem.overlay.test', '""'),
+ RmTask("/data/property/persist.oem.overlay.test"),
]
return CompoundTask(TASK_DISABLE_OVERLAYS, tasks)
@@ -435,9 +458,23 @@
PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_b.apk'),
PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'),
PushTask('/data/app/com.android.overlaytest.second_app_overlay/com.android.overlaytest.second_app_overlay.apk', '/vendor/overlay/app_b.apk'),
+ PushTask('/data/app/com.android.overlaytest.filtered_app_overlay/com.android.overlaytest.filtered_app_overlay.apk', '/vendor/overlay/app_c.apk'),
]
return CompoundTask(TASK_ENABLE_MULTIPLE_OVERLAYS, tasks)
+def _create_enable_filtered_overlays_task():
+ tasks = [
+ _create_disable_overlays_task(),
+ SetPropTask('persist.oem.overlay.test', 'foo'),
+ MkdirTask('/system/vendor'),
+ MkdirTask('/vendor/overlay'),
+ PushTask('/data/app/com.android.overlaytest.overlay/com.android.overlaytest.overlay.apk', '/vendor/overlay/framework_b.apk'),
+ PushTask('/data/app/com.android.overlaytest.first_app_overlay/com.android.overlaytest.first_app_overlay.apk', '/vendor/overlay/app_a.apk'),
+ PushTask('/data/app/com.android.overlaytest.second_app_overlay/com.android.overlaytest.second_app_overlay.apk', '/vendor/overlay/app_b.apk'),
+ PushTask('/data/app/com.android.overlaytest.filtered_app_overlay/com.android.overlaytest.filtered_app_overlay.apk', '/vendor/overlay/app_c.apk'),
+ ]
+ return CompoundTask(TASK_ENABLE_FILTERED_OVERLAYS, tasks)
+
def _create_setup_idmap_path_task(idmaps, symlinks):
tasks = [
_create_enable_single_overlay_task(),
@@ -450,12 +487,11 @@
def _create_setup_idmap_scan_task(idmaps, symlinks):
tasks = [
- _create_enable_single_overlay_task(),
+ _create_enable_filtered_overlays_task(),
RmTask(symlinks),
RmTask(idmaps),
MkdirTask(idmaps),
MkdirTask(symlinks),
- _create_enable_multiple_overlays_task(),
]
return CompoundTask(TASK_SETUP_IDMAP_SCAN, tasks)
@@ -538,7 +574,7 @@
help='do not rebuild test projects')
parser.add_option('-i', '--test-idmap', action='store_true',
dest='test_idmap', default=False,
- help='run tests for single overlay')
+ help='run tests for idmap')
parser.add_option('-0', '--test-no-overlay', action='store_true',
dest='test_no_overlay', default=False,
help='run tests without any overlay')
@@ -548,16 +584,21 @@
parser.add_option('-2', '--test-multiple-overlays', action='store_true',
dest='test_multiple_overlays', default=False,
help='run tests for multiple overlays')
+ parser.add_option('-3', '--test-filtered-overlays', action='store_true',
+ dest='test_filtered_overlays', default=False,
+ help='run tests for filtered (sys prop) overlays')
return parser
if __name__ == '__main__':
opt_parser = _create_opt_parser()
opts, args = opt_parser.parse_args(sys.argv[1:])
- if not opts.test_idmap and not opts.test_no_overlay and not opts.test_single_overlay and not opts.test_multiple_overlays:
+ if not opts.test_idmap and not opts.test_no_overlay and not opts.test_single_overlay and not opts.test_multiple_overlays and not opts.test_filtered_overlays:
opts.test_idmap = True
opts.test_no_overlay = True
opts.test_single_overlay = True
opts.test_multiple_overlays = True
+ opts.test_filtered_overlays = True
+
if len(args) > 0:
opt_parser.error("unexpected arguments: %s" % " ".join(args))
# will never reach this: opt_parser.error will call sys.exit
@@ -580,6 +621,7 @@
tasks.append(CompilationTask('OverlayTestOverlay/Android.mk'))
tasks.append(CompilationTask('OverlayAppFirst/Android.mk'))
tasks.append(CompilationTask('OverlayAppSecond/Android.mk'))
+ tasks.append(CompilationTask('OverlayAppFiltered/Android.mk'))
# remount filesystem, install test project
tasks.append(RootTask())
@@ -600,13 +642,13 @@
tasks.append(GrepIdmapTest(idmaps + '/a.idmap', 'bool/config_annoy_dianne', 1))
# idmap --scan
- idmap = idmaps + '/vendor@overlay@framework_b.apk@idmap'
tasks.append(StopTask())
tasks.append(_create_setup_idmap_scan_task(idmaps, symlinks))
tasks.append(StartTask())
tasks.append(IdmapScanTask('/vendor/overlay', 'android', '/system/framework/framework-res.apk', idmaps, symlinks))
- tasks.append(FileExistsTest(idmap))
- tasks.append(GrepIdmapTest(idmap, 'bool/config_annoy_dianne', 1))
+ tasks.append(FileExistsTest(idmaps + '/vendor@overlay@framework_b.apk@idmap'))
+ tasks.append(GrepIdmapTest(idmaps + '/vendor@overlay@framework_b.apk@idmap', 'bool/config_annoy_dianne', 1))
+
# overlays.list
overlays_list_path = idmaps + '/overlays.list'
@@ -620,27 +662,38 @@
tasks.append(RmTask(symlinks))
tasks.append(RmTask(idmaps))
- # test no overlay
+ # test no overlay: all overlays cleared
if opts.test_no_overlay:
tasks.append(StopTask())
tasks.append(_create_disable_overlays_task())
tasks.append(StartTask())
tasks.append(InstrumentationTask('com.android.overlaytest.WithoutOverlayTest'))
- # test single overlay
+ # test single overlay: one overlay (a)
if opts.test_single_overlay:
tasks.append(StopTask())
tasks.append(_create_enable_single_overlay_task())
tasks.append(StartTask())
tasks.append(InstrumentationTask('com.android.overlaytest.WithOverlayTest'))
- # test multiple overlays
+ # test multiple overlays: all overlays - including 'disabled' filtered
+ # overlay (system property unset) so expect 'b[p=2]' overrides 'a[p=1]' but
+ # 'c[p=3]' should be ignored
if opts.test_multiple_overlays:
tasks.append(StopTask())
tasks.append(_create_enable_multiple_overlays_task())
tasks.append(StartTask())
tasks.append(InstrumentationTask('com.android.overlaytest.WithMultipleOverlaysTest'))
+ # test filtered overlays: all overlays - including 'enabled' filtered
+ # overlay (system property set/matched) so expect c[p=3] to override both a
+ # & b where applicable
+ if opts.test_filtered_overlays:
+ tasks.append(StopTask())
+ tasks.append(_create_enable_filtered_overlays_task())
+ tasks.append(StartTask())
+ tasks.append(InstrumentationTask('com.android.overlaytest.WithFilteredOverlaysTest'))
+
ignored_errors = 0
for t in tasks:
type = t.get_type()
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index ec653d0..7f07f03 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -40,6 +40,7 @@
<privapp-permissions package="com.android.defcontainer">
<permission name="android.permission.ACCESS_CACHE_FILESYSTEM"/>
+ <permission name="android.permission.ALLOCATE_AGGRESSIVE"/>
<permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
</privapp-permissions>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 91906850..599406c 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -916,18 +916,20 @@
* @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
* mark the bitmap as opaque. Doing so will clear the bitmap in black
* instead of transparent.
- * @param colorSpace The color space of the bitmap. If null,
- * {@link ColorSpace.Named#SRGB sRGB} is assumed. This argument is
- * ignored if the config is not {@link Config#ARGB_8888}.
+ * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16},
+ * {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the
+ * config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB}
+ * is assumed.
*
* @throws IllegalArgumentException if the width or height are <= 0, if
* Config is Config.HARDWARE (because hardware bitmaps are always
* immutable), if the specified color space is not {@link ColorSpace.Model#RGB RGB},
- * or if the specified color space's transfer function is not an
- * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
+ * if the specified color space's transfer function is not an
+ * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or if
+ * the color space is null
*/
public static Bitmap createBitmap(int width, int height, @NonNull Config config,
- boolean hasAlpha, @Nullable ColorSpace colorSpace) {
+ boolean hasAlpha, @NonNull ColorSpace colorSpace) {
return createBitmap(null, width, height, config, hasAlpha, colorSpace);
}
@@ -951,7 +953,8 @@
*/
public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height,
@NonNull Config config, boolean hasAlpha) {
- return createBitmap(display, width, height, config, hasAlpha, null);
+ return createBitmap(display, width, height, config, hasAlpha,
+ ColorSpace.get(ColorSpace.Named.SRGB));
}
/**
@@ -968,27 +971,32 @@
* @param hasAlpha If the bitmap is ARGB_8888 or RGBA_16F this flag can be used to
* mark the bitmap as opaque. Doing so will clear the bitmap in black
* instead of transparent.
- * @param colorSpace The color space of the bitmap. If null,
- * {@link ColorSpace.Named#SRGB sRGB} is assumed. This argument is
- * ignored if the config is not {@link Config#ARGB_8888}.
+ * @param colorSpace The color space of the bitmap. If the config is {@link Config#RGBA_F16},
+ * {@link ColorSpace.Named#EXTENDED_SRGB scRGB} is assumed, and if the
+ * config is not {@link Config#ARGB_8888}, {@link ColorSpace.Named#SRGB sRGB}
+ * is assumed.
*
* @throws IllegalArgumentException if the width or height are <= 0, if
* Config is Config.HARDWARE (because hardware bitmaps are always
* immutable), if the specified color space is not {@link ColorSpace.Model#RGB RGB},
- * or if the specified color space's transfer function is not an
- * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
+ * if the specified color space's transfer function is not an
+ * {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}, or if
+ * the color space is null
*/
public static Bitmap createBitmap(@Nullable DisplayMetrics display, int width, int height,
- @NonNull Config config, boolean hasAlpha, @Nullable ColorSpace colorSpace) {
+ @NonNull Config config, boolean hasAlpha, @NonNull ColorSpace colorSpace) {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("width and height must be > 0");
}
if (config == Config.HARDWARE) {
throw new IllegalArgumentException("can't create mutable bitmap with Config.HARDWARE");
}
+ if (colorSpace == null) {
+ throw new IllegalArgumentException("can't create bitmap without a color space");
+ }
Bitmap bm;
- if (colorSpace == null || config != Config.ARGB_8888) {
+ if (config != Config.ARGB_8888) {
bm = nativeCreate(null, 0, width, width, height, config.nativeInt, true, null, null);
} else {
if (!(colorSpace instanceof ColorSpace.Rgb)) {
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index ceedc1f..3b272c8 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -43,7 +43,6 @@
* the same result from the decoder as if null were passed.
*/
public Options() {
- inDither = false;
inScaled = true;
inPremultiplied = true;
}
@@ -114,8 +113,8 @@
/**
* If set to true, the decoder will return null (no bitmap), but
- * the out... fields will still be set, allowing the caller to query
- * the bitmap without having to allocate the memory for its pixels.
+ * the <code>out...</code> fields will still be set, allowing the caller to
+ * query the bitmap without having to allocate the memory for its pixels.
*/
public boolean inJustDecodeBounds;
@@ -144,6 +143,35 @@
public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
/**
+ * <p>If this is non-null, the decoder will try to decode into this
+ * color space. If it is null, or the request cannot be met,
+ * the decoder will pick either the color space embedded in the image
+ * or the color space best suited for the requested image configuration
+ * (for instance {@link ColorSpace.Named#SRGB sRGB} for
+ * the {@link Bitmap.Config#ARGB_8888} configuration).</p>
+ *
+ * <p>{@link Bitmap.Config#RGBA_F16} always uses the
+ * {@link ColorSpace.Named#LINEAR_EXTENDED_SRGB scRGB} color space).
+ * Bitmaps in other configurations without an embedded color space are
+ * assumed to be in the {@link ColorSpace.Named#SRGB sRGB} color space.</p>
+ *
+ * <p class="note">Only {@link ColorSpace.Model#RGB} color spaces are
+ * currently supported. An <code>IllegalArgumentException</code> will
+ * be thrown by the decode methods when setting a non-RGB color space
+ * such as {@link ColorSpace.Named#CIE_LAB Lab}.</p>
+ *
+ * <p class="note">The specified color space's transfer function must be
+ * an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}. An
+ * <code>IllegalArgumentException</code> will be thrown by the decode methods
+ * if calling {@link ColorSpace.Rgb#getTransferParameters()} on the
+ * specified color space returns null.</p>
+ *
+ * <p>After decode, the bitmap's color space is stored in
+ * {@link #outColorSpace}.</p>
+ */
+ public ColorSpace inPreferredColorSpace = null;
+
+ /**
* If true (which is the default), the resulting bitmap will have its
* color channels pre-multipled by the alpha channel.
*
@@ -403,9 +431,22 @@
}
static void validate(Options opts) {
- if (opts != null && opts.inMutable && opts.inPreferredConfig == Bitmap.Config.HARDWARE) {
+ if (opts == null) return;
+
+ if (opts.inMutable && opts.inPreferredConfig == Bitmap.Config.HARDWARE) {
throw new IllegalArgumentException("Bitmaps with Config.HARWARE are always immutable");
}
+
+ if (opts.inPreferredColorSpace != null) {
+ if (!(opts.inPreferredColorSpace instanceof ColorSpace.Rgb)) {
+ throw new IllegalArgumentException("The destination color space must use the " +
+ "RGB color model");
+ }
+ if (((ColorSpace.Rgb) opts.inPreferredColorSpace).getTransferParameters() == null) {
+ throw new IllegalArgumentException("The destination color space must use an " +
+ "ICC parametric transfer function");
+ }
+ }
}
}
@@ -421,7 +462,9 @@
* size be returned (in opts.outWidth and opts.outHeight)
* @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
* is {@link android.graphics.Bitmap.Config#HARDWARE}
- * and {@link BitmapFactory.Options#inMutable} is set.
+ * and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
+ * is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
+ * function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
*/
public static Bitmap decodeFile(String pathName, Options opts) {
validate(opts);
@@ -463,7 +506,9 @@
* resources, which we pass to be able to scale the bitmap accordingly.
* @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
* is {@link android.graphics.Bitmap.Config#HARDWARE}
- * and {@link BitmapFactory.Options#inMutable} is set.
+ * and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
+ * is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
+ * function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
*/
public static Bitmap decodeResourceStream(Resources res, TypedValue value,
InputStream is, Rect pad, Options opts) {
@@ -501,7 +546,9 @@
* size be returned (in opts.outWidth and opts.outHeight)
* @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
* is {@link android.graphics.Bitmap.Config#HARDWARE}
- * and {@link BitmapFactory.Options#inMutable} is set.
+ * and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
+ * is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
+ * function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
*/
public static Bitmap decodeResource(Resources res, int id, Options opts) {
validate(opts);
@@ -559,7 +606,9 @@
* size be returned (in opts.outWidth and opts.outHeight)
* @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
* is {@link android.graphics.Bitmap.Config#HARDWARE}
- * and {@link BitmapFactory.Options#inMutable} is set.
+ * and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
+ * is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
+ * function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
*/
public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
if ((offset | length) < 0 || data.length < offset + length) {
@@ -641,7 +690,9 @@
* size be returned (in opts.outWidth and opts.outHeight)
* @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
* is {@link android.graphics.Bitmap.Config#HARDWARE}
- * and {@link BitmapFactory.Options#inMutable} is set.
+ * and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
+ * is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
+ * function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
*
* <p class="note">Prior to {@link android.os.Build.VERSION_CODES#KITKAT},
* if {@link InputStream#markSupported is.markSupported()} returns true,
@@ -720,7 +771,9 @@
* @return the decoded bitmap, or null
* @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
* is {@link android.graphics.Bitmap.Config#HARDWARE}
- * and {@link BitmapFactory.Options#inMutable} is set.
+ * and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
+ * is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
+ * function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
*/
public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
validate(opts);
diff --git a/graphics/java/android/graphics/BitmapRegionDecoder.java b/graphics/java/android/graphics/BitmapRegionDecoder.java
index 04abca1..2da27c7 100644
--- a/graphics/java/android/graphics/BitmapRegionDecoder.java
+++ b/graphics/java/android/graphics/BitmapRegionDecoder.java
@@ -180,7 +180,9 @@
* decoded.
* @throws IllegalArgumentException if {@link BitmapFactory.Options#inPreferredConfig}
* is {@link android.graphics.Bitmap.Config#HARDWARE}
- * and {@link BitmapFactory.Options#inMutable} is set.
+ * and {@link BitmapFactory.Options#inMutable} is set, if the specified color space
+ * is not {@link ColorSpace.Model#RGB RGB}, or if the specified color space's transfer
+ * function is not an {@link ColorSpace.Rgb.TransferParameters ICC parametric curve}
*/
public Bitmap decodeRegion(Rect rect, BitmapFactory.Options options) {
BitmapFactory.Options.validate(options);
diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java
index 7e6756e..5577f53 100644
--- a/graphics/java/android/graphics/BitmapShader.java
+++ b/graphics/java/android/graphics/BitmapShader.java
@@ -41,35 +41,16 @@
* @param tileY The tiling mode for y to draw the bitmap in.
*/
public BitmapShader(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) {
- set(bitmap, tileX, tileY);
+ this(bitmap, tileX.nativeInt, tileY.nativeInt);
}
private BitmapShader(Bitmap bitmap, int tileX, int tileY) {
- setInternal(bitmap, tileX, tileY);
- }
-
- /**
- * Reinitialize the BitmapShader's Bitmap and tile modes.
- *
- * @param bitmap The bitmap to use inside the shader
- * @param tileX The tiling mode for x to draw the bitmap in.
- * @param tileY The tiling mode for y to draw the bitmap in.
- */
- public void set(@NonNull Bitmap bitmap, @NonNull TileMode tileX, @NonNull TileMode tileY) {
- if (tileX == null || tileY == null) {
- throw new IllegalArgumentException();
- }
- setInternal(bitmap, tileX.nativeInt, tileY.nativeInt);
- }
-
- private void setInternal(Bitmap bitmap, int tileX, int tileY) {
if (bitmap == null) {
throw new IllegalArgumentException("Bitmap must be non-null");
}
if (bitmap == mBitmap && tileX == mTileX && tileY == mTileY) {
return;
}
- discardNativeInstance();
mBitmap = bitmap;
mTileX = tileX;
mTileY = tileY;
diff --git a/graphics/java/android/graphics/ColorMatrixColorFilter.java b/graphics/java/android/graphics/ColorMatrixColorFilter.java
index 61f6cc5..9201a2e 100644
--- a/graphics/java/android/graphics/ColorMatrixColorFilter.java
+++ b/graphics/java/android/graphics/ColorMatrixColorFilter.java
@@ -73,6 +73,8 @@
* @see #getColorMatrix(ColorMatrix)
* @see #setColorMatrixArray(float[])
* @see ColorMatrix#reset()
+ *
+ * @hide
*/
public void setColorMatrix(@Nullable ColorMatrix matrix) {
discardNativeInstance();
@@ -99,6 +101,8 @@
*
* @throws ArrayIndexOutOfBoundsException if the specified array's
* length is < 20
+ *
+ * @hide
*/
public void setColorMatrixArray(@Nullable float[] array) {
// called '...Array' so that passing null isn't ambiguous
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index 67504cf..f2957a3 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -1592,7 +1592,7 @@
Math.abs(a.a - b.a) < 1e-3 &&
Math.abs(a.b - b.b) < 1e-3 &&
Math.abs(a.c - b.c) < 1e-3 &&
- Math.abs(a.d - b.d) < 1e-3 &&
+ Math.abs(a.d - b.d) < 2e-3 && // Special case for variations in sRGB OETF/EOTF
Math.abs(a.e - b.e) < 1e-3 &&
Math.abs(a.f - b.f) < 1e-3 &&
Math.abs(a.g - b.g) < 1e-3;
diff --git a/graphics/java/android/graphics/ComposeShader.java b/graphics/java/android/graphics/ComposeShader.java
index 8438bf2..e107ea7 100644
--- a/graphics/java/android/graphics/ComposeShader.java
+++ b/graphics/java/android/graphics/ComposeShader.java
@@ -59,43 +59,10 @@
}
private ComposeShader(Shader shaderA, Shader shaderB, int nativeMode) {
- setInternal(shaderA, shaderB, nativeMode);
- }
-
- /**
- * Reinitialize the ComposeShader's component Shaders and blend mode.
- *
- * @param shaderA The colors from this shader are seen as the "dst" by the mode
- * @param shaderB The colors from this shader are seen as the "src" by the mode
- * @param mode The PorterDuff mode that combines the colors from the two shaders.
- */
- public void set(@NonNull Shader shaderA, @NonNull Shader shaderB, @NonNull Xfermode mode) {
- setInternal(shaderA, shaderB, mode.porterDuffMode);
- }
-
- /**
- * Reinitialize the ComposeShader's component Shaders and blend mode.
- *
- * @param shaderA The colors from this shader are seen as the "dst" by the mode
- * @param shaderB The colors from this shader are seen as the "src" by the mode
- * @param mode The PorterDuff mode that combines the colors from the two shaders.
- */
- public void set(@NonNull Shader shaderA, @NonNull Shader shaderB,
- @NonNull PorterDuff.Mode mode) {
- setInternal(shaderA, shaderB, mode.nativeInt);
- }
-
- private void setInternal(Shader shaderA, Shader shaderB, int nativeMode) {
if (shaderA == null || shaderB == null) {
throw new IllegalArgumentException("Shader parameters must not be null");
}
- if (shaderA == mShaderA && shaderB == mShaderB && mPorterDuffMode == nativeMode) {
- // no work to do...
- return;
- }
-
- discardNativeInstance();
mShaderA = shaderA;
mShaderB = shaderB;
mPorterDuffMode = nativeMode;
@@ -109,16 +76,6 @@
mShaderA.getNativeInstance(), mShaderB.getNativeInstance(), mPorterDuffMode);
}
- @Override
- void verifyNativeInstance() {
- if (mShaderA.getNativeInstance() != mNativeInstanceShaderA
- || mShaderB.getNativeInstance() != mNativeInstanceShaderB) {
- // Child shader native instance has been updated,
- // so our cached native instance is no longer valid - discard it
- discardNativeInstance();
- }
- }
-
/**
* @hide
*/
diff --git a/graphics/java/android/graphics/LightingColorFilter.java b/graphics/java/android/graphics/LightingColorFilter.java
index b0c145b..1578ffb 100644
--- a/graphics/java/android/graphics/LightingColorFilter.java
+++ b/graphics/java/android/graphics/LightingColorFilter.java
@@ -57,8 +57,6 @@
/**
* Returns the RGB color used to multiply the source color when the
* color filter is applied.
- *
- * @see #setColorMultiply(int)
*/
@ColorInt
public int getColorMultiply() {
@@ -71,6 +69,8 @@
* The alpha channel of this color is ignored.
*
* @see #getColorMultiply()
+ *
+ * @hide
*/
public void setColorMultiply(@ColorInt int mul) {
if (mMul != mul) {
@@ -82,8 +82,6 @@
/**
* Returns the RGB color that will be added to the source color
* when the color filter is applied.
- *
- * @see #setColorAdd(int)
*/
@ColorInt
public int getColorAdd() {
@@ -96,6 +94,8 @@
* The alpha channel of this color is ignored.
*
* @see #getColorAdd()
+ *
+ * @hide
*/
public void setColorAdd(@ColorInt int add) {
if (mAdd != add) {
diff --git a/graphics/java/android/graphics/LinearGradient.java b/graphics/java/android/graphics/LinearGradient.java
index 0e4cd0a..7139efe 100644
--- a/graphics/java/android/graphics/LinearGradient.java
+++ b/graphics/java/android/graphics/LinearGradient.java
@@ -57,7 +57,20 @@
*/
public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],
@Nullable float positions[], @NonNull TileMode tile) {
- set(x0, y0, x1, y1, colors, positions, tile);
+ if (colors.length < 2) {
+ throw new IllegalArgumentException("needs >= 2 number of colors");
+ }
+ if (positions != null && colors.length != positions.length) {
+ throw new IllegalArgumentException("color and position arrays must be of equal length");
+ }
+ mType = TYPE_COLORS_AND_POSITIONS;
+ mX0 = x0;
+ mY0 = y0;
+ mX1 = x1;
+ mY1 = y1;
+ mColors = colors.clone();
+ mPositions = positions != null ? positions.clone() : null;
+ mTileMode = tile;
}
/**
@@ -74,56 +87,6 @@
public LinearGradient(float x0, float y0, float x1, float y1,
@ColorInt int color0, @ColorInt int color1,
@NonNull TileMode tile) {
- set(x0, y0, x1, y1, color0, color1, tile);
- }
-
- /**
- * Reinitialize the shader.
- *
- * @param x0 The x-coordinate for the start of the gradient line
- * @param y0 The y-coordinate for the start of the gradient line
- * @param x1 The x-coordinate for the end of the gradient line
- * @param y1 The y-coordinate for the end of the gradient line
- * @param colors The colors to be distributed along the gradient line
- * @param positions May be null. The relative positions [0..1] of
- * each corresponding color in the colors array. If this is null,
- * the the colors are distributed evenly along the gradient line.
- * @param tile The Shader tiling mode
- */
- public void set(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[],
- @Nullable float positions[], @NonNull TileMode tile) {
- if (colors.length < 2) {
- throw new IllegalArgumentException("needs >= 2 number of colors");
- }
- if (positions != null && colors.length != positions.length) {
- throw new IllegalArgumentException("color and position arrays must be of equal length");
- }
- discardNativeInstance();
- mType = TYPE_COLORS_AND_POSITIONS;
- mX0 = x0;
- mY0 = y0;
- mX1 = x1;
- mY1 = y1;
- mColors = colors.clone();
- mPositions = positions != null ? positions.clone() : null;
- mTileMode = tile;
- }
-
- /**
- * Reinitialize the shader.
- *
- * @param x0 The x-coordinate for the start of the gradient line
- * @param y0 The y-coordinate for the start of the gradient line
- * @param x1 The x-coordinate for the end of the gradient line
- * @param y1 The y-coordinate for the end of the gradient line
- * @param color0 The color at the start of the gradient line.
- * @param color1 The color at the end of the gradient line.
- * @param tile The Shader tiling mode
- */
- public void set(float x0, float y0, float x1, float y1,
- @ColorInt int color0, @ColorInt int color1,
- @NonNull TileMode tile) {
- discardNativeInstance();
mType = TYPE_COLOR_START_AND_COLOR_END;
mX0 = x0;
mY0 = y0;
diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java
index ccc6ead..01d5825 100644
--- a/graphics/java/android/graphics/PorterDuffColorFilter.java
+++ b/graphics/java/android/graphics/PorterDuffColorFilter.java
@@ -49,6 +49,8 @@
*
* @see Color
* @see #setColor(int)
+ *
+ * @hide
*/
@ColorInt
public int getColor() {
@@ -64,6 +66,8 @@
* @see Color
* @see #getColor()
* @see #getMode()
+ *
+ * @hide
*/
public void setColor(@ColorInt int color) {
if (mColor != color) {
@@ -78,6 +82,8 @@
*
* @see PorterDuff
* @see #setMode(android.graphics.PorterDuff.Mode)
+ *
+ * @hide
*/
public PorterDuff.Mode getMode() {
return mMode;
@@ -90,6 +96,8 @@
* @see PorterDuff
* @see #getMode()
* @see #getColor()
+ *
+ * @hide
*/
public void setMode(@NonNull PorterDuff.Mode mode) {
if (mode == null) {
diff --git a/graphics/java/android/graphics/RadialGradient.java b/graphics/java/android/graphics/RadialGradient.java
index ae8f7da..f4b1191 100644
--- a/graphics/java/android/graphics/RadialGradient.java
+++ b/graphics/java/android/graphics/RadialGradient.java
@@ -57,7 +57,22 @@
public RadialGradient(float centerX, float centerY, float radius,
@NonNull @ColorInt int colors[], @Nullable float stops[],
@NonNull TileMode tileMode) {
- set(centerX, centerY, radius, colors, stops, tileMode);
+ if (radius <= 0) {
+ throw new IllegalArgumentException("radius must be > 0");
+ }
+ if (colors.length < 2) {
+ throw new IllegalArgumentException("needs >= 2 number of colors");
+ }
+ if (stops != null && colors.length != stops.length) {
+ throw new IllegalArgumentException("color and position arrays must be of equal length");
+ }
+ mType = TYPE_COLORS_AND_POSITIONS;
+ mX = centerX;
+ mY = centerY;
+ mRadius = radius;
+ mColors = colors.clone();
+ mPositions = stops != null ? stops.clone() : null;
+ mTileMode = tileMode;
}
/**
@@ -72,59 +87,9 @@
*/
public RadialGradient(float centerX, float centerY, float radius,
@ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) {
- set(centerX, centerY, radius, centerColor, edgeColor, tileMode);
- }
-
- /**
- * Reinitialize the shader.
- *
- * @param centerX The x-coordinate of the center of the radius
- * @param centerY The y-coordinate of the center of the radius
- * @param radius Must be positive. The radius of the circle for this gradient.
- * @param colors The colors to be distributed between the center and edge of the circle
- * @param stops May be <code>null</code>. Valid values are between <code>0.0f</code> and
- * <code>1.0f</code>. The relative position of each corresponding color in
- * the colors array. If <code>null</code>, colors are distributed evenly
- * between the center and edge of the circle.
- * @param tileMode The Shader tiling mode
- */
- public void set(float centerX, float centerY, float radius,
- @NonNull @ColorInt int colors[], @Nullable float stops[], @NonNull TileMode tileMode) {
if (radius <= 0) {
throw new IllegalArgumentException("radius must be > 0");
}
- if (colors.length < 2) {
- throw new IllegalArgumentException("needs >= 2 number of colors");
- }
- if (stops != null && colors.length != stops.length) {
- throw new IllegalArgumentException("color and position arrays must be of equal length");
- }
- discardNativeInstance();
- mType = TYPE_COLORS_AND_POSITIONS;
- mX = centerX;
- mY = centerY;
- mRadius = radius;
- mColors = colors.clone();
- mPositions = stops != null ? stops.clone() : null;
- mTileMode = tileMode;
- }
-
- /**
- * Reinitialize the shader.
- *
- * @param centerX The x-coordinate of the center of the radius
- * @param centerY The y-coordinate of the center of the radius
- * @param radius Must be positive. The radius of the circle for this gradient
- * @param centerColor The color at the center of the circle.
- * @param edgeColor The color at the edge of the circle.
- * @param tileMode The Shader tiling mode
- */
- public void set(float centerX, float centerY, float radius,
- @ColorInt int centerColor, @ColorInt int edgeColor, @NonNull TileMode tileMode) {
- if (radius <= 0) {
- throw new IllegalArgumentException("radius must be > 0");
- }
- discardNativeInstance();
mType = TYPE_COLOR_CENTER_AND_COLOR_EDGE;
mX = centerX;
mY = centerY;
diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java
index 8410ab2..c744757 100644
--- a/graphics/java/android/graphics/Shader.java
+++ b/graphics/java/android/graphics/Shader.java
@@ -105,20 +105,13 @@
return 0;
}
- void discardNativeInstance() {
+ private void discardNativeInstance() {
if (mNativeInstance != 0) {
nativeSafeUnref(mNativeInstance);
mNativeInstance = 0;
}
}
- /**
- * Callback for subclasses to call {@link #discardNativeInstance()} if the most recently
- * constructed native instance is no longer valid.
- */
- void verifyNativeInstance() {
- }
-
@Override
protected void finalize() throws Throwable {
try {
@@ -155,9 +148,6 @@
throw new IllegalStateException("attempting to use a finalized Shader");
}
- // verify mNativeInstance is valid
- verifyNativeInstance();
-
if (mNativeInstance == 0) {
mNativeInstance = createNativeInstance(mLocalMatrix == null
? 0 : mLocalMatrix.native_instance);
diff --git a/graphics/java/android/graphics/SweepGradient.java b/graphics/java/android/graphics/SweepGradient.java
index 0a1aef6..b6b80b4 100644
--- a/graphics/java/android/graphics/SweepGradient.java
+++ b/graphics/java/android/graphics/SweepGradient.java
@@ -54,7 +54,18 @@
*/
public SweepGradient(float cx, float cy,
@NonNull @ColorInt int colors[], @Nullable float positions[]) {
- set(cx, cy, colors, positions);
+ if (colors.length < 2) {
+ throw new IllegalArgumentException("needs >= 2 number of colors");
+ }
+ if (positions != null && colors.length != positions.length) {
+ throw new IllegalArgumentException(
+ "color and position arrays must be of equal length");
+ }
+ mType = TYPE_COLORS_AND_POSITIONS;
+ mCx = cx;
+ mCy = cy;
+ mColors = colors.clone();
+ mPositions = positions != null ? positions.clone() : null;
}
/**
@@ -66,50 +77,6 @@
* @param color1 The color to use at the end of the sweep
*/
public SweepGradient(float cx, float cy, @ColorInt int color0, @ColorInt int color1) {
- set(cx, cy, color0, color1);
- }
-
- /**
- * Reinitialize the shader.
- *
- * @param cx The x-coordinate of the center
- * @param cy The y-coordinate of the center
- * @param colors The colors to be distributed between around the center.
- * There must be at least 2 colors in the array.
- * @param positions May be NULL. The relative position of
- * each corresponding color in the colors array, beginning
- * with 0 and ending with 1.0. If the values are not
- * monotonic, the drawing may produce unexpected results.
- * If positions is NULL, then the colors are automatically
- * spaced evenly.
- */
- public void set(float cx, float cy,
- @NonNull @ColorInt int colors[], @Nullable float positions[]) {
- if (colors.length < 2) {
- throw new IllegalArgumentException("needs >= 2 number of colors");
- }
- if (positions != null && colors.length != positions.length) {
- throw new IllegalArgumentException(
- "color and position arrays must be of equal length");
- }
- discardNativeInstance();
- mType = TYPE_COLORS_AND_POSITIONS;
- mCx = cx;
- mCy = cy;
- mColors = colors.clone();
- mPositions = positions != null ? positions.clone() : null;
- }
-
- /**
- * Reinitialize the shader.
- *
- * @param cx The x-coordinate of the center
- * @param cy The y-coordinate of the center
- * @param color0 The color to use at the start of the sweep
- * @param color1 The color to use at the end of the sweep
- */
- public void set(float cx, float cy, @ColorInt int color0, @ColorInt int color1) {
- discardNativeInstance();
mType = TYPE_COLOR_START_AND_COLOR_END;
mCx = cx;
mCy = cy;
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index e097daa..18dc0dc 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -1138,6 +1138,7 @@
// Treat as system error since reaching here means that a system pre-installed font
// can't be used by our font stack.
Log.e(TAG, "Unable to load Family: " + family.getName() + ":" + family.getLanguage());
+ return null;
}
return fontFamily;
}
@@ -1163,7 +1164,10 @@
for (int i = 0; i < fontConfig.getFamilies().length; i++) {
FontConfig.Family f = fontConfig.getFamilies()[i];
if (i == 0 || f.getName() == null) {
- familyList.add(makeFamilyFromParsed(f, bufferForPath));
+ FontFamily family = makeFamilyFromParsed(f, bufferForPath);
+ if (family != null) {
+ familyList.add(family);
+ }
}
}
sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
@@ -1180,6 +1184,9 @@
typeface = sDefaultTypeface;
} else {
FontFamily fontFamily = makeFamilyFromParsed(f, bufferForPath);
+ if (fontFamily == null) {
+ continue;
+ }
FontFamily[] families = { fontFamily };
typeface = Typeface.createFromFamiliesWithDefault(families,
RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE);
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 3e7a246..931a55a 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -600,12 +600,12 @@
void GlopBuilder::build() {
REQUIRE_STAGES(kAllStages);
if (mOutGlop->mesh.vertices.attribFlags & VertexAttribFlags::TextureCoord) {
- if (mOutGlop->fill.texture.texture->target() == GL_TEXTURE_2D) {
+ Texture* texture = mOutGlop->fill.texture.texture;
+ if (texture->target() == GL_TEXTURE_2D) {
mDescription.hasTexture = true;
} else {
mDescription.hasExternalTexture = true;
}
- Texture* texture = mOutGlop->fill.texture.texture;
mDescription.hasLinearTexture = texture->isLinear();
mDescription.hasColorSpaceConversion = texture->hasColorSpaceConversion();
mDescription.transferFunction = texture->getTransferFunctionType();
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index d95acff..3e5e3bf 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -390,14 +390,8 @@
}
bool SkiaCanvas::clipPath(const SkPath* path, SkClipOp op) {
- SkRRect roundRect;
- if (path->isRRect(&roundRect)) {
- this->recordClip(roundRect, op);
- mCanvas->clipRRect(roundRect, op);
- } else {
- this->recordClip(*path, op);
- mCanvas->clipPath(*path, op);
- }
+ this->recordClip(*path, op);
+ mCanvas->clipPath(*path, op);
return !mCanvas->isClipEmpty();
}
diff --git a/libs/hwui/tests/unit/FontRendererTests.cpp b/libs/hwui/tests/unit/FontRendererTests.cpp
index ee20236..773a7ea 100644
--- a/libs/hwui/tests/unit/FontRendererTests.cpp
+++ b/libs/hwui/tests/unit/FontRendererTests.cpp
@@ -28,7 +28,7 @@
return true;
}
-RENDERTHREAD_OPENGL_PIPELINE_TEST(FontRenderer, renderDropShadow) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(FontRenderer, DISABLED_renderDropShadow) {
SkPaint paint;
paint.setTextSize(10);
paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
diff --git a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
index 7ae58a6..dafa074 100644
--- a/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
+++ b/libs/hwui/tests/unit/SkiaBehaviorTests.cpp
@@ -20,6 +20,7 @@
#include <SkColorMatrixFilter.h>
#include <SkColorSpace.h>
#include <SkImagePriv.h>
+#include <SkPathOps.h>
#include <SkShader.h>
using namespace android;
@@ -90,6 +91,16 @@
ASSERT_EQ(expected, paint.getBlendMode());
}
+TEST(SkiaBehavior, pathIntersection) {
+ SkPath p0, p1, result;
+ p0.addRect(SkRect::MakeXYWH(-5.0f, 0.0f, 1080.0f, 242.0f));
+ p1.addRect(SkRect::MakeXYWH(0.0f, 0.0f, 1080.0f, 242.0f));
+ Op(p0, p1, kIntersect_SkPathOp, &result);
+ SkRect resultRect;
+ ASSERT_TRUE(result.isRect(&resultRect));
+ ASSERT_EQ(SkRect::MakeXYWH(0.0f, 0.0f, 1075.0f, 242.0f), resultRect);
+}
+
TEST(SkiaBehavior, srgbColorSpaceIsSingleton) {
sk_sp<SkColorSpace> sRGB1 = SkColorSpace::MakeSRGB();
sk_sp<SkColorSpace> sRGB2 = SkColorSpace::MakeSRGB();
diff --git a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
index 8312bda..5383e57 100644
--- a/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
+++ b/libs/hwui/tests/unit/TextDropShadowCacheTests.cpp
@@ -26,7 +26,7 @@
using namespace android;
using namespace android::uirenderer;
-RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, addRemove) {
+RENDERTHREAD_OPENGL_PIPELINE_TEST(TextDropShadowCache, DISABLED_addRemove) {
SkPaint paint;
paint.setTextSize(20);
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index b222a6d..68f46ad 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -646,7 +646,10 @@
*
* <p>Following this call {@link #hasAltitude} will return false,
* and {@link #getAltitude} will return 0.0.
+ *
+ * @deprecated use a new Location object for location updates.
*/
+ @Deprecated
public void removeAltitude() {
mAltitude = 0.0f;
mFieldsMask &= ~HAS_ALTITUDE_MASK;
@@ -683,7 +686,10 @@
*
* <p>Following this call {@link #hasSpeed} will return false,
* and {@link #getSpeed} will return 0.0.
+ *
+ * @deprecated use a new Location object for location updates.
*/
+ @Deprecated
public void removeSpeed() {
mSpeed = 0.0f;
mFieldsMask &= ~HAS_SPEED_MASK;
@@ -733,7 +739,10 @@
*
* <p>Following this call {@link #hasBearing} will return false,
* and {@link #getBearing} will return 0.0.
+ *
+ * @deprecated use a new Location object for location updates.
*/
+ @Deprecated
public void removeBearing() {
mBearing = 0.0f;
mFieldsMask &= ~HAS_BEARING_MASK;
@@ -790,7 +799,10 @@
*
* <p>Following this call {@link #hasAccuracy} will return false, and
* {@link #getAccuracy} will return 0.0.
+ *
+ * @deprecated use a new Location object for location updates.
*/
+ @Deprecated
public void removeAccuracy() {
mHorizontalAccuracyMeters = 0.0f;
mFieldsMask &= ~HAS_HORIZONTAL_ACCURACY_MASK;
@@ -839,7 +851,11 @@
*
* <p>Following this call {@link #hasVerticalAccuracy} will return false, and
* {@link #getVerticalAccuracyMeters} will return 0.0.
+ *
+ * @deprecated use a new Location object for location updates.
+ * @removed
*/
+ @Deprecated
public void removeVerticalAccuracy() {
mVerticalAccuracyMeters = 0.0f;
mFieldsMask &= ~HAS_VERTICAL_ACCURACY_MASK;
@@ -883,7 +899,11 @@
*
* <p>Following this call {@link #hasSpeedAccuracy} will return false, and
* {@link #getSpeedAccuracyMetersPerSecond} will return 0.0.
+ *
+ * @deprecated use a new Location object for location updates.
+ * @removed
*/
+ @Deprecated
public void removeSpeedAccuracy() {
mSpeedAccuracyMetersPerSecond = 0.0f;
mFieldsMask &= ~HAS_SPEED_ACCURACY_MASK;
@@ -927,7 +947,11 @@
*
* <p>Following this call {@link #hasBearingAccuracy} will return false, and
* {@link #getBearingAccuracyDegrees} will return 0.0.
+ *
+ * @deprecated use a new Location object for location updates.
+ * @removed
*/
+ @Deprecated
public void removeBearingAccuracy() {
mBearingAccuracyDegrees = 0.0f;
mFieldsMask &= ~HAS_BEARING_ACCURACY_MASK;
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index e628d18..6cab56c 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -23,12 +23,12 @@
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.media.MediaCodecInfo.CodecCapabilities;
-import android.media.MediaMetricsSet;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.PersistableBundle;
import android.view.Surface;
import java.io.IOException;
@@ -3188,20 +3188,19 @@
/**
* Return Metrics data about the current codec instance.
*
- * @return a MediaMetricsSet containing the set of attributes and values
+ * @return a {@link PersistableBundle} containing the set of attributes and values
* available for the media being handled by this instance of MediaCodec
- * The attributes are descibed in {@link MediaMetricsSet.MediaCodec}.
+ * The attributes are descibed in {@link MetricsConstants}.
*
- * Additional vendor-specific fields may also be present in
- * the return value.
+ * Additional vendor-specific fields may also be present in
+ * the return value.
*/
- public MediaMetricsSet getMetrics() {
- Bundle bundle = native_getMetrics();
- MediaMetricsSet mSet = new MediaMetricsSet(bundle);
- return mSet;
+ public PersistableBundle getMetrics() {
+ PersistableBundle bundle = native_getMetrics();
+ return bundle;
}
- private native Bundle native_getMetrics();
+ private native PersistableBundle native_getMetrics();
/**
* Change a video encoder's target bitrate on the fly. The value is an
@@ -3660,4 +3659,80 @@
private final ByteBuffer mData;
}
}
+
+ public final static class MetricsConstants
+ {
+ private MetricsConstants() {}
+
+ /**
+ * Key to extract the codec being used
+ * from the {@link MediaCodec#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String CODEC = "android.media.mediacodec.codec";
+
+ /**
+ * Key to extract the MIME type
+ * from the {@link MediaCodec#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String MIME_TYPE = "android.media.mediacodec.mime";
+
+ /**
+ * Key to extract what the codec mode
+ * from the {@link MediaCodec#getMetrics} return value.
+ * The value is a String. Values will be one of the constants
+ * {@link #MODE_AUDIO} or {@link #MODE_VIDEO}.
+ */
+ public static final String MODE = "android.media.mediacodec.mode";
+
+ /**
+ * The value returned for the key {@link #MODE} when the
+ * codec is a audio codec.
+ */
+ public static final String MODE_AUDIO = "audio";
+
+ /**
+ * The value returned for the key {@link #MODE} when the
+ * codec is a video codec.
+ */
+ public static final String MODE_VIDEO = "video";
+
+ /**
+ * Key to extract the flag indicating whether the codec is running
+ * as an encoder or decoder from the {@link MediaCodec#getMetrics} return value.
+ * The value is an integer.
+ * A 0 indicates decoder; 1 indicates encoder.
+ */
+ public static final String ENCODER = "android.media.mediacodec.encoder";
+
+ /**
+ * Key to extract the flag indicating whether the codec is running
+ * in secure (DRM) mode from the {@link MediaCodec#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String SECURE = "android.media.mediacodec.secure";
+
+ /**
+ * Key to extract the width (in pixels) of the video track
+ * from the {@link MediaCodec#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String WIDTH = "android.media.mediacodec.width";
+
+ /**
+ * Key to extract the height (in pixels) of the video track
+ * from the {@link MediaCodec#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String HEIGHT = "android.media.mediacodec.height";
+
+ /**
+ * Key to extract the rotation (in degrees) to properly orient the video
+ * from the {@link MediaCodec#getMetrics} return.
+ * The value is a integer.
+ */
+ public static final String ROTATION = "android.media.mediacodec.rotation";
+
+ }
}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index a0a6a1e..fe461be 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -25,10 +25,10 @@
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.MediaHTTPService;
-import android.media.MediaMetricsSet;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.PersistableBundle;
import com.android.internal.util.Preconditions;
@@ -689,22 +689,21 @@
/**
* Return Metrics data about the current media container.
*
- * @return a MediaMetricsSet containing the set of attributes and values
+ * @return a {@link PersistableBundle} containing the set of attributes and values
* available for the media container being handled by this instance
* of MediaExtractor.
- * The attributes are descibed in {@link MediaMetricsSet.MediaExtractor}.
+ * The attributes are descibed in {@link MetricsConstants}.
*
* Additional vendor-specific fields may also be present in
* the return value.
*/
- public MediaMetricsSet getMetrics() {
- Bundle bundle = native_getMetrics();
- MediaMetricsSet mSet = new MediaMetricsSet(bundle);
- return mSet;
+ public PersistableBundle getMetrics() {
+ PersistableBundle bundle = native_getMetrics();
+ return bundle;
}
- private native Bundle native_getMetrics();
+ private native PersistableBundle native_getMetrics();
private static native final void native_init();
private native final void native_setup();
@@ -718,4 +717,32 @@
private MediaCas mMediaCas;
private long mNativeContext;
+
+ public final static class MetricsConstants
+ {
+ private MetricsConstants() {}
+
+ /**
+ * Key to extract the container format
+ * from the {@link MediaExtractor#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String FORMAT = "android.media.mediaextractor.fmt";
+
+ /**
+ * Key to extract the container MIME type
+ * from the {@link MediaExtractor#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String MIME_TYPE = "android.media.mediaextractor.mime";
+
+ /**
+ * Key to extract the number of tracks in the container
+ * from the {@link MediaExtractor#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String TRACKS = "android.media.mediaextractor.ntrk";
+
+ }
+
}
diff --git a/media/java/android/media/MediaMetricsSet.java b/media/java/android/media/MediaMetricsSet.java
deleted file mode 100644
index 5ecbee2..0000000
--- a/media/java/android/media/MediaMetricsSet.java
+++ /dev/null
@@ -1,491 +0,0 @@
-/*
- * 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.media;
-
-import android.os.Bundle;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.Runnable;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
-import java.net.HttpCookie;
-import java.net.HttpURLConnection;
-import java.net.InetSocketAddress;
-import java.net.URL;
-import java.nio.ByteOrder;
-import java.util.Arrays;
-import java.util.BitSet;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Scanner;
-import java.util.Set;
-import java.util.UUID;
-import java.util.Vector;
-
-
-/**
- * MediaMetricsSet contains the results returned by the getMetrics()
- * methods defined in other Media classes such as
- * {@link MediaCodec}, {@link MediaExtractor}, {@link MediaPlayer},
- * and {@link MediaRecorder}.
- *
- * MediaMetricsSet behaves similarly to a {@link Bundle}. It contains
- * a set of keys and values.
- * Methods such as {@link #getInt} and {@link #getString} are provided
- * to extract values of the corresponding types.
- * The {@link #keySet} method can be used to discover all of the keys
- * that are present in the particular instance.
- *
- */
-public final class MediaMetricsSet
-{
-
- /**
- * This MediaCodec class holds the constants defining keys related to
- * the metrics for a MediaCodec.
- */
- public final static class MediaCodec
- {
- private MediaCodec() {}
-
- /**
- * Key to extract the codec being used
- * from the {@link MediaCodec#getMetrics} return value.
- * The value is a String.
- */
- public static final String KEY_CODEC = "android.media.mediacodec.codec";
-
- /**
- * Key to extract the MIME type
- * from the {@link MediaCodec#getMetrics} return value.
- * The value is a String.
- */
- public static final String KEY_MIME = "android.media.mediacodec.mime";
-
- /**
- * Key to extract what the codec mode
- * from the {@link MediaCodec#getMetrics} return value.
- * The value is a String. Values will be one of the constants
- * MODE_AUDIO or MODE_VIDEO.
- */
- public static final String KEY_MODE = "android.media.mediacodec.mode";
-
- /**
- * The value returned for the key {@link #KEY_MODE} when the
- * codec is a audio codec.
- */
- public static final String MODE_AUDIO = "audio";
-
- /**
- * The value returned for the key {@link #KEY_MODE} when the
- * codec is a video codec.
- */
- public static final String MODE_VIDEO = "video";
-
- /**
- * Key to extract the flag indicating whether the codec is running
- * as an encoder or decoder from the {@link MediaCodec#getMetrics} return value.
- * The value is an integer.
- * A 0 indicates decoder; 1 indicates encoder.
- */
- public static final String KEY_ENCODER = "android.media.mediacodec.encoder";
-
- /**
- * Key to extract the flag indicating whether the codec is running
- * in secure (DRM) mode from the {@link MediaCodec#getMetrics} return value.
- * The value is an integer.
- */
- public static final String KEY_SECURE = "android.media.mediacodec.secure";
-
- /**
- * Key to extract the width (in pixels) of the video track
- * from the {@link MediaCodec#getMetrics} return value.
- * The value is an integer.
- */
- public static final String KEY_WIDTH = "android.media.mediacodec.width";
-
- /**
- * Key to extract the height (in pixels) of the video track
- * from the {@link MediaCodec#getMetrics} return value.
- * The value is an integer.
- */
- public static final String KEY_HEIGHT = "android.media.mediacodec.height";
-
- /**
- * Key to extract the rotation (in degrees) to properly orient the video
- * from the {@link MediaCodec#getMetrics} return.
- * The value is a integer.
- */
- public static final String KEY_ROTATION = "android.media.mediacodec.rotation";
-
- }
-
- /**
- * This class holds the constants defining keys related to
- * the metrics for a MediaExtractor.
- */
- public final static class MediaExtractor
- {
- private MediaExtractor() {}
-
- /**
- * Key to extract the container format
- * from the {@link MediaExtractor#getMetrics} return value.
- * The value is a String.
- */
- public static final String KEY_FORMAT = "android.media.mediaextractor.fmt";
-
- /**
- * Key to extract the container MIME type
- * from the {@link MediaExtractor#getMetrics} return value.
- * The value is a String.
- */
- public static final String KEY_MIME = "android.media.mediaextractor.mime";
-
- /**
- * Key to extract the number of tracks in the container
- * from the {@link MediaExtractor#getMetrics} return value.
- * The value is an integer.
- */
- public static final String KEY_TRACKS = "android.media.mediaextractor.ntrk";
-
- }
-
- /**
- * This class holds the constants defining keys related to
- * the metrics for a MediaPlayer.
- */
- public final static class MediaPlayer
- {
- private MediaPlayer() {}
-
- /**
- * Key to extract the MIME type of the video track
- * from the {@link MediaPlayer#getMetrics} return value.
- * The value is a String.
- */
- public static final String KEY_MIME_VIDEO = "android.media.mediaplayer.video.mime";
-
- /**
- * Key to extract the codec being used to decode the video track
- * from the {@link MediaPlayer#getMetrics} return value.
- * The value is a String.
- */
- public static final String KEY_CODEC_VIDEO = "android.media.mediaplayer.video.codec";
-
- /**
- * Key to extract the width (in pixels) of the video track
- * from the {@link MediaPlayer#getMetrics} return value.
- * The value is an integer.
- */
- public static final String KEY_WIDTH = "android.media.mediaplayer.width";
-
- /**
- * Key to extract the height (in pixels) of the video track
- * from the {@link MediaPlayer#getMetrics} return value.
- * The value is an integer.
- */
- public static final String KEY_HEIGHT = "android.media.mediaplayer.height";
-
- /**
- * Key to extract the count of video frames played
- * from the {@link MediaPlayer#getMetrics} return value.
- * The value is an integer.
- */
- public static final String KEY_FRAMES = "android.media.mediaplayer.frames";
-
- /**
- * Key to extract the count of video frames dropped
- * from the {@link MediaPlayer#getMetrics} return value.
- * The value is an integer.
- */
- public static final String KEY_FRAMES_DROPPED = "android.media.mediaplayer.dropped";
-
- /**
- * Key to extract the MIME type of the audio track
- * from the {@link MediaPlayer#getMetrics} return value.
- * The value is a String.
- */
- public static final String KEY_MIME_AUDIO = "android.media.mediaplayer.audio.mime";
-
- /**
- * Key to extract the codec being used to decode the audio track
- * from the {@link MediaPlayer#getMetrics} return value.
- * The value is a String.
- */
- public static final String KEY_CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
-
- /**
- * Key to extract the duration (in milliseconds) of the
- * media being played
- * from the {@link MediaPlayer#getMetrics} return value.
- * The value is a long.
- */
- public static final String KEY_DURATION = "android.media.mediaplayer.durationMs";
-
- /**
- * Key to extract the playing time (in milliseconds) of the
- * media being played
- * from the {@link MediaPlayer#getMetrics} return value.
- * The value is a long.
- */
- public static final String KEY_PLAYING = "android.media.mediaplayer.playingMs";
-
- /**
- * Key to extract the count of errors encountered while
- * playing the media
- * from the {@link MediaPlayer#getMetrics} return value.
- * The value is an integer.
- */
- public static final String KEY_ERRORS = "android.media.mediaplayer.err";
-
- /**
- * Key to extract an (optional) error code detected while
- * playing the media
- * from the {@link MediaPlayer#getMetrics} return value.
- * The value is an integer.
- */
- public static final String KEY_ERROR_CODE = "android.media.mediaplayer.errcode";
-
- }
-
- /**
- * This class holds the constants defining keys related to
- * the metrics for a MediaRecorder.
- */
- public final static class MediaRecorder
- {
- private MediaRecorder() {}
-
- /**
- * Key to extract the audio bitrate
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- */
- public static final String KEY_AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
-
- /**
- * Key to extract the number of audio channels
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- */
- public static final String KEY_AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
-
- /**
- * Key to extract the audio samplerate
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- */
- public static final String KEY_AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
-
- /**
- * Key to extract the audio timescale
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- */
- public static final String KEY_AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
-
- /**
- * Key to extract the video capture frame rate
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is a double.
- */
- public static final String KEY_CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
-
- /**
- * Key to extract the video capture framerate enable value
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- */
- public static final String KEY_CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
-
- /**
- * Key to extract the intended playback frame rate
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- */
- public static final String KEY_FRAMERATE = "android.media.mediarecorder.frame-rate";
-
- /**
- * Key to extract the height (in pixels) of the captured video
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- */
- public static final String KEY_HEIGHT = "android.media.mediarecorder.height";
-
- /**
- * Key to extract the recorded movies time units
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- * A value of 1000 indicates that the movie's timing is in milliseconds.
- */
- public static final String KEY_MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
-
- /**
- * Key to extract the rotation (in degrees) to properly orient the video
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- */
- public static final String KEY_ROTATION = "android.media.mediarecorder.rotation";
-
- /**
- * Key to extract the video bitrate from being used
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- */
- public static final String KEY_VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
-
- /**
- * Key to extract the value for how often video iframes are generated
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- */
- public static final String KEY_VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
-
- /**
- * Key to extract the video encoding level
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- */
- public static final String KEY_VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
-
- /**
- * Key to extract the video encoding profile
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- */
- public static final String KEY_VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
-
- /**
- * Key to extract the recorded video time units
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- * A value of 1000 indicates that the video's timing is in milliseconds.
- */
- public static final String KEY_VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
-
- /**
- * Key to extract the width (in pixels) of the captured video
- * from the {@link MediaRecorder#getMetrics} return.
- * The value is an integer.
- */
- public static final String KEY_WIDTH = "android.media.mediarecorder.width";
-
- }
-
- /*
- * Methods that we want
- */
-
- private Bundle mBundle;
-
- MediaMetricsSet(Bundle bundle) {
- mBundle = bundle;
- }
-
- /**
- * Returns the number of mappings contained in this Bundle.
- *
- * @return the number of mappings as an int.
- */
- public int size() {
- return mBundle.size();
- }
-
- /**
- * Returns true if the mapping of this MediaMetricsSet is empty,
- * false otherwise.
- */
- public boolean isEmpty() {
- return mBundle.isEmpty();
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @param defaultValue Value to return if key does not exist
- * @return a double value
- */
- public double getDouble(String key, double defaultValue) {
- return mBundle.getDouble(key, defaultValue);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @param defaultValue Value to return if key does not exist
- * @return an int value
- */
- public int getInt(String key, int defaultValue) {
- return mBundle.getInt(key, defaultValue);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key.
- *
- * @param key a String
- * @param defaultValue Value to return if key does not exist
- * @return a long value
- */
- public long getLong(String key, long defaultValue) {
- return mBundle.getLong(key, defaultValue);
- }
-
- /**
- * Returns the value associated with the given key, or defaultValue if
- * no mapping of the desired type exists for the given key or if a null
- * value is explicitly associated with the given key.
- *
- * @param key a String
- * @param defaultValue Value to return if key does not exist or if a null
- * value is associated with the given key.
- * @return the String value associated with the given key, or defaultValue
- * if no valid String object is currently mapped to that key.
- */
- public String getString(String key, String defaultValue) {
- return mBundle.getString(key, defaultValue);
- }
-
- /**
- * Returns a Set containing the Strings used as keys in this Bundle.
- *
- * @return a Set of String keys
- */
- public Set<String> keySet() {
- return mBundle.keySet();
- }
-
-
-
- public String toString() {
- return mBundle.toString();
- }
-
-}
-
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 71a968b..d5efc97 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -33,6 +33,7 @@
import android.os.Message;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.PersistableBundle;
import android.os.Process;
import android.os.PowerManager;
import android.os.SystemProperties;
@@ -48,7 +49,6 @@
import android.media.AudioManager;
import android.media.MediaDrm;
import android.media.MediaFormat;
-import android.media.MediaMetricsSet;
import android.media.MediaTimeProvider;
import android.media.PlaybackParams;
import android.media.SubtitleController;
@@ -1007,13 +1007,14 @@
* @param context the Context to use when resolving the Uri
* @param uri the Content URI of the data you want to play
* @param headers the headers to be sent together with the request for the data
- * Note that the cross domain redirection is allowed by default, but that can be
- * changed with key/value pairs through the headers parameter with
- * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
- * to disallow or allow cross domain redirection.
* The headers must not include cookies. Instead, use the cookies param.
* @param cookies the cookies to be sent together with the request
* @throws IllegalStateException if it is called in an invalid state
+ *
+ * <p><strong>Note</strong> that the cross domain redirection is allowed by default,
+ * but that can be changed with key/value pairs through the headers parameter with
+ * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to
+ * disallow or allow cross domain redirection.
*/
public void setDataSource(@NonNull Context context, @NonNull Uri uri,
@Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies)
@@ -1056,11 +1057,12 @@
* @param context the Context to use when resolving the Uri
* @param uri the Content URI of the data you want to play
* @param headers the headers to be sent together with the request for the data
- * Note that the cross domain redirection is allowed by default, but that can be
- * changed with key/value pairs through the headers parameter with
- * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value
- * to disallow or allow cross domain redirection.
* @throws IllegalStateException if it is called in an invalid state
+ *
+ * <p><strong>Note</strong> that the cross domain redirection is allowed by default,
+ * but that can be changed with key/value pairs through the headers parameter with
+ * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to
+ * disallow or allow cross domain redirection.
*/
public void setDataSource(@NonNull Context context, @NonNull Uri uri,
@Nullable Map<String, String> headers)
@@ -1491,20 +1493,19 @@
/**
* Return Metrics data about the current player.
*
- * @return a MediaMetricsSet containing the set of attributes and values
+ * @return a {@link PersistableBundle} containing the set of attributes and values
* available for the media being handled by this instance of MediaPlayer
- * The attributes are descibed in {@link MediaMetricsSet.MediaPlayer}.
+ * The attributes are descibed in {@link MetricsConstants}.
*
* Additional vendor-specific fields may also be present in
* the return value.
*/
- public MediaMetricsSet getMetrics() {
- Bundle bundle = native_getMetrics();
- MediaMetricsSet mSet = new MediaMetricsSet(bundle);
- return mSet;
+ public PersistableBundle getMetrics() {
+ PersistableBundle bundle = native_getMetrics();
+ return bundle;
}
- private native Bundle native_getMetrics();
+ private native PersistableBundle native_getMetrics();
/**
* Checks whether the MediaPlayer is playing.
@@ -1982,7 +1983,7 @@
mOnSubtitleDataListener = null;
// Modular DRM clean up
- mOnDrmConfigListener = null;
+ mOnDrmConfigHelper = null;
mOnDrmInfoHandlerDelegate = null;
mOnDrmPreparedHandlerDelegate = null;
resetDrmState();
@@ -3906,11 +3907,11 @@
* 'securityLevel', which has to be set after DRM scheme creation but
* before the DRM session is opened.
*
- * The only allowed DRM calls in this listener are getDrmPropertyString
- * and setDrmPropertyString.
+ * The only allowed DRM calls in this listener are {@code getDrmPropertyString}
+ * and {@code setDrmPropertyString}.
*
*/
- public interface OnDrmConfigListener
+ public interface OnDrmConfigHelper
{
/**
* Called to give the app the opportunity to configure DRM before the session is created
@@ -3923,19 +3924,19 @@
/**
* Register a callback to be invoked for configuration of the DRM object before
* the session is created.
- * The callback will be invoked synchronously half-way into the execution
+ * The callback will be invoked synchronously during the execution
* of {@link #prepareDrm(UUID uuid)}.
*
* @param listener the callback that will be run
*/
- public void setOnDrmConfigListener(OnDrmConfigListener listener)
+ public void setOnDrmConfigHelper(OnDrmConfigHelper listener)
{
synchronized (mDrmLock) {
- mOnDrmConfigListener = listener;
+ mOnDrmConfigHelper = listener;
} // synchronized
}
- private OnDrmConfigListener mOnDrmConfigListener;
+ private OnDrmConfigHelper mOnDrmConfigHelper;
/**
* Interface definition of a callback to be invoked when the
@@ -3947,7 +3948,7 @@
* Called to indicate DRM info is available
*
* @param mp the {@code MediaPlayer} associated with this callback
- * @param drmInfo DRM info of the source including PSSH, mimes, and subset
+ * @param drmInfo DRM info of the source including PSSH, and subset
* of crypto schemes supported by this device
*/
public void onDrmInfo(MediaPlayer mp, DrmInfo drmInfo);
@@ -3983,6 +3984,41 @@
private OnDrmInfoHandlerDelegate mOnDrmInfoHandlerDelegate;
+
+ /**
+ * The status codes for {@link OnDrmPreparedListener#onDrmPrepared} listener.
+ * <p>
+ *
+ * DRM preparation has succeeded.
+ */
+ public static final int PREPARE_DRM_STATUS_SUCCESS = 0;
+
+ /**
+ * The device required DRM provisioning but couldn't reach the provisioning server.
+ */
+ public static final int PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR = 1;
+
+ /**
+ * The device required DRM provisioning but the provisioning server denied the request.
+ */
+ public static final int PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR = 2;
+
+ /**
+ * The DRM preparation has failed .
+ */
+ public static final int PREPARE_DRM_STATUS_PREPARATION_ERROR = 3;
+
+
+ /** @hide */
+ @IntDef({
+ PREPARE_DRM_STATUS_SUCCESS,
+ PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR,
+ PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR,
+ PREPARE_DRM_STATUS_PREPARATION_ERROR,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PrepareDrmStatusCode {}
+
/**
* Interface definition of a callback to notify the app when the
* DRM is ready for key request/response
@@ -3993,9 +4029,13 @@
* Called to notify the app that prepareDrm is finished and ready for key request/response
*
* @param mp the {@code MediaPlayer} associated with this callback
- * @param success the result of DRM preparation
+ * @param status the result of DRM preparation which can be
+ * {@link #PREPARE_DRM_STATUS_SUCCESS},
+ * {@link #PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR},
+ * {@link #PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR}, or
+ * {@link #PREPARE_DRM_STATUS_PREPARATION_ERROR}.
*/
- public void onDrmPrepared(MediaPlayer mp, boolean success);
+ public void onDrmPrepared(MediaPlayer mp, @PrepareDrmStatusCode int status);
}
/**
@@ -4039,30 +4079,28 @@
mOnDrmInfoListener = listener;
// find the looper for our new event handler
- Looper looper = null;
if (handler != null) {
- looper = handler.getLooper();
- }
-
- // construct the event handler with this looper
- if (looper != null) {
- // implement the event handler delegate
- mHandler = new Handler(looper) {
- public void handleMessage(Message msg) {
- DrmInfo drmInfo = (DrmInfo)msg.obj;
- mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo);
- }
- };
+ mHandler = handler;
+ } else {
+ // handler == null
+ // Will let OnDrmInfoListener be called in mEventHandler similar to other
+ // legacy notifications. This is because MEDIA_DRM_INFO's notification has to be
+ // sent before MEDIA_PREPARED's (i.e., in the same order they are issued by
+ // mediaserver). As a result, the callback has to be called directly by
+ // EventHandler.handleMessage similar to onPrepared.
}
}
void notifyClient(DrmInfo drmInfo) {
- if ( mHandler != null ) {
- Message msg = new Message(); // no message type needed
- msg.obj = drmInfo;
- mHandler.sendMessage(msg);
+ if (mHandler != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo);
+ }
+ });
}
- else { // no handler: direct call
+ else { // no handler: direct call by mEventHandler
mOnDrmInfoListener.onDrmInfo(mMediaPlayer, drmInfo);
}
}
@@ -4079,31 +4117,26 @@
mOnDrmPreparedListener = listener;
// find the looper for our new event handler
- Looper looper = null;
if (handler != null) {
- looper = handler.getLooper();
- }
-
- // construct the event handler with this looper
- if (looper != null) {
- // implement the event handler delegate
- mHandler = new Handler(looper) {
- public void handleMessage(Message msg) {
- boolean success = (msg.arg1 == 0) ? false : true;
- mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, success);
- }
- };
+ mHandler = handler;
+ } else if (mEventHandler != null) {
+ // Otherwise, use mEventHandler
+ mHandler = mEventHandler;
+ } else {
+ Log.e(TAG, "OnDrmPreparedHandlerDelegate: Unexpected null mEventHandler");
}
}
- void notifyClient(boolean success) {
- if ( mHandler != null ) {
- Message msg = new Message(); // no message type needed
- msg.arg1 = success ? 1 : 0;
- mHandler.sendMessage(msg);
- }
- else { // no handler: direct call
- mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, success);
+ void notifyClient(int status) {
+ if (mHandler != null) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mOnDrmPreparedListener.onDrmPrepared(mMediaPlayer, status);
+ }
+ });
+ } else {
+ Log.e(TAG, "OnDrmPreparedHandlerDelegate:notifyClient: Unexpected null mHandler");
}
}
}
@@ -4138,7 +4171,7 @@
/**
* Prepares the DRM for the current source
* <p>
- * If {@code OnDrmConfigListener} is registered, it will be called half-way into
+ * If {@code OnDrmConfigHelper} is registered, it will be called during
* preparation to allow configuration of the DRM properties before opening the
* DRM session. Note that the callback is called synchronously in the thread that called
* {@code prepareDrm}. It should be used only for a series of {@code getDrmPropertyString}
@@ -4149,9 +4182,9 @@
* complete depending on the network connectivity.
* If {@code OnDrmPreparedListener} is registered, prepareDrm() runs in non-blocking
* mode by launching the provisioning in the background and returning. The listener
- * will be called when provisioning and preperation has finished. If a
+ * will be called when provisioning and preparation has finished. If a
* {@code OnDrmPreparedListener} is not registered, prepareDrm() waits till provisioning
- * and preperation has finished, i.e., runs in blocking mode.
+ * and preparation has finished, i.e., runs in blocking mode.
* <p>
* If {@code OnDrmPreparedListener} is registered, it is called to indicate the DRM
* session being ready. The application should not make any assumption about its call
@@ -4159,18 +4192,23 @@
* execute the listener (unless the listener is registered with a handler thread).
* <p>
*
- * @param uuid The UUID of the crypto scheme.
+ * @param uuid The UUID of the crypto scheme. If not known beforehand, it can be retrieved
+ * from the source through {@code getDrmInfo} or registering a {@code onDrmInfoListener}.
*
- * @throws IllegalStateException if called before prepare(), or there exists a Drm already
- * @throws UnsupportedSchemeException if the crypto scheme is not supported
- * @throws ResourceBusyException if required DRM resources are in use
- * @throws ProvisioningErrorException if provisioning is required but an attempt failed
+ * @throws IllegalStateException if called before prepare(), or the DRM was
+ * prepared already
+ * @throws UnsupportedSchemeException if the crypto scheme is not supported
+ * @throws ResourceBusyException if required DRM resources are in use
+ * @throws ProvisioningNetworkErrorException if provisioning is required but failed due to a
+ * network error
+ * @throws ProvisioningServerErrorException if provisioning is required but failed due to
+ * the request denied by the provisioning server
*/
public void prepareDrm(@NonNull UUID uuid)
- throws UnsupportedSchemeException,
- ResourceBusyException, ProvisioningErrorException
+ throws UnsupportedSchemeException, ResourceBusyException,
+ ProvisioningNetworkErrorException, ProvisioningServerErrorException
{
- Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigListener: " + mOnDrmConfigListener);
+ Log.v(TAG, "prepareDrm: uuid: " + uuid + " mOnDrmConfigHelper: " + mOnDrmConfigHelper);
boolean allDoneWithoutProvisioning = false;
// get a snapshot as we'll use them outside the lock
@@ -4178,7 +4216,7 @@
synchronized (mDrmLock) {
- // only allowing if tied to a protected source; might releax for releasing offline keys
+ // only allowing if tied to a protected source; might relax for releasing offline keys
if (mDrmInfo == null) {
final String msg = "prepareDrm(): Wrong usage: The player must be prepared and " +
"DRM info be retrieved before this call.";
@@ -4227,8 +4265,8 @@
// call the callback outside the lock
- if (mOnDrmConfigListener != null) {
- mOnDrmConfigListener.onDrmConfig(this);
+ if (mOnDrmConfigHelper != null) {
+ mOnDrmConfigHelper.onDrmConfig(this);
}
synchronized (mDrmLock) {
@@ -4252,15 +4290,33 @@
Log.w(TAG, "prepareDrm: NotProvisionedException");
// handle provisioning internally; it'll reset mPrepareDrmInProgress
- boolean result = HandleProvisioninig(uuid);
+ int result = HandleProvisioninig(uuid);
// if blocking mode, we're already done;
// if non-blocking mode, we attempted to launch background provisioning
- if (result == false) {
- final String msg = "prepareDrm: Provisioning was required but failed.";
- Log.e(TAG, msg);
+ if (result != PREPARE_DRM_STATUS_SUCCESS) {
earlyExit = true;
- throw new ProvisioningErrorException(msg);
+ String msg;
+
+ switch (result) {
+ case PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR:
+ msg = "prepareDrm: Provisioning was required but failed " +
+ "due to a network error.";
+ Log.e(TAG, msg);
+ throw new ProvisioningNetworkErrorException(msg);
+
+ case PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR:
+ msg = "prepareDrm: Provisioning was required but the request " +
+ "was denied by the server.";
+ Log.e(TAG, msg);
+ throw new ProvisioningServerErrorException(msg);
+
+ case PREPARE_DRM_STATUS_PREPARATION_ERROR:
+ default: // default for safeguard
+ msg = "prepareDrm: Post-provisioning preparation failed.";
+ Log.e(TAG, msg);
+ throw new IllegalStateException(msg);
+ }
}
// nothing else to do;
// if blocking or non-blocking, HandleProvisioninig does the re-attempt & cleanup
@@ -4282,7 +4338,7 @@
// if finished successfully without provisioning, call the callback outside the lock
if (allDoneWithoutProvisioning) {
if (onDrmPreparedHandlerDelegate != null)
- onDrmPreparedHandlerDelegate.notifyClient(true /*success*/);
+ onDrmPreparedHandlerDelegate.notifyClient(PREPARE_DRM_STATUS_SUCCESS);
}
}
@@ -4292,6 +4348,10 @@
/**
* Releases the DRM session
+ * <p>
+ * The player has to have an active DRM session and be in stopped, or prepared
+ * state before this call is made.
+ * A {@code reset()} call will release the DRM session implicitly.
*
* @throws NoDrmSchemeException if there is no active DRM session to release
*/
@@ -4308,7 +4368,7 @@
try {
// we don't have the player's state in this layer. The below call raises
- // exception if we're in a non-stopped/idle state.
+ // exception if we're in a non-stopped/prepared state.
// for cleaning native/mediaserver crypto object
_releaseDrm();
@@ -4317,9 +4377,11 @@
cleanDrmObj();
mActiveDrmScheme = false;
- } catch (Exception e) {
+ } catch (IllegalStateException e) {
Log.w(TAG, "releaseDrm: Exception ", e);
- throw e;
+ throw new IllegalStateException("releaseDrm: The player is not in a valid state.");
+ } catch (Exception e) {
+ Log.e(TAG, "releaseDrm: Exception ", e);
}
} // synchronized
}
@@ -4338,21 +4400,23 @@
* it should deliver to the response to the DRM engine plugin using the method
* {@link #provideKeyResponse}.
*
- * @param scope may be a container-specific initialization data or a keySetId,
- * depending on the specified keyType.
- * When the keyType is KEY_TYPE_STREAMING or KEY_TYPE_OFFLINE, scope should be set to
- * the container-specific initialization data. Its meaning is interpreted based on the
- * mime type provided in the mimeType parameter. It could contain, for example,
- * the content ID, key ID or other data obtained from the content metadata that is
- * required in generating the key request.
- * When the keyType is KEY_TYPE_RELEASE, scope should be set to the keySetId of
- * the keys being released.
+ * @param keySetId is the key-set identifier of the offline keys being released when keyType is
+ * {@link MediaDrm#KEY_TYPE_RELEASE}. It should be set to null for other key requests, when
+ * keyType is {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}.
+ *
+ * @param initData is the container-specific initialization data when the keyType is
+ * {@link MediaDrm#KEY_TYPE_STREAMING} or {@link MediaDrm#KEY_TYPE_OFFLINE}. Its meaning is
+ * interpreted based on the mime type provided in the mimeType parameter. It could
+ * contain, for example, the content ID, key ID or other data obtained from the content
+ * metadata that is required in generating the key request.
+ * When the keyType is {@link MediaDrm#KEY_TYPE_RELEASE}, it should be set to null.
*
* @param mimeType identifies the mime type of the content
*
- * @param keyType specifes the type of the request. The request may be to acquire
- * keys for streaming or offline content, or to release previously acquired
- * keys, which are identified by a keySetId.
+ * @param keyType specifies the type of the request. The request may be to acquire
+ * keys for streaming, {@link MediaDrm#KEY_TYPE_STREAMING}, or for offline content
+ * {@link MediaDrm#KEY_TYPE_OFFLINE}, or to release previously acquired
+ * keys ({@link MediaDrm#KEY_TYPE_RELEASE}), which are identified by a keySetId.
*
* @param optionalParameters are included in the key request message to
* allow a client application to provide additional message parameters to the server.
@@ -4361,12 +4425,13 @@
* @throws NoDrmSchemeException if there is no active DRM session
*/
@NonNull
- public MediaDrm.KeyRequest getKeyRequest(@NonNull byte[] scope, @Nullable String mimeType,
- @MediaDrm.KeyType int keyType, @Nullable Map<String, String> optionalParameters)
+ public MediaDrm.KeyRequest getKeyRequest(@Nullable byte[] keySetId, @Nullable byte[] initData,
+ @Nullable String mimeType, @MediaDrm.KeyType int keyType,
+ @Nullable Map<String, String> optionalParameters)
throws NoDrmSchemeException
{
Log.v(TAG, "getKeyRequest: " +
- " scope: " + scope + " mimeType: " + mimeType +
+ " keySetId: " + keySetId + " initData:" + initData + " mimeType: " + mimeType +
" keyType: " + keyType + " optionalParameters: " + optionalParameters);
synchronized (mDrmLock) {
@@ -4376,20 +4441,16 @@
}
try {
- byte[] scopeOut = (keyType != MediaDrm.KEY_TYPE_RELEASE) ?
- mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
- scope; // keySetId for KEY_TYPE_RELEASE
-
- byte[] initData = (keyType != MediaDrm.KEY_TYPE_RELEASE) ?
- scope : // initData for KEY_TYPE_STREAMING/OFFLINE
- null; // not used for KEY_TYPE_RELEASE
+ byte[] scope = (keyType != MediaDrm.KEY_TYPE_RELEASE) ?
+ mDrmSessionId : // sessionId for KEY_TYPE_STREAMING/OFFLINE
+ keySetId; // keySetId for KEY_TYPE_RELEASE
HashMap<String, String> hmapOptionalParameters =
(optionalParameters != null) ?
new HashMap<String, String>(optionalParameters) :
null;
- MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scopeOut, initData, mimeType,
+ MediaDrm.KeyRequest request = mDrmObj.getKeyRequest(scope, initData, mimeType,
keyType, hmapOptionalParameters);
Log.v(TAG, "getKeyRequest: --> request: " + request);
@@ -4500,8 +4561,8 @@
* @param propertyName the property name
*
* Standard fields names are:
- * {link #PROPERTY_VENDOR}, {link #PROPERTY_VERSION},
- * {link #PROPERTY_DESCRIPTION}, {link #PROPERTY_ALGORITHMS}
+ * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION},
+ * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
*/
@NonNull
public String getDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName)
@@ -4538,8 +4599,8 @@
* @param value the property value
*
* Standard fields names are:
- * {link #PROPERTY_VENDOR}, {link #PROPERTY_VERSION},
- * {link #PROPERTY_DESCRIPTION}, {link #PROPERTY_ALGORITHMS}
+ * {@link MediaDrm#PROPERTY_VENDOR}, {@link MediaDrm#PROPERTY_VERSION},
+ * {@link MediaDrm#PROPERTY_DESCRIPTION}, {@link MediaDrm#PROPERTY_ALGORITHMS}
*/
public void setDrmPropertyString(@NonNull @MediaDrm.StringProperty String propertyName,
@NonNull String value)
@@ -4566,8 +4627,6 @@
public static final class DrmInfo {
private Map<UUID, byte[]> mapPssh;
private UUID[] supportedSchemes;
- // TODO: Won't need this in final release. Only keeping it for the existing test app.
- private String[] mimes;
public Map<UUID, byte[]> getPssh() {
return mapPssh;
@@ -4575,15 +4634,10 @@
public UUID[] getSupportedSchemes() {
return supportedSchemes;
}
- // TODO: Won't need this in final release. Only keeping it for the existing test app.
- public String[] getMimes() {
- return mimes;
- }
- private DrmInfo(Map<UUID, byte[]> Pssh, UUID[] SupportedSchemes, String[] Mimes) {
+ private DrmInfo(Map<UUID, byte[]> Pssh, UUID[] SupportedSchemes) {
mapPssh = Pssh;
supportedSchemes = SupportedSchemes;
- mimes = Mimes;
}
private DrmInfo(Parcel parcel) {
@@ -4609,18 +4663,12 @@
supportedSchemes[i]);
}
- // TODO: Won't need this in final release. Only keeping it for the test app.
- mimes = parcel.readStringArray();
- int mimeCount = mimes.length;
- Log.v(TAG, "DrmInfo() mime: " + Arrays.toString(mimes));
-
Log.v(TAG, "DrmInfo() Parcel psshsize: " + psshsize +
- " supportedDRMsCount: " + supportedDRMsCount +
- " mimeCount: " + mimeCount);
+ " supportedDRMsCount: " + supportedDRMsCount);
}
private DrmInfo makeCopy() {
- return new DrmInfo(this.mapPssh, this.supportedSchemes, this.mimes);
+ return new DrmInfo(this.mapPssh, this.supportedSchemes);
}
private String arrToHex(byte[] bytes) {
@@ -4715,11 +4763,22 @@
/**
* Thrown when the device requires DRM provisioning but the provisioning attempt has
- * failed (for example: network timeout, provisioning server error).
+ * failed due to a network error (Internet reachability, timeout, etc.).
* Extends MediaDrm.MediaDrmException
*/
- public static final class ProvisioningErrorException extends MediaDrmException {
- public ProvisioningErrorException(String detailMessage) {
+ public static final class ProvisioningNetworkErrorException extends MediaDrmException {
+ public ProvisioningNetworkErrorException(String detailMessage) {
+ super(detailMessage);
+ }
+ }
+
+ /**
+ * Thrown when the device requires DRM provisioning but the provisioning attempt has
+ * failed due to the provisioning server denying the request.
+ * Extends MediaDrm.MediaDrmException
+ */
+ public static final class ProvisioningServerErrorException extends MediaDrmException {
+ public ProvisioningServerErrorException(String detailMessage) {
super(detailMessage);
}
}
@@ -4771,14 +4830,13 @@
private UUID uuid;
private String urlStr;
- private byte[] response;
private Object drmLock;
private OnDrmPreparedHandlerDelegate onDrmPreparedHandlerDelegate;
private MediaPlayer mediaPlayer;
- private boolean succeeded;
+ private int status;
private boolean finished;
- public boolean succeeded() {
- return succeeded;
+ public int status() {
+ return status;
}
public ProvisioningThread initialize(MediaDrm.ProvisionRequest request,
@@ -4791,12 +4849,15 @@
urlStr = request.getDefaultUrl() + "&signedRequest=" + new String(request.getData());
this.uuid = uuid;
+ status = PREPARE_DRM_STATUS_PREPARATION_ERROR;
+
Log.v(TAG, "HandleProvisioninig: Thread is initialised url: " + urlStr);
return this;
}
public void run() {
+ byte[] response = null;
boolean provisioningSucceeded = false;
try {
URL url = new URL(urlStr);
@@ -4814,11 +4875,13 @@
Log.v(TAG, "HandleProvisioninig: Thread run: response " +
response.length + " " + response);
} catch (Exception e) {
+ status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
Log.w(TAG, "HandleProvisioninig: Thread run: connect " + e + " url: " + url);
} finally {
connection.disconnect();
}
} catch (Exception e) {
+ status = PREPARE_DRM_STATUS_PROVISIONING_NETWORK_ERROR;
Log.w(TAG, "HandleProvisioninig: Thread run: openConnection " + e);
}
@@ -4829,12 +4892,15 @@
"provideProvisionResponse SUCCEEDED!");
provisioningSucceeded = true;
- } catch (Exception e) {
+ } catch (Exception e) {
+ status = PREPARE_DRM_STATUS_PROVISIONING_SERVER_ERROR;
Log.w(TAG, "HandleProvisioninig: Thread run: " +
"provideProvisionResponse " + e);
}
}
+ boolean succeeded = false;
+
// non-blocking mode needs the lock
if (onDrmPreparedHandlerDelegate != null) {
@@ -4842,6 +4908,9 @@
// continuing with prepareDrm
if (provisioningSucceeded) {
succeeded = mediaPlayer.resumePrepareDrm(uuid);
+ status = (succeeded) ?
+ PREPARE_DRM_STATUS_SUCCESS :
+ PREPARE_DRM_STATUS_PREPARATION_ERROR;
}
mediaPlayer.mDrmProvisioningInProgress = false;
mediaPlayer.mPrepareDrmInProgress = false;
@@ -4851,12 +4920,15 @@
} // synchronized
// calling the callback outside the lock
- onDrmPreparedHandlerDelegate.notifyClient(succeeded);
+ onDrmPreparedHandlerDelegate.notifyClient(status);
} else { // blocking mode already has the lock
// continuing with prepareDrm
if (provisioningSucceeded) {
succeeded = mediaPlayer.resumePrepareDrm(uuid);
+ status = (succeeded) ?
+ PREPARE_DRM_STATUS_SUCCESS :
+ PREPARE_DRM_STATUS_PREPARATION_ERROR;
}
mediaPlayer.mDrmProvisioningInProgress = false;
mediaPlayer.mPrepareDrmInProgress = false;
@@ -4870,19 +4942,19 @@
} // ProvisioningThread
- private boolean HandleProvisioninig(UUID uuid)
+ private int HandleProvisioninig(UUID uuid)
{
// the lock is already held by the caller
if (mDrmProvisioningInProgress) {
Log.e(TAG, "HandleProvisioninig: Unexpected mDrmProvisioningInProgress");
- return false;
+ return PREPARE_DRM_STATUS_PREPARATION_ERROR;
}
MediaDrm.ProvisionRequest provReq = mDrmObj.getProvisionRequest();
if (provReq == null) {
Log.e(TAG, "HandleProvisioninig: getProvisionRequest returned null.");
- return false;
+ return PREPARE_DRM_STATUS_PREPARATION_ERROR;
}
Log.v(TAG, "HandleProvisioninig provReq " +
@@ -4894,11 +4966,11 @@
mDrmProvisioningThread = new ProvisioningThread().initialize(provReq, uuid, this);
mDrmProvisioningThread.start();
- boolean result = false;
+ int result;
- // non-blocking
+ // non-blocking: this is not the final result
if (mOnDrmPreparedHandlerDelegate != null) {
- result = true;
+ result = PREPARE_DRM_STATUS_SUCCESS;
} else {
// if blocking mode, wait till provisioning is done
try {
@@ -4906,7 +4978,7 @@
} catch (Exception e) {
Log.w(TAG, "HandleProvisioninig: Thread.join Exception " + e);
}
- result = mDrmProvisioningThread.succeeded();
+ result = mDrmProvisioningThread.status();
// no longer need the thread
mDrmProvisioningThread = null;
}
@@ -5418,4 +5490,98 @@
}
}
}
+
+ public final static class MetricsConstants
+ {
+ private MetricsConstants() {}
+
+ /**
+ * Key to extract the MIME type of the video track
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String MIME_TYPE_VIDEO = "android.media.mediaplayer.video.mime";
+
+ /**
+ * Key to extract the codec being used to decode the video track
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String CODEC_VIDEO = "android.media.mediaplayer.video.codec";
+
+ /**
+ * Key to extract the width (in pixels) of the video track
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String WIDTH = "android.media.mediaplayer.width";
+
+ /**
+ * Key to extract the height (in pixels) of the video track
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String HEIGHT = "android.media.mediaplayer.height";
+
+ /**
+ * Key to extract the count of video frames played
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String FRAMES = "android.media.mediaplayer.frames";
+
+ /**
+ * Key to extract the count of video frames dropped
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String FRAMES_DROPPED = "android.media.mediaplayer.dropped";
+
+ /**
+ * Key to extract the MIME type of the audio track
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String MIME_TYPE_AUDIO = "android.media.mediaplayer.audio.mime";
+
+ /**
+ * Key to extract the codec being used to decode the audio track
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is a String.
+ */
+ public static final String CODEC_AUDIO = "android.media.mediaplayer.audio.codec";
+
+ /**
+ * Key to extract the duration (in milliseconds) of the
+ * media being played
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is a long.
+ */
+ public static final String DURATION = "android.media.mediaplayer.durationMs";
+
+ /**
+ * Key to extract the playing time (in milliseconds) of the
+ * media being played
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is a long.
+ */
+ public static final String PLAYING = "android.media.mediaplayer.playingMs";
+
+ /**
+ * Key to extract the count of errors encountered while
+ * playing the media
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String ERRORS = "android.media.mediaplayer.err";
+
+ /**
+ * Key to extract an (optional) error code detected while
+ * playing the media
+ * from the {@link MediaPlayer#getMetrics} return value.
+ * The value is an integer.
+ */
+ public static final String ERROR_CODE = "android.media.mediaplayer.errcode";
+
+ }
}
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 4675e32..997d562 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -20,14 +20,15 @@
import android.annotation.SystemApi;
import android.app.ActivityThread;
import android.hardware.Camera;
-import android.media.MediaMetricsSet;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.PersistableBundle;
import android.util.Log;
import android.view.Surface;
+import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.RandomAccessFile;
@@ -92,6 +93,7 @@
private String mPath;
private FileDescriptor mFd;
+ private File mFile;
private EventHandler mEventHandler;
private OnErrorListener mOnErrorListener;
private OnInfoListener mOnInfoListener;
@@ -804,10 +806,26 @@
public void setOutputFile(FileDescriptor fd) throws IllegalStateException
{
mPath = null;
+ mFile = null;
mFd = fd;
}
/**
+ * Pass in the file object to be written. Call this after setOutputFormat() but before prepare().
+ * File should be seekable. After setting the next output file, application should not use the
+ * file until {@link #stop}. Application is responsible for cleaning up unused files after
+ * {@link #stop} is called.
+ *
+ * @param file the file object to be written into.
+ */
+ public void setOutputFile(File file)
+ {
+ mPath = null;
+ mFd = null;
+ mFile = file;
+ }
+
+ /**
* Sets the next output file descriptor to be used when the maximum filesize is reached
* on the prior output {@link #setOutputFile} or {@link #setNextOutputFile}). File descriptor
* must be seekable and writable. After setting the next output file, application should not
@@ -842,15 +860,16 @@
public void setOutputFile(String path) throws IllegalStateException
{
mFd = null;
+ mFile = null;
mPath = path;
}
/**
- * Sets the next output file path to be used when the maximum filesize is reached
- * on the prior output {@link #setOutputFile} or {@link #setNextOutputFile}). File should
- * be seekable. After setting the next output file, application should not use the file
- * referenced by this file descriptor until {@link #stop}. Application must call this
- * after receiving on the {@link android.media.MediaRecorder.OnInfoListener} a "what" code of
+ * Sets the next output file to be used when the maximum filesize is reached on the prior
+ * output {@link #setOutputFile} or {@link #setNextOutputFile}). File should be seekable.
+ * After setting the next output file, application should not use the file until {@link #stop}.
+ * Application must call this after receiving on the
+ * {@link android.media.MediaRecorder.OnInfoListener} a "what" code of
* {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING} and before receiving a "what" code of
* {@link #MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED}. The file is not used until switching to
* that output. Application will receive {@link #MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED}
@@ -858,19 +877,17 @@
* the previous one has not been used. Application is responsible for cleaning up unused files
* after {@link #stop} is called.
*
- * @param path The pathname to use.
+ * @param file The file to use.
* @throws IllegalStateException if it is called before prepare().
* @throws IOException if setNextOutputFile fails otherwise.
*/
- public void setNextOutputFile(String path) throws IllegalStateException, IOException
+ public void setNextOutputFile(File file) throws IllegalStateException, IOException
{
- if (path != null) {
- RandomAccessFile file = new RandomAccessFile(path, "rws");
- try {
- _setNextOutputFile(file.getFD());
- } finally {
- file.close();
- }
+ RandomAccessFile f = new RandomAccessFile(file, "rws");
+ try {
+ _setNextOutputFile(f.getFD());
+ } finally {
+ f.close();
}
}
@@ -899,6 +916,13 @@
}
} else if (mFd != null) {
_setOutputFile(mFd);
+ } else if (mFile != null) {
+ RandomAccessFile file = new RandomAccessFile(mFile, "rws");
+ try {
+ _setOutputFile(file.getFD());
+ } finally {
+ file.close();
+ }
} else {
throw new IOException("No valid output file");
}
@@ -1267,23 +1291,142 @@
/**
* Return Metrics data about the current Mediarecorder instance.
*
- * @return a MediaMetricsSet containing the set of attributes and values
+ * @return a {@link PersistableBundle} containing the set of attributes and values
* available for the media being generated by this instance of
* MediaRecorder.
- * The attributes are descibed in {@link MediaMetricsSet.MediaRecorder}.
+ * The attributes are descibed in {@link MetricsConstants}.
*
* Additional vendor-specific fields may also be present in
* the return value.
*/
- public MediaMetricsSet getMetrics() {
- Bundle bundle = native_getMetrics();
- MediaMetricsSet mSet = new MediaMetricsSet(bundle);
- return mSet;
+ public PersistableBundle getMetrics() {
+ PersistableBundle bundle = native_getMetrics();
+ return bundle;
}
- private native Bundle native_getMetrics();
+ private native PersistableBundle native_getMetrics();
@Override
protected void finalize() { native_finalize(); }
+
+ public final static class MetricsConstants
+ {
+ private MetricsConstants() {}
+
+ /**
+ * Key to extract the audio bitrate
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String AUDIO_BITRATE = "android.media.mediarecorder.audio-bitrate";
+
+ /**
+ * Key to extract the number of audio channels
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String AUDIO_CHANNELS = "android.media.mediarecorder.audio-channels";
+
+ /**
+ * Key to extract the audio samplerate
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String AUDIO_SAMPLERATE = "android.media.mediarecorder.audio-samplerate";
+
+ /**
+ * Key to extract the audio timescale
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String AUDIO_TIMESCALE = "android.media.mediarecorder.audio-timescale";
+
+ /**
+ * Key to extract the video capture frame rate
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is a double.
+ */
+ public static final String CAPTURE_FPS = "android.media.mediarecorder.capture-fps";
+
+ /**
+ * Key to extract the video capture framerate enable value
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String CAPTURE_FPS_ENABLE = "android.media.mediarecorder.capture-fpsenable";
+
+ /**
+ * Key to extract the intended playback frame rate
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String FRAMERATE = "android.media.mediarecorder.frame-rate";
+
+ /**
+ * Key to extract the height (in pixels) of the captured video
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String HEIGHT = "android.media.mediarecorder.height";
+
+ /**
+ * Key to extract the recorded movies time units
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ * A value of 1000 indicates that the movie's timing is in milliseconds.
+ */
+ public static final String MOVIE_TIMESCALE = "android.media.mediarecorder.movie-timescale";
+
+ /**
+ * Key to extract the rotation (in degrees) to properly orient the video
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String ROTATION = "android.media.mediarecorder.rotation";
+
+ /**
+ * Key to extract the video bitrate from being used
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String VIDEO_BITRATE = "android.media.mediarecorder.video-bitrate";
+
+ /**
+ * Key to extract the value for how often video iframes are generated
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String VIDEO_IFRAME_INTERVAL = "android.media.mediarecorder.video-iframe-interval";
+
+ /**
+ * Key to extract the video encoding level
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String VIDEO_LEVEL = "android.media.mediarecorder.video-encoder-level";
+
+ /**
+ * Key to extract the video encoding profile
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String VIDEO_PROFILE = "android.media.mediarecorder.video-encoder-profile";
+
+ /**
+ * Key to extract the recorded video time units
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ * A value of 1000 indicates that the video's timing is in milliseconds.
+ */
+ public static final String VIDEO_TIMESCALE = "android.media.mediarecorder.video-timescale";
+
+ /**
+ * Key to extract the width (in pixels) of the captured video
+ * from the {@link MediaRecorder#getMetrics} return.
+ * The value is an integer.
+ */
+ public static final String WIDTH = "android.media.mediarecorder.width";
+
+ }
}
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 5bf205e..789d5e0 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -365,7 +365,7 @@
* @param parentId The id of the parent media item whose list of children
* will be subscribed.
* @param options The bundle of service-specific arguments to send to the media
- * browse service. The contents of this bundle may affect the
+ * browser service. The contents of this bundle may affect the
* information returned when browsing.
* @param callback The callback to receive the list of children.
*/
@@ -453,7 +453,7 @@
try {
mServiceBinder.getMediaItem(mediaId, receiver, mServiceCallbacks);
} catch (RemoteException e) {
- Log.i(TAG, "Remote error getting media item.", e);
+ Log.i(TAG, "Remote error getting media item.");
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -463,62 +463,6 @@
}
}
- /**
- * Searches {@link MediaItem media items} from the connected service. Not
- * all services may support this, and {@link SearchCallback#onError} will be
- * called if not implemented.
- *
- * @param query The search query that contains keywords separated by space. Should not be
- * an empty string.
- * @param extras The bundle of service-specific arguments to send to the media browser
- * service. The contents of this bundle may affect the search result.
- * @param callback The callback to receive the search result.
- * @throws IllegalStateException if the browser is not connected to the media browser service.
- */
- public void search(@NonNull final String query, final Bundle extras, SearchCallback callback) {
- if (TextUtils.isEmpty(query)) {
- throw new IllegalArgumentException("query cannot be empty.");
- }
- if (callback == null) {
- throw new IllegalArgumentException("callback cannot be null.");
- }
- if (mState != CONNECT_STATE_CONNECTED) {
- throw new IllegalStateException("search() called while not connected (state="
- + getStateLabel(mState) + ")");
- }
- ResultReceiver receiver = new ResultReceiver(mHandler) {
- @Override
- protected void onReceiveResult(int resultCode, Bundle resultData) {
- if (resultCode != 0 || resultData == null
- || !resultData.containsKey(MediaBrowserService.KEY_SEARCH_RESULTS)) {
- callback.onError(query, extras);
- return;
- }
- Parcelable[] items = resultData.getParcelableArray(
- MediaBrowserService.KEY_SEARCH_RESULTS);
- List<MediaItem> results = null;
- if (items != null) {
- results = new ArrayList<>();
- for (Parcelable item : items) {
- results.add((MediaItem) item);
- }
- }
- callback.onSearchResult(query, extras, results);
- }
- };
- try {
- mServiceBinder.search(query, extras, receiver, mServiceCallbacks);
- } catch (RemoteException e) {
- Log.i(TAG, "Remote error getting media item.", e);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- callback.onError(query, extras);
- }
- });
- }
- }
-
private void subscribeInternal(String parentId, Bundle options, SubscriptionCallback callback) {
// Check arguments.
if (TextUtils.isEmpty(parentId)) {
@@ -945,7 +889,7 @@
* @param parentId The media id of the parent media item.
* @param children The children which were loaded.
* @param options The bundle of service-specific arguments sent to the media
- * browse service. The contents of this bundle may affect the
+ * browser service. The contents of this bundle may affect the
* information returned when browsing.
*/
public void onChildrenLoaded(@NonNull String parentId, @NonNull List<MediaItem> children,
@@ -1004,32 +948,6 @@
}
/**
- * Callback for receiving the result of {@link #search}.
- */
- public static abstract class SearchCallback {
- /**
- * Called when the {@link #search} finished successfully.
- *
- * @param query The search query sent for the search request to the connected service.
- * @param extras The bundle of service-specific arguments sent to the connected service.
- * @param items The list of media items which contains the search result.
- */
- public void onSearchResult(@NonNull String query, Bundle extras,
- @NonNull List<MediaItem> items) {
- }
-
- /**
- * Called when an error happens while {@link #search} or the connected service doesn't
- * support {@link #search}.
- *
- * @param query The search query sent for the search request to the connected service.
- * @param extras The bundle of service-specific arguments sent to the connected service.
- */
- public void onError(@NonNull String query, Bundle extras) {
- }
- }
-
- /**
* ServiceConnection to the other app.
*/
private class MediaServiceConnection implements ServiceConnection {
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 4f3314c..3affee5c0 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -45,8 +45,6 @@
void setQueueTitle(CharSequence title);
void setExtras(in Bundle extras);
void setRatingType(int type);
- void setRepeatMode(int repeatMode);
- void setShuffleModeEnabled(boolean enabled);
// These commands relate to volume handling
void setPlaybackToLocal(in AudioAttributes attributes);
diff --git a/media/java/android/media/session/ISessionCallback.aidl b/media/java/android/media/session/ISessionCallback.aidl
index a146c62..893bd3c 100644
--- a/media/java/android/media/session/ISessionCallback.aidl
+++ b/media/java/android/media/session/ISessionCallback.aidl
@@ -16,7 +16,6 @@
package android.media.session;
import android.content.Intent;
-import android.media.MediaDescription;
import android.media.Rating;
import android.net.Uri;
import android.os.Bundle;
@@ -47,13 +46,7 @@
void onRewind();
void onSeekTo(long pos);
void onRate(in Rating rating);
- void onRepeatMode(int repeatMode);
- void onShuffleMode(boolean enabled);
void onCustomAction(String action, in Bundle args);
- void onAddQueueItem(in MediaDescription description);
- void onAddQueueItemAt(in MediaDescription description, int index);
- void onRemoveQueueItem(in MediaDescription description);
- void onRemoveQueueItemAt(int index);
// These callbacks are for volume handling
void onAdjustVolume(int direction);
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index 7b5233a..249bcdc 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -18,7 +18,6 @@
import android.app.PendingIntent;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
-import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.session.ISessionControllerCallback;
@@ -49,19 +48,6 @@
ParcelableVolumeInfo getVolumeAttributes();
void adjustVolume(int direction, int flags, String packageName);
void setVolumeTo(int value, int flags, String packageName);
- MediaMetadata getMetadata();
- PlaybackState getPlaybackState();
- ParceledListSlice getQueue();
- void addQueueItem(in MediaDescription description);
- void addQueueItemAt(in MediaDescription description, int index);
- void removeQueueItem(in MediaDescription description);
- void removeQueueItemAt(int index);
-
- CharSequence getQueueTitle();
- Bundle getExtras();
- int getRatingType();
- int getRepeatMode();
- boolean isShuffleModeEnabled();
// These commands are for the TransportControls
void prepare();
@@ -81,7 +67,11 @@
void rewind();
void seekTo(long pos);
void rate(in Rating rating);
- void repeatMode(int repeatMode);
- void shuffleMode(boolean enabled);
void sendCustomAction(String action, in Bundle args);
+ MediaMetadata getMetadata();
+ PlaybackState getPlaybackState();
+ ParceledListSlice getQueue();
+ CharSequence getQueueTitle();
+ Bundle getExtras();
+ int getRatingType();
}
diff --git a/media/java/android/media/session/ISessionControllerCallback.aidl b/media/java/android/media/session/ISessionControllerCallback.aidl
index 9517c08..cf31767 100644
--- a/media/java/android/media/session/ISessionControllerCallback.aidl
+++ b/media/java/android/media/session/ISessionControllerCallback.aidl
@@ -36,6 +36,4 @@
void onQueueTitleChanged(CharSequence title);
void onExtrasChanged(in Bundle extras);
void onVolumeInfoChanged(in ParcelableVolumeInfo info);
- void onRepeatModeChanged(int repeatMode);
- void onShuffleModeChanged(boolean shuffleMode);
}
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index bab2af2..622900f 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -23,7 +23,6 @@
import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.AudioManager;
-import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.Rating;
import android.media.VolumeProvider;
@@ -39,7 +38,6 @@
import android.view.KeyEvent;
import java.lang.ref.WeakReference;
-import java.lang.UnsupportedOperationException;
import java.util.ArrayList;
import java.util.List;
@@ -65,9 +63,7 @@
private static final int MSG_UPDATE_QUEUE = 5;
private static final int MSG_UPDATE_QUEUE_TITLE = 6;
private static final int MSG_UPDATE_EXTRAS = 7;
- private static final int MSG_UPDATE_REPEAT_MODE = 8;
- private static final int MSG_UPDATE_SHUFFLE_MODE = 9;
- private static final int MSG_DESTROYED = 10;
+ private static final int MSG_DESTROYED = 8;
private final ISessionController mSessionBinder;
@@ -113,7 +109,8 @@
}
/**
- * Get a {@link TransportControls} instance to send transport actions to this session.
+ * Get a {@link TransportControls} instance to send transport actions to
+ * the associated session.
*
* @return A transport controls instance.
*/
@@ -152,7 +149,7 @@
try {
return mSessionBinder.getPlaybackState();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getPlaybackState", e);
+ Log.wtf(TAG, "Error calling getPlaybackState.", e);
return null;
}
}
@@ -166,7 +163,7 @@
try {
return mSessionBinder.getMetadata();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getMetadata", e);
+ Log.wtf(TAG, "Error calling getMetadata.", e);
return null;
}
}
@@ -184,103 +181,12 @@
return queue.getList();
}
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getQueue", e);
+ Log.wtf(TAG, "Error calling getQueue.", e);
}
return null;
}
/**
- * Add a queue item from the given {@code description} at the end of the play queue
- * of this session. Not all sessions may support this.
- *
- * @param description The {@link MediaDescription} for creating the
- * {@link MediaSession.QueueItem} to be inserted.
- * @throws UnsupportedOperationException If this session doesn't support this.
- * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS
- */
- public void addQueueItem(MediaDescription description) {
- try {
- long flags = mSessionBinder.getFlags();
- if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
- throw new UnsupportedOperationException(
- "This session doesn't support queue management operations");
- }
- mSessionBinder.addQueueItem(description);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling addQueueItem", e);
- }
- }
-
- /**
- * Add a queue item from the given {@code description} at the specified position
- * in the play queue of this session. Shifts the queue item currently at that position
- * (if any) and any subsequent queue items to the right (adds one to their indices).
- * Not all sessions may support this.
- *
- * @param description The {@link MediaDescription} for creating the
- * {@link MediaSession.QueueItem} to be inserted.
- * @param index The index at which the created {@link MediaSession.QueueItem} is to be inserted.
- * @throws UnsupportedOperationException If this session doesn't support this.
- * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS
- */
- public void addQueueItem(MediaDescription description, int index) {
- try {
- long flags = mSessionBinder.getFlags();
- if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
- throw new UnsupportedOperationException(
- "This session doesn't support queue management operations");
- }
- mSessionBinder.addQueueItemAt(description, index);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling addQueueItemAt", e);
- }
- }
-
- /**
- * Remove the first occurrence of the specified {@link MediaSession.QueueItem}
- * with the given {@link MediaDescription description} in the play queue of the associated
- * session. Not all sessions may support this.
- *
- * @param description The {@link MediaDescription} for denoting the
- * {@link MediaSession.QueueItem} to be removed.
- * @throws UnsupportedOperationException If this session doesn't support this.
- * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS
- */
- public void removeQueueItem(MediaDescription description) {
- try {
- long flags = mSessionBinder.getFlags();
- if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
- throw new UnsupportedOperationException(
- "This session doesn't support queue management operations");
- }
- mSessionBinder.removeQueueItem(description);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling removeQueueItem", e);
- }
- }
-
- /**
- * Remove an queue item at the specified position in the play queue
- * of this session. Not all sessions may support this.
- *
- * @param index The index of the element to be removed.
- * @throws UnsupportedOperationException If this session doesn't support this.
- * @see MediaSession#FLAG_HANDLES_QUEUE_COMMANDS
- */
- public void removeQueueItemAt(int index) {
- try {
- long flags = mSessionBinder.getFlags();
- if ((flags & MediaSession.FLAG_HANDLES_QUEUE_COMMANDS) == 0) {
- throw new UnsupportedOperationException(
- "This session doesn't support queue management operations");
- }
- mSessionBinder.removeQueueItemAt(index);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling removeQueueItemAt", e);
- }
- }
-
- /**
* Get the queue title for this session.
*/
public @Nullable CharSequence getQueueTitle() {
@@ -322,41 +228,12 @@
try {
return mSessionBinder.getRatingType();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getRatingType", e);
+ Log.wtf(TAG, "Error calling getRatingType.", e);
return Rating.RATING_NONE;
}
}
/**
- * Get the repeat mode for this session.
- *
- * @return The latest repeat mode set to the session, or
- * {@link PlaybackState#REPEAT_MODE_NONE} if not set.
- */
- public int getRepeatMode() {
- try {
- return mSessionBinder.getRepeatMode();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getRepeatMode", e);
- return PlaybackState.REPEAT_MODE_NONE;
- }
- }
-
- /**
- * Return whether the shuffle mode is enabled for this session.
- *
- * @return {@code true} if the shuffle mode is enabled, {@code false} if disabled or not set.
- */
- public boolean isShuffleModeEnabled() {
- try {
- return mSessionBinder.isShuffleModeEnabled();
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling isShuffleModeEnabled", e);
- return false;
- }
- }
-
- /**
* Get the flags for this session. Flags are defined in {@link MediaSession}.
*
* @return The current set of flags for the session.
@@ -365,7 +242,7 @@
try {
return mSessionBinder.getFlags();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getFlags", e);
+ Log.wtf(TAG, "Error calling getFlags.", e);
}
return 0;
}
@@ -382,7 +259,7 @@
result.maxVolume, result.currentVolume);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getAudioInfo", e);
+ Log.wtf(TAG, "Error calling getAudioInfo.", e);
}
return null;
}
@@ -397,7 +274,7 @@
try {
return mSessionBinder.getLaunchPendingIntent();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling getPendingIntent", e);
+ Log.wtf(TAG, "Error calling getPendingIntent.", e);
}
return null;
}
@@ -426,7 +303,7 @@
try {
mSessionBinder.setVolumeTo(value, flags, mContext.getPackageName());
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling setVolumeTo", e);
+ Log.wtf(TAG, "Error calling setVolumeTo.", e);
}
}
@@ -447,7 +324,7 @@
try {
mSessionBinder.adjustVolume(direction, flags, mContext.getPackageName());
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling adjustVolumeBy", e);
+ Log.wtf(TAG, "Error calling adjustVolumeBy.", e);
}
}
@@ -513,7 +390,7 @@
try {
mSessionBinder.sendCommand(command, args, cb);
} catch (RemoteException e) {
- Log.d(TAG, "Dead object in sendCommand", e);
+ Log.d(TAG, "Dead object in sendCommand.", e);
}
}
@@ -527,7 +404,7 @@
try {
mPackageName = mSessionBinder.getPackageName();
} catch (RemoteException e) {
- Log.d(TAG, "Dead object in getPackageName", e);
+ Log.d(TAG, "Dead object in getPackageName.", e);
}
}
return mPackageName;
@@ -544,7 +421,7 @@
try {
mTag = mSessionBinder.getTag();
} catch (RemoteException e) {
- Log.d(TAG, "Dead object in getTag", e);
+ Log.d(TAG, "Dead object in getTag.", e);
}
}
return mTag;
@@ -702,25 +579,6 @@
*/
public void onAudioInfoChanged(PlaybackInfo info) {
}
-
- /**
- * Override to handle changes to the repeat mode.
- *
- * @param repeatMode The repeat mode. It should be one of followings:
- * {@link PlaybackState#REPEAT_MODE_NONE},
- * {@link PlaybackState#REPEAT_MODE_ONE},
- * {@link PlaybackState#REPEAT_MODE_ALL}
- */
- public void onRepeatModeChanged(@PlaybackState.RepeatMode int repeatMode) {
- }
-
- /**
- * Override to handle changes to the shuffle mode.
- *
- * @param enabled {@code true} if the shuffle mode is enabled, {@code false} otherwise.
- */
- public void onShuffleModeChanged(boolean enabled) {
- }
}
/**
@@ -744,7 +602,7 @@
try {
mSessionBinder.prepare();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling prepare", e);
+ Log.wtf(TAG, "Error calling prepare.", e);
}
}
@@ -763,12 +621,12 @@
public void prepareFromMediaId(String mediaId, Bundle extras) {
if (TextUtils.isEmpty(mediaId)) {
throw new IllegalArgumentException(
- "You must specify a non-empty String for prepareFromMediaId");
+ "You must specify a non-empty String for prepareFromMediaId.");
}
try {
mSessionBinder.prepareFromMediaId(mediaId, extras);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling prepare(" + mediaId + ")", e);
+ Log.wtf(TAG, "Error calling prepare(" + mediaId + ").", e);
}
}
@@ -794,7 +652,7 @@
try {
mSessionBinder.prepareFromSearch(query, extras);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling prepare(" + query + ")", e);
+ Log.wtf(TAG, "Error calling prepare(" + query + ").", e);
}
}
@@ -813,12 +671,12 @@
public void prepareFromUri(Uri uri, Bundle extras) {
if (uri == null || Uri.EMPTY.equals(uri)) {
throw new IllegalArgumentException(
- "You must specify a non-empty Uri for prepareFromUri");
+ "You must specify a non-empty Uri for prepareFromUri.");
}
try {
mSessionBinder.prepareFromUri(uri, extras);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling prepare(" + uri + ")", e);
+ Log.wtf(TAG, "Error calling prepare(" + uri + ").", e);
}
}
@@ -829,7 +687,7 @@
try {
mSessionBinder.play();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling play", e);
+ Log.wtf(TAG, "Error calling play.", e);
}
}
@@ -843,12 +701,12 @@
public void playFromMediaId(String mediaId, Bundle extras) {
if (TextUtils.isEmpty(mediaId)) {
throw new IllegalArgumentException(
- "You must specify a non-empty String for playFromMediaId");
+ "You must specify a non-empty String for playFromMediaId.");
}
try {
mSessionBinder.playFromMediaId(mediaId, extras);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling play(" + mediaId + ")", e);
+ Log.wtf(TAG, "Error calling play(" + mediaId + ").", e);
}
}
@@ -870,7 +728,7 @@
try {
mSessionBinder.playFromSearch(query, extras);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling play(" + query + ")", e);
+ Log.wtf(TAG, "Error calling play(" + query + ").", e);
}
}
@@ -884,12 +742,12 @@
public void playFromUri(Uri uri, Bundle extras) {
if (uri == null || Uri.EMPTY.equals(uri)) {
throw new IllegalArgumentException(
- "You must specify a non-empty Uri for playFromUri");
+ "You must specify a non-empty Uri for playFromUri.");
}
try {
mSessionBinder.playFromUri(uri, extras);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling play(" + uri + ")", e);
+ Log.wtf(TAG, "Error calling play(" + uri + ").", e);
}
}
@@ -901,7 +759,7 @@
try {
mSessionBinder.skipToQueueItem(id);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling skipToItem(" + id + ")", e);
+ Log.wtf(TAG, "Error calling skipToItem(" + id + ").", e);
}
}
@@ -913,7 +771,7 @@
try {
mSessionBinder.pause();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling pause", e);
+ Log.wtf(TAG, "Error calling pause.", e);
}
}
@@ -925,7 +783,7 @@
try {
mSessionBinder.stop();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling stop", e);
+ Log.wtf(TAG, "Error calling stop.", e);
}
}
@@ -938,7 +796,7 @@
try {
mSessionBinder.seekTo(pos);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling seekTo", e);
+ Log.wtf(TAG, "Error calling seekTo.", e);
}
}
@@ -950,7 +808,7 @@
try {
mSessionBinder.fastForward();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling fastForward", e);
+ Log.wtf(TAG, "Error calling fastForward.", e);
}
}
@@ -961,7 +819,7 @@
try {
mSessionBinder.next();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling next", e);
+ Log.wtf(TAG, "Error calling next.", e);
}
}
@@ -973,7 +831,7 @@
try {
mSessionBinder.rewind();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling rewind", e);
+ Log.wtf(TAG, "Error calling rewind.", e);
}
}
@@ -984,7 +842,7 @@
try {
mSessionBinder.previous();
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling previous", e);
+ Log.wtf(TAG, "Error calling previous.", e);
}
}
@@ -999,36 +857,7 @@
try {
mSessionBinder.rate(rating);
} catch (RemoteException e) {
- Log.wtf(TAG, "Error calling rate", e);
- }
- }
-
- /**
- * Set the repeat mode for this session.
- *
- * @param repeatMode The repeat mode. Must be one of the followings:
- * {@link PlaybackState#REPEAT_MODE_NONE},
- * {@link PlaybackState#REPEAT_MODE_ONE},
- * {@link PlaybackState#REPEAT_MODE_ALL}
- */
- public void setRepeatMode(@PlaybackState.RepeatMode int repeatMode) {
- try {
- mSessionBinder.repeatMode(repeatMode);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling setRepeatMode", e);
- }
- }
-
- /**
- * Set the shuffle mode for this session.
- *
- * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable.
- */
- public void setShuffleModeEnabled(boolean enabled) {
- try {
- mSessionBinder.shuffleMode(enabled);
- } catch (RemoteException e) {
- Log.wtf(TAG, "Error calling shuffleMode", e);
+ Log.wtf(TAG, "Error calling rate.", e);
}
}
@@ -1042,7 +871,7 @@
public void sendCustomAction(@NonNull PlaybackState.CustomAction customAction,
@Nullable Bundle args) {
if (customAction == null) {
- throw new IllegalArgumentException("CustomAction cannot be null");
+ throw new IllegalArgumentException("CustomAction cannot be null.");
}
sendCustomAction(customAction.getAction(), args);
}
@@ -1058,12 +887,12 @@
*/
public void sendCustomAction(@NonNull String action, @Nullable Bundle args) {
if (TextUtils.isEmpty(action)) {
- throw new IllegalArgumentException("CustomAction cannot be null");
+ throw new IllegalArgumentException("CustomAction cannot be null.");
}
try {
mSessionBinder.sendCustomAction(action, args);
} catch (RemoteException e) {
- Log.d(TAG, "Dead object in sendCustomAction", e);
+ Log.d(TAG, "Dead object in sendCustomAction.", e);
}
}
}
@@ -1233,21 +1062,6 @@
}
}
- @Override
- public void onRepeatModeChanged(int repeatMode) {
- MediaController controller = mController.get();
- if (controller != null) {
- controller.postMessage(MSG_UPDATE_REPEAT_MODE, repeatMode, null);
- }
- }
-
- @Override
- public void onShuffleModeChanged(boolean enabled) {
- MediaController controller = mController.get();
- if (controller != null) {
- controller.postMessage(MSG_UPDATE_SHUFFLE_MODE, enabled, null);
- }
- }
}
private final static class MessageHandler extends Handler {
@@ -1286,12 +1100,6 @@
case MSG_UPDATE_VOLUME:
mCallback.onAudioInfoChanged((PlaybackInfo) msg.obj);
break;
- case MSG_UPDATE_REPEAT_MODE:
- mCallback.onRepeatModeChanged((int) msg.obj);
- break;
- case MSG_UPDATE_SHUFFLE_MODE:
- mCallback.onShuffleModeChanged((boolean) msg.obj);
- break;
case MSG_DESTROYED:
mCallback.onSessionDestroyed();
break;
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index f10f442..dfd2bb35 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -93,12 +93,6 @@
public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 1 << 1;
/**
- * Set this flag on the session to indicate that it handles queue
- * management commands through its {@link Callback}.
- */
- public static final int FLAG_HANDLES_QUEUE_COMMANDS = 1 << 2;
-
- /**
* System only flag for a session that needs to have priority over all other
* sessions. This flag ensures this session will receive media button events
* regardless of the current ordering in the system.
@@ -112,7 +106,6 @@
@IntDef(flag = true, value = {
FLAG_HANDLES_MEDIA_BUTTONS,
FLAG_HANDLES_TRANSPORT_CONTROLS,
- FLAG_HANDLES_QUEUE_COMMANDS,
FLAG_EXCLUSIVE_GLOBAL_PRIORITY })
public @interface SessionFlags { }
@@ -493,41 +486,6 @@
}
/**
- * Set the repeat mode for this session.
- * <p>
- * Note that if this method is not called before, {@link MediaController#getRepeatMode}
- * will return {@link PlaybackState#REPEAT_MODE_NONE}.
- *
- * @param repeatMode The repeat mode. Must be one of the followings:
- * {@link PlaybackState#REPEAT_MODE_NONE},
- * {@link PlaybackState#REPEAT_MODE_ONE},
- * {@link PlaybackState#REPEAT_MODE_ALL}
- */
- public void setRepeatMode(@PlaybackState.RepeatMode int repeatMode) {
- try {
- mBinder.setRepeatMode(repeatMode);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setRepeatMode.", e);
- }
- }
-
- /**
- * Set the shuffle mode for this session.
- * <p>
- * Note that if this method is not called before, {@link MediaController#isShuffleModeEnabled}
- * will return {@code false}.
- *
- * @param enabled {@code true} to enable the shuffle mode, {@code false} to disable.
- */
- public void setShuffleModeEnabled(boolean enabled) {
- try {
- mBinder.setShuffleModeEnabled(enabled);
- } catch (RemoteException e) {
- Log.e(TAG, "Error in setShuffleModeEnabled.", e);
- }
- }
-
- /**
* Set some extras that can be associated with the {@link MediaSession}. No assumptions should
* be made as to how a {@link MediaController} will handle these extras.
* Keys should be fully qualified (e.g. com.example.MY_EXTRA) to avoid conflicts.
@@ -646,34 +604,10 @@
postToCallback(CallbackMessageHandler.MSG_RATE, rating);
}
- private void dispatchRepeatMode(int repeatMode) {
- postToCallback(CallbackMessageHandler.MSG_REPEAT_MODE, repeatMode);
- }
-
- private void dispatchShuffleMode(boolean enabled) {
- postToCallback(CallbackMessageHandler.MSG_SHUFFLE_MODE, enabled);
- }
-
private void dispatchCustomAction(String action, Bundle args) {
postToCallback(CallbackMessageHandler.MSG_CUSTOM_ACTION, action, args);
}
- private void dispatchAddQueueItem(MediaDescription description) {
- postToCallback(CallbackMessageHandler.MSG_ADD_QUEUE_ITEM, description);
- }
-
- private void dispatchAddQueueItem(MediaDescription description, int index) {
- postToCallback(CallbackMessageHandler.MSG_ADD_QUEUE_ITEM_AT, description, index);
- }
-
- private void dispatchRemoveQueueItem(MediaDescription description) {
- postToCallback(CallbackMessageHandler.MSG_REMOVE_QUEUE_ITEM, description);
- }
-
- private void dispatchRemoveQueueItemAt(int index) {
- postToCallback(CallbackMessageHandler.MSG_REMOVE_QUEUE_ITEM_AT, index);
- }
-
private void dispatchMediaButton(Intent mediaButtonIntent) {
postToCallback(CallbackMessageHandler.MSG_MEDIA_BUTTON, mediaButtonIntent);
}
@@ -695,22 +629,10 @@
postToCallback(CallbackMessageHandler.MSG_COMMAND, cmd);
}
- private void postToCallback(int what, int arg1) {
- postToCallback(what, null, arg1);
- }
-
private void postToCallback(int what, Object obj) {
postToCallback(what, obj, null);
}
- private void postToCallback(int what, Object obj, int arg1) {
- synchronized (mLock) {
- if (mCallback != null) {
- mCallback.post(what, obj, arg1);
- }
- }
- }
-
private void postToCallback(int what, Object obj, Bundle extras) {
synchronized (mLock) {
if (mCallback != null) {
@@ -1048,33 +970,6 @@
}
/**
- * Override to handle the setting of the repeat mode.
- * <p>
- * You should call {@link #setRepeatMode} before end of this method in order to notify
- * the change to the {@link MediaController}, or {@link MediaController#getRepeatMode}
- * could return an invalid value.
- *
- * @param repeatMode The repeat mode which is one of followings:
- * {@link PlaybackState#REPEAT_MODE_NONE},
- * {@link PlaybackState#REPEAT_MODE_ONE},
- * {@link PlaybackState#REPEAT_MODE_ALL}
- */
- public void onSetRepeatMode(@PlaybackState.RepeatMode int repeatMode) {
- }
-
- /**
- * Override to handle the setting of the shuffle mode.
- * <p>
- * You should call {@link #setShuffleModeEnabled} before the end of this method in order to
- * notify the change to the {@link MediaController}, or
- * {@link MediaController#isShuffleModeEnabled} could return an invalid value.
- *
- * @param enabled true when the shuffle mode is enabled, false otherwise.
- */
- public void onSetShuffleModeEnabled(boolean enabled) {
- }
-
- /**
* Called when a {@link MediaController} wants a {@link PlaybackState.CustomAction} to be
* performed.
*
@@ -1084,47 +979,6 @@
*/
public void onCustomAction(@NonNull String action, @Nullable Bundle extras) {
}
-
- /**
- * Called when a {@link MediaController} wants to add a {@link QueueItem} with the given
- * {@link MediaDescription description} at the end of the play queue.
- *
- * @param description The {@link MediaDescription} for creating the {@link QueueItem} to be
- * inserted.
- */
- public void onAddQueueItem(MediaDescription description) {
- }
-
- /**
- * Called when a {@link MediaController} wants to add a {@link QueueItem} with the given
- * {@link MediaDescription description} at the specified position in the play queue.
- *
- * @param description The {@link MediaDescription} for creating the {@link QueueItem} to be
- * inserted.
- * @param index The index at which the created {@link QueueItem} is to be inserted.
- */
- public void onAddQueueItem(MediaDescription description, int index) {
- }
-
- /**
- * Called when a {@link MediaController} wants to remove the first occurrence of the
- * specified {@link QueueItem} with the given {@link MediaDescription description}
- * in the play queue.
- *
- * @param description The {@link MediaDescription} for denoting the {@link QueueItem} to be
- * removed.
- */
- public void onRemoveQueueItem(MediaDescription description) {
- }
-
- /**
- * Called when a {@link MediaController} wants to remove a {@link QueueItem} at the
- * specified position in the play queue.
- *
- * @param index The index of the element to be removed.
- */
- public void onRemoveQueueItemAt(int index) {
- }
}
/**
@@ -1297,22 +1151,6 @@
}
@Override
- public void onRepeatMode(int repeatMode) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchRepeatMode(repeatMode);
- }
- }
-
- @Override
- public void onShuffleMode(boolean enabled) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchShuffleMode(enabled);
- }
- }
-
- @Override
public void onCustomAction(String action, Bundle args) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1321,38 +1159,6 @@
}
@Override
- public void onAddQueueItem(MediaDescription description) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchAddQueueItem(description);
- }
- }
-
- @Override
- public void onAddQueueItemAt(MediaDescription description, int index) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchAddQueueItem(description, index);
- }
- }
-
- @Override
- public void onRemoveQueueItem(MediaDescription description) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchRemoveQueueItem(description);
- }
- }
-
- @Override
- public void onRemoveQueueItemAt(int index) {
- MediaSession session = mMediaSession.get();
- if (session != null) {
- session.dispatchRemoveQueueItemAt(index);
- }
- }
-
- @Override
public void onAdjustVolume(int direction) {
MediaSession session = mMediaSession.get();
if (session != null) {
@@ -1376,7 +1182,7 @@
*/
public static final class QueueItem implements Parcelable {
/**
- * This id is reserved. No items can be explicitly asigned this id.
+ * This id is reserved. No items can be explicitly assigned this id.
*/
public static final int UNKNOWN_ID = -1;
@@ -1485,15 +1291,9 @@
private static final int MSG_REWIND = 17;
private static final int MSG_SEEK_TO = 18;
private static final int MSG_RATE = 19;
- private static final int MSG_REPEAT_MODE = 20;
- private static final int MSG_SHUFFLE_MODE = 21;
- private static final int MSG_CUSTOM_ACTION = 22;
- private static final int MSG_ADJUST_VOLUME = 23;
- private static final int MSG_SET_VOLUME = 24;
- private static final int MSG_ADD_QUEUE_ITEM = 25;
- private static final int MSG_ADD_QUEUE_ITEM_AT = 26;
- private static final int MSG_REMOVE_QUEUE_ITEM = 27;
- private static final int MSG_REMOVE_QUEUE_ITEM_AT = 28;
+ private static final int MSG_CUSTOM_ACTION = 20;
+ private static final int MSG_ADJUST_VOLUME = 21;
+ private static final int MSG_SET_VOLUME = 22;
private MediaSession.Callback mCallback;
@@ -1582,33 +1382,15 @@
case MSG_RATE:
mCallback.onSetRating((Rating) msg.obj);
break;
- case MSG_REPEAT_MODE:
- mCallback.onSetRepeatMode(msg.arg1);
- break;
- case MSG_SHUFFLE_MODE:
- mCallback.onSetShuffleModeEnabled((boolean) msg.obj);
- break;
case MSG_CUSTOM_ACTION:
mCallback.onCustomAction((String) msg.obj, msg.getData());
break;
- case MSG_ADD_QUEUE_ITEM:
- mCallback.onAddQueueItem((MediaDescription) msg.obj);
- break;
- case MSG_ADD_QUEUE_ITEM_AT:
- mCallback.onAddQueueItem((MediaDescription) msg.obj, msg.arg1);
- break;
- case MSG_REMOVE_QUEUE_ITEM:
- mCallback.onRemoveQueueItem((MediaDescription) msg.obj);
- break;
- case MSG_REMOVE_QUEUE_ITEM_AT:
- mCallback.onRemoveQueueItemAt(msg.arg1);
- break;
case MSG_ADJUST_VOLUME:
synchronized (mLock) {
vp = mVolumeProvider;
}
if (vp != null) {
- vp.onAdjustVolume(msg.arg1);
+ vp.onAdjustVolume((int) msg.obj);
}
break;
case MSG_SET_VOLUME:
@@ -1616,7 +1398,7 @@
vp = mVolumeProvider;
}
if (vp != null) {
- vp.onSetVolumeTo(msg.arg1);
+ vp.onSetVolumeTo((int) msg.obj);
}
break;
}
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 1ea6109..8283c8b 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -45,8 +45,7 @@
ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING,
ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH,
ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE,
- ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI,
- ACTION_SET_REPEAT_MODE, ACTION_SET_SHUFFLE_MODE_ENABLED})
+ ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI})
@Retention(RetentionPolicy.SOURCE)
public @interface Actions {}
@@ -177,20 +176,6 @@
public static final long ACTION_PREPARE_FROM_URI = 1 << 17;
/**
- * Indicates this session supports the set repeat mode command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_SET_REPEAT_MODE = 1 << 18;
-
- /**
- * Indicates this session supports the set shuffle mode enabled command.
- *
- * @see Builder#setActions(long)
- */
- public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 1 << 19;
-
- /**
* @hide
*/
@IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING,
@@ -296,30 +281,6 @@
*/
public final static long PLAYBACK_POSITION_UNKNOWN = -1;
- /**
- * @hide
- */
- @IntDef({REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL})
- @Retention(RetentionPolicy.SOURCE)
- public @interface RepeatMode {}
- /**
- * Use this value with {@link MediaController.TransportControls#setRepeatMode}
- * to indicate that the playback will be stopped at the end of the playing media list.
- */
- public final static int REPEAT_MODE_NONE = 0;
-
- /**
- * Use this value with {@link MediaController.TransportControls#setRepeatMode}
- * to indicate that the playback of the current playing media item will be repeated.
- */
- public final static int REPEAT_MODE_ONE = 1;
-
- /**
- * Use this value with {@link MediaController.TransportControls#setRepeatMode}
- * to indicate that the playback of the playing media list will be repeated.
- */
- public final static int REPEAT_MODE_ALL = 2;
-
private final int mState;
private final long mPosition;
private final long mBufferedPosition;
@@ -466,8 +427,6 @@
* <li> {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}</li>
* <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li>
* <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li>
- * <li> {@link PlaybackState#ACTION_SET_REPEAT_MODE}</li>
- * <li> {@link PlaybackState#ACTION_SET_SHUFFLE_MODE_ENABLED}</li>
* </ul>
*/
@Actions
@@ -1002,8 +961,6 @@
* <li> {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}</li>
* <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li>
* <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li>
- * <li> {@link PlaybackState#ACTION_SET_REPEAT_MODE}</li>
- * <li> {@link PlaybackState#ACTION_SET_SHUFFLE_MODE_ENABLED}</li>
* </ul>
*
* @param actions The set of actions allowed.
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 1b55380..71f9ba25 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -436,6 +436,14 @@
public static final String PARAM_CANONICAL_GENRE = "canonical_genre";
/**
+ * A query, update or delete URI parameter that allows the caller to operate only on preview or
+ * non-preview channels. If set to "true", the operation affects the rows for preview channels
+ * only. If set to "false", the operation affects the rows for non-preview channels only.
+ * @hide
+ */
+ public static final String PARAM_PREVIEW = "preview";
+
+ /**
* Builds an ID that uniquely identifies a TV input service.
*
* @param name The {@link ComponentName} of the TV input service to build ID for.
diff --git a/media/java/android/service/media/IMediaBrowserService.aidl b/media/java/android/service/media/IMediaBrowserService.aidl
index e95154f..84f41f6 100644
--- a/media/java/android/service/media/IMediaBrowserService.aidl
+++ b/media/java/android/service/media/IMediaBrowserService.aidl
@@ -19,10 +19,8 @@
void addSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks);
void removeSubscriptionDeprecated(String uri, IMediaBrowserServiceCallbacks callbacks);
- void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks);
- void search(String query, in Bundle extras, in ResultReceiver cb,
- IMediaBrowserServiceCallbacks callbacks);
+ void getMediaItem(String uri, in ResultReceiver cb, IMediaBrowserServiceCallbacks callbacks);
void addSubscription(String uri, in IBinder token, in Bundle options,
IMediaBrowserServiceCallbacks callbacks);
void removeSubscription(String uri, in IBinder token, IMediaBrowserServiceCallbacks callbacks);
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index d372efb..b52906d 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -89,15 +89,8 @@
*/
public static final String KEY_MEDIA_ITEM = "media_item";
- /**
- * A key for passing the list of MediaItems to the ResultReceiver in search.
- * @hide
- */
- public static final String KEY_SEARCH_RESULTS = "search_results";
-
private static final int RESULT_FLAG_OPTION_NOT_HANDLED = 1 << 0;
private static final int RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED = 1 << 1;
- private static final int RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED = 1 << 2;
private static final int RESULT_ERROR = -1;
private static final int RESULT_OK = 0;
@@ -105,7 +98,7 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag=true, value = { RESULT_FLAG_OPTION_NOT_HANDLED,
- RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED, RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED })
+ RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED })
private @interface ResultFlags { }
private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
@@ -137,7 +130,6 @@
*
* @see #onLoadChildren
* @see #onLoadItem
- * @see #onSearch
*/
public class Result<T> {
private Object mDebug;
@@ -330,23 +322,6 @@
}
});
}
-
- @Override
- public void search(final String query, Bundle extras, ResultReceiver receiver,
- final IMediaBrowserServiceCallbacks callbacks) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- final IBinder b = callbacks.asBinder();
- ConnectionRecord connection = mConnections.get(b);
- if (connection == null) {
- Log.w(TAG, "search for callback that isn't registered query=" + query);
- return;
- }
- performSearch(query, extras, connection, receiver);
- }
- });
- }
}
@Override
@@ -472,32 +447,6 @@
}
/**
- * Called to get the search result.
- * <p>
- * Implementations must call {@link Result#sendResult result.sendResult}. If
- * the search will be an expensive operation {@link Result#detach result.detach}
- * may be called before returning from this function, and then {@link Result#sendResult
- * result.sendResult} called when the search has been completed.
- * </p><p>
- * In case there are no search results, call {@link Result#sendResult} with an empty list.
- * In case there are some errors happened, call {@link Result#sendResult result.sendResult}
- * with {@code null}, which will invoke {@link MediaBrowser.SearchCallback#onError}.
- * </p><p>
- * The default implementation will invoke {@link MediaBrowser.SearchCallback#onError}.
- * </p>
- *
- * @param query The search query sent from the media browser. It contains keywords separated
- * by space.
- * @param extras The bundle of service-specific arguments sent from the media browser.
- * @param result The {@link Result} to send the search result.
- */
- public void onSearch(@NonNull String query, Bundle extras,
- Result<List<MediaBrowser.MediaItem>> result) {
- result.setFlags(RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED);
- result.sendResult(null);
- }
-
- /**
* Call to set the media session.
* <p>
* This should be called as soon as possible during the service's startup.
@@ -545,16 +494,16 @@
* media browser service when connecting and retrieving the root id for browsing, or null if
* none. The contents of this bundle may affect the information returned when browsing.
*
- * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren},
- * {@link #onLoadItem} or {@link #onSearch}.
+ * @throws IllegalStateException If this method is called outside of {@link #onLoadChildren} or
+ * {@link #onLoadItem}.
* @see MediaBrowserService.BrowserRoot#EXTRA_RECENT
* @see MediaBrowserService.BrowserRoot#EXTRA_OFFLINE
* @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
*/
public final Bundle getBrowserRootHints() {
if (mCurConnection == null) {
- throw new IllegalStateException("This should be called inside of onLoadChildren,"
- + " onLoadItem or onSearch methods");
+ throw new IllegalStateException("This should be called inside of onLoadChildren or"
+ + " onLoadItem methods");
}
return mCurConnection.rootHints == null ? null : new Bundle(mCurConnection.rootHints);
}
@@ -771,34 +720,6 @@
}
}
- private void performSearch(String query, Bundle extras, final ConnectionRecord connection,
- final ResultReceiver receiver) {
- final Result<List<MediaBrowser.MediaItem>> result =
- new Result<List<MediaBrowser.MediaItem>>(query) {
- @Override
- void onResultSent(List<MediaBrowser.MediaItem> items, @ResultFlags int flag) {
- if ((flag & RESULT_FLAG_ON_SEARCH_NOT_IMPLEMENTED) != 0
- || items == null) {
- receiver.send(RESULT_ERROR, null);
- return;
- }
- Bundle bundle = new Bundle();
- bundle.putParcelableArray(KEY_SEARCH_RESULTS,
- items.toArray(new MediaBrowser.MediaItem[0]));
- receiver.send(RESULT_OK, bundle);
- }
- };
-
- mCurConnection = connection;
- onSearch(query, extras, result);
- mCurConnection = null;
-
- if (!result.isDone()) {
- throw new IllegalStateException("onSearch must call detach() or sendResult()"
- + " before returning for query=" + query);
- }
- }
-
/**
* Contains information that the browser service needs to send to the client
* when first connected.
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index a8dd313..2178607 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -2002,7 +2002,7 @@
{ "getName", "()Ljava/lang/String;",
(void *)android_media_MediaCodec_getName },
- { "native_getMetrics", "()Landroid/os/Bundle;",
+ { "native_getMetrics", "()Landroid/os/PersistableBundle;",
(void *)android_media_MediaCodec_native_getMetrics},
{ "setParameters", "([Ljava/lang/String;[Ljava/lang/Object;)V",
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index c2cfed9..9e5d3d1 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -905,7 +905,7 @@
{ "hasCacheReachedEndOfStream", "()Z",
(void *)android_media_MediaExtractor_hasCacheReachedEOS },
- {"native_getMetrics", "()Landroid/os/Bundle;",
+ {"native_getMetrics", "()Landroid/os/PersistableBundle;",
(void *)android_media_MediaExtractor_native_getMetrics},
};
diff --git a/media/jni/android_media_MediaMetricsJNI.cpp b/media/jni/android_media_MediaMetricsJNI.cpp
index fb606ba..8979cec 100644
--- a/media/jni/android_media_MediaMetricsJNI.cpp
+++ b/media/jni/android_media_MediaMetricsJNI.cpp
@@ -24,15 +24,12 @@
namespace android {
-// place the attributes into a java Bundle object
-// decide whether this is appropriately scoped here.
-// if we do it somewhere else, we have to figure a "give me all the attrs"
-// access to the inside of MediaAnalyticsItem
+// place the attributes into a java PersistableBundle object
jobject MediaMetricsJNI::writeMetricsToBundle(JNIEnv* env, MediaAnalyticsItem *item, jobject mybundle) {
- jclass clazzBundle = env->FindClass("android/os/Bundle");
+ jclass clazzBundle = env->FindClass("android/os/PersistableBundle");
if (clazzBundle==NULL) {
- ALOGD("can't find android/os/Bundle");
+ ALOGD("can't find android/os/PersistableBundle");
return NULL;
}
// sometimes the caller provides one for us to fill
@@ -58,7 +55,7 @@
// -- get name, get type, get value
// -- insert appropriately into the bundle
for (size_t i = 0 ; i < item->mPropCount; i++ ) {
- MediaAnalyticsItem::Prop *prop = &item->mProps[i];
+ MediaAnalyticsItem::Prop *prop = &item->mProps[i];
// build the key parameter from prop->mName
jstring keyName = env->NewStringUTF(prop->mName);
// invoke the appropriate method to insert
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 1b52cf5..2fc4afd 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1393,7 +1393,7 @@
{"_stop", "()V", (void *)android_media_MediaPlayer_stop},
{"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth},
{"getVideoHeight", "()I", (void *)android_media_MediaPlayer_getVideoHeight},
- {"native_getMetrics", "()Landroid/os/Bundle;", (void *)android_media_MediaPlayer_native_getMetrics},
+ {"native_getMetrics", "()Landroid/os/PersistableBundle;", (void *)android_media_MediaPlayer_native_getMetrics},
{"setPlaybackParams", "(Landroid/media/PlaybackParams;)V", (void *)android_media_MediaPlayer_setPlaybackParams},
{"getPlaybackParams", "()Landroid/media/PlaybackParams;", (void *)android_media_MediaPlayer_getPlaybackParams},
{"setSyncParams", "(Landroid/media/SyncParams;)V", (void *)android_media_MediaPlayer_setSyncParams},
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 7a63e00..2c1e834 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -688,7 +688,7 @@
{"native_finalize", "()V", (void *)android_media_MediaRecorder_native_finalize},
{"native_setInputSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaRecorder_setInputSurface },
- {"native_getMetrics", "()Landroid/os/Bundle;", (void *)android_media_MediaRecorder_native_getMetrics},
+ {"native_getMetrics", "()Landroid/os/PersistableBundle;", (void *)android_media_MediaRecorder_native_getMetrics},
};
// This function only registers the native methods, and is called from
diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
index 444705c..ece700d 100644
--- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
+++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java
@@ -100,8 +100,8 @@
int newState) {
String intentAction;
if (newState == BluetoothProfile.STATE_CONNECTED) {
- Log.i(TAG, "Connected to GATT server.");
- Log.i(TAG, "Attempting to start service discovery:" +
+ Log.d(TAG, "Connected to GATT server.");
+ Log.d(TAG, "Attempting to start service discovery:" +
mBluetoothGatt.discoverServices());
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i(TAG, "Disconnected from GATT server.");
@@ -112,24 +112,24 @@
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
- List<BluetoothGattService> services = mBluetoothGatt.getServices();
- for (BluetoothGattService service : services) {
- if (MIDI_SERVICE.equals(service.getUuid())) {
- Log.d(TAG, "found MIDI_SERVICE");
- List<BluetoothGattCharacteristic> characteristics
- = service.getCharacteristics();
- for (BluetoothGattCharacteristic characteristic : characteristics) {
- if (MIDI_CHARACTERISTIC.equals(characteristic.getUuid())) {
- Log.d(TAG, "found MIDI_CHARACTERISTIC");
- mCharacteristic = characteristic;
+ BluetoothGattService service = gatt.getService(MIDI_SERVICE);
+ if (service != null) {
+ Log.d(TAG, "found MIDI_SERVICE");
+ BluetoothGattCharacteristic characteristic
+ = service.getCharacteristic(MIDI_CHARACTERISTIC);
+ if (characteristic != null) {
+ Log.d(TAG, "found MIDI_CHARACTERISTIC");
+ mCharacteristic = characteristic;
- // Specification says to read the characteristic first and then
- // switch to receiving notifications
- mBluetoothGatt.readCharacteristic(characteristic);
- break;
- }
- }
- break;
+ // Request a lower Connection Interval for better latency.
+ boolean result = gatt.requestConnectionPriority(
+ BluetoothGatt.CONNECTION_PRIORITY_HIGH);
+ Log.d(TAG, "requestConnectionPriority(CONNECTION_PRIORITY_HIGH):"
+ + result);
+
+ // Specification says to read the characteristic first and then
+ // switch to receiving notifications
+ mBluetoothGatt.readCharacteristic(characteristic);
}
}
} else {
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index ae16949..8e58210 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "sensor"
#include <utils/Log.h>
-#include <android/hardware_buffer.h>
#include <android/looper.h>
#include <android/sensor.h>
#include <android/sharedmem.h>
@@ -28,6 +27,7 @@
#include <utils/Looper.h>
#include <utils/RefBase.h>
#include <utils/Timers.h>
+#include <vndk/hardware_buffer.h>
#include <poll.h>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
index 76a64e5..b145290 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceChooserActivity.java
@@ -34,6 +34,7 @@
import android.widget.TextView;
import com.android.companiondevicemanager.DeviceDiscoveryService.DeviceFilterPair;
+import com.android.internal.util.Preconditions;
public class DeviceChooserActivity extends Activity {
@@ -78,22 +79,35 @@
}
mPairButton = findViewById(R.id.button_pair);
- mPairButton.setOnClickListener((view) ->
- onPairTapped(getService().mSelectedDevice));
+ mPairButton.setOnClickListener(v -> onPairTapped(getService().mSelectedDevice));
updatePairButtonEnabled();
mCancelButton = findViewById(R.id.button_cancel);
- mCancelButton.setOnClickListener((view) -> {
- setResult(RESULT_CANCELED);
- finish();
- });
+ mCancelButton.setOnClickListener(v -> cancel());
+ }
+
+ private void cancel() {
+ getService().onCancel();
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (!isFinishing()) {
+ cancel();
+ }
}
private CharSequence getCallingAppName() {
try {
final PackageManager packageManager = getPackageManager();
+ String callingPackage = Preconditions.checkStringNotEmpty(
+ getCallingPackage(),
+ "This activity must be called for result");
return packageManager.getApplicationLabel(
- packageManager.getApplicationInfo(getCallingPackage(), 0));
+ packageManager.getApplicationInfo(callingPackage, 0));
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(e);
}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index 1b6aca1..5beb412 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -110,6 +110,11 @@
private final ScanCallback mBLEScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
+ if (DEBUG) {
+ Log.i(LOG_TAG,
+ "BLE.onScanResult(callbackType = " + callbackType + ", result = " + result
+ + ")");
+ }
final DeviceFilterPair<ScanResult> deviceFilterPair
= DeviceFilterPair.findMatch(result, mBLEFilters);
if (deviceFilterPair == null) return;
@@ -126,6 +131,10 @@
private BroadcastReceiver mBluetoothDeviceFoundBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ if (DEBUG) {
+ Log.i(LOG_TAG,
+ "BL.onReceive(context = " + context + ", intent = " + intent + ")");
+ }
final BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
final DeviceFilterPair<BluetoothDevice> deviceFilterPair
= DeviceFilterPair.findMatch(device, mBluetoothFilters);
@@ -191,7 +200,8 @@
mBLEScanFilters = CollectionUtils.map(mBLEFilters, BluetoothLEDeviceFilter::getScanFilter);
reset();
- }
+ } else if (DEBUG) Log.i(LOG_TAG, "startDiscovery: duplicate request: " + request);
+
if (!ArrayUtils.isEmpty(mDevicesFound)) {
onReadyToShowUI();
}
@@ -221,6 +231,7 @@
}
private void reset() {
+ if (DEBUG) Log.i(LOG_TAG, "reset()");
mDevicesFound.clear();
mSelectedDevice = null;
mDevicesAdapter.notifyDataSetChanged();
@@ -294,9 +305,17 @@
}
}
+ void onCancel() {
+ try {
+ mServiceCallback.onDeviceSelectionCancel();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
class DevicesAdapter extends ArrayAdapter<DeviceFilterPair> {
- //TODO wifi icon
private Drawable BLUETOOTH_ICON = icon(android.R.drawable.stat_sys_data_bluetooth);
+ private Drawable WIFI_ICON = icon(com.android.internal.R.drawable.ic_wifi_signal_3);
private Drawable icon(int drawableRes) {
Drawable icon = getResources().getDrawable(drawableRes, null);
@@ -326,6 +345,11 @@
device.equals(mSelectedDevice)
? Color.GRAY
: Color.TRANSPARENT);
+ textView.setCompoundDrawablesWithIntrinsicBounds(
+ device.device instanceof android.net.wifi.ScanResult
+ ? WIFI_ICON
+ : BLUETOOTH_ICON,
+ null, null, null);
textView.setOnClickListener((view) -> {
mSelectedDevice = device;
notifyDataSetChanged();
@@ -338,8 +362,6 @@
textView.setTextColor(Color.BLACK);
final int padding = DeviceChooserActivity.getPadding(getResources());
textView.setPadding(padding, padding, padding, padding);
- textView.setCompoundDrawablesWithIntrinsicBounds(
- BLUETOOTH_ICON, null, null, null);
textView.setCompoundDrawablePadding(padding);
return textView;
}
@@ -369,8 +391,15 @@
public static <T extends Parcelable> DeviceFilterPair<T> findMatch(
T dev, @Nullable List<? extends DeviceFilter<T>> filters) {
if (isEmpty(filters)) return new DeviceFilterPair<>(dev, null);
- final DeviceFilter<T> matchingFilter = CollectionUtils.find(filters, (f) -> f.matches(dev));
- return matchingFilter != null ? new DeviceFilterPair<>(dev, matchingFilter) : null;
+ final DeviceFilter<T> matchingFilter
+ = CollectionUtils.find(filters, f -> f.matches(dev));
+
+ DeviceFilterPair<T> result = matchingFilter != null
+ ? new DeviceFilterPair<>(dev, matchingFilter)
+ : null;
+ if (DEBUG) Log.i(LOG_TAG, "findMatch(dev = " + dev + ", filters = " + filters +
+ ") -> " + result);
+ return result;
}
public String getDisplayName() {
diff --git a/packages/DefaultContainerService/AndroidManifest.xml b/packages/DefaultContainerService/AndroidManifest.xml
index 55d000c..e399fb1 100644
--- a/packages/DefaultContainerService/AndroidManifest.xml
+++ b/packages/DefaultContainerService/AndroidManifest.xml
@@ -1,5 +1,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.defcontainer" coreApp="true">
+ <uses-permission android:name="android.permission.ALLOCATE_AGGRESSIVE"/>
<uses-permission android:name="android.permission.ASEC_ACCESS"/>
<uses-permission android:name="android.permission.ASEC_CREATE"/>
<uses-permission android:name="android.permission.ASEC_DESTROY"/>
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 37a68e0..9347877 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -30,6 +30,7 @@
import android.content.pm.PackageParser.PackageParserException;
import android.content.res.ObbInfo;
import android.content.res.ObbScanner;
+import android.os.Binder;
import android.os.Environment;
import android.os.Environment.UserEnvironment;
import android.os.FileUtils;
@@ -179,6 +180,15 @@
return ret;
}
+ final int recommendedInstallLocation;
+ final long token = Binder.clearCallingIdentity();
+ try {
+ recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
+ pkg.packageName, pkg.installLocation, sizeBytes, flags);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
ret.packageName = pkg.packageName;
ret.splitNames = pkg.splitNames;
ret.versionCode = pkg.versionCode;
@@ -186,8 +196,7 @@
ret.splitRevisionCodes = pkg.splitRevisionCodes;
ret.installLocation = pkg.installLocation;
ret.verifiers = pkg.verifiers;
- ret.recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
- pkg.packageName, pkg.installLocation, sizeBytes, flags);
+ ret.recommendedInstallLocation = recommendedInstallLocation;
ret.multiArch = pkg.multiArch;
return ret;
diff --git a/packages/SettingsLib/res/layout/settings_with_drawer.xml b/packages/SettingsLib/res/layout/settings_with_drawer.xml
index e9c175f..55c192d 100644
--- a/packages/SettingsLib/res/layout/settings_with_drawer.xml
+++ b/packages/SettingsLib/res/layout/settings_with_drawer.xml
@@ -25,17 +25,13 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:fitsSystemWindows="true">
- <FrameLayout
+ <Toolbar
+ android:id="@+id/action_bar"
style="?android:attr/actionBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:theme="?android:attr/actionBarTheme">
- <Toolbar
- android:id="@+id/action_bar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:navigationContentDescription="@*android:string/action_bar_up_description"/>
- </FrameLayout>
+ android:theme="?android:attr/actionBarTheme"
+ android:navigationContentDescription="@*android:string/action_bar_up_description"/>
<FrameLayout
android:id="@+id/content_header_container"
style="?android:attr/actionBarStyle"
diff --git a/packages/SettingsLib/res/layout/usage_view.xml b/packages/SettingsLib/res/layout/usage_view.xml
index 1d56668..151d1ee 100644
--- a/packages/SettingsLib/res/layout/usage_view.xml
+++ b/packages/SettingsLib/res/layout/usage_view.xml
@@ -76,17 +76,23 @@
android:id="@+id/bottom_label_space"
android:layout_width="@dimen/usage_graph_labels_width"
android:layout_height="wrap_content"/>
- <include android:id="@+id/label_start"
- layout="@layout/usage_side_label" />
-
- <Space
+ <LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_weight="1" />
+ android:layout_weight="1"
+ android:orientation="horizontal"
+ android:layoutDirection="ltr">
+ <include android:id="@+id/label_start"
+ layout="@layout/usage_side_label" />
- <include android:id="@+id/label_end"
- layout="@layout/usage_side_label" />
+ <Space
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1" />
+ <include android:id="@+id/label_end"
+ layout="@layout/usage_side_label" />
+ </LinearLayout>
</LinearLayout>
</LinearLayout>
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityButtonHelper.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityButtonHelper.java
new file mode 100644
index 0000000..972ea34
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityButtonHelper.java
@@ -0,0 +1,62 @@
+/*
+ * 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.settingslib.accessibility;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.Context;
+import android.content.res.Resources;
+import android.provider.Settings;
+import android.view.accessibility.AccessibilityManager;
+
+import java.util.List;
+
+/**
+ * A helper class to assist determining the state of the accessibility button that can appear
+ * within the software-rendered navigation area.
+ */
+public class AccessibilityButtonHelper {
+ public static boolean isRequestedByMagnification(Context ctx) {
+ return Settings.Secure.getInt(ctx.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1;
+ }
+
+ public static boolean isRequestedByAccessibilityService(Context ctx) {
+ final AccessibilityManager accessibilityManager = ctx.getSystemService(
+ AccessibilityManager.class);
+ List<AccessibilityServiceInfo> services =
+ accessibilityManager.getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+ if (services != null) {
+ for (int i = 0, size = services.size(); i < size; i++) {
+ if ((services.get(i).flags
+ & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON)
+ != 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static boolean isRequested(Context ctx) {
+ return isRequestedByMagnification(ctx) || isRequestedByAccessibilityService(ctx);
+ }
+
+ public static boolean isDeviceSupported(Resources res) {
+ return res.getBoolean(com.android.internal.R.bool.config_showNavigationBar);
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 8a86c13..c109704 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -29,6 +29,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageStats;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
@@ -45,6 +46,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.text.format.Formatter;
+import android.util.IconDrawableFactory;
import android.util.Log;
import android.util.SparseArray;
@@ -52,6 +54,7 @@
import com.android.settingslib.R;
import java.io.File;
+import java.io.IOException;
import java.text.Collator;
import java.text.Normalizer;
import java.text.Normalizer.Form;
@@ -61,6 +64,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
+import java.util.UUID;
import java.util.regex.Pattern;
/**
@@ -92,6 +96,7 @@
final Context mContext;
final PackageManager mPm;
+ final IconDrawableFactory mDrawableFactory;
final IPackageManager mIpm;
final UserManager mUm;
final StorageStatsManager mStats;
@@ -114,7 +119,7 @@
final ArrayList<AppEntry> mAppEntries = new ArrayList<AppEntry>();
List<ApplicationInfo> mApplications = new ArrayList<ApplicationInfo>();
long mCurId = 1;
- String mCurComputingSizeUuid;
+ UUID mCurComputingSizeUuid;
String mCurComputingSizePkg;
int mCurComputingSizeUserId;
boolean mSessionsChanged;
@@ -129,6 +134,7 @@
private ApplicationsState(Application app) {
mContext = app;
mPm = mContext.getPackageManager();
+ mDrawableFactory = IconDrawableFactory.newInstance(mContext);
mIpm = AppGlobals.getPackageManager();
mUm = mContext.getSystemService(UserManager.class);
mStats = mContext.getSystemService(StorageStatsManager.class);
@@ -324,7 +330,7 @@
return;
}
synchronized (entry) {
- entry.ensureIconLocked(mContext, mPm);
+ entry.ensureIconLocked(mContext, mDrawableFactory);
}
}
@@ -334,15 +340,23 @@
AppEntry entry = mEntriesMap.get(userId).get(packageName);
if (entry != null && (entry.info.flags & ApplicationInfo.FLAG_INSTALLED) != 0) {
mBackgroundHandler.post(() -> {
- final StorageStats stats = mStats.queryStatsForPackage(entry.info.volumeUuid,
- packageName, UserHandle.of(userId));
- final PackageStats legacyStats = new PackageStats(packageName, userId);
- legacyStats.codeSize = stats.getCodeBytes();
- legacyStats.dataSize = stats.getDataBytes();
- legacyStats.cacheSize = stats.getCacheBytes();
try {
- mBackgroundHandler.mStatsObserver.onGetStatsCompleted(legacyStats, true);
- } catch (RemoteException ignored) {
+ final StorageStats stats = mStats.queryStatsForPackage(
+ entry.info.storageUuid, packageName, UserHandle.of(userId));
+ final PackageStats legacy = new PackageStats(packageName, userId);
+ legacy.codeSize = stats.getCodeBytes();
+ legacy.dataSize = stats.getDataBytes();
+ legacy.cacheSize = stats.getCacheBytes();
+ try {
+ mBackgroundHandler.mStatsObserver.onGetStatsCompleted(legacy, true);
+ } catch (RemoteException ignored) {
+ }
+ } catch (NameNotFoundException | IOException e) {
+ Log.w(TAG, "Failed to query stats: " + e);
+ try {
+ mBackgroundHandler.mStatsObserver.onGetStatsCompleted(null, false);
+ } catch (RemoteException ignored) {
+ }
}
});
}
@@ -932,7 +946,7 @@
AppEntry entry = mAppEntries.get(i);
if (entry.icon == null || !entry.mounted) {
synchronized (entry) {
- if (entry.ensureIconLocked(mContext, mPm)) {
+ if (entry.ensureIconLocked(mContext, mDrawableFactory)) {
if (!mRunning) {
mRunning = true;
Message m = mMainHandler.obtainMessage(
@@ -979,7 +993,7 @@
mMainHandler.sendMessage(m);
}
entry.sizeLoadStart = now;
- mCurComputingSizeUuid = entry.info.volumeUuid;
+ mCurComputingSizeUuid = entry.info.storageUuid;
mCurComputingSizePkg = entry.info.packageName;
mCurComputingSizeUserId = UserHandle.getUserId(entry.info.uid);
@@ -988,17 +1002,17 @@
final StorageStats stats = mStats.queryStatsForPackage(
mCurComputingSizeUuid, mCurComputingSizePkg,
UserHandle.of(mCurComputingSizeUserId));
- final PackageStats legacyStats = new PackageStats(
+ final PackageStats legacy = new PackageStats(
mCurComputingSizePkg, mCurComputingSizeUserId);
- legacyStats.codeSize = stats.getCodeBytes();
- legacyStats.dataSize = stats.getDataBytes();
- legacyStats.cacheSize = stats.getCacheBytes();
+ legacy.codeSize = stats.getCodeBytes();
+ legacy.dataSize = stats.getDataBytes();
+ legacy.cacheSize = stats.getCacheBytes();
try {
- mStatsObserver.onGetStatsCompleted(legacyStats, true);
+ mStatsObserver.onGetStatsCompleted(legacy, true);
} catch (RemoteException ignored) {
}
- } catch (IllegalStateException e) {
- Log.e(TAG,"An exception occurred while fetching app size", e);
+ } catch (NameNotFoundException | IOException e) {
+ Log.w(TAG, "Failed to query stats: " + e);
try {
mStatsObserver.onGetStatsCompleted(null, false);
} catch (RemoteException ignored) {
@@ -1255,10 +1269,10 @@
}
}
- boolean ensureIconLocked(Context context, PackageManager pm) {
+ boolean ensureIconLocked(Context context, IconDrawableFactory drawableFactory) {
if (this.icon == null) {
if (this.apkFile.exists()) {
- this.icon = getBadgedIcon(pm);
+ this.icon = drawableFactory.getBadgedIcon(info);
return true;
} else {
this.mounted = false;
@@ -1270,19 +1284,13 @@
// its icon.
if (this.apkFile.exists()) {
this.mounted = true;
- this.icon = getBadgedIcon(pm);
+ this.icon = drawableFactory.getBadgedIcon(info);
return true;
}
}
return false;
}
- private Drawable getBadgedIcon(PackageManager pm) {
- // Do badging ourself so that it comes from the user of the app not the current user.
- return pm.getUserBadgedIcon(pm.loadUnbadgedItemIcon(info, info),
- new UserHandle(UserHandle.getUserId(info.uid)));
- }
-
public String getVersion(Context context) {
try {
return context.getPackageManager().getPackageInfo(info.packageName, 0).versionName;
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java b/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
index 2183573..34fdc9d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/StorageStatsSource.java
@@ -19,9 +19,12 @@
import android.app.usage.StorageStats;
import android.app.usage.StorageStatsManager;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.support.annotation.VisibleForTesting;
+import java.io.IOException;
+
/**
* StorageStatsSource wraps the StorageStatsManager for testability purposes.
*/
@@ -32,17 +35,21 @@
mStorageStatsManager = context.getSystemService(StorageStatsManager.class);
}
- public StorageStatsSource.ExternalStorageStats getExternalStorageStats(String volumeUuid, UserHandle user) {
+ public StorageStatsSource.ExternalStorageStats getExternalStorageStats(String volumeUuid,
+ UserHandle user) throws IOException {
return new StorageStatsSource.ExternalStorageStats(
mStorageStatsManager.queryExternalStatsForUser(volumeUuid, user));
}
- public StorageStatsSource.AppStorageStats getStatsForUid(String volumeUuid, int uid) {
- return new StorageStatsSource.AppStorageStatsImpl(mStorageStatsManager.queryStatsForUid(volumeUuid, uid));
+ public StorageStatsSource.AppStorageStats getStatsForUid(String volumeUuid, int uid)
+ throws IOException {
+ return new StorageStatsSource.AppStorageStatsImpl(
+ mStorageStatsManager.queryStatsForUid(volumeUuid, uid));
}
public StorageStatsSource.AppStorageStats getStatsForPackage(
- String volumeUuid, String packageName, UserHandle user) {
+ String volumeUuid, String packageName, UserHandle user)
+ throws PackageManager.NameNotFoundException, IOException {
return new StorageStatsSource.AppStorageStatsImpl(
mStorageStatsManager.queryStatsForPackage(volumeUuid, packageName, user));
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java
index 88f133c..ccf7a0b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/PrivateStorageInfo.java
@@ -20,11 +20,16 @@
import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.os.storage.VolumeInfo;
+import android.util.Log;
+
+import java.io.IOException;
/**
* PrivateStorageInfo provides information about the total and free storage on the device.
*/
public class PrivateStorageInfo {
+ private static final String TAG = "PrivateStorageInfo";
+
public final long freeBytes;
public final long totalBytes;
@@ -41,8 +46,12 @@
long privateTotalBytes = 0;
for (VolumeInfo info : sm.getVolumes()) {
if (info.getType() == VolumeInfo.TYPE_PRIVATE && info.isMountedReadable()) {
- privateTotalBytes += sm.getTotalBytes(stats, info);
- privateFreeBytes += sm.getFreeBytes(stats, info);
+ try {
+ privateTotalBytes += sm.getTotalBytes(stats, info);
+ privateFreeBytes += sm.getFreeBytes(stats, info);
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ }
}
}
return new PrivateStorageInfo(privateFreeBytes, privateTotalBytes);
@@ -51,6 +60,11 @@
public static long getTotalSize(VolumeInfo info, long totalInternalStorage) {
final Context context = AppGlobals.getInitialApplication();
final StorageStatsManager stats = context.getSystemService(StorageStatsManager.class);
- return stats.getTotalBytes(info.getFsUuid());
+ try {
+ return stats.getTotalBytes(info.getFsUuid());
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ return 0;
+ }
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java
index 11060e6..b57b6cc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageManagerVolumeProvider.java
@@ -20,6 +20,7 @@
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
+import java.io.IOException;
import java.util.List;
/**
@@ -49,12 +50,12 @@
}
@Override
- public long getTotalBytes(StorageStatsManager stats, VolumeInfo volume) {
+ public long getTotalBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException {
return stats.getTotalBytes(volume.getFsUuid());
}
@Override
- public long getFreeBytes(StorageStatsManager stats, VolumeInfo volume) {
+ public long getFreeBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException {
return stats.getFreeBytes(volume.getFsUuid());
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
index 60e10a1..ea28fe6 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageMeasurement.java
@@ -32,6 +32,7 @@
import android.util.SparseArray;
import android.util.SparseLongArray;
+import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.List;
@@ -154,7 +155,7 @@
try {
details.totalSize = mStats.getTotalBytes(mVolume.fsUuid);
details.availSize = mStats.getFreeBytes(mVolume.fsUuid);
- } catch (IllegalStateException e) {
+ } catch (IOException e) {
// The storage volume became null while we were measuring it.
Log.w(TAG, e);
return details;
@@ -169,8 +170,14 @@
final HashMap<String, Long> mediaMap = new HashMap<>();
details.mediaSize.put(user.id, mediaMap);
- final ExternalStorageStats stats = mStats
- .queryExternalStatsForUser(mSharedVolume.fsUuid, UserHandle.of(user.id));
+ final ExternalStorageStats stats;
+ try {
+ stats = mStats.queryExternalStatsForUser(mSharedVolume.fsUuid,
+ UserHandle.of(user.id));
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ continue;
+ }
addValue(details.usersSize, user.id, stats.getTotalBytes());
@@ -190,8 +197,13 @@
if ((mVolume.getType() == VolumeInfo.TYPE_PRIVATE) && mVolume.isMountedReadable()) {
for (UserInfo user : users) {
- final StorageStats stats = mStats.queryStatsForUser(mVolume.fsUuid,
- UserHandle.of(user.id));
+ final StorageStats stats;
+ try {
+ stats = mStats.queryStatsForUser(mVolume.fsUuid, UserHandle.of(user.id));
+ } catch (IOException e) {
+ Log.w(TAG, e);
+ continue;
+ }
// Only count code once against current user
if (user.id == UserHandle.myUserId()) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java
index e5d85d1..4c45413 100644
--- a/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/deviceinfo/StorageVolumeProvider.java
@@ -19,6 +19,7 @@
import android.app.usage.StorageStatsManager;
import android.os.storage.VolumeInfo;
+import java.io.IOException;
import java.util.List;
/**
@@ -46,12 +47,12 @@
*
* @pre The volume is a private volume and is readable.
*/
- long getTotalBytes(StorageStatsManager stats, VolumeInfo volume);
+ long getTotalBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException;
/**
* Returns the free bytes for a given storage volume.
*
* @pre The volume is a private volume and is readable.
*/
- long getFreeBytes(StorageStatsManager stats, VolumeInfo volume);
+ long getFreeBytes(StorageStatsManager stats, VolumeInfo volume) throws IOException;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
index d6bde81..9d09737 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
@@ -40,6 +40,8 @@
"com.android.settings.category.ia.language";
public static final String CATEGORY_SYSTEM_DEVELOPMENT =
"com.android.settings.category.ia.development";
+ public static final String CATEGORY_NOTIFICATIONS =
+ "com.android.settings.category.ia.notifications";
public static final Map<String, String> KEY_COMPAT_MAP;
diff --git a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
index 231fc69..3f826cc 100644
--- a/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
+++ b/packages/SettingsLib/src/com/android/settingslib/location/RecentLocationApps.java
@@ -27,6 +27,7 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.IconDrawableFactory;
import android.util.Log;
import java.util.ArrayList;
@@ -48,10 +49,12 @@
private final PackageManager mPackageManager;
private final Context mContext;
+ private final IconDrawableFactory mDrawableFactory;
public RecentLocationApps(Context context) {
mContext = context;
mPackageManager = context.getPackageManager();
+ mDrawableFactory = IconDrawableFactory.newInstance(context);
}
/**
@@ -146,8 +149,7 @@
}
final UserHandle userHandle = new UserHandle(userId);
- Drawable appIcon = mPackageManager.getApplicationIcon(appInfo);
- Drawable icon = mPackageManager.getUserBadgedIcon(appIcon, userHandle);
+ Drawable icon = mDrawableFactory.getBadgedIcon(appInfo, userId);
CharSequence appLabel = mPackageManager.getApplicationLabel(appInfo);
CharSequence badgedAppLabel = mPackageManager.getUserBadgedLabel(appLabel, userHandle);
if (appLabel.toString().contentEquals(badgedAppLabel)) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index f277165..314791e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -234,10 +234,19 @@
}
/**
- * Forces an update of the wifi networks when not scanning.
+ * Synchronously update the list of access points with the latest information.
*/
public void forceUpdate() {
+ mWorkHandler.removeMessages(WorkHandler.MSG_UPDATE_ACCESS_POINTS);
+
+ mLastInfo = mWifiManager.getConnectionInfo();
+ mLastNetworkInfo = mConnectivityManager.getNetworkInfo(mWifiManager.getCurrentNetwork());
updateAccessPoints();
+
+ // Synchronously copy access points
+ mMainHandler.removeMessages(MainHandler.MSG_ACCESS_POINT_CHANGED);
+ mMainHandler.handleMessage(
+ Message.obtain(mMainHandler, MainHandler.MSG_ACCESS_POINT_CHANGED));
}
/**
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index f9e695d..b938fe2 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -20,7 +20,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.atLeast;
@@ -33,12 +32,13 @@
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
+import android.net.Network;
import android.net.NetworkBadging;
import android.net.NetworkInfo;
import android.net.NetworkKey;
import android.net.NetworkScoreManager;
-import android.net.ScoredNetwork;
import android.net.RssiCurve;
+import android.net.ScoredNetwork;
import android.net.WifiKey;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
@@ -47,10 +47,10 @@
import android.net.wifi.WifiNetworkScoreCache;
import android.net.wifi.WifiSsid;
import android.os.Bundle;
-import android.os.SystemClock;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
+import android.os.SystemClock;
import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
@@ -62,8 +62,8 @@
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
-import org.mockito.Matchers;
import org.mockito.Captor;
+import org.mockito.Matchers;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
@@ -666,4 +666,29 @@
verify(mockWifiManager, atLeast(2)).getConnectionInfo();
assertThat(tracker.getAccessPoints().get(0).getRssi()).isEqualTo(newRssi);
}
+
+ @Test
+ public void forceUpdateShouldSynchronouslyFetchLatestInformation() throws Exception {
+ when(mockWifiManager.getConnectionInfo()).thenReturn(CONNECTED_AP_1_INFO);
+
+ WifiConfiguration configuration = new WifiConfiguration();
+ configuration.SSID = SSID_1;
+ configuration.BSSID = BSSID_1;
+ configuration.networkId = CONNECTED_NETWORK_ID;
+ when(mockWifiManager.getConfiguredNetworks()).thenReturn(Arrays.asList(configuration));
+
+ NetworkInfo networkInfo = new NetworkInfo(
+ ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype");
+ networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "connected", "test");
+ when(mockConnectivityManager.getNetworkInfo(any(Network.class))).thenReturn(networkInfo);
+
+
+ WifiTracker tracker = createMockedWifiTracker();
+ startTracking(tracker);
+ tracker.forceUpdate();
+
+ verify(mockWifiListener).onAccessPointsChanged();
+ assertThat(tracker.getAccessPoints().size()).isEqualTo(2);
+ assertThat(tracker.getAccessPoints().get(0).isActive()).isTrue();
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/res/anim/tv_pip_onboarding_background_enter_animation.xml b/packages/SystemUI/res/anim/tv_pip_onboarding_background_enter_animation.xml
deleted file mode 100644
index 9ab41d0..0000000
--- a/packages/SystemUI/res/anim/tv_pip_onboarding_background_enter_animation.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
- <objectAnimator
- android:propertyName="alpha"
- android:valueFrom="0"
- android:valueTo="0.9"
- android:interpolator="@android:interpolator/linear"
- android:duration="@integer/tv_pip_onboarding_anim_duration" />
-</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_onboarding_button_enter_animation.xml b/packages/SystemUI/res/anim/tv_pip_onboarding_button_enter_animation.xml
deleted file mode 100644
index 01c263b..0000000
--- a/packages/SystemUI/res/anim/tv_pip_onboarding_button_enter_animation.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
- <objectAnimator
- android:propertyName="translationY"
- android:valueFrom="114dp"
- android:valueTo="0dp"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="@integer/tv_pip_onboarding_anim_duration" />
- <objectAnimator
- android:propertyName="alpha"
- android:valueTo="1"
- android:interpolator="@android:interpolator/linear"
- android:duration="@integer/tv_pip_onboarding_anim_duration" />
-</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_onboarding_description_enter_animation.xml b/packages/SystemUI/res/anim/tv_pip_onboarding_description_enter_animation.xml
deleted file mode 100644
index a12b3b6..0000000
--- a/packages/SystemUI/res/anim/tv_pip_onboarding_description_enter_animation.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
- <objectAnimator
- android:propertyName="translationY"
- android:valueFrom="84dp"
- android:valueTo="0dp"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="@integer/tv_pip_onboarding_anim_duration" />
- <objectAnimator
- android:propertyName="alpha"
- android:valueTo="1"
- android:interpolator="@android:interpolator/linear"
- android:duration="@integer/tv_pip_onboarding_anim_duration" />
-</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_onboarding_image_enter_animation.xml b/packages/SystemUI/res/anim/tv_pip_onboarding_image_enter_animation.xml
deleted file mode 100644
index ae9677e..0000000
--- a/packages/SystemUI/res/anim/tv_pip_onboarding_image_enter_animation.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
- <objectAnimator
- android:propertyName="translationY"
- android:valueFrom="114dp"
- android:valueTo="0dp"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="@integer/tv_pip_onboarding_anim_duration" />
- <objectAnimator
- android:propertyName="alpha"
- android:valueTo="1"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="@integer/tv_pip_onboarding_anim_duration" />
-</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_onboarding_title_enter_animation.xml b/packages/SystemUI/res/anim/tv_pip_onboarding_title_enter_animation.xml
deleted file mode 100644
index 4bde070..0000000
--- a/packages/SystemUI/res/anim/tv_pip_onboarding_title_enter_animation.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android">
-
- <objectAnimator
- android:propertyName="translationY"
- android:valueFrom="84dp"
- android:valueTo="0dp"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="@integer/tv_pip_onboarding_anim_duration" />
- <objectAnimator
- android:propertyName="alpha"
- android:valueTo="1"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="@integer/tv_pip_onboarding_anim_duration" />
-</set>
diff --git a/packages/SystemUI/res/anim/tv_pip_overlay_fade_in_animation.xml b/packages/SystemUI/res/anim/tv_pip_overlay_fade_in_animation.xml
deleted file mode 100644
index 33bceaa..0000000
--- a/packages/SystemUI/res/anim/tv_pip_overlay_fade_in_animation.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:propertyName="alpha"
- android:valueTo="1"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="350" />
diff --git a/packages/SystemUI/res/anim/tv_pip_overlay_fade_out_animation.xml b/packages/SystemUI/res/anim/tv_pip_overlay_fade_out_animation.xml
deleted file mode 100644
index a12ddff..0000000
--- a/packages/SystemUI/res/anim/tv_pip_overlay_fade_out_animation.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
- android:propertyName="alpha"
- android:valueTo="0"
- android:interpolator="@android:interpolator/fast_out_slow_in"
- android:duration="500" />
diff --git a/packages/SystemUI/res/color/segmented_button_text_selector.xml b/packages/SystemUI/res/color/segmented_button_text_selector.xml
index 537cbb8..d4f0181 100644
--- a/packages/SystemUI/res/color/segmented_button_text_selector.xml
+++ b/packages/SystemUI/res/color/segmented_button_text_selector.xml
@@ -18,4 +18,4 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:color="?android:attr/textColorPrimary"/>
<item android:alpha="0.58" android:color="?android:attr/colorForeground"/>
-</selector>
\ No newline at end of file
+</selector>
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_0.png b/packages/SystemUI/res/drawable-xhdpi/remote_0.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_0.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_1.png b/packages/SystemUI/res/drawable-xhdpi/remote_1.png
deleted file mode 100644
index 252ff5e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_1.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_10.png b/packages/SystemUI/res/drawable-xhdpi/remote_10.png
deleted file mode 100644
index 5e52b37..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_10.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_100.png b/packages/SystemUI/res/drawable-xhdpi/remote_100.png
deleted file mode 100644
index f604808..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_100.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_101.png b/packages/SystemUI/res/drawable-xhdpi/remote_101.png
deleted file mode 100644
index 0272e45..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_101.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_102.png b/packages/SystemUI/res/drawable-xhdpi/remote_102.png
deleted file mode 100644
index aaa35c0..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_102.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_103.png b/packages/SystemUI/res/drawable-xhdpi/remote_103.png
deleted file mode 100644
index 20f95a5..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_103.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_104.png b/packages/SystemUI/res/drawable-xhdpi/remote_104.png
deleted file mode 100644
index 052f23e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_104.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_105.png b/packages/SystemUI/res/drawable-xhdpi/remote_105.png
deleted file mode 100644
index c5c8256..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_105.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_106.png b/packages/SystemUI/res/drawable-xhdpi/remote_106.png
deleted file mode 100644
index 4e804fe..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_106.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_107.png b/packages/SystemUI/res/drawable-xhdpi/remote_107.png
deleted file mode 100644
index 76669cc..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_107.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_108.png b/packages/SystemUI/res/drawable-xhdpi/remote_108.png
deleted file mode 100644
index c1b2f3b..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_108.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_109.png b/packages/SystemUI/res/drawable-xhdpi/remote_109.png
deleted file mode 100644
index 79e1d7b..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_109.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_11.png b/packages/SystemUI/res/drawable-xhdpi/remote_11.png
deleted file mode 100644
index cfec6cb..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_11.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_110.png b/packages/SystemUI/res/drawable-xhdpi/remote_110.png
deleted file mode 100644
index d902297..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_110.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_111.png b/packages/SystemUI/res/drawable-xhdpi/remote_111.png
deleted file mode 100644
index e48ed0b..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_111.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_112.png b/packages/SystemUI/res/drawable-xhdpi/remote_112.png
deleted file mode 100644
index 90dd1a2..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_112.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_113.png b/packages/SystemUI/res/drawable-xhdpi/remote_113.png
deleted file mode 100644
index 8fb4ad2..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_113.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_114.png b/packages/SystemUI/res/drawable-xhdpi/remote_114.png
deleted file mode 100644
index 76873fb..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_114.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_115.png b/packages/SystemUI/res/drawable-xhdpi/remote_115.png
deleted file mode 100644
index e923d4c..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_115.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_116.png b/packages/SystemUI/res/drawable-xhdpi/remote_116.png
deleted file mode 100644
index 41d5124..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_116.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_117.png b/packages/SystemUI/res/drawable-xhdpi/remote_117.png
deleted file mode 100644
index eacde64..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_117.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_118.png b/packages/SystemUI/res/drawable-xhdpi/remote_118.png
deleted file mode 100644
index 5dc1fb0..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_118.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_119.png b/packages/SystemUI/res/drawable-xhdpi/remote_119.png
deleted file mode 100644
index a16f037..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_119.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_12.png b/packages/SystemUI/res/drawable-xhdpi/remote_12.png
deleted file mode 100644
index 28bb387..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_12.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_120.png b/packages/SystemUI/res/drawable-xhdpi/remote_120.png
deleted file mode 100644
index fe3ef41..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_120.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_121.png b/packages/SystemUI/res/drawable-xhdpi/remote_121.png
deleted file mode 100644
index ef2b892..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_121.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_122.png b/packages/SystemUI/res/drawable-xhdpi/remote_122.png
deleted file mode 100644
index 5342976..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_122.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_123.png b/packages/SystemUI/res/drawable-xhdpi/remote_123.png
deleted file mode 100644
index bb8a53a..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_123.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_124.png b/packages/SystemUI/res/drawable-xhdpi/remote_124.png
deleted file mode 100644
index b68337e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_124.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_125.png b/packages/SystemUI/res/drawable-xhdpi/remote_125.png
deleted file mode 100644
index 81fa3a7..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_125.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_126.png b/packages/SystemUI/res/drawable-xhdpi/remote_126.png
deleted file mode 100644
index 2339d74..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_126.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_127.png b/packages/SystemUI/res/drawable-xhdpi/remote_127.png
deleted file mode 100644
index 90d1e0b..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_127.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_128.png b/packages/SystemUI/res/drawable-xhdpi/remote_128.png
deleted file mode 100644
index 6de4eb8..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_128.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_129.png b/packages/SystemUI/res/drawable-xhdpi/remote_129.png
deleted file mode 100644
index 9086074..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_129.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_13.png b/packages/SystemUI/res/drawable-xhdpi/remote_13.png
deleted file mode 100644
index a1e212d..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_13.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_130.png b/packages/SystemUI/res/drawable-xhdpi/remote_130.png
deleted file mode 100644
index 2bc9698..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_130.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_131.png b/packages/SystemUI/res/drawable-xhdpi/remote_131.png
deleted file mode 100644
index d18d2c6..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_131.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_132.png b/packages/SystemUI/res/drawable-xhdpi/remote_132.png
deleted file mode 100644
index 10a00cd..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_132.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_133.png b/packages/SystemUI/res/drawable-xhdpi/remote_133.png
deleted file mode 100644
index 6f495b4..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_133.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_134.png b/packages/SystemUI/res/drawable-xhdpi/remote_134.png
deleted file mode 100644
index 703f2c6..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_134.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_135.png b/packages/SystemUI/res/drawable-xhdpi/remote_135.png
deleted file mode 100644
index f4105b0..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_135.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_136.png b/packages/SystemUI/res/drawable-xhdpi/remote_136.png
deleted file mode 100644
index 0c3a5bc..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_136.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_137.png b/packages/SystemUI/res/drawable-xhdpi/remote_137.png
deleted file mode 100644
index cbebc05..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_137.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_138.png b/packages/SystemUI/res/drawable-xhdpi/remote_138.png
deleted file mode 100644
index 6dfefb0..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_138.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_139.png b/packages/SystemUI/res/drawable-xhdpi/remote_139.png
deleted file mode 100644
index 1acfdd6..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_139.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_14.png b/packages/SystemUI/res/drawable-xhdpi/remote_14.png
deleted file mode 100644
index a503cdf..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_14.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_140.png b/packages/SystemUI/res/drawable-xhdpi/remote_140.png
deleted file mode 100644
index 70d27c1..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_140.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_141.png b/packages/SystemUI/res/drawable-xhdpi/remote_141.png
deleted file mode 100644
index d523a0c..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_141.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_142.png b/packages/SystemUI/res/drawable-xhdpi/remote_142.png
deleted file mode 100644
index ed6a65d..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_142.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_143.png b/packages/SystemUI/res/drawable-xhdpi/remote_143.png
deleted file mode 100644
index 9b048e6..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_143.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_144.png b/packages/SystemUI/res/drawable-xhdpi/remote_144.png
deleted file mode 100644
index 9e2337d..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_144.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_145.png b/packages/SystemUI/res/drawable-xhdpi/remote_145.png
deleted file mode 100644
index 3f30629..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_145.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_146.png b/packages/SystemUI/res/drawable-xhdpi/remote_146.png
deleted file mode 100644
index 1288039..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_146.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_147.png b/packages/SystemUI/res/drawable-xhdpi/remote_147.png
deleted file mode 100644
index d060539..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_147.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_148.png b/packages/SystemUI/res/drawable-xhdpi/remote_148.png
deleted file mode 100644
index 5be839d..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_148.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_149.png b/packages/SystemUI/res/drawable-xhdpi/remote_149.png
deleted file mode 100644
index 39d31ba..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_149.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_15.png b/packages/SystemUI/res/drawable-xhdpi/remote_15.png
deleted file mode 100644
index 5695595..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_15.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_150.png b/packages/SystemUI/res/drawable-xhdpi/remote_150.png
deleted file mode 100644
index ec2667c..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_150.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_151.png b/packages/SystemUI/res/drawable-xhdpi/remote_151.png
deleted file mode 100644
index ec2667c..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_151.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_152.png b/packages/SystemUI/res/drawable-xhdpi/remote_152.png
deleted file mode 100644
index a5d58c8..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_152.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_153.png b/packages/SystemUI/res/drawable-xhdpi/remote_153.png
deleted file mode 100644
index a5d58c8..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_153.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_154.png b/packages/SystemUI/res/drawable-xhdpi/remote_154.png
deleted file mode 100644
index a5d58c8..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_154.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_155.png b/packages/SystemUI/res/drawable-xhdpi/remote_155.png
deleted file mode 100644
index 793d411..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_155.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_156.png b/packages/SystemUI/res/drawable-xhdpi/remote_156.png
deleted file mode 100644
index 793d411..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_156.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_157.png b/packages/SystemUI/res/drawable-xhdpi/remote_157.png
deleted file mode 100644
index f9d6cdc..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_157.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_158.png b/packages/SystemUI/res/drawable-xhdpi/remote_158.png
deleted file mode 100644
index da8d5d7..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_158.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_159.png b/packages/SystemUI/res/drawable-xhdpi/remote_159.png
deleted file mode 100644
index 1e0b097..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_159.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_16.png b/packages/SystemUI/res/drawable-xhdpi/remote_16.png
deleted file mode 100644
index 4ae106c..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_16.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_160.png b/packages/SystemUI/res/drawable-xhdpi/remote_160.png
deleted file mode 100644
index 8aa68ad..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_160.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_161.png b/packages/SystemUI/res/drawable-xhdpi/remote_161.png
deleted file mode 100644
index e49fdd9..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_161.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_162.png b/packages/SystemUI/res/drawable-xhdpi/remote_162.png
deleted file mode 100644
index 69257a0..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_162.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_163.png b/packages/SystemUI/res/drawable-xhdpi/remote_163.png
deleted file mode 100644
index 8f0c3d5..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_163.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_164.png b/packages/SystemUI/res/drawable-xhdpi/remote_164.png
deleted file mode 100644
index a4c3229..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_164.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_165.png b/packages/SystemUI/res/drawable-xhdpi/remote_165.png
deleted file mode 100644
index 46fae23..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_165.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_166.png b/packages/SystemUI/res/drawable-xhdpi/remote_166.png
deleted file mode 100644
index 40d5a9b..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_166.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_167.png b/packages/SystemUI/res/drawable-xhdpi/remote_167.png
deleted file mode 100644
index 6bd0380..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_167.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_168.png b/packages/SystemUI/res/drawable-xhdpi/remote_168.png
deleted file mode 100644
index 03904bf..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_168.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_169.png b/packages/SystemUI/res/drawable-xhdpi/remote_169.png
deleted file mode 100644
index 564a161..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_169.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_17.png b/packages/SystemUI/res/drawable-xhdpi/remote_17.png
deleted file mode 100644
index 0703e1a..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_17.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_170.png b/packages/SystemUI/res/drawable-xhdpi/remote_170.png
deleted file mode 100644
index 0411f94..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_170.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_171.png b/packages/SystemUI/res/drawable-xhdpi/remote_171.png
deleted file mode 100644
index ec141e9..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_171.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_172.png b/packages/SystemUI/res/drawable-xhdpi/remote_172.png
deleted file mode 100644
index cb9ecaf..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_172.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_173.png b/packages/SystemUI/res/drawable-xhdpi/remote_173.png
deleted file mode 100644
index 484ee51..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_173.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_174.png b/packages/SystemUI/res/drawable-xhdpi/remote_174.png
deleted file mode 100644
index 85a3135..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_174.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_175.png b/packages/SystemUI/res/drawable-xhdpi/remote_175.png
deleted file mode 100644
index edd6507..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_175.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_176.png b/packages/SystemUI/res/drawable-xhdpi/remote_176.png
deleted file mode 100644
index d95a68b..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_176.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_177.png b/packages/SystemUI/res/drawable-xhdpi/remote_177.png
deleted file mode 100644
index 305641f..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_177.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_178.png b/packages/SystemUI/res/drawable-xhdpi/remote_178.png
deleted file mode 100644
index 59de0e5..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_178.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_179.png b/packages/SystemUI/res/drawable-xhdpi/remote_179.png
deleted file mode 100644
index 414e548..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_179.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_18.png b/packages/SystemUI/res/drawable-xhdpi/remote_18.png
deleted file mode 100644
index 74df1e4..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_18.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_180.png b/packages/SystemUI/res/drawable-xhdpi/remote_180.png
deleted file mode 100644
index b5d925c..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_180.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_181.png b/packages/SystemUI/res/drawable-xhdpi/remote_181.png
deleted file mode 100644
index e8a7127..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_181.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_182.png b/packages/SystemUI/res/drawable-xhdpi/remote_182.png
deleted file mode 100644
index 29c7037..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_182.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_183.png b/packages/SystemUI/res/drawable-xhdpi/remote_183.png
deleted file mode 100644
index 9491d94..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_183.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_184.png b/packages/SystemUI/res/drawable-xhdpi/remote_184.png
deleted file mode 100644
index 4aa0e32..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_184.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_185.png b/packages/SystemUI/res/drawable-xhdpi/remote_185.png
deleted file mode 100644
index 2a0dde8..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_185.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_186.png b/packages/SystemUI/res/drawable-xhdpi/remote_186.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_186.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_187.png b/packages/SystemUI/res/drawable-xhdpi/remote_187.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_187.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_188.png b/packages/SystemUI/res/drawable-xhdpi/remote_188.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_188.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_189.png b/packages/SystemUI/res/drawable-xhdpi/remote_189.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_189.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_19.png b/packages/SystemUI/res/drawable-xhdpi/remote_19.png
deleted file mode 100644
index 222ea31..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_19.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_190.png b/packages/SystemUI/res/drawable-xhdpi/remote_190.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_190.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_191.png b/packages/SystemUI/res/drawable-xhdpi/remote_191.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_191.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_192.png b/packages/SystemUI/res/drawable-xhdpi/remote_192.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_192.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_193.png b/packages/SystemUI/res/drawable-xhdpi/remote_193.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_193.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_194.png b/packages/SystemUI/res/drawable-xhdpi/remote_194.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_194.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_195.png b/packages/SystemUI/res/drawable-xhdpi/remote_195.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_195.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_196.png b/packages/SystemUI/res/drawable-xhdpi/remote_196.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_196.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_197.png b/packages/SystemUI/res/drawable-xhdpi/remote_197.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_197.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_198.png b/packages/SystemUI/res/drawable-xhdpi/remote_198.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_198.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_199.png b/packages/SystemUI/res/drawable-xhdpi/remote_199.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_199.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_2.png b/packages/SystemUI/res/drawable-xhdpi/remote_2.png
deleted file mode 100644
index fb3f7ef..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_2.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_20.png b/packages/SystemUI/res/drawable-xhdpi/remote_20.png
deleted file mode 100644
index 646587c..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_20.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_200.png b/packages/SystemUI/res/drawable-xhdpi/remote_200.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_200.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_201.png b/packages/SystemUI/res/drawable-xhdpi/remote_201.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_201.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_202.png b/packages/SystemUI/res/drawable-xhdpi/remote_202.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_202.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_203.png b/packages/SystemUI/res/drawable-xhdpi/remote_203.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_203.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_204.png b/packages/SystemUI/res/drawable-xhdpi/remote_204.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_204.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_205.png b/packages/SystemUI/res/drawable-xhdpi/remote_205.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_205.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_206.png b/packages/SystemUI/res/drawable-xhdpi/remote_206.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_206.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_207.png b/packages/SystemUI/res/drawable-xhdpi/remote_207.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_207.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_208.png b/packages/SystemUI/res/drawable-xhdpi/remote_208.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_208.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_209.png b/packages/SystemUI/res/drawable-xhdpi/remote_209.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_209.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_21.png b/packages/SystemUI/res/drawable-xhdpi/remote_21.png
deleted file mode 100644
index 5858ba92..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_21.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_210.png b/packages/SystemUI/res/drawable-xhdpi/remote_210.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_210.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_211.png b/packages/SystemUI/res/drawable-xhdpi/remote_211.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_211.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_212.png b/packages/SystemUI/res/drawable-xhdpi/remote_212.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_212.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_213.png b/packages/SystemUI/res/drawable-xhdpi/remote_213.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_213.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_214.png b/packages/SystemUI/res/drawable-xhdpi/remote_214.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_214.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_215.png b/packages/SystemUI/res/drawable-xhdpi/remote_215.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_215.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_216.png b/packages/SystemUI/res/drawable-xhdpi/remote_216.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_216.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_217.png b/packages/SystemUI/res/drawable-xhdpi/remote_217.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_217.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_218.png b/packages/SystemUI/res/drawable-xhdpi/remote_218.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_218.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_219.png b/packages/SystemUI/res/drawable-xhdpi/remote_219.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_219.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_22.png b/packages/SystemUI/res/drawable-xhdpi/remote_22.png
deleted file mode 100644
index 1974802..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_22.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_220.png b/packages/SystemUI/res/drawable-xhdpi/remote_220.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_220.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_221.png b/packages/SystemUI/res/drawable-xhdpi/remote_221.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_221.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_222.png b/packages/SystemUI/res/drawable-xhdpi/remote_222.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_222.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_223.png b/packages/SystemUI/res/drawable-xhdpi/remote_223.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_223.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_224.png b/packages/SystemUI/res/drawable-xhdpi/remote_224.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_224.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_225.png b/packages/SystemUI/res/drawable-xhdpi/remote_225.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_225.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_226.png b/packages/SystemUI/res/drawable-xhdpi/remote_226.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_226.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_227.png b/packages/SystemUI/res/drawable-xhdpi/remote_227.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_227.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_228.png b/packages/SystemUI/res/drawable-xhdpi/remote_228.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_228.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_229.png b/packages/SystemUI/res/drawable-xhdpi/remote_229.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_229.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_23.png b/packages/SystemUI/res/drawable-xhdpi/remote_23.png
deleted file mode 100644
index 96b7c35..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_23.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_230.png b/packages/SystemUI/res/drawable-xhdpi/remote_230.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_230.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_231.png b/packages/SystemUI/res/drawable-xhdpi/remote_231.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_231.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_232.png b/packages/SystemUI/res/drawable-xhdpi/remote_232.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_232.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_233.png b/packages/SystemUI/res/drawable-xhdpi/remote_233.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_233.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_234.png b/packages/SystemUI/res/drawable-xhdpi/remote_234.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_234.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_235.png b/packages/SystemUI/res/drawable-xhdpi/remote_235.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_235.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_236.png b/packages/SystemUI/res/drawable-xhdpi/remote_236.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_236.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_237.png b/packages/SystemUI/res/drawable-xhdpi/remote_237.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_237.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_238.png b/packages/SystemUI/res/drawable-xhdpi/remote_238.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_238.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_239.png b/packages/SystemUI/res/drawable-xhdpi/remote_239.png
deleted file mode 100644
index 5bda52e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_239.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_24.png b/packages/SystemUI/res/drawable-xhdpi/remote_24.png
deleted file mode 100644
index 0437200..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_24.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_25.png b/packages/SystemUI/res/drawable-xhdpi/remote_25.png
deleted file mode 100644
index 60b0f15..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_25.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_26.png b/packages/SystemUI/res/drawable-xhdpi/remote_26.png
deleted file mode 100644
index c34ed97..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_26.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_27.png b/packages/SystemUI/res/drawable-xhdpi/remote_27.png
deleted file mode 100644
index 1a83664..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_27.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_28.png b/packages/SystemUI/res/drawable-xhdpi/remote_28.png
deleted file mode 100644
index a3685ad..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_28.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_29.png b/packages/SystemUI/res/drawable-xhdpi/remote_29.png
deleted file mode 100644
index f7135eb..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_29.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_3.png b/packages/SystemUI/res/drawable-xhdpi/remote_3.png
deleted file mode 100644
index 937da65..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_3.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_30.png b/packages/SystemUI/res/drawable-xhdpi/remote_30.png
deleted file mode 100644
index 718cf52..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_30.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_31.png b/packages/SystemUI/res/drawable-xhdpi/remote_31.png
deleted file mode 100644
index c0b55df..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_31.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_32.png b/packages/SystemUI/res/drawable-xhdpi/remote_32.png
deleted file mode 100644
index 7a1ce9f..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_32.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_33.png b/packages/SystemUI/res/drawable-xhdpi/remote_33.png
deleted file mode 100644
index 5428bcf..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_33.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_34.png b/packages/SystemUI/res/drawable-xhdpi/remote_34.png
deleted file mode 100644
index 264efe8..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_34.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_35.png b/packages/SystemUI/res/drawable-xhdpi/remote_35.png
deleted file mode 100644
index a5c450f..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_35.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_36.png b/packages/SystemUI/res/drawable-xhdpi/remote_36.png
deleted file mode 100644
index 3e469e4..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_36.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_37.png b/packages/SystemUI/res/drawable-xhdpi/remote_37.png
deleted file mode 100644
index 124ebe4..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_37.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_38.png b/packages/SystemUI/res/drawable-xhdpi/remote_38.png
deleted file mode 100644
index b2b4844..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_38.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_39.png b/packages/SystemUI/res/drawable-xhdpi/remote_39.png
deleted file mode 100644
index a6d1733..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_39.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_4.png b/packages/SystemUI/res/drawable-xhdpi/remote_4.png
deleted file mode 100644
index c282f40..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_4.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_40.png b/packages/SystemUI/res/drawable-xhdpi/remote_40.png
deleted file mode 100644
index 4cd615c..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_40.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_41.png b/packages/SystemUI/res/drawable-xhdpi/remote_41.png
deleted file mode 100644
index c746ae0..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_41.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_42.png b/packages/SystemUI/res/drawable-xhdpi/remote_42.png
deleted file mode 100644
index a93f198..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_42.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_43.png b/packages/SystemUI/res/drawable-xhdpi/remote_43.png
deleted file mode 100644
index 966e563..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_43.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_44.png b/packages/SystemUI/res/drawable-xhdpi/remote_44.png
deleted file mode 100644
index beb7031..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_44.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_45.png b/packages/SystemUI/res/drawable-xhdpi/remote_45.png
deleted file mode 100644
index 718e167..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_45.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_46.png b/packages/SystemUI/res/drawable-xhdpi/remote_46.png
deleted file mode 100644
index aa54ddb..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_46.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_47.png b/packages/SystemUI/res/drawable-xhdpi/remote_47.png
deleted file mode 100644
index 1d11270..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_47.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_48.png b/packages/SystemUI/res/drawable-xhdpi/remote_48.png
deleted file mode 100644
index ab31163..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_48.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_49.png b/packages/SystemUI/res/drawable-xhdpi/remote_49.png
deleted file mode 100644
index 219b7f6..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_49.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_5.png b/packages/SystemUI/res/drawable-xhdpi/remote_5.png
deleted file mode 100644
index 15f69e1..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_5.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_50.png b/packages/SystemUI/res/drawable-xhdpi/remote_50.png
deleted file mode 100644
index 8a9725f..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_50.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_51.png b/packages/SystemUI/res/drawable-xhdpi/remote_51.png
deleted file mode 100644
index bc839cf..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_51.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_52.png b/packages/SystemUI/res/drawable-xhdpi/remote_52.png
deleted file mode 100644
index 7bab6e5..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_52.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_53.png b/packages/SystemUI/res/drawable-xhdpi/remote_53.png
deleted file mode 100644
index 34ab855..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_53.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_54.png b/packages/SystemUI/res/drawable-xhdpi/remote_54.png
deleted file mode 100644
index 5bd9f59..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_54.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_55.png b/packages/SystemUI/res/drawable-xhdpi/remote_55.png
deleted file mode 100644
index 1ff17f4..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_55.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_56.png b/packages/SystemUI/res/drawable-xhdpi/remote_56.png
deleted file mode 100644
index d57e067..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_56.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_57.png b/packages/SystemUI/res/drawable-xhdpi/remote_57.png
deleted file mode 100644
index a1bdae1..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_57.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_58.png b/packages/SystemUI/res/drawable-xhdpi/remote_58.png
deleted file mode 100644
index c8bc6a4..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_58.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_59.png b/packages/SystemUI/res/drawable-xhdpi/remote_59.png
deleted file mode 100644
index 526a24e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_59.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_6.png b/packages/SystemUI/res/drawable-xhdpi/remote_6.png
deleted file mode 100644
index 2b6732f..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_6.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_60.png b/packages/SystemUI/res/drawable-xhdpi/remote_60.png
deleted file mode 100644
index 080619e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_60.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_61.png b/packages/SystemUI/res/drawable-xhdpi/remote_61.png
deleted file mode 100644
index 5932a8c..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_61.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_62.png b/packages/SystemUI/res/drawable-xhdpi/remote_62.png
deleted file mode 100644
index d1233dd..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_62.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_63.png b/packages/SystemUI/res/drawable-xhdpi/remote_63.png
deleted file mode 100644
index 4f230c7..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_63.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_64.png b/packages/SystemUI/res/drawable-xhdpi/remote_64.png
deleted file mode 100644
index 6b89fcb..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_64.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_65.png b/packages/SystemUI/res/drawable-xhdpi/remote_65.png
deleted file mode 100644
index 87597b1..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_65.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_66.png b/packages/SystemUI/res/drawable-xhdpi/remote_66.png
deleted file mode 100644
index 0ee8c1e..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_66.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_67.png b/packages/SystemUI/res/drawable-xhdpi/remote_67.png
deleted file mode 100644
index 9aca8fd..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_67.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_68.png b/packages/SystemUI/res/drawable-xhdpi/remote_68.png
deleted file mode 100644
index 5f263ae..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_68.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_69.png b/packages/SystemUI/res/drawable-xhdpi/remote_69.png
deleted file mode 100644
index 1a22b61..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_69.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_7.png b/packages/SystemUI/res/drawable-xhdpi/remote_7.png
deleted file mode 100644
index 9d9d699..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_7.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_70.png b/packages/SystemUI/res/drawable-xhdpi/remote_70.png
deleted file mode 100644
index 0372c50..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_70.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_71.png b/packages/SystemUI/res/drawable-xhdpi/remote_71.png
deleted file mode 100644
index 854e3e2..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_71.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_72.png b/packages/SystemUI/res/drawable-xhdpi/remote_72.png
deleted file mode 100644
index 6919624..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_72.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_73.png b/packages/SystemUI/res/drawable-xhdpi/remote_73.png
deleted file mode 100644
index d8e9ae1..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_73.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_74.png b/packages/SystemUI/res/drawable-xhdpi/remote_74.png
deleted file mode 100644
index 24e5b6a..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_74.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_75.png b/packages/SystemUI/res/drawable-xhdpi/remote_75.png
deleted file mode 100644
index 369a3a9..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_75.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_76.png b/packages/SystemUI/res/drawable-xhdpi/remote_76.png
deleted file mode 100644
index 96824c6..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_76.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_77.png b/packages/SystemUI/res/drawable-xhdpi/remote_77.png
deleted file mode 100644
index dd60cca..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_77.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_78.png b/packages/SystemUI/res/drawable-xhdpi/remote_78.png
deleted file mode 100644
index aa3460b..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_78.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_79.png b/packages/SystemUI/res/drawable-xhdpi/remote_79.png
deleted file mode 100644
index 9a60e3c..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_79.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_8.png b/packages/SystemUI/res/drawable-xhdpi/remote_8.png
deleted file mode 100644
index b73c7ef..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_8.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_80.png b/packages/SystemUI/res/drawable-xhdpi/remote_80.png
deleted file mode 100644
index cbf883c..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_80.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_81.png b/packages/SystemUI/res/drawable-xhdpi/remote_81.png
deleted file mode 100644
index 11a6add..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_81.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_82.png b/packages/SystemUI/res/drawable-xhdpi/remote_82.png
deleted file mode 100644
index e05105d..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_82.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_83.png b/packages/SystemUI/res/drawable-xhdpi/remote_83.png
deleted file mode 100644
index 57813aa..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_83.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_84.png b/packages/SystemUI/res/drawable-xhdpi/remote_84.png
deleted file mode 100644
index 0f6f0fe..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_84.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_85.png b/packages/SystemUI/res/drawable-xhdpi/remote_85.png
deleted file mode 100644
index ada83ec..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_85.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_86.png b/packages/SystemUI/res/drawable-xhdpi/remote_86.png
deleted file mode 100644
index 442dd51..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_86.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_87.png b/packages/SystemUI/res/drawable-xhdpi/remote_87.png
deleted file mode 100644
index bdb4962..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_87.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_88.png b/packages/SystemUI/res/drawable-xhdpi/remote_88.png
deleted file mode 100644
index b318002..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_88.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_89.png b/packages/SystemUI/res/drawable-xhdpi/remote_89.png
deleted file mode 100644
index c4ed874..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_89.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_9.png b/packages/SystemUI/res/drawable-xhdpi/remote_9.png
deleted file mode 100644
index ce5041f..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_9.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_90.png b/packages/SystemUI/res/drawable-xhdpi/remote_90.png
deleted file mode 100644
index 6a662f9..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_90.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_91.png b/packages/SystemUI/res/drawable-xhdpi/remote_91.png
deleted file mode 100644
index 21be887..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_91.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_92.png b/packages/SystemUI/res/drawable-xhdpi/remote_92.png
deleted file mode 100644
index 1bc5361..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_92.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_93.png b/packages/SystemUI/res/drawable-xhdpi/remote_93.png
deleted file mode 100644
index 76495ac..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_93.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_94.png b/packages/SystemUI/res/drawable-xhdpi/remote_94.png
deleted file mode 100644
index 081c84b..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_94.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_95.png b/packages/SystemUI/res/drawable-xhdpi/remote_95.png
deleted file mode 100644
index e9c27a8..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_95.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_96.png b/packages/SystemUI/res/drawable-xhdpi/remote_96.png
deleted file mode 100644
index 1369603..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_96.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_97.png b/packages/SystemUI/res/drawable-xhdpi/remote_97.png
deleted file mode 100644
index fbd1458..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_97.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_98.png b/packages/SystemUI/res/drawable-xhdpi/remote_98.png
deleted file mode 100644
index ccdd7a7..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_98.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/remote_99.png b/packages/SystemUI/res/drawable-xhdpi/remote_99.png
deleted file mode 100644
index f3cb4db..0000000
--- a/packages/SystemUI/res/drawable-xhdpi/remote_99.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_dnd.xml b/packages/SystemUI/res/drawable/ic_dnd.xml
index 17ecf21..e658e68 100644
--- a/packages/SystemUI/res/drawable/ic_dnd.xml
+++ b/packages/SystemUI/res/drawable/ic_dnd.xml
@@ -17,7 +17,8 @@
android:height="24dp"
android:viewportHeight="48.0"
android:viewportWidth="48.0"
- android:width="24dp" >
+ android:width="24dp"
+ android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_dnd_total_silence.xml b/packages/SystemUI/res/drawable/ic_dnd_total_silence.xml
index 4875974..0515b35 100644
--- a/packages/SystemUI/res/drawable/ic_dnd_total_silence.xml
+++ b/packages/SystemUI/res/drawable/ic_dnd_total_silence.xml
@@ -17,7 +17,8 @@
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0"
- android:width="24dp" >
+ android:width="24dp"
+ android:tint="?android:attr/colorControlNormal">
<path
android:fillColor="#FFFFFFFF"
diff --git a/packages/SystemUI/res/drawable/ic_info_outline.xml b/packages/SystemUI/res/drawable/ic_info_outline.xml
new file mode 100644
index 0000000..a4a3e9a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_info_outline.xml
@@ -0,0 +1,25 @@
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32dp"
+ android:height="32dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml b/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml
index 5aeceba..169cc71 100644
--- a/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml
+++ b/packages/SystemUI/res/drawable/ic_invert_colors_enable.xml
@@ -16,7 +16,6 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:name="root"
- android:alpha="0.3"
android:height="48dp"
android:width="48dp"
android:viewportHeight="48"
diff --git a/packages/SystemUI/res/drawable/ic_pause_white_24dp.xml b/packages/SystemUI/res/drawable/ic_pause_white.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_pause_white_24dp.xml
rename to packages/SystemUI/res/drawable/ic_pause_white.xml
diff --git a/packages/SystemUI/res/drawable/ic_play_arrow_white_24dp.xml b/packages/SystemUI/res/drawable/ic_play_arrow_white.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/ic_play_arrow_white_24dp.xml
rename to packages/SystemUI/res/drawable/ic_play_arrow_white.xml
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml b/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
index 236fdac..5e7cd974 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_in.xml
@@ -17,7 +17,8 @@
android:width="6.0dp"
android:height="32dp"
android:viewportWidth="6.0"
- android:viewportHeight="24.0">
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/textColorSecondary">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6.000000,15.700000l-3.000000,5.599999 -3.000000,-5.599999z"/>
diff --git a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml b/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
index c510972..0dca1db 100644
--- a/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
+++ b/packages/SystemUI/res/drawable/ic_qs_signal_out.xml
@@ -17,7 +17,8 @@
android:width="6.0dp"
android:height="32dp"
android:viewportWidth="6.0"
- android:viewportHeight="24.0">
+ android:viewportHeight="24.0"
+ android:tint="?android:attr/textColorSecondary">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M0.000000,13.700000l3.000000,-5.700000 3.000000,5.700000z"/>
diff --git a/packages/SystemUI/res/drawable/ic_skip_next_white.xml b/packages/SystemUI/res/drawable/ic_skip_next_white.xml
new file mode 100644
index 0000000..040c7e6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_skip_next_white.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z" />
+ <path
+ android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_skip_previous_white.xml b/packages/SystemUI/res/drawable/ic_skip_previous_white.xml
new file mode 100644
index 0000000..b9b94b7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_skip_previous_white.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M6 6h2v12H6zm3.5 6l8.5 6V6z" />
+ <path
+ android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/segmented_buttons_background.xml b/packages/SystemUI/res/drawable/segmented_buttons_background.xml
index b243dc7c..755c917 100644
--- a/packages/SystemUI/res/drawable/segmented_buttons_background.xml
+++ b/packages/SystemUI/res/drawable/segmented_buttons_background.xml
@@ -17,6 +17,6 @@
<corners android:radius="@dimen/borderless_button_radius" />
- <solid android:color="@color/segmented_buttons_background" />
+ <solid android:color="?android:attr/colorPrimaryDark" />
-</shape>
\ No newline at end of file
+</shape>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_1x.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_1x.xml
index d7463a4..be9a7e2 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_1x.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_1x.xml
@@ -14,10 +14,10 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="8.5dp"
- android:height="17dp"
- android:viewportWidth="12.0"
- android:viewportHeight="24.0">
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="12.0"
+ android:viewportHeight="12.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M3.500000,11.000000L1.800000,11.000000L1.800000,4.400000L0.200000,5.100000L0.200000,3.700000l3.100000,-1.300000l0.200000,0.000000L3.500000,11.000000z"/>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_3g.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_3g.xml
index 6309b6d..fd7a658 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_3g.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_3g.xml
@@ -14,10 +14,10 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="9.208dp"
+ android:width="17dp"
android:height="17dp"
android:viewportWidth="13.0"
- android:viewportHeight="24.0">
+ android:viewportHeight="13.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M2.000000,6.000000l0.800000,0.000000c0.300000,0.000000 0.500000,-0.100000 0.700000,-0.300000s0.200000,-0.500000 0.200000,-0.900000c0.000000,-0.300000 -0.100000,-0.600000 -0.200000,-0.800000S3.200000,3.700000 2.900000,3.700000C2.700000,3.700000 2.500000,3.800000 2.300000,4.000000S2.100000,4.400000 2.100000,4.700000L0.500000,4.700000C0.500000,4.000000 0.700000,3.400000 1.100000,3.000000s1.000000,-0.600000 1.700000,-0.600000c0.800000,0.000000 1.400000,0.200000 1.900000,0.600000s0.700000,1.000000 0.700000,1.800000c0.000000,0.400000 -0.100000,0.700000 -0.300000,1.100000S4.600000,6.500000 4.300000,6.600000C4.700000,6.800000 5.000000,7.100000 5.200000,7.400000s0.300000,0.700000 0.300000,1.200000c0.000000,0.800000 -0.200000,1.400000 -0.700000,1.800000s-1.100000,0.700000 -1.900000,0.700000c-0.700000,0.000000 -1.300000,-0.200000 -1.800000,-0.600000s-0.700000,-1.000000 -0.700000,-1.800000L2.000000,8.700000C2.000000,9.000000 2.100000,9.300000 2.300000,9.500000s0.400000,0.300000 0.600000,0.300000c0.300000,0.000000 0.500000,-0.100000 0.700000,-0.300000S3.900000,9.000000 3.900000,8.600000c0.000000,-0.500000 -0.100000,-0.800000 -0.300000,-1.000000S3.200000,7.300000 2.800000,7.300000L2.000000,7.300000L2.000000,6.000000z"/>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g.xml
index 4067ae5..02c4ab6 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g.xml
@@ -14,10 +14,10 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="8.5dp"
+ android:width="17dp"
android:height="17dp"
android:viewportWidth="12.0"
- android:viewportHeight="24.0">
+ android:viewportHeight="12.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M4.600000,7.800000l0.700000,0.000000l0.000000,1.300000L4.600000,9.100000L4.600000,11.000000L3.000000,11.000000L3.000000,9.200000L0.100000,9.200000L0.000000,8.100000L3.000000,2.500000l1.700000,0.000000L4.700000,7.800000zM1.600000,7.800000L3.000000,7.800000l0.000000,-3.000000L2.900000,5.000000L1.600000,7.800000z"/>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml
index 3cdd3e1..daf4061 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_4g_plus.xml
@@ -14,10 +14,10 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="17.0dp"
+ android:width="25.5dp"
android:height="17.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
+ android:viewportWidth="18.0"
+ android:viewportHeight="12.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M4.6,7.8l0.7,0.0l0.0,1.3L4.6,9.1L4.6,11.0L3.0,11.0L3.0,9.2L0.1,9.2L0.0,8.2l3.0,-5.7l1.7,0.0L4.6,7.8L4.6,7.8zM1.7,7.8L3.0,7.8l0.0,-3.0L2.9,5.0L1.7,7.8z"/>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_e.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_e.xml
index acaa9b1..cd0cc65 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_e.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_e.xml
@@ -14,10 +14,10 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="3.541dp"
+ android:width="7.083dp"
android:height="17dp"
android:viewportWidth="5.0"
- android:viewportHeight="24.0">
+ android:viewportHeight="12.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M4.400000,7.300000L1.700000,7.300000l0.000000,2.400000l3.300000,0.000000L5.000000,11.000000L0.000000,11.000000L0.000000,2.500000l4.900000,0.000000l0.000000,1.300000L1.700000,3.800000l0.000000,2.100000l2.800000,0.000000L4.500000,7.300000z"/>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_g.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_g.xml
index 7985237..92ed49c 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_g.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_g.xml
@@ -14,10 +14,10 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="4.958dp"
+ android:width="9.154dp"
android:height="17dp"
android:viewportWidth="7.0"
- android:viewportHeight="24.0">
+ android:viewportHeight="13">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6.500000,9.900000c-0.200000,0.400000 -0.600000,0.700000 -1.000000,0.900000s-1.000000,0.400000 -1.800000,0.400000c-0.900000,0.000000 -1.700000,-0.300000 -2.200000,-0.800000S0.700000,9.000000 0.700000,7.900000L0.700000,5.600000c0.000000,-1.100000 0.300000,-1.900000 0.800000,-2.400000s1.200000,-0.800000 2.100000,-0.800000c1.000000,0.000000 1.700000,0.200000 2.100000,0.700000s0.700000,1.200000 0.700000,2.100000L4.700000,5.200000c0.000000,-0.500000 -0.100000,-0.900000 -0.200000,-1.100000S4.000000,3.700000 3.600000,3.700000c-0.400000,0.000000 -0.700000,0.200000 -0.900000,0.500000S2.300000,5.000000 2.300000,5.600000l0.000000,2.300000c0.000000,0.700000 0.100000,1.100000 0.300000,1.400000s0.600000,0.500000 1.000000,0.500000c0.300000,0.000000 0.600000,0.000000 0.700000,-0.100000s0.300000,-0.200000 0.400000,-0.300000L4.700000,7.800000L3.500000,7.800000L3.500000,6.600000l2.900000,0.000000L6.400000,9.900000z"/>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_h.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_h.xml
index fda8761..ca61b6f 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_h.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_h.xml
@@ -14,10 +14,10 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="4.25dp"
+ android:width="9.5dp"
android:height="17dp"
android:viewportWidth="6.0"
- android:viewportHeight="24.0">
+ android:viewportHeight="12.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6.000000,11.000000L4.400000,11.000000L4.400000,7.500000L1.700000,7.500000L1.700000,11.000000L0.000000,11.000000L0.000000,2.500000l1.700000,0.000000l0.000000,3.700000l2.700000,0.000000L4.400000,2.500000L6.000000,2.500000L6.000000,11.000000z"/>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte.xml
index c08ff20..add96b4 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte.xml
@@ -14,10 +14,10 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="9.208dp"
+ android:width="18.417dp"
android:height="17dp"
- android:viewportWidth="13.0"
- android:viewportHeight="24.0">
+ android:viewportWidth="13"
+ android:viewportHeight="12.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M2.000000,9.700000l2.000000,0.000000L4.000000,11.000000L0.300000,11.000000L0.300000,2.500000L2.000000,2.500000L2.000000,9.700000z"/>
diff --git a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml
index db18fad..8811d2f 100644
--- a/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_data_fully_connected_lte_plus.xml
@@ -14,10 +14,10 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="17.0dp"
+ android:width="25.0dp"
android:height="17.0dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
+ android:viewportWidth="18.0"
+ android:viewportHeight="12.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M2.0,9.7l2.0,0.0L4.0,11.0L0.4,11.0L0.4,2.5L2.0,2.5L2.0,9.7z"/>
diff --git a/packages/SystemUI/res/drawable/stat_sys_roaming.xml b/packages/SystemUI/res/drawable/stat_sys_roaming.xml
index 4baa472..363e231 100644
--- a/packages/SystemUI/res/drawable/stat_sys_roaming.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_roaming.xml
@@ -14,10 +14,10 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="8.5dp"
+ android:width="4.25dp"
android:height="17dp"
android:viewportWidth="6.0"
- android:viewportHeight="12.0">
+ android:viewportHeight="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M2.800000,7.900000l-1.000000,0.000000L1.800000,11.000000L0.200000,11.000000L0.200000,2.500000l2.700000,0.000000c0.900000,0.000000 1.500000,0.200000 2.000000,0.700000s0.700000,1.100000 0.700000,1.900000c0.000000,0.600000 -0.100000,1.100000 -0.300000,1.500000S4.800000,7.200000 4.400000,7.400000l1.500000,3.500000L5.900000,11.000000L4.100000,11.000000L2.800000,7.900000zM1.800000,6.500000l1.100000,0.000000c0.400000,0.000000 0.600000,-0.100000 0.800000,-0.400000S4.000000,5.600000 4.000000,5.200000c0.000000,-0.400000 -0.100000,-0.800000 -0.300000,-1.000000S3.300000,3.800000 2.900000,3.800000L1.800000,3.800000L1.800000,6.500000z"/>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_disconnected.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_disconnected.xml
new file mode 100644
index 0000000..f4e8af4
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_disconnected.xml
@@ -0,0 +1,42 @@
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="18.41dp"
+ android:height="17dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="?attr/backgroundColor"
+ android:pathData="M21.0,8.5
+ c0.85,0.0 1.6,0.23 2.3,0.62l2.24,-2.79
+ C25.1,5.96 20.26,2.0 13.0,2.0
+ S0.9,5.9 0.42,6.32
+ l12.57,15.6 4.21,-5.17
+ c-0.76,-0.87 -1.22,-2.0 -1.22,-3.25
+ c0.0,-2.76 2.24,-5.0 5.0,-5.0z"
+ android:fillAlpha=".3"/>
+ <path
+ android:fillColor="?attr/backgroundColor"
+ android:pathData="M21.0,10.0
+ c-1.93,0.0 -3.5,1.57 -3.5,3.5l1.75,0.0
+ c0.0,-0.9 0.78,-1.75 1.75,-1.75s1.7,0.78 1.75,1.75
+ c0.0,0.48 -0.2,0.92 -0.51,1.24l-1.09,1.1
+ c-0.6,0.63 -1.02,1.51 -1.02,2.47l0.0,0.44l1.75,0.0
+ c0.0,-1.3 0.39,-1.84 1.03,-2.47l0.78,-0.8
+ c0.5,-0.5 0.82,-1.2 0.82,-1.97
+ C24.5,11.57 22.93,10.0 21.0,10.0z
+ m-0.95,11.95l1.9,0.0l0.0,-1.9l-1.9,0.0l0.0,1.9z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_null.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_null.xml
index 5169de4..c1856fa 100644
--- a/packages/SystemUI/res/drawable/stat_sys_wifi_signal_null.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_signal_null.xml
@@ -20,5 +20,8 @@
android:viewportHeight="24.0">
<path
android:fillColor="?attr/backgroundColor"
- android:pathData="M13.000000,2.000000C7.700000,2.000000 3.700000,3.900000 0.400000,6.400000L13.000000,22.000000L25.600000,6.500000C22.299999,4.000000 18.299999,2.000000 13.000000,2.000000zM13.000000,18.600000L3.300000,7.000000l0.000000,0.000000l0.000000,0.000000C6.000000,5.300000 8.700000,4.000000 13.000000,4.000000s7.000000,1.400000 9.700000,3.000000l0.000000,0.000000l0.000000,0.000000L13.000000,18.600000z"/>
+ android:pathData="M17.500000,16.500000L5.800000,3.400000c0.000000,0.000000 0.000000,0.000000 0.000000,0.000000l-2.700000,-3.000000L1.600000,1.800000l2.200000,2.500000c-2.000000,1.000000 -3.200000,2.000000 -3.400000,2.200000L13.000000,22.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l0.000000,0.000000l3.200000,-3.900000l2.400000,2.700000l1.500000,-1.400000L17.500000,16.500000L17.500000,16.500000z"/>
+ <path
+ android:fillColor="?attr/backgroundColor"
+ android:pathData="M25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000c-1.900000,0.000000 -3.600000,0.300000 -5.200000,0.700000L18.700001,15.000000L25.600000,6.500000z"/>
</vector>
diff --git a/packages/SystemUI/res/drawable/tv_pip_onboarding_remote.xml b/packages/SystemUI/res/drawable/tv_pip_onboarding_remote.xml
deleted file mode 100644
index d46108a..0000000
--- a/packages/SystemUI/res/drawable/tv_pip_onboarding_remote.xml
+++ /dev/null
@@ -1,259 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
- android:oneshot="false">
-
- <item android:drawable="@drawable/remote_0" android:duration="13" />
- <item android:drawable="@drawable/remote_1" android:duration="13" />
- <item android:drawable="@drawable/remote_2" android:duration="13" />
- <item android:drawable="@drawable/remote_3" android:duration="13" />
- <item android:drawable="@drawable/remote_4" android:duration="13" />
- <item android:drawable="@drawable/remote_5" android:duration="13" />
- <item android:drawable="@drawable/remote_6" android:duration="13" />
- <item android:drawable="@drawable/remote_7" android:duration="13" />
- <item android:drawable="@drawable/remote_8" android:duration="13" />
- <item android:drawable="@drawable/remote_9" android:duration="13" />
- <item android:drawable="@drawable/remote_10" android:duration="13" />
- <item android:drawable="@drawable/remote_11" android:duration="13" />
- <item android:drawable="@drawable/remote_12" android:duration="13" />
- <item android:drawable="@drawable/remote_13" android:duration="13" />
- <item android:drawable="@drawable/remote_14" android:duration="13" />
- <item android:drawable="@drawable/remote_15" android:duration="13" />
- <item android:drawable="@drawable/remote_16" android:duration="13" />
- <item android:drawable="@drawable/remote_17" android:duration="13" />
- <item android:drawable="@drawable/remote_18" android:duration="13" />
- <item android:drawable="@drawable/remote_19" android:duration="13" />
- <item android:drawable="@drawable/remote_20" android:duration="13" />
- <item android:drawable="@drawable/remote_21" android:duration="13" />
- <item android:drawable="@drawable/remote_22" android:duration="13" />
- <item android:drawable="@drawable/remote_23" android:duration="13" />
- <item android:drawable="@drawable/remote_24" android:duration="13" />
- <item android:drawable="@drawable/remote_25" android:duration="13" />
- <item android:drawable="@drawable/remote_26" android:duration="13" />
- <item android:drawable="@drawable/remote_27" android:duration="13" />
- <item android:drawable="@drawable/remote_28" android:duration="13" />
- <item android:drawable="@drawable/remote_29" android:duration="13" />
- <item android:drawable="@drawable/remote_30" android:duration="13" />
- <item android:drawable="@drawable/remote_31" android:duration="13" />
- <item android:drawable="@drawable/remote_32" android:duration="13" />
- <item android:drawable="@drawable/remote_33" android:duration="13" />
- <item android:drawable="@drawable/remote_34" android:duration="13" />
- <item android:drawable="@drawable/remote_35" android:duration="13" />
- <item android:drawable="@drawable/remote_36" android:duration="13" />
- <item android:drawable="@drawable/remote_37" android:duration="13" />
- <item android:drawable="@drawable/remote_38" android:duration="13" />
- <item android:drawable="@drawable/remote_39" android:duration="13" />
- <item android:drawable="@drawable/remote_40" android:duration="13" />
- <item android:drawable="@drawable/remote_41" android:duration="13" />
- <item android:drawable="@drawable/remote_42" android:duration="13" />
- <item android:drawable="@drawable/remote_43" android:duration="13" />
- <item android:drawable="@drawable/remote_44" android:duration="13" />
- <item android:drawable="@drawable/remote_45" android:duration="13" />
- <item android:drawable="@drawable/remote_46" android:duration="13" />
- <item android:drawable="@drawable/remote_47" android:duration="13" />
- <item android:drawable="@drawable/remote_48" android:duration="13" />
- <item android:drawable="@drawable/remote_49" android:duration="13" />
- <item android:drawable="@drawable/remote_50" android:duration="13" />
- <item android:drawable="@drawable/remote_51" android:duration="13" />
- <item android:drawable="@drawable/remote_52" android:duration="13" />
- <item android:drawable="@drawable/remote_53" android:duration="13" />
- <item android:drawable="@drawable/remote_54" android:duration="13" />
- <item android:drawable="@drawable/remote_55" android:duration="13" />
- <item android:drawable="@drawable/remote_56" android:duration="13" />
- <item android:drawable="@drawable/remote_57" android:duration="13" />
- <item android:drawable="@drawable/remote_58" android:duration="13" />
- <item android:drawable="@drawable/remote_59" android:duration="13" />
- <item android:drawable="@drawable/remote_60" android:duration="13" />
- <item android:drawable="@drawable/remote_61" android:duration="13" />
- <item android:drawable="@drawable/remote_62" android:duration="13" />
- <item android:drawable="@drawable/remote_63" android:duration="13" />
- <item android:drawable="@drawable/remote_64" android:duration="13" />
- <item android:drawable="@drawable/remote_65" android:duration="13" />
- <item android:drawable="@drawable/remote_66" android:duration="13" />
- <item android:drawable="@drawable/remote_67" android:duration="13" />
- <item android:drawable="@drawable/remote_68" android:duration="13" />
- <item android:drawable="@drawable/remote_69" android:duration="13" />
- <item android:drawable="@drawable/remote_70" android:duration="13" />
- <item android:drawable="@drawable/remote_71" android:duration="13" />
- <item android:drawable="@drawable/remote_72" android:duration="13" />
- <item android:drawable="@drawable/remote_73" android:duration="13" />
- <item android:drawable="@drawable/remote_74" android:duration="13" />
- <item android:drawable="@drawable/remote_75" android:duration="13" />
- <item android:drawable="@drawable/remote_76" android:duration="13" />
- <item android:drawable="@drawable/remote_77" android:duration="13" />
- <item android:drawable="@drawable/remote_78" android:duration="13" />
- <item android:drawable="@drawable/remote_79" android:duration="13" />
- <item android:drawable="@drawable/remote_80" android:duration="13" />
- <item android:drawable="@drawable/remote_81" android:duration="13" />
- <item android:drawable="@drawable/remote_82" android:duration="13" />
- <item android:drawable="@drawable/remote_83" android:duration="13" />
- <item android:drawable="@drawable/remote_84" android:duration="13" />
- <item android:drawable="@drawable/remote_85" android:duration="13" />
- <item android:drawable="@drawable/remote_86" android:duration="13" />
- <item android:drawable="@drawable/remote_87" android:duration="13" />
- <item android:drawable="@drawable/remote_88" android:duration="13" />
- <item android:drawable="@drawable/remote_89" android:duration="13" />
- <item android:drawable="@drawable/remote_90" android:duration="13" />
- <item android:drawable="@drawable/remote_91" android:duration="13" />
- <item android:drawable="@drawable/remote_92" android:duration="13" />
- <item android:drawable="@drawable/remote_93" android:duration="13" />
- <item android:drawable="@drawable/remote_94" android:duration="13" />
- <item android:drawable="@drawable/remote_95" android:duration="13" />
- <item android:drawable="@drawable/remote_96" android:duration="13" />
- <item android:drawable="@drawable/remote_97" android:duration="13" />
- <item android:drawable="@drawable/remote_98" android:duration="13" />
- <item android:drawable="@drawable/remote_99" android:duration="13" />
- <item android:drawable="@drawable/remote_100" android:duration="13" />
- <item android:drawable="@drawable/remote_101" android:duration="13" />
- <item android:drawable="@drawable/remote_102" android:duration="13" />
- <item android:drawable="@drawable/remote_103" android:duration="13" />
- <item android:drawable="@drawable/remote_104" android:duration="13" />
- <item android:drawable="@drawable/remote_105" android:duration="13" />
- <item android:drawable="@drawable/remote_106" android:duration="13" />
- <item android:drawable="@drawable/remote_107" android:duration="13" />
- <item android:drawable="@drawable/remote_108" android:duration="13" />
- <item android:drawable="@drawable/remote_109" android:duration="13" />
- <item android:drawable="@drawable/remote_110" android:duration="13" />
- <item android:drawable="@drawable/remote_111" android:duration="13" />
- <item android:drawable="@drawable/remote_112" android:duration="13" />
- <item android:drawable="@drawable/remote_113" android:duration="13" />
- <item android:drawable="@drawable/remote_114" android:duration="13" />
- <item android:drawable="@drawable/remote_115" android:duration="13" />
- <item android:drawable="@drawable/remote_116" android:duration="13" />
- <item android:drawable="@drawable/remote_117" android:duration="13" />
- <item android:drawable="@drawable/remote_118" android:duration="13" />
- <item android:drawable="@drawable/remote_119" android:duration="13" />
- <item android:drawable="@drawable/remote_120" android:duration="13" />
- <item android:drawable="@drawable/remote_121" android:duration="13" />
- <item android:drawable="@drawable/remote_122" android:duration="13" />
- <item android:drawable="@drawable/remote_123" android:duration="13" />
- <item android:drawable="@drawable/remote_124" android:duration="13" />
- <item android:drawable="@drawable/remote_125" android:duration="13" />
- <item android:drawable="@drawable/remote_126" android:duration="13" />
- <item android:drawable="@drawable/remote_127" android:duration="13" />
- <item android:drawable="@drawable/remote_128" android:duration="13" />
- <item android:drawable="@drawable/remote_129" android:duration="13" />
- <item android:drawable="@drawable/remote_130" android:duration="13" />
- <item android:drawable="@drawable/remote_131" android:duration="13" />
- <item android:drawable="@drawable/remote_132" android:duration="13" />
- <item android:drawable="@drawable/remote_133" android:duration="13" />
- <item android:drawable="@drawable/remote_134" android:duration="13" />
- <item android:drawable="@drawable/remote_135" android:duration="13" />
- <item android:drawable="@drawable/remote_136" android:duration="13" />
- <item android:drawable="@drawable/remote_137" android:duration="13" />
- <item android:drawable="@drawable/remote_138" android:duration="13" />
- <item android:drawable="@drawable/remote_139" android:duration="13" />
- <item android:drawable="@drawable/remote_140" android:duration="13" />
- <item android:drawable="@drawable/remote_141" android:duration="13" />
- <item android:drawable="@drawable/remote_142" android:duration="13" />
- <item android:drawable="@drawable/remote_143" android:duration="13" />
- <item android:drawable="@drawable/remote_144" android:duration="13" />
- <item android:drawable="@drawable/remote_145" android:duration="13" />
- <item android:drawable="@drawable/remote_146" android:duration="13" />
- <item android:drawable="@drawable/remote_147" android:duration="13" />
- <item android:drawable="@drawable/remote_148" android:duration="13" />
- <item android:drawable="@drawable/remote_149" android:duration="13" />
- <item android:drawable="@drawable/remote_150" android:duration="13" />
- <item android:drawable="@drawable/remote_151" android:duration="13" />
- <item android:drawable="@drawable/remote_152" android:duration="13" />
- <item android:drawable="@drawable/remote_153" android:duration="13" />
- <item android:drawable="@drawable/remote_154" android:duration="13" />
- <item android:drawable="@drawable/remote_155" android:duration="13" />
- <item android:drawable="@drawable/remote_156" android:duration="13" />
- <item android:drawable="@drawable/remote_157" android:duration="13" />
- <item android:drawable="@drawable/remote_158" android:duration="13" />
- <item android:drawable="@drawable/remote_159" android:duration="13" />
- <item android:drawable="@drawable/remote_160" android:duration="13" />
- <item android:drawable="@drawable/remote_161" android:duration="13" />
- <item android:drawable="@drawable/remote_162" android:duration="13" />
- <item android:drawable="@drawable/remote_163" android:duration="13" />
- <item android:drawable="@drawable/remote_164" android:duration="13" />
- <item android:drawable="@drawable/remote_165" android:duration="13" />
- <item android:drawable="@drawable/remote_166" android:duration="13" />
- <item android:drawable="@drawable/remote_167" android:duration="13" />
- <item android:drawable="@drawable/remote_168" android:duration="13" />
- <item android:drawable="@drawable/remote_169" android:duration="13" />
- <item android:drawable="@drawable/remote_170" android:duration="13" />
- <item android:drawable="@drawable/remote_171" android:duration="13" />
- <item android:drawable="@drawable/remote_172" android:duration="13" />
- <item android:drawable="@drawable/remote_173" android:duration="13" />
- <item android:drawable="@drawable/remote_174" android:duration="13" />
- <item android:drawable="@drawable/remote_175" android:duration="13" />
- <item android:drawable="@drawable/remote_176" android:duration="13" />
- <item android:drawable="@drawable/remote_177" android:duration="13" />
- <item android:drawable="@drawable/remote_178" android:duration="13" />
- <item android:drawable="@drawable/remote_179" android:duration="13" />
- <item android:drawable="@drawable/remote_180" android:duration="13" />
- <item android:drawable="@drawable/remote_181" android:duration="13" />
- <item android:drawable="@drawable/remote_182" android:duration="13" />
- <item android:drawable="@drawable/remote_183" android:duration="13" />
- <item android:drawable="@drawable/remote_184" android:duration="13" />
- <item android:drawable="@drawable/remote_185" android:duration="13" />
- <item android:drawable="@drawable/remote_186" android:duration="13" />
- <item android:drawable="@drawable/remote_187" android:duration="13" />
- <item android:drawable="@drawable/remote_188" android:duration="13" />
- <item android:drawable="@drawable/remote_189" android:duration="13" />
- <item android:drawable="@drawable/remote_190" android:duration="13" />
- <item android:drawable="@drawable/remote_191" android:duration="13" />
- <item android:drawable="@drawable/remote_192" android:duration="13" />
- <item android:drawable="@drawable/remote_193" android:duration="13" />
- <item android:drawable="@drawable/remote_194" android:duration="13" />
- <item android:drawable="@drawable/remote_195" android:duration="13" />
- <item android:drawable="@drawable/remote_196" android:duration="13" />
- <item android:drawable="@drawable/remote_197" android:duration="13" />
- <item android:drawable="@drawable/remote_198" android:duration="13" />
- <item android:drawable="@drawable/remote_199" android:duration="13" />
- <item android:drawable="@drawable/remote_200" android:duration="13" />
- <item android:drawable="@drawable/remote_201" android:duration="13" />
- <item android:drawable="@drawable/remote_202" android:duration="13" />
- <item android:drawable="@drawable/remote_203" android:duration="13" />
- <item android:drawable="@drawable/remote_204" android:duration="13" />
- <item android:drawable="@drawable/remote_205" android:duration="13" />
- <item android:drawable="@drawable/remote_206" android:duration="13" />
- <item android:drawable="@drawable/remote_207" android:duration="13" />
- <item android:drawable="@drawable/remote_208" android:duration="13" />
- <item android:drawable="@drawable/remote_209" android:duration="13" />
- <item android:drawable="@drawable/remote_210" android:duration="13" />
- <item android:drawable="@drawable/remote_211" android:duration="13" />
- <item android:drawable="@drawable/remote_212" android:duration="13" />
- <item android:drawable="@drawable/remote_213" android:duration="13" />
- <item android:drawable="@drawable/remote_214" android:duration="13" />
- <item android:drawable="@drawable/remote_215" android:duration="13" />
- <item android:drawable="@drawable/remote_216" android:duration="13" />
- <item android:drawable="@drawable/remote_217" android:duration="13" />
- <item android:drawable="@drawable/remote_218" android:duration="13" />
- <item android:drawable="@drawable/remote_219" android:duration="13" />
- <item android:drawable="@drawable/remote_220" android:duration="13" />
- <item android:drawable="@drawable/remote_221" android:duration="13" />
- <item android:drawable="@drawable/remote_222" android:duration="13" />
- <item android:drawable="@drawable/remote_223" android:duration="13" />
- <item android:drawable="@drawable/remote_224" android:duration="13" />
- <item android:drawable="@drawable/remote_225" android:duration="13" />
- <item android:drawable="@drawable/remote_226" android:duration="13" />
- <item android:drawable="@drawable/remote_227" android:duration="13" />
- <item android:drawable="@drawable/remote_228" android:duration="13" />
- <item android:drawable="@drawable/remote_229" android:duration="13" />
- <item android:drawable="@drawable/remote_230" android:duration="13" />
- <item android:drawable="@drawable/remote_231" android:duration="13" />
- <item android:drawable="@drawable/remote_232" android:duration="13" />
- <item android:drawable="@drawable/remote_233" android:duration="13" />
- <item android:drawable="@drawable/remote_234" android:duration="13" />
- <item android:drawable="@drawable/remote_235" android:duration="13" />
- <item android:drawable="@drawable/remote_236" android:duration="13" />
- <item android:drawable="@drawable/remote_237" android:duration="13" />
- <item android:drawable="@drawable/remote_238" android:duration="13" />
- <item android:drawable="@drawable/remote_239" android:duration="13" />
-</animation-list>
diff --git a/packages/SystemUI/res/drawable/tv_pip_overlay_background.xml b/packages/SystemUI/res/drawable/tv_pip_overlay_background.xml
deleted file mode 100644
index 2b58fc5..0000000
--- a/packages/SystemUI/res/drawable/tv_pip_overlay_background.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
-
- <stroke android:width="1dp" android:color="#33FFFFFF" />
-</shape>
diff --git a/packages/SystemUI/res/drawable/tv_pip_overlay_text_background.xml b/packages/SystemUI/res/drawable/tv_pip_overlay_text_background.xml
deleted file mode 100644
index e247dec..0000000
--- a/packages/SystemUI/res/drawable/tv_pip_overlay_text_background.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
-
- <gradient
- android:startColor="#B2000000"
- android:endColor="#00000000"
- android:angle="90"/>
-</shape>
diff --git a/packages/SystemUI/res/drawable/tv_pip_recents_overlay_scrim.xml b/packages/SystemUI/res/drawable/tv_pip_recents_overlay_scrim.xml
deleted file mode 100644
index 57bee75..0000000
--- a/packages/SystemUI/res/drawable/tv_pip_recents_overlay_scrim.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<!--
- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="rectangle">
-
- <gradient
- android:startColor="#80000000"
- android:endColor="#00000000"
- android:angle="90"/>
-</shape>
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index 33effba..6d4365c 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -63,20 +63,21 @@
systemui:hasOverlappingRendering="false"
/>
<ImageView
- android:id="@+id/mobile_type"
+ android:id="@+id/mobile_roaming"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
+ android:src="@drawable/stat_sys_roaming"
+ android:contentDescription="@string/accessibility_data_connection_roaming"
+ android:visibility="gone"
/>
<ImageView
- android:id="@+id/mobile_roaming"
+ android:id="@+id/mobile_type"
android:layout_width="wrap_content"
android:layout_height="17dp"
- android:paddingStart="22dp"
+ android:paddingStart="19dp"
android:paddingTop="1.5dp"
android:paddingBottom="3dp"
android:scaleType="fitCenter"
- android:src="@drawable/stat_sys_roaming"
- android:contentDescription="@string/accessibility_data_connection_roaming"
android:visibility="gone" />
</FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml
index ff22ffb..0c9858d 100644
--- a/packages/SystemUI/res/layout/notification_info.xml
+++ b/packages/SystemUI/res/layout/notification_info.xml
@@ -114,6 +114,7 @@
android:layout_height="wrap_content"
android:layout_gravity="end|center_vertical"
android:layout_weight="1"
+ android:contentDescription="@string/notification_channel_switch_accessibility"
android:background="@null" />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml
index 5bd64de..b70f24b 100644
--- a/packages/SystemUI/res/layout/notification_snooze.xml
+++ b/packages/SystemUI/res/layout/notification_snooze.xml
@@ -19,47 +19,56 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:minHeight="@dimen/snooze_snackbar_min_height"
- android:id="@+id/notification_snooze"
- android:clickable="true"
- android:gravity="center_vertical"
- android:orientation="horizontal"
- android:paddingStart="24dp"
- android:paddingEnd="24dp"
- android:background="@color/snooze_snackbar_bg">
-
- <TextView
- android:id="@+id/snooze_option_default"
- style="@style/TextAppearance.SnoozeSnackBar"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:gravity="center_vertical"
- android:drawableTint="@android:color/white"
- android:drawableEnd="@drawable/notification_expand_more"/>
-
- <android.widget.Space
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="1"
- />
-
- <TextView
- android:id="@+id/undo"
- style="@style/TextAppearance.SnoozeSnackBar.Button"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_marginEnd="8dp"
- android:layout_marginStart="8dp"
- android:background="@drawable/btn_borderless_rect"
- android:layout_gravity="end"
- android:text="@string/snooze_undo" />
-
+ android:orientation="vertical"
+ android:background="@color/notification_guts_bg_color"
+ android:theme="@*android:style/Theme.DeviceDefault.Light">
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:id="@+id/notification_snooze"
+ android:layout_height="@dimen/snooze_snackbar_min_height">
+
+ <TextView
+ android:id="@+id/snooze_option_default"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:paddingStart="16dp"
+ android:textColor="#DD000000"
+ android:paddingEnd="4dp"/>
+
+ <ImageView
+ android:id="@+id/expand_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_toEndOf="@+id/snooze_option_default"
+ android:layout_centerVertical="true"
+ android:paddingTop="1dp"
+ android:tint="#9E9E9E" />
+
+ <TextView
+ android:id="@+id/undo"
+ style="@style/TextAppearance.NotificationInfo.Button"
+ android:layout_width="wrap_content"
+ android:layout_height="36dp"
+ android:layout_marginEnd="8dp"
+ android:layout_alignParentEnd="true"
+ android:layout_centerVertical="true"
+ android:text="@string/snooze_undo" />
+ </RelativeLayout>
+
+ <View
+ android:id="@+id/divider"
+ android:layout_width="match_parent"
+ android:layout_height="0.5dp"
+ android:background="#9E9E9E" />
+
<LinearLayout
android:id="@+id/snooze_options"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingTop="8dp"
android:paddingBottom="8dp"
- android:orientation="vertical"/>
-
+ android:orientation="vertical" />
+
</com.android.systemui.statusbar.NotificationSnooze>
diff --git a/packages/SystemUI/res/layout/notification_snooze_option.xml b/packages/SystemUI/res/layout/notification_snooze_option.xml
new file mode 100644
index 0000000..aaf45f3
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_snooze_option.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 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.
+-->
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="48dp"
+ android:layout_marginStart="@*android:dimen/notification_content_margin_start"
+ android:layout_marginEnd="@*android:dimen/notification_content_margin_end"
+ android:gravity="center_vertical"
+ android:textSize="14sp"
+ android:textColor="#DD000000"/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/quick_settings_footer.xml b/packages/SystemUI/res/layout/quick_settings_footer.xml
index 8667a5a..bb1b288 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer.xml
@@ -14,41 +14,31 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:paddingBottom="@dimen/qs_tile_padding_top"
- android:paddingTop="@dimen/qs_tile_padding_top" >
+ android:paddingTop="@dimen/qs_tile_padding_top"
+ android:paddingStart="@dimen/qs_footer_padding_start"
+ android:paddingEnd="@dimen/qs_footer_padding_end"
+ android:gravity="center_vertical"
+ android:background="?android:attr/colorPrimaryDark" >
<TextView
android:id="@+id/footer_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:textSize="@dimen/qs_tile_text_size" />
+ android:gravity="start"
+ android:layout_weight="1"
+ android:textAppearance="@style/TextAppearance.QS.TileLabel"
+ android:textColor="?android:attr/textColorSecondary"/>
<ImageView
android:id="@+id/footer_icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_marginEnd="8dp"
- android:layout_toStartOf="@id/footer_text"
+ android:layout_width="@dimen/qs_footer_icon_size"
+ android:layout_height="@dimen/qs_footer_icon_size"
android:contentDescription="@null"
- android:src="@drawable/ic_qs_vpn"
- android:visibility="invisible" />
+ android:src="@drawable/ic_info_outline" />
- <!-- Only shown if both images are visible -->
- <ImageView
- android:id="@+id/footer_icon2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_marginEnd="8dp"
- android:layout_toStartOf="@id/footer_icon"
- android:contentDescription="@null"
- android:src="@drawable/ic_qs_network_logging"
- android:visibility="invisible" />
-
-</RelativeLayout>
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/signal_cluster_view.xml b/packages/SystemUI/res/layout/signal_cluster_view.xml
index da7e4d7..5766dc1 100644
--- a/packages/SystemUI/res/layout/signal_cluster_view.xml
+++ b/packages/SystemUI/res/layout/signal_cluster_view.xml
@@ -52,9 +52,54 @@
android:alpha="0.0"
/>
</FrameLayout>
+ <ViewStub
+ android:id="@+id/connected_device_signals_stub"
+ android:layout="@layout/connected_device_signal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ <LinearLayout
+ android:id="@+id/mobile_signal_group"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ >
+ </LinearLayout>
+ <View
+ android:id="@+id/wifi_signal_spacer"
+ android:layout_width="@dimen/status_bar_wifi_signal_spacer_width"
+ android:layout_height="4dp"
+ android:visibility="gone"
+ />
+ <FrameLayout
+ android:id="@+id/no_sims_combo"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:contentDescription="@string/accessibility_no_sims">
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:theme="@style/DualToneLightTheme"
+ android:id="@+id/no_sims"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:src="@drawable/stat_sys_no_sims"
+ />
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:theme="@style/DualToneDarkTheme"
+ android:id="@+id/no_sims_dark"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:src="@drawable/stat_sys_no_sims"
+ android:alpha="0.0"
+ />
+ </FrameLayout>
+ <View
+ android:id="@+id/wifi_airplane_spacer"
+ android:layout_width="@dimen/status_bar_airplane_spacer_width"
+ android:layout_height="4dp"
+ android:visibility="gone"
+ />
<FrameLayout
android:layout_height="17dp"
- android:layout_width="wrap_content">
+ android:layout_width="wrap_content"
+ android:paddingStart="2dp">
<ImageView
android:id="@+id/wifi_in"
android:layout_height="wrap_content"
@@ -96,50 +141,6 @@
android:layout_width="wrap_content"
/>
</FrameLayout>
- <View
- android:id="@+id/wifi_signal_spacer"
- android:layout_width="@dimen/status_bar_wifi_signal_spacer_width"
- android:layout_height="4dp"
- android:visibility="gone"
- />
- <ViewStub
- android:id="@+id/connected_device_signals_stub"
- android:layout="@layout/connected_device_signal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- <LinearLayout
- android:id="@+id/mobile_signal_group"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- >
- </LinearLayout>
- <FrameLayout
- android:id="@+id/no_sims_combo"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:contentDescription="@string/accessibility_no_sims">
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:theme="@style/DualToneLightTheme"
- android:id="@+id/no_sims"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:src="@drawable/stat_sys_no_sims"
- />
- <com.android.systemui.statusbar.AlphaOptimizedImageView
- android:theme="@style/DualToneDarkTheme"
- android:id="@+id/no_sims_dark"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:src="@drawable/stat_sys_no_sims"
- android:alpha="0.0"
- />
- </FrameLayout>
- <View
- android:id="@+id/wifi_airplane_spacer"
- android:layout_width="@dimen/status_bar_airplane_spacer_width"
- android:layout_height="4dp"
- android:visibility="gone"
- />
<ImageView
android:id="@+id/airplane"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/packages/SystemUI/res/layout/tv_pip_controls.xml
index c6bcd32..61ac6f69 100644
--- a/packages/SystemUI/res/layout/tv_pip_controls.xml
+++ b/packages/SystemUI/res/layout/tv_pip_controls.xml
@@ -40,7 +40,7 @@
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginStart="-50dp"
- android:src="@drawable/ic_pause_white_24dp"
+ android:src="@drawable/ic_pause_white"
android:text="@string/pip_pause"
android:visibility="gone" />
</merge>
diff --git a/packages/SystemUI/res/layout/tv_pip_onboarding.xml b/packages/SystemUI/res/layout/tv_pip_onboarding.xml
deleted file mode 100644
index fe80b94..0000000
--- a/packages/SystemUI/res/layout/tv_pip_onboarding.xml
+++ /dev/null
@@ -1,102 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2016, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/pip_onboarding"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <View
- android:id="@+id/background"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#000000"
- android:alpha="0" />
-
- <ImageView
- android:id="@+id/remote"
- android:layout_width="72dp"
- android:layout_height="273dp"
- android:layout_marginTop="136dp"
- android:layout_marginStart="304dp"
- android:layout_alignParentTop="true"
- android:layout_alignParentStart="true"
- android:adjustViewBounds="true"
- android:src="@drawable/remote"
- android:alpha="0" />
- <ImageView
- android:id="@+id/remote_button"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:layout_marginTop="256dp"
- android:layout_marginStart="315dp"
- android:layout_alignParentTop="true"
- android:layout_alignParentStart="true"
- android:scaleType="fitXY"
- android:src="@drawable/tv_pip_onboarding_remote"
- android:alpha="0" />
- <TextView
- android:id="@+id/title"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="188dp"
- android:layout_marginStart="406dp"
- android:layout_alignParentTop="true"
- android:layout_alignParentStart="true"
- android:fontFamily="sans-serif"
- android:textSize="24sp"
- android:textColor="#EEEEEE"
- android:text="@string/pip_onboarding_title"
- android:alpha="0" />
- <TextView
- android:id="@+id/description"
- android:layout_width="200dp"
- android:layout_height="wrap_content"
- android:layout_marginTop="4dp"
- android:layout_marginStart="408dp"
- android:layout_below="@id/title"
- android:layout_alignParentStart="true"
- android:fontFamily="sans-serif"
- android:textSize="14sp"
- android:textColor="#EEEEEE"
- android:lineSpacingMultiplier="1.46286"
- android:text="@string/pip_onboarding_description"
- android:alpha="0" />
- <Button
- android:id="@+id/button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="16dp"
- android:layout_marginStart="408dp"
- android:layout_below="@id/description"
- android:layout_alignParentStart="true"
- android:gravity="center"
- android:paddingTop="10dp"
- android:paddingBottom="10dp"
- android:paddingStart="24dp"
- android:paddingEnd="24dp"
- android:fontFamily="sans-serif-condensed"
- android:textSize="16sp"
- android:textColor="#FFFFFF"
- android:textAllCaps="true"
- android:text="@string/pip_onboarding_button"
- android:alpha="0"
- android:background="#009688"
- android:elevation="4dp" />
-</RelativeLayout>
diff --git a/packages/SystemUI/res/layout/tv_pip_overlay.xml b/packages/SystemUI/res/layout/tv_pip_overlay.xml
deleted file mode 100644
index 608680c..0000000
--- a/packages/SystemUI/res/layout/tv_pip_overlay.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 2016, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="@drawable/tv_pip_overlay_background">
-
- <TextView
- android:id="@+id/guide_overlay"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:paddingTop="6dp"
- android:paddingBottom="6dp"
- android:paddingStart="10dp"
- android:paddingEnd="10dp"
- android:textSize="14sp"
- android:textColor="#EEEEEE"
- android:fontFamily="sans-serif"
- android:background="@drawable/tv_pip_overlay_text_background"
- android:lineSpacingMultiplier="1.465"
- android:gravity="center"
- android:maxLines="2"
- android:text="@string/pip_hold_home" />
-</RelativeLayout>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index f461bb3..83a0b7b 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -96,10 +96,6 @@
<!-- The "inside" of a notification, reached via longpress -->
<color name="notification_guts_bg_color">#eeeeee</color>
- <!-- Colors of the snooze menu reached via snooze icon behind a notification -->
- <color name="snooze_snackbar_bg">#FF4A4A4A</color>
- <color name="snooze_snackbar_text">#FFA6BAFF</color>
-
<color name="assist_orb_color">#ffffff</color>
<color name="keyguard_user_switcher_background_gradient_color">#77000000</color>
diff --git a/packages/SystemUI/res/values/colors_tv.xml b/packages/SystemUI/res/values/colors_tv.xml
index 3817da0..6e56d4a 100644
--- a/packages/SystemUI/res/values/colors_tv.xml
+++ b/packages/SystemUI/res/values/colors_tv.xml
@@ -21,4 +21,4 @@
<color name="recents_tv_card_title_text_color">#CCEEEEEE</color>
<color name="recents_tv_dismiss_text_color">#7FEEEEEE</color>
<color name="recents_tv_text_shadow_color">#7F000000</color>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 9c7a6a0..4edadea 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -119,7 +119,7 @@
<dimen name="notification_menu_icon_padding">20dp</dimen>
<!-- The minimum height for the snackbar shown after the snooze option has been chosen. -->
- <dimen name="snooze_snackbar_min_height">48dp</dimen>
+ <dimen name="snooze_snackbar_min_height">56dp</dimen>
<!-- The text size of options in the snooze menu. -->
<dimen name="snooze_option_text_size">14sp</dimen>
@@ -263,6 +263,9 @@
<dimen name="qs_detail_item_icon_size">24dp</dimen>
<dimen name="qs_detail_item_icon_marginStart">0dp</dimen>
<dimen name="qs_detail_item_icon_marginEnd">20dp</dimen>
+ <dimen name="qs_footer_padding_start">16dp</dimen>
+ <dimen name="qs_footer_padding_end">24dp</dimen>
+ <dimen name="qs_footer_icon_size">16dp</dimen>
<!-- Desired qs icon overlay size. -->
<dimen name="qs_detail_icon_overlay_size">24dp</dimen>
diff --git a/packages/SystemUI/res/values/integers_tv.xml b/packages/SystemUI/res/values/integers_tv.xml
deleted file mode 100644
index 09547da..0000000
--- a/packages/SystemUI/res/values/integers_tv.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2016 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources>
- <!-- Delay of the onboarding animation start after it launches -->
- <integer name="tv_pip_onboarding_anim_start_delay">1000</integer>
- <!-- Duration of the onboarding animation duration -->
- <integer name="tv_pip_onboarding_anim_duration">1000</integer>
-</resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index eeb28c8..5f88773 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -376,13 +376,13 @@
<string name="accessibility_no_sim">No SIM.</string>
<!-- Content description of the cell data. [CHAR LIMIT=NONE] -->
- <string name="accessibility_cell_data">Cellular Data</string>
+ <string name="accessibility_cell_data">Mobile Data</string>
<!-- Content description of the cell data being enabled. [CHAR LIMIT=NONE] -->
- <string name="accessibility_cell_data_on">Cellular Data On</string>
+ <string name="accessibility_cell_data_on">Mobile Data On</string>
<!-- Content description of the cell data being disabled. [CHAR LIMIT=NONE] -->
- <string name="accessibility_cell_data_off">Cellular Data Off</string>
+ <string name="accessibility_cell_data_off">Mobile Data Off</string>
<!-- Content description of the bluetooth tethering icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_bluetooth_tether">Bluetooth tethering.</string>
@@ -574,11 +574,11 @@
<!-- Title of dialog shown when 4G data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
<string name="data_usage_disabled_dialog_4g_title">4G data is paused</string>
<!-- Title of dialog shown when mobile data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
- <string name="data_usage_disabled_dialog_mobile_title">Cellular data is paused</string>
+ <string name="data_usage_disabled_dialog_mobile_title">Mobile data is paused</string>
<!-- Title of dialog shown when data usage has exceeded limit and has been disabled. [CHAR LIMIT=48] -->
<string name="data_usage_disabled_dialog_title">Data is paused</string>
<!-- Body of dialog shown when data usage has exceeded limit and has been disabled. [CHAR LIMIT=NONE] -->
- <string name="data_usage_disabled_dialog">The data limit you set has been reached. You are no longer using cellular data.\n\nIf you resume, charges may apply for data usage.</string>
+ <string name="data_usage_disabled_dialog">The data limit you set has been reached. You are no longer using mobile data.\n\nIf you resume, charges may apply for data usage.</string>
<!-- Dialog button indicating that data connection should be re-enabled. [CHAR LIMIT=28] -->
<string name="data_usage_disabled_dialog_enable">Resume</string>
@@ -749,7 +749,7 @@
<!-- QuickSettings: Flashlight [CHAR LIMIT=NONE] -->
<string name="quick_settings_flashlight_label">Flashlight</string>
<!-- QuickSettings: Cellular detail panel title [CHAR LIMIT=NONE] -->
- <string name="quick_settings_cellular_detail_title">Cellular data</string>
+ <string name="quick_settings_cellular_detail_title">Mobile data</string>
<!-- QuickSettings: Cellular detail panel, data usage title [CHAR LIMIT=NONE] -->
<string name="quick_settings_cellular_detail_data_usage">Data usage</string>
<!-- QuickSettings: Cellular detail panel, remaining data title [CHAR LIMIT=NONE] -->
@@ -1466,6 +1466,15 @@
<item quantity="other"><xliff:g id="channel_name_1">%1$s</xliff:g>, <xliff:g id="channel_name_2">%2$s</xliff:g>, and <xliff:g id="number">%3$d</xliff:g> others</item>
</plurals>
+ <!-- Notification: Control panel: Accessibility description for expanded inline controls view, used
+ to control settings about notifications related to the current notification. -->
+ <string name="notification_channel_controls_opened_accessibility">Notification controls for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> opened</string>
+ <!-- Notification: Control panel: Accessibility description for announcing the closing of the
+ inline controls view. -->
+ <string name="notification_channel_controls_closed_accessibility">Notification controls for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> closed</string>
+ <!-- Notification: Control panel: Accessibility description for switch that is used to enable
+ or disable notifications from this channel -->
+ <string name="notification_channel_switch_accessibility">Allow notifications from this channel</string>
<!-- Notification: Control panel: Label for button that launches notification settings. Used
when this app has defined more than a single channel for notifications. -->
<string name="notification_all_categories">All Categories</string>
@@ -1493,8 +1502,6 @@
<string name="snooze_option_30_min">30 minutes</string>
<!-- Notification: Menu row: Snooze options: 1 hour option. [CHAR LIMIT=50]-->
<string name="snooze_option_1_hour">1 hour</string>
- <!-- Notification: Menu row: Snooze options: cancel snoozing option. [CHAR LIMIT=50] -->
- <string name="snooze_option_dont_snooze">Cancel</string>
<!-- Notification: Menu row: Snooze undo button label. [CHAR LIMIT=50]-->
<string name="snooze_undo">UNDO</string>
@@ -1888,6 +1895,18 @@
<!-- PiP minimize description. [CHAR LIMIT=NONE] -->
<string name="pip_minimize_description" translatable="false">Drag or fling the PIP to the edges of the screen to minimize it.</string>
+ <!-- Button to play the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
+ <string name="pip_play">Play</string>
+
+ <!-- Button to pause the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
+ <string name="pip_pause">Pause</string>
+
+ <!-- Button to skip to the next media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
+ <string name="pip_skip_to_next">Skip to next</string>
+
+ <!-- Button to skip to the prev media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
+ <string name="pip_skip_to_prev">Skip to previous</string>
+
<!-- Tuner string -->
<string name="change_theme_reboot" translatable="false">Changing the theme requires a restart.</string>
<!-- Tuner string -->
diff --git a/packages/SystemUI/res/values/strings_tv.xml b/packages/SystemUI/res/values/strings_tv.xml
index fb9e1f2..e578068 100644
--- a/packages/SystemUI/res/values/strings_tv.xml
+++ b/packages/SystemUI/res/values/strings_tv.xml
@@ -23,18 +23,4 @@
<string name="pip_close">Close PIP</string>
<!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=30] -->
<string name="pip_fullscreen">Full screen</string>
- <!-- Button to play the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
- <string name="pip_play">Play</string>
- <!-- Button to pause the current media on picture-in-picture (PIP) [CHAR LIMIT=30] -->
- <string name="pip_pause">Pause</string>
- <!-- Overlay text on picture-in-picture (PIP) to indicate that longpress HOME key to control PIP [CHAR LIMIT=52] -->
- <string name="pip_hold_home">Hold <b>HOME</b> to control PIP</string>
- <!-- Picture-in-Picture (PIP) onboarding screen -->
- <eat-comment />
- <!-- Title for picture-in-picture (PIP) onboarding screen to indicate that an user is in PIP mode. [CHAR LIMIT=NONE] -->
- <string name="pip_onboarding_title">Picture-in-picture</string>
- <!-- Description for picture-in-picture (PIP) onboarding screen to indicate that longpress HOME key to control PIP. [CHAR LIMIT=NONE] -->
- <string name="pip_onboarding_description">This keeps your video in view until you play another one. Press and hold <b>HOME</b> to control it.</string>
- <!-- Button to close picture-in-picture (PIP) onboarding screen. -->
- <string name="pip_onboarding_button">Got it</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index c9479b8..dbdbd1e 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -299,13 +299,13 @@
<style name="TextAppearance.Volume">
<item name="android:textStyle">normal</item>
- <item name="android:textColor">#ffffffff</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:fontFamily">sans-serif</item>
</style>
<style name="TextAppearance.Volume.Header">
<item name="android:textSize">12sp</item>
- <item name="android:textColor">@color/volume_slider_inactive</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
<style name="TextAppearance.Volume.ZenSummary">
@@ -316,7 +316,7 @@
<style name="TextAppearance.Volume.ZenDetail">
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">sans-serif</item>
- <item name="android:textColor">@*android:color/quaternary_device_default_settings</item>
+ <item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
<style name="VolumeButtons" parent="@android:style/Widget.Material.Button.Borderless">
@@ -386,20 +386,6 @@
<item name="android:paddingEnd">8dp</item>
</style>
- <style name="TextAppearance.SnoozeSnackBar">
- <item name="android:textSize">14sp</item>
- <item name="android:fontFamily">sans-serif</item>
- <item name="android:textColor">@android:color/white</item>
- </style>
-
- <style name="TextAppearance.SnoozeSnackBar.Button">
- <item name="android:textSize">14sp</item>
- <item name="android:textAllCaps">true</item>
- <item name="android:fontFamily">sans-serif-medium</item>
- <item name="android:gravity">center</item>
- <item name="android:textColor">@color/snooze_snackbar_text</item>
- </style>
-
<style name="edit_theme" parent="@*android:style/Theme.DeviceDefault.QuickSettings">
<item name="android:colorBackground">?android:attr/colorSecondary</item>
</style>
diff --git a/packages/SystemUI/res/values/styles_tv.xml b/packages/SystemUI/res/values/styles_tv.xml
index 3f0caab..0c4fd23 100644
--- a/packages/SystemUI/res/values/styles_tv.xml
+++ b/packages/SystemUI/res/values/styles_tv.xml
@@ -21,5 +21,6 @@
<style name="PipTheme" parent="@android:style/Theme.Translucent.NoTitleBar.Fullscreen">
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:backgroundDimEnabled">false</item>
+ <item name="android:windowDisablePreview">true</item>
</style>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index b447979..616e5b9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -15,6 +15,7 @@
*/
package com.android.keyguard;
+import android.R.style;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.admin.DevicePolicyManager;
@@ -23,6 +24,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
@@ -73,7 +75,8 @@
}
public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
+ super(new ContextThemeWrapper(context, android.R.style.Theme_DeviceDefault), attrs,
+ defStyle);
mSecurityModel = new KeyguardSecurityModel(context);
mLockPatternUtils = new LockPatternUtils(context);
mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f8d1bfb..7a6ac57 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -165,11 +165,6 @@
private int mPhoneState;
private boolean mKeyguardIsVisible;
- /**
- * If true, fingerprint was already authenticated and we don't need to start listening again
- * until the Keyguard has been dismissed.
- */
- private boolean mFingerprintAlreadyAuthenticated;
private boolean mGoingToSleep;
private boolean mBouncer;
private boolean mBootCompleted;
@@ -409,11 +404,8 @@
private void onFingerprintAuthenticated(int userId) {
Trace.beginSection("KeyGuardUpdateMonitor#onFingerPrintAuthenticated");
mUserFingerprintAuthenticated.put(userId, true);
-
- // If fingerprint unlocking is allowed, this event will lead to a Keyguard dismiss or to a
- // wake-up (if Keyguard is not showing), so we don't need to listen until Keyguard is
- // fully gone.
- mFingerprintAlreadyAuthenticated = isUnlockingWithFingerprintAllowed();
+ // Don't send cancel if authentication succeeds
+ mFingerprintCancelSignal = null;
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -922,7 +914,6 @@
}
}
mGoingToSleep = true;
- mFingerprintAlreadyAuthenticated = false;
updateFingerprintListeningState();
}
@@ -1092,8 +1083,7 @@
private boolean shouldListenForFingerprint() {
return (mKeyguardIsVisible || !mDeviceInteractive || mBouncer || mGoingToSleep)
- && !mSwitchingUser && !mFingerprintAlreadyAuthenticated
- && !isFingerprintDisabled(getCurrentUser());
+ && !mSwitchingUser && !isFingerprintDisabled(getCurrentUser());
}
private void startListeningForFingerprint() {
@@ -1416,9 +1406,6 @@
cb.onKeyguardVisibilityChangedRaw(showing);
}
}
- if (!showing) {
- mFingerprintAlreadyAuthenticated = false;
- }
updateFingerprintListeningState();
}
diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java
index e1aaaa3..1e9cbdc 100644
--- a/packages/SystemUI/src/com/android/systemui/Prefs.java
+++ b/packages/SystemUI/src/com/android/systemui/Prefs.java
@@ -41,7 +41,6 @@
Key.DND_FAVORITE_BUCKET_INDEX,
Key.DND_NONE_SELECTED,
Key.DND_FAVORITE_ZEN,
- Key.TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN,
Key.QS_HOTSPOT_ADDED,
Key.QS_DATA_SAVER_ADDED,
Key.QS_DATA_SAVER_DIALOG_SHOWN,
@@ -62,7 +61,6 @@
String DND_FAVORITE_BUCKET_INDEX = "DndCountdownMinuteIndex";
String DND_NONE_SELECTED = "DndNoneSelected";
String DND_FAVORITE_ZEN = "DndFavoriteZen";
- String TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN = "TvPictureInPictureOnboardingShown";
String QS_HOTSPOT_ADDED = "QsHotspotAdded";
String QS_DATA_SAVER_ADDED = "QsDataSaverAdded";
String QS_DATA_SAVER_DIALOG_SHOWN = "QsDataSaverDialogShown";
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 5a04108d..8c4159a 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -462,11 +462,20 @@
int duration = SNAP_ANIM_LEN;
anim.setDuration(duration);
anim.addListener(new AnimatorListenerAdapter() {
+ boolean wasCancelled = false;
+
+ @Override
+ public void onAnimationCancel(Animator animator) {
+ wasCancelled = true;
+ }
+
@Override
public void onAnimationEnd(Animator animator) {
mSnappingChild = false;
- updateSwipeProgressFromOffset(animView, canBeDismissed);
- mCallback.onChildSnappedBack(animView, targetLeft);
+ if (!wasCancelled) {
+ updateSwipeProgressFromOffset(animView, canBeDismissed);
+ mCallback.onChildSnappedBack(animView, targetLeft);
+ }
}
});
prepareSnapBackAnimation(animView, anim);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index f3fb1ef..ec56e15 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -33,6 +33,8 @@
boolean isPulsingBlocked();
void startPendingIntentDismissingKeyguard(PendingIntent intent);
+ void abortPulsing();
+ void extendPulse();
interface Callback {
default void onNotificationHeadsUp() {}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
index 870d4d1..90e1c07 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java
@@ -145,6 +145,11 @@
log("screenOff why=" + why);
}
+ public static void traceMissedTick(String delay) {
+ if (!ENABLED) return;
+ log("missedTick by=" + delay);
+ }
+
public static void traceKeyguard(boolean showing) {
if (!ENABLED) return;
log("keyguard " + showing);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index f27521e..1cc5fb9 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -58,12 +58,15 @@
/** Pulse is done showing. Followed by transition to DOZE or DOZE_AOD. */
DOZE_PULSE_DONE,
/** Doze is done. DozeService is finished. */
- FINISH;
+ FINISH,
+ /** AOD, but the display is temporarily off. */
+ DOZE_AOD_PAUSED;
boolean canPulse() {
switch (this) {
case DOZE:
case DOZE_AOD:
+ case DOZE_AOD_PAUSED:
return true;
default:
return false;
@@ -85,6 +88,7 @@
case UNINITIALIZED:
case INITIALIZED:
case DOZE:
+ case DOZE_AOD_PAUSED:
return Display.STATE_OFF;
case DOZE_PULSING:
case DOZE_AOD:
@@ -241,6 +245,11 @@
if (mState == State.FINISH) {
return State.FINISH;
}
+ if ((mState == State.DOZE_AOD_PAUSED || mState == State.DOZE_AOD || mState == State.DOZE)
+ && requestedState == State.DOZE_PULSE_DONE) {
+ Log.i(TAG, "Dropping pulse done because current state is already done: " + mState);
+ return mState;
+ }
if (requestedState == State.DOZE_REQUEST_PULSE && !mState.canPulse()) {
Log.i(TAG, "Dropping pulse request because current state can't pulse: " + mState);
return mState;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 2ac0657..73f5222 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -22,6 +22,8 @@
import android.content.Context;
import android.database.ContentObserver;
import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
@@ -40,6 +42,7 @@
import java.io.PrintWriter;
import java.util.List;
+import java.util.function.Consumer;
public class DozeSensors {
@@ -55,18 +58,22 @@
private final DozeParameters mDozeParameters;
private final AmbientDisplayConfiguration mConfig;
private final WakeLock mWakeLock;
+ private final Consumer<Boolean> mProxCallback;
private final Callback mCallback;
private final Handler mHandler = new Handler();
+ private final ProxSensor mProxSensor;
public DozeSensors(Context context, SensorManager sensorManager, DozeParameters dozeParameters,
- AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback) {
+ AmbientDisplayConfiguration config, WakeLock wakeLock, Callback callback,
+ Consumer<Boolean> proxCallback) {
mContext = context;
mSensorManager = sensorManager;
mDozeParameters = dozeParameters;
mConfig = config;
mWakeLock = wakeLock;
+ mProxCallback = proxCallback;
mResolver = mContext.getContentResolver();
mSensors = new TriggerSensor[] {
@@ -86,6 +93,8 @@
true /* configured */,
DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP)
};
+
+ mProxSensor = new ProxSensor();
mCallback = callback;
}
@@ -129,6 +138,10 @@
}
}
+ public void setProxListening(boolean listen) {
+ mProxSensor.setRegistered(listen);
+ }
+
private final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Uri uri, int userId) {
@@ -152,6 +165,43 @@
}
}
+ private class ProxSensor implements SensorEventListener {
+
+ boolean mRegistered;
+ Boolean mCurrentlyFar;
+
+ void setRegistered(boolean register) {
+ if (mRegistered == register) {
+ // Send an update even if we don't re-register.
+ mHandler.post(() -> {
+ if (mCurrentlyFar != null) {
+ mProxCallback.accept(mCurrentlyFar);
+ }
+ });
+ return;
+ }
+ if (register) {
+ mRegistered = mSensorManager.registerListener(this,
+ mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY),
+ SensorManager.SENSOR_DELAY_NORMAL, mHandler);
+ } else {
+ mSensorManager.unregisterListener(this);
+ mRegistered = false;
+ mCurrentlyFar = null;
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ mCurrentlyFar = event.values[0] >= event.sensor.getMaximumRange();
+ mProxCallback.accept(mCurrentlyFar);
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+ }
+
private class TriggerSensor extends TriggerEventListener {
final Sensor mSensor;
final boolean mConfigured;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 1b9bf73..9b3593b 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -46,6 +46,7 @@
public class DozeTriggers implements DozeMachine.Part {
private static final String TAG = "DozeTriggers";
+ private static final boolean DEBUG = DozeService.DEBUG;
/** adb shell am broadcast -a com.android.systemui.doze.pulse com.android.systemui */
private static final String PULSE_ACTION = "com.android.systemui.doze.pulse";
@@ -81,7 +82,7 @@
mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters, config,
- wakeLock, this::onSensor);
+ wakeLock, this::onSensor, this::onProximityFar);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
}
@@ -113,6 +114,22 @@
}
}
+ private void onProximityFar(boolean far) {
+ final boolean near = !far;
+ DozeMachine.State state = mMachine.getState();
+ if (near && state == DozeMachine.State.DOZE_PULSING) {
+ if (DEBUG) Log.i(TAG, "Prox NEAR, ending pulse");
+ mMachine.requestState(DozeMachine.State.DOZE_PULSE_DONE);
+ }
+ if (far && state == DozeMachine.State.DOZE_AOD_PAUSED) {
+ if (DEBUG) Log.i(TAG, "Prox FAR, unpausing AOD");
+ mMachine.requestState(DozeMachine.State.DOZE_AOD);
+ } else if (near && state == DozeMachine.State.DOZE_AOD) {
+ if (DEBUG) Log.i(TAG, "Prox NEAR, pausing AOD");
+ mMachine.requestState(DozeMachine.State.DOZE_AOD_PAUSED);
+ }
+ }
+
private void onCarMode() {
mMachine.requestState(DozeMachine.State.FINISH);
}
@@ -131,15 +148,21 @@
break;
case DOZE:
case DOZE_AOD:
+ case DOZE_AOD_PAUSED:
+ mDozeSensors.setProxListening(newState != DozeMachine.State.DOZE);
mDozeSensors.setListening(true);
if (oldState != DozeMachine.State.INITIALIZED) {
mDozeSensors.reregisterAllSensors();
}
break;
+ case DOZE_PULSING:
+ mDozeSensors.setProxListening(true);
+ break;
case FINISH:
mBroadcastReceiver.unregister(mContext);
mDozeHost.removeCallback(mHostCallback);
mDozeSensors.setListening(false);
+ mDozeSensors.setProxListening(false);
break;
default:
}
@@ -156,6 +179,7 @@
private void requestPulse(final int reason, boolean performedProxCheck) {
Assert.isMainThread();
+ mDozeHost.extendPulse();
if (mPulsePending || !mAllowPulseTriggers || !canPulse()) {
return;
}
@@ -286,6 +310,8 @@
}
private class TriggerReceiver extends BroadcastReceiver {
+ private boolean mRegistered;
+
@Override
public void onReceive(Context context, Intent intent) {
if (PULSE_ACTION.equals(intent.getAction())) {
@@ -301,14 +327,22 @@
}
public void register(Context context) {
+ if (mRegistered) {
+ return;
+ }
IntentFilter filter = new IntentFilter(PULSE_ACTION);
filter.addAction(UiModeManager.ACTION_ENTER_CAR_MODE);
filter.addAction(Intent.ACTION_USER_SWITCHED);
context.registerReceiver(this, filter);
+ mRegistered = true;
}
public void unregister(Context context) {
+ if (!mRegistered) {
+ return;
+ }
context.unregisterReceiver(this);
+ mRegistered = false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index f577654..03076cc 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -20,6 +20,8 @@
import android.content.Context;
import android.os.Handler;
import android.os.SystemClock;
+import android.text.format.Formatter;
+import android.util.Log;
import com.android.systemui.util.wakelock.WakeLock;
@@ -31,6 +33,7 @@
*/
public class DozeUi implements DozeMachine.Part {
+ private static final long TIME_TICK_DEADLINE_MILLIS = 90 * 1000; // 1.5min
private final Context mContext;
private final AlarmManager mAlarmManager;
private final DozeHost mHost;
@@ -40,6 +43,7 @@
private final AlarmManager.OnAlarmListener mTimeTick;
private boolean mTimeTickScheduled = false;
+ private long mLastTimeTickElapsed = 0;
public DozeUi(Context context, AlarmManager alarmManager, DozeMachine machine,
WakeLock wakeLock, DozeHost host, Handler handler) {
@@ -75,11 +79,14 @@
scheduleTimeTick();
break;
case DOZE:
+ case DOZE_AOD_PAUSED:
unscheduleTimeTick();
break;
case DOZE_REQUEST_PULSE:
pulseWhileDozing(DozeLog.PULSE_REASON_NOTIFICATION /* TODO */);
break;
+ case DOZE_PULSE_DONE:
+ mHost.abortPulsing();
case INITIALIZED:
mHost.startDozing();
break;
@@ -100,15 +107,26 @@
SystemClock.elapsedRealtime() + delta, "doze_time_tick", mTimeTick, mHandler);
mTimeTickScheduled = true;
+ mLastTimeTickElapsed = SystemClock.elapsedRealtime();
}
private void unscheduleTimeTick() {
if (!mTimeTickScheduled) {
return;
}
+ verifyLastTimeTick();
mAlarmManager.cancel(mTimeTick);
}
+ private void verifyLastTimeTick() {
+ long millisSinceLastTick = SystemClock.elapsedRealtime() - mLastTimeTickElapsed;
+ if (millisSinceLastTick > TIME_TICK_DEADLINE_MILLIS) {
+ String delay = Formatter.formatShortElapsedTime(mContext, millisSinceLastTick);
+ DozeLog.traceMissedTick(delay);
+ Log.e(DozeMachine.TAG, "Missed AOD time tick by " + delay);
+ }
+ }
+
private long roundToNextMinute(long timeInMillis) {
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTimeInMillis(timeInMillis);
@@ -124,6 +142,7 @@
// Alarm was canceled, but we still got the callback. Ignore.
return;
}
+ verifyLastTimeTick();
mHost.dozeTimeTick();
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
index 86bb0de..4b3cdfb 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -39,7 +39,8 @@
// This delay controls how long to wait before we show the target when the user first moves
// the PIP, to prevent the target from animating if the user just wants to fling the PIP
private static final int SHOW_TARGET_DELAY = 100;
- private static final int SHOW_TARGET_DURATION = 200;
+ private static final int SHOW_TARGET_DURATION = 350;
+ private static final int HIDE_TARGET_DURATION = 225;
private Context mContext;
private WindowManager mWindowManager;
@@ -96,7 +97,7 @@
public void showDismissTarget() {
mDismissView.animate()
.alpha(1f)
- .setInterpolator(Interpolators.LINEAR_OUT_SLOW_IN)
+ .setInterpolator(Interpolators.LINEAR)
.setStartDelay(SHOW_TARGET_DELAY)
.setDuration(SHOW_TARGET_DURATION)
.start();
@@ -109,9 +110,9 @@
if (mDismissView != null) {
mDismissView.animate()
.alpha(0f)
- .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN)
+ .setInterpolator(Interpolators.LINEAR)
.setStartDelay(0)
- .setDuration(SHOW_TARGET_DURATION)
+ .setDuration(HIDE_TARGET_DURATION)
.withEndAction(new Runnable() {
@Override
public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
index c565373..28bd23c 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java
@@ -143,10 +143,10 @@
@Override
public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- Rect animatingBounds, boolean fromImeAdjustement) {
+ Rect animatingBounds, boolean fromImeAdjustement, int displayRotation) {
mHandler.post(() -> {
mTouchHandler.onMovementBoundsChanged(insetBounds, normalBounds, animatingBounds,
- fromImeAdjustement);
+ fromImeAdjustement, displayRotation);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
index 3a4caa9..62ec09b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMediaController.java
@@ -48,6 +48,8 @@
private static final String ACTION_PLAY = "com.android.systemui.pip.phone.PLAY";
private static final String ACTION_PAUSE = "com.android.systemui.pip.phone.PAUSE";
+ private static final String ACTION_NEXT = "com.android.systemui.pip.phone.NEXT";
+ private static final String ACTION_PREV = "com.android.systemui.pip.phone.PREV";
/**
* A listener interface to receive notification on changes to the media actions.
@@ -67,6 +69,8 @@
private RemoteAction mPauseAction;
private RemoteAction mPlayAction;
+ private RemoteAction mNextAction;
+ private RemoteAction mPrevAction;
private BroadcastReceiver mPlayPauseActionReceiver = new BroadcastReceiver() {
@Override
@@ -76,6 +80,10 @@
mMediaController.getTransportControls().play();
} else if (action.equals(ACTION_PAUSE)) {
mMediaController.getTransportControls().pause();
+ } else if (action.equals(ACTION_NEXT)) {
+ mMediaController.getTransportControls().skipToNext();
+ } else if (action.equals(ACTION_PREV)) {
+ mMediaController.getTransportControls().skipToPrevious();
}
}
};
@@ -95,6 +103,8 @@
IntentFilter mediaControlFilter = new IntentFilter();
mediaControlFilter.addAction(ACTION_PLAY);
mediaControlFilter.addAction(ACTION_PAUSE);
+ mediaControlFilter.addAction(ACTION_NEXT);
+ mediaControlFilter.addAction(ACTION_PREV);
mContext.registerReceiver(mPlayPauseActionReceiver, mediaControlFilter);
createMediaActions();
@@ -143,11 +153,21 @@
int state = mMediaController.getPlaybackState().getState();
boolean isPlaying = MediaSession.isActiveState(state);
long actions = mMediaController.getPlaybackState().getActions();
+
+ // Prev action
+ mPrevAction.setEnabled((actions & PlaybackState.ACTION_SKIP_TO_PREVIOUS) != 0);
+ mediaActions.add(mPrevAction);
+
+ // Play/pause action
if (!isPlaying && ((actions & PlaybackState.ACTION_PLAY) != 0)) {
mediaActions.add(mPlayAction);
} else if (isPlaying && ((actions & PlaybackState.ACTION_PAUSE) != 0)) {
mediaActions.add(mPauseAction);
}
+
+ // Next action
+ mNextAction.setEnabled((actions & PlaybackState.ACTION_SKIP_TO_NEXT) != 0);
+ mediaActions.add(mNextAction);
return mediaActions;
}
@@ -157,15 +177,27 @@
private void createMediaActions() {
String pauseDescription = mContext.getString(R.string.pip_pause);
mPauseAction = new RemoteAction(Icon.createWithResource(mContext,
- R.drawable.ic_pause_white_24dp), pauseDescription, pauseDescription,
+ R.drawable.ic_pause_white), pauseDescription, pauseDescription,
PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PAUSE),
FLAG_UPDATE_CURRENT));
String playDescription = mContext.getString(R.string.pip_play);
mPlayAction = new RemoteAction(Icon.createWithResource(mContext,
- R.drawable.ic_play_arrow_white_24dp), playDescription, playDescription,
+ R.drawable.ic_play_arrow_white), playDescription, playDescription,
PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PLAY),
FLAG_UPDATE_CURRENT));
+
+ String nextDescription = mContext.getString(R.string.pip_skip_to_next);
+ mNextAction = new RemoteAction(Icon.createWithResource(mContext,
+ R.drawable.ic_skip_next_white), nextDescription, nextDescription,
+ PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_NEXT),
+ FLAG_UPDATE_CURRENT));
+
+ String prevDescription = mContext.getString(R.string.pip_skip_to_prev);
+ mPrevAction = new RemoteAction(Icon.createWithResource(mContext,
+ R.drawable.ic_skip_previous_white), prevDescription, prevDescription,
+ PendingIntent.getBroadcast(mContext, 0, new Intent(ACTION_PREV),
+ FLAG_UPDATE_CURRENT));
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
index 9c4f16b..79ac816 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java
@@ -195,6 +195,7 @@
updateFromIntent(getIntent());
setTitle(R.string.pip_menu_title);
+ setDisablePreviewScreenshots(true);
}
@Override
@@ -220,6 +221,13 @@
}
@Override
+ protected void onStop() {
+ super.onStop();
+
+ cancelDelayedFinish();
+ }
+
+ @Override
protected void onDestroy() {
super.onDestroy();
@@ -404,16 +412,26 @@
} else {
actionsContainer.setVisibility(View.VISIBLE);
if (mActionsGroup != null) {
- mActionsGroup.removeAllViews();
+ // Hide extra views
+ for (int i = mActions.size(); i < mActionsGroup.getChildCount(); i++) {
+ mActionsGroup.getChildAt(i).setVisibility(View.GONE);
+ }
+ // Add needed views
+ final LayoutInflater inflater = LayoutInflater.from(this);
+ while (mActionsGroup.getChildCount() < mActions.size()) {
+ final ImageView actionView = (ImageView) inflater.inflate(
+ R.layout.pip_menu_action, mActionsGroup, false);
+ mActionsGroup.addView(actionView);
+ }
// Recreate the layout
final boolean isLandscapePip = stackBounds != null &&
(stackBounds.width() > stackBounds.height());
- final LayoutInflater inflater = LayoutInflater.from(this);
for (int i = 0; i < mActions.size(); i++) {
final RemoteAction action = mActions.get(i);
- final ImageView actionView = (ImageView) inflater.inflate(
- R.layout.pip_menu_action, mActionsGroup, false);
+ final ImageView actionView = (ImageView) mActionsGroup.getChildAt(i);
+
+ // TODO: Check if the action drawable has changed before we reload it
action.getIcon().loadDrawableAsync(this, d -> {
d.setTint(Color.WHITE);
actionView.setImageDrawable(d);
@@ -431,12 +449,11 @@
actionView.setAlpha(DISABLED_ACTION_ALPHA);
actionView.setEnabled(false);
}
- if (isLandscapePip && i > 0) {
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
- actionView.getLayoutParams();
- lp.leftMargin = mBetweenActionPaddingLand;
- }
- mActionsGroup.addView(actionView);
+
+ // Update the margin between actions
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
+ actionView.getLayoutParams();
+ lp.leftMargin = (isLandscapePip && i > 0) ? mBetweenActionPaddingLand : 0;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index fb8574d..67255d3 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -37,10 +37,12 @@
import android.os.Handler;
import android.os.RemoteException;
import android.util.Log;
+import android.view.Choreographer;
import android.view.animation.Interpolator;
import com.android.internal.os.BackgroundThread;
import com.android.internal.policy.PipSnapAlgorithm;
+import com.android.internal.view.SurfaceFlingerVsyncChoreographer;
import com.android.systemui.recents.misc.SystemServicesProxy;
import com.android.systemui.statusbar.FlingAnimationUtils;
@@ -72,6 +74,7 @@
private Context mContext;
private IActivityManager mActivityManager;
+ private SurfaceFlingerVsyncChoreographer mVsyncChoreographer;
private Handler mHandler;
private PipSnapAlgorithm mSnapAlgorithm;
@@ -96,6 +99,8 @@
mActivityManager = activityManager;
mSnapAlgorithm = snapAlgorithm;
mFlingAnimationUtils = flingAnimationUtils;
+ mVsyncChoreographer = new SurfaceFlingerVsyncChoreographer(mHandler, mContext.getDisplay(),
+ Choreographer.getInstance());
onConfigurationChanged();
}
@@ -311,7 +316,8 @@
* Animates the PiP from the expanded state to the normal state after the menu is hidden.
*/
void animateToUnexpandedState(Rect normalBounds, float savedSnapFraction,
- Rect normalMovementBounds, Rect currentMovementBounds, boolean minimized) {
+ Rect normalMovementBounds, Rect currentMovementBounds, boolean minimized,
+ boolean immediate) {
if (savedSnapFraction < 0f) {
// If there are no saved snap fractions, then just use the current bounds
savedSnapFraction = mSnapAlgorithm.getSnapFraction(new Rect(mBounds),
@@ -321,7 +327,11 @@
if (minimized) {
normalBounds = getClosestMinimizedBounds(normalBounds, normalMovementBounds);
}
- resizeAndAnimatePipUnchecked(normalBounds, SHRINK_STACK_FROM_MENU_DURATION);
+ if (immediate) {
+ movePip(normalBounds);
+ } else {
+ resizeAndAnimatePipUnchecked(normalBounds, SHRINK_STACK_FROM_MENU_DURATION);
+ }
}
/**
@@ -394,7 +404,7 @@
*/
private void resizePipUnchecked(Rect toBounds) {
if (!toBounds.equals(mBounds)) {
- mHandler.post(() -> {
+ mVsyncChoreographer.scheduleAtSfVsync(() -> {
try {
mActivityManager.resizePinnedStack(toBounds, null /* tempPinnedTaskBounds */);
mBounds.set(toBounds);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 3f26fdd..3223f9f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -63,7 +63,7 @@
private static final int METRIC_VALUE_DISMISSED_BY_TAP = 0;
private static final int METRIC_VALUE_DISMISSED_BY_DRAG = 1;
- private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 200;
+ private static final int SHOW_DISMISS_AFFORDANCE_DELAY = 225;
// Allow dragging the PIP to a location to close it
private static final boolean ENABLE_DISMISS_DRAG_TO_EDGE = true;
@@ -78,6 +78,7 @@
private final PipDismissViewController mDismissViewController;
private final PipSnapAlgorithm mSnapAlgorithm;
private final AccessibilityManager mAccessibilityManager;
+ private boolean mShowPipMenuOnAnimationEnd = false;
// The current movement bounds
private Rect mMovementBounds = new Rect();
@@ -89,6 +90,11 @@
private Rect mExpandedMovementBounds = new Rect();
private int mExpandedShortestEdgeSize;
+ // Used to workaround an issue where the WM rotation happens before we are notified, allowing
+ // us to send stale bounds
+ private int mDeferResizeToNormalBoundsUntilRotation = -1;
+ private int mDisplayRotation;
+
private Handler mHandler = new Handler();
private Runnable mShowDismissAffordance = new Runnable() {
@Override
@@ -216,13 +222,18 @@
setMinimizedStateInternal(false);
}
mDismissViewController.destroyDismissTarget();
- mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
- mMovementBounds, true /* allowMenuTimeout */);
+ mShowPipMenuOnAnimationEnd = true;
}
public void onPinnedStackAnimationEnded() {
// Always synchronize the motion helper bounds once PiP animations finish
mMotionHelper.synchronizePinnedStackBounds();
+
+ if (mShowPipMenuOnAnimationEnd) {
+ mMenuController.showMenu(MENU_STATE_CLOSE, mMotionHelper.getBounds(),
+ mMovementBounds, true /* allowMenuTimeout */);
+ mShowPipMenuOnAnimationEnd = false;
+ }
}
@Override
@@ -250,7 +261,7 @@
}
public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds, Rect animatingBounds,
- boolean fromImeAdjustement) {
+ boolean fromImeAdjustement, int displayRotation) {
// Re-calculate the expanded bounds
mNormalBounds = normalBounds;
Rect normalMovementBounds = new Rect();
@@ -304,7 +315,17 @@
// above
mNormalMovementBounds = normalMovementBounds;
mExpandedMovementBounds = expandedMovementBounds;
+ mDisplayRotation = displayRotation;
updateMovementBounds(mMenuState);
+
+ // If we have a deferred resize, apply it now
+ if (mDeferResizeToNormalBoundsUntilRotation == displayRotation) {
+ mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
+ mNormalMovementBounds, mMovementBounds, mIsMinimized,
+ true /* immediate */);
+ mSavedSnapFraction = -1f;
+ mDeferResizeToNormalBoundsUntilRotation = -1;
+ }
}
private void onRegistrationChanged(boolean isRegistered) {
@@ -474,11 +495,34 @@
// Try and restore the PiP to the closest edge, using the saved snap fraction
// if possible
if (resize) {
- Rect normalBounds = new Rect(mNormalBounds);
- mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
- mNormalMovementBounds, mMovementBounds, mIsMinimized);
+ // This is a very special case: when the menu is expanded and visible, navigating to
+ // another activity can trigger auto-enter PiP, and if the revealed activity has a
+ // forced rotation set, then the controller will get updated with the new rotation
+ // of the display. However, at the same time, SystemUI will try to hide the menu by
+ // creating an animation to the normal bounds which are now stale. In such a case
+ // we defer the animation to the normal bounds until after the next
+ // onMovementBoundsChanged() call to get the bounds in the new orientation
+ if (mDeferResizeToNormalBoundsUntilRotation == -1) {
+ try {
+ int displayRotation = mPinnedStackController.getDisplayRotation();
+ if (mDisplayRotation != displayRotation) {
+ mDeferResizeToNormalBoundsUntilRotation = displayRotation;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not get display rotation from controller");
+ }
+ }
+
+ if (mDeferResizeToNormalBoundsUntilRotation == -1) {
+ Rect normalBounds = new Rect(mNormalBounds);
+ mMotionHelper.animateToUnexpandedState(normalBounds, mSavedSnapFraction,
+ mNormalMovementBounds, mMovementBounds, mIsMinimized,
+ false /* immediate */);
+ mSavedSnapFraction = -1f;
+ }
+ } else {
+ mSavedSnapFraction = -1f;
}
- mSavedSnapFraction = -1f;
}
mMenuState = menuState;
updateMovementBounds(menuState);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
index 4c81907..acea3b6 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java
@@ -186,10 +186,10 @@
} else {
mPlayPauseButtonView.setVisibility(View.VISIBLE);
if (state == PipManager.PLAYBACK_STATE_PLAYING) {
- mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white_24dp);
+ mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white);
mPlayPauseButtonView.setText(R.string.pip_pause);
} else {
- mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white_24dp);
+ mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white);
mPlayPauseButtonView.setText(R.string.pip_play);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index ad290c3..657f08b 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -56,16 +56,12 @@
import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.view.Display.DEFAULT_DISPLAY;
-import static com.android.systemui.Prefs.Key.TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN;
-
/**
* Manages the picture-in-picture (PIP) UI and states.
*/
public class PipManager implements BasePipManager {
private static final String TAG = "PipManager";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final boolean DEBUG_FORCE_ONBOARDING =
- SystemProperties.getBoolean("debug.tv.pip_force_onboarding", false);
private static final String SETTINGS_PACKAGE_AND_CLASS_DELIMITER = "/";
private static PipManager sPipManager;
@@ -76,10 +72,9 @@
*/
public static final int STATE_NO_PIP = 0;
/**
- * State when PIP is shown with an overlay message on top of it.
- * This is used as default PIP state.
+ * State when PIP is shown. This is used as default PIP state.
*/
- public static final int STATE_PIP_OVERLAY = 1;
+ public static final int STATE_PIP = 1;
/**
* State when PIP menu dialog is shown.
*/
@@ -126,7 +121,6 @@
private int mPipTaskId = TASK_ID_NO_PIP;
private ComponentName mPipComponentName;
private MediaController mPipMediaController;
- private boolean mOnboardingShown;
private String[] mLastPackagesResourceGranted;
private final PinnedStackListener mPinnedStackListener = new PinnedStackListener();
@@ -184,7 +178,7 @@
@Override
public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
- Rect animatingBounds, boolean fromImeAdjustement) {
+ Rect animatingBounds, boolean fromImeAdjustement, int displayRotation) {
mHandler.post(() -> {
mDefaultPipBounds.set(normalBounds);
});
@@ -212,8 +206,6 @@
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
mContext.registerReceiver(mBroadcastReceiver, intentFilter);
- mOnboardingShown = Prefs.getBoolean(
- mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, false);
if (sSettingsPackageAndClassNamePairList == null) {
String[] settings = mContext.getResources().getStringArray(
@@ -267,7 +259,7 @@
// 1. Configuration changed due to the language change (RTL <-> RTL)
// 2. SystemUI restarts after the crash
mPipBounds = isSettingsShown() ? mSettingsPipBounds : mDefaultPipBounds;
- resizePinnedStack(getPinnedStackInfo() == null ? STATE_NO_PIP : STATE_PIP_OVERLAY);
+ resizePinnedStack(getPinnedStackInfo() == null ? STATE_NO_PIP : STATE_PIP);
}
/**
@@ -281,7 +273,7 @@
* Shows the picture-in-picture menu if an activity is in picture-in-picture mode.
*/
public void showPictureInPictureMenu() {
- if (mState == STATE_PIP_OVERLAY) {
+ if (mState == STATE_PIP) {
resizePinnedStack(STATE_PIP_MENU);
}
}
@@ -325,16 +317,6 @@
}
/**
- * Shows PIP overlay UI by launching {@link PipOverlayActivity}. It also locates the pinned
- * stack to the default PIP bound {@link com.android.internal.R.string
- * .config_defaultPictureInPictureBounds}.
- */
- private void showPipOverlay() {
- if (DEBUG) Log.d(TAG, "showPipOverlay()");
- PipOverlayActivity.showPipOverlay(mContext);
- }
-
- /**
* Suspends resizing operation on the Pip until {@link #resumePipResizing} is called
* @param reason The reason for suspending resizing operations on the Pip.
*/
@@ -388,7 +370,7 @@
case STATE_PIP_MENU:
mCurrentPipBounds = mMenuModePipBounds;
break;
- case STATE_PIP_OVERLAY:
+ case STATE_PIP:
mCurrentPipBounds = mPipBounds;
break;
default:
@@ -454,17 +436,6 @@
mMediaListeners.remove(listener);
}
- private void launchPipOnboardingActivityIfNeeded() {
- if (DEBUG_FORCE_ONBOARDING || !mOnboardingShown) {
- mOnboardingShown = true;
- Prefs.putBoolean(mContext, TV_PICTURE_IN_PICTURE_ONBOARDING_SHOWN, true);
-
- Intent intent = new Intent(mContext, PipOnboardingActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
- }
- }
-
/**
* Returns {@code true} if PIP is shown.
*/
@@ -617,11 +588,11 @@
return;
}
}
- if (mState == STATE_PIP_OVERLAY) {
+ if (mState == STATE_PIP) {
Rect bounds = isSettingsShown() ? mSettingsPipBounds : mDefaultPipBounds;
if (mPipBounds != bounds) {
mPipBounds = bounds;
- resizePinnedStack(STATE_PIP_OVERLAY);
+ resizePinnedStack(STATE_PIP);
}
}
}
@@ -641,10 +612,9 @@
mPipTaskId = stackInfo.taskIds[stackInfo.taskIds.length - 1];
mPipComponentName = ComponentName.unflattenFromString(
stackInfo.taskNames[stackInfo.taskNames.length - 1]);
- // Set state to overlay so we show it when the pinned stack animation ends.
- mState = STATE_PIP_OVERLAY;
+ // Set state to STATE_PIP so we show it when the pinned stack animation ends.
+ mState = STATE_PIP;
mCurrentPipBounds = mPipBounds;
- launchPipOnboardingActivityIfNeeded();
mMediaSessionManager.addOnActiveSessionsChangedListener(
mActiveMediaSessionListener, null);
updateMediaController(mMediaSessionManager.getActiveSessions(null));
@@ -671,9 +641,6 @@
return;
}
switch (mState) {
- case STATE_PIP_OVERLAY:
- showPipOverlay();
- break;
case STATE_PIP_MENU:
showPipMenu();
break;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
index 9945079..ce1bea1 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java
@@ -56,7 +56,7 @@
private void restorePipAndFinish() {
if (mRestorePipSizeWhenClose) {
// When PIP menu activity is closed, restore to the default position.
- mPipManager.resizePinnedStack(PipManager.STATE_PIP_OVERLAY);
+ mPipManager.resizePinnedStack(PipManager.STATE_PIP);
}
finish();
}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipOnboardingActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipOnboardingActivity.java
deleted file mode 100644
index 423530a..0000000
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipOnboardingActivity.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.pip.tv;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.app.Activity;
-import android.graphics.drawable.AnimationDrawable;
-import android.os.Bundle;
-import android.view.View;
-import android.view.KeyEvent;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-
-/**
- * Activity to show an overlay on top of PIP activity to show how to pop up PIP menu.
- */
-public class PipOnboardingActivity extends Activity implements PipManager.Listener {
- private final PipManager mPipManager = PipManager.getInstance();
- private AnimatorSet mEnterAnimator;
-
- @Override
- protected void onCreate(Bundle bundle) {
- super.onCreate(bundle);
- setContentView(R.layout.tv_pip_onboarding);
- findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- finish();
- }
- });
-
- mPipManager.addListener(this);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- mEnterAnimator = new AnimatorSet();
- mEnterAnimator.playTogether(
- loadAnimator(R.id.background, R.anim.tv_pip_onboarding_background_enter_animation),
- loadAnimator(R.id.remote, R.anim.tv_pip_onboarding_image_enter_animation),
- loadAnimator(R.id.remote_button, R.anim.tv_pip_onboarding_image_enter_animation),
- loadAnimator(R.id.title, R.anim.tv_pip_onboarding_title_enter_animation),
- loadAnimator(R.id.description,
- R.anim.tv_pip_onboarding_description_enter_animation),
- loadAnimator(R.id.button, R.anim.tv_pip_onboarding_button_enter_animation));
- mEnterAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- ImageView button = findViewById(R.id.remote_button);
- ((AnimationDrawable) button.getDrawable()).start();
- }
- });
- int delay = getResources().getInteger(R.integer.tv_pip_onboarding_anim_start_delay);
- mEnterAnimator.setStartDelay(delay);
- mEnterAnimator.start();
- }
-
- private Animator loadAnimator(int viewResId, int animResId) {
- Animator animator = AnimatorInflater.loadAnimator(this, animResId);
- animator.setTarget(findViewById(viewResId));
- return animator;
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (mEnterAnimator.isStarted()) {
- return true;
- }
- return super.onKeyUp(keyCode, event);
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (mEnterAnimator.isStarted()) {
- return true;
- }
- return super.onKeyDown(keyCode, event);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- finish();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- mPipManager.removeListener(this);
- }
-
- @Override
- public void onPipEntered() { }
-
- @Override
- public void onPipActivityClosed() {
- finish();
- }
-
- @Override
- public void onShowPipMenu() {
- finish();
- }
-
- @Override
- public void onMoveToFullscreen() {
- finish();
- }
-
- @Override
- public void onPipResizeAboutToStart() { }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipOverlayActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipOverlayActivity.java
deleted file mode 100644
index f52121f..0000000
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipOverlayActivity.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.pip.tv;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.app.Activity;
-import android.app.ActivityOptions;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.view.View;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-
-import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-
-/**
- * Activity to show an overlay on top of PIP activity to show how to pop up PIP menu.
- */
-public class PipOverlayActivity extends Activity implements PipManager.Listener {
- private static final long SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS = 4000;
-
- /**
- * A flag to ensure the single instance of PipOverlayActivity to prevent it from restarting.
- * Note that {@link PipManager} moves the PIPed activity to fullscreen if the activity is
- * restarted. It's because the activity may be started by the Launcher or an intent again,
- * but we don't want do so for the PipOverlayActivity.
- */
- private static boolean sActivityCreated;
-
- private final PipManager mPipManager = PipManager.getInstance();
- private final Handler mHandler = new Handler();
- private View mGuideOverlayView;
- private View mGuideButtonsView;
- private ImageView mGuideButtonPlayPauseImageView;
- private final Runnable mHideGuideOverlayRunnable = new Runnable() {
- public void run() {
- mFadeOutAnimation.start();
- }
- };
- private Animator mFadeInAnimation;
- private Animator mFadeOutAnimation;
-
- /**
- * Shows PIP overlay UI only if it's not there.
- */
- static void showPipOverlay(Context context) {
- if (!sActivityCreated) {
- Intent intent = new Intent(context, PipOverlayActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- final ActivityOptions options = ActivityOptions.makeBasic();
- options.setLaunchStackId(PINNED_STACK_ID);
- context.startActivity(intent, options.toBundle());
- }
- }
-
- @Override
- protected void onCreate(Bundle bundle) {
- super.onCreate(bundle);
- sActivityCreated = true;
- setContentView(R.layout.tv_pip_overlay);
- mGuideOverlayView = findViewById(R.id.guide_overlay);
- mPipManager.addListener(this);
- mFadeInAnimation = AnimatorInflater.loadAnimator(
- this, R.anim.tv_pip_overlay_fade_in_animation);
- mFadeInAnimation.setTarget(mGuideOverlayView);
- mFadeOutAnimation = AnimatorInflater.loadAnimator(
- this, R.anim.tv_pip_overlay_fade_out_animation);
- mFadeOutAnimation.setTarget(mGuideOverlayView);
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- mFadeInAnimation.start();
- mHandler.removeCallbacks(mHideGuideOverlayRunnable);
- mHandler.postDelayed(mHideGuideOverlayRunnable, SHOW_GUIDE_OVERLAY_VIEW_DURATION_MS);
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- mHandler.removeCallbacks(mHideGuideOverlayRunnable);
- finish();
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- sActivityCreated = false;
- mHandler.removeCallbacksAndMessages(null);
- mPipManager.removeListener(this);
- mPipManager.resumePipResizing(
- PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH);
- }
-
- @Override
- public void onPipEntered() { }
-
- @Override
- public void onPipActivityClosed() {
- finish();
- }
-
- @Override
- public void onShowPipMenu() {
- finish();
- }
-
- @Override
- public void onMoveToFullscreen() {
- finish();
- }
-
- @Override
- public void onPipResizeAboutToStart() {
- finish();
- mPipManager.suspendPipResizing(
- PipManager.SUSPEND_PIP_RESIZE_REASON_WAITING_FOR_OVERLAY_ACTIVITY_FINISH);
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index d51fe8a..53c36e6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -167,6 +167,9 @@
if (mAlarmShowing) {
builder.addFloat(mDate, "alpha", 1, 0)
.addFloat(mDateTimeGroup, "translationX", 0, -mDate.getWidth());
+ } else {
+ mDate.setAlpha(1);
+ mDateTimeGroup.setTranslationX(0);
}
mAnimator = builder.build();
setExpansion(mExpansionAmount);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 38485c7..f5e096eb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -98,9 +98,6 @@
setupTileLayout();
- mFooter = new QSSecurityFooter(this, context);
- addView(mFooter.getView());
-
mPageIndicator = LayoutInflater.from(context).inflate(
R.layout.qs_page_indicator, this, false);
addView(mPageIndicator);
@@ -110,6 +107,9 @@
addDivider();
+ mFooter = new QSSecurityFooter(this, context);
+ addView(mFooter.getView());
+
updateResources();
mBrightnessController = new BrightnessController(getContext(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 5b9d95d..51c6ad8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -1,4 +1,3 @@
-
/*
* Copyright (C) 2014 The Android Open Source Project
*
@@ -51,24 +50,21 @@
private final View mRootView;
private final TextView mFooterText;
private final ImageView mFooterIcon;
- private final ImageView mFooterIcon2;
private final Context mContext;
private final Callback mCallback = new Callback();
private final SecurityController mSecurityController;
private final ActivityStarter mActivityStarter;
private final Handler mMainHandler;
+ private final View mDivider;
private AlertDialog mDialog;
private QSTileHost mHost;
protected H mHandler;
private boolean mIsVisible;
- private boolean mIsIconVisible;
- private boolean mIsIcon2Visible;
private CharSequence mFooterTextContent = null;
private int mFooterTextId;
private int mFooterIconId;
- private int mFooterIcon2Id;
public QSSecurityFooter(QSPanel qsPanel, Context context) {
mRootView = LayoutInflater.from(context)
@@ -76,14 +72,13 @@
mRootView.setOnClickListener(this);
mFooterText = (TextView) mRootView.findViewById(R.id.footer_text);
mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon);
- mFooterIcon2 = (ImageView) mRootView.findViewById(R.id.footer_icon2);
- mFooterIconId = R.drawable.ic_qs_vpn;
- mFooterIcon2Id = R.drawable.ic_qs_network_logging;
+ mFooterIconId = R.drawable.ic_info_outline;
mContext = context;
mMainHandler = new Handler(Looper.getMainLooper());
mActivityStarter = Dependency.get(ActivityStarter.class);
mSecurityController = Dependency.get(SecurityController.class);
mHandler = new H(Dependency.get(Dependency.BG_LOOPER));
+ mDivider = qsPanel == null ? null : qsPanel.getDivider();
}
public void setHostEnvironment(QSTileHost host) {
@@ -130,45 +125,102 @@
}
private void handleRefreshState() {
- boolean isVpnEnabled = mSecurityController.isVpnEnabled();
- boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
- mIsIconVisible = isVpnEnabled || isNetworkLoggingEnabled;
- mIsIcon2Visible = isVpnEnabled && isNetworkLoggingEnabled;
- if (mSecurityController.isDeviceManaged()) {
- final CharSequence organizationName =
- mSecurityController.getDeviceOwnerOrganizationName();
- if (organizationName != null) {
- mFooterTextContent = mContext.getResources().getString(
- R.string.do_disclosure_with_name, organizationName);
- } else {
- mFooterTextContent =
- mContext.getResources().getString(R.string.do_disclosure_generic);
- }
- mIsVisible = true;
- int footerIconId = isVpnEnabled
- ? R.drawable.ic_qs_vpn
- : R.drawable.ic_qs_network_logging;
- if (mFooterIconId != footerIconId) {
- mFooterIconId = footerIconId;
- mMainHandler.post(mUpdateIcon);
- }
- } else {
- boolean isBranded = mSecurityController.isVpnBranded();
- mFooterTextContent = mContext.getResources().getText(
- isBranded ? R.string.branded_vpn_footer : R.string.vpn_footer);
- // Update the VPN footer icon, if needed.
- int footerIconId = isVpnEnabled
- ? (isBranded ? R.drawable.ic_qs_branded_vpn : R.drawable.ic_qs_vpn)
- : R.drawable.ic_qs_network_logging;
- if (mFooterIconId != footerIconId) {
- mFooterIconId = footerIconId;
- mMainHandler.post(mUpdateIcon);
- }
- mIsVisible = mIsIconVisible;
+ final boolean isDeviceManaged = mSecurityController.isDeviceManaged();
+ final boolean hasWorkProfile = mSecurityController.hasWorkProfile();
+ final boolean hasCACerts = mSecurityController.hasCACertInCurrentUser();
+ final boolean hasCACertsInWorkProfile = mSecurityController.hasCACertInWorkProfile();
+ final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
+ final String vpnName = mSecurityController.getPrimaryVpnName();
+ final String vpnNameWorkProfile = mSecurityController.getWorkProfileVpnName();
+ final CharSequence organizationName = mSecurityController.getDeviceOwnerOrganizationName();
+ final CharSequence workProfileName = mSecurityController.getWorkProfileOrganizationName();
+ // Update visibility of footer
+ mIsVisible = isDeviceManaged || hasCACerts || hasCACertsInWorkProfile ||
+ vpnName != null || vpnNameWorkProfile != null;
+ // Update the string
+ mFooterTextContent = getFooterText(isDeviceManaged, hasWorkProfile,
+ hasCACerts, hasCACertsInWorkProfile, isNetworkLoggingEnabled, vpnName,
+ vpnNameWorkProfile, organizationName, workProfileName);
+ // Update the icon
+ int footerIconId = vpnName != null || vpnNameWorkProfile != null
+ ? R.drawable.ic_qs_vpn
+ : R.drawable.ic_info_outline;
+ if (mFooterIconId != footerIconId) {
+ mFooterIconId = footerIconId;
+ mMainHandler.post(mUpdateIcon);
}
mMainHandler.post(mUpdateDisplayState);
}
+ protected CharSequence getFooterText(boolean isDeviceManaged, boolean hasWorkProfile,
+ boolean hasCACerts, boolean hasCACertsInWorkProfile, boolean isNetworkLoggingEnabled,
+ String vpnName, String vpnNameWorkProfile, CharSequence organizationName,
+ CharSequence workProfileName) {
+ if (isDeviceManaged) {
+ if (hasCACerts || hasCACertsInWorkProfile || isNetworkLoggingEnabled) {
+ if (organizationName == null) {
+ return mContext.getString(
+ R.string.quick_settings_disclosure_management_monitoring);
+ }
+ return mContext.getString(
+ R.string.quick_settings_disclosure_named_management_monitoring,
+ organizationName);
+ }
+ if (vpnName != null && vpnNameWorkProfile != null) {
+ if (organizationName == null) {
+ return mContext.getString(R.string.quick_settings_disclosure_management_vpns);
+ }
+ return mContext.getString(R.string.quick_settings_disclosure_named_management_vpns,
+ organizationName);
+ }
+ if (vpnName != null || vpnNameWorkProfile != null) {
+ if (organizationName == null) {
+ return mContext.getString(
+ R.string.quick_settings_disclosure_management_named_vpn,
+ vpnName != null ? vpnName : vpnNameWorkProfile);
+ }
+ return mContext.getString(
+ R.string.quick_settings_disclosure_named_management_named_vpn,
+ organizationName,
+ vpnName != null ? vpnName : vpnNameWorkProfile);
+ }
+ if (organizationName == null) {
+ return mContext.getString(R.string.quick_settings_disclosure_management);
+ }
+ return mContext.getString(R.string.quick_settings_disclosure_named_management,
+ organizationName);
+ } // end if(isDeviceManaged)
+ if (hasCACertsInWorkProfile) {
+ if (workProfileName == null) {
+ return mContext.getString(
+ R.string.quick_settings_disclosure_managed_profile_monitoring);
+ }
+ return mContext.getString(
+ R.string.quick_settings_disclosure_named_managed_profile_monitoring,
+ workProfileName);
+ }
+ if (hasCACerts) {
+ return mContext.getString(R.string.quick_settings_disclosure_monitoring);
+ }
+ if (vpnName != null && vpnNameWorkProfile != null) {
+ return mContext.getString(R.string.quick_settings_disclosure_vpns);
+ }
+ if (vpnNameWorkProfile != null) {
+ return mContext.getString(R.string.quick_settings_disclosure_managed_profile_named_vpn,
+ vpnNameWorkProfile);
+ }
+ if (vpnName != null) {
+ if (hasWorkProfile) {
+ return mContext.getString(
+ R.string.quick_settings_disclosure_personal_profile_named_vpn,
+ vpnName);
+ }
+ return mContext.getString(R.string.quick_settings_disclosure_named_vpn,
+ vpnName);
+ }
+ return null;
+ }
+
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_NEGATIVE) {
@@ -182,18 +234,15 @@
final String profileOwnerPackage = mSecurityController.getProfileOwnerName();
final boolean isNetworkLoggingEnabled = mSecurityController.isNetworkLoggingEnabled();
final String primaryVpn = mSecurityController.getPrimaryVpnName();
- final String profileVpn = mSecurityController.getProfileVpnName();
+ final String profileVpn = mSecurityController.getWorkProfileVpnName();
final CharSequence deviceOwnerOrganization =
mSecurityController.getDeviceOwnerOrganizationName();
boolean hasProfileOwner = mSecurityController.hasProfileOwner();
- boolean isBranded = deviceOwnerPackage == null && mSecurityController.isVpnBranded();
mDialog = new SystemUIDialog(mContext);
- if (!isBranded) {
- mDialog.setTitle(getTitle(deviceOwnerPackage));
- }
+ mDialog.setTitle(getTitle(deviceOwnerPackage));
CharSequence msg = getMessage(deviceOwnerPackage, profileOwnerPackage, primaryVpn,
- profileVpn, deviceOwnerOrganization, hasProfileOwner, isBranded);
+ profileVpn, deviceOwnerOrganization, hasProfileOwner);
if (deviceOwnerPackage == null) {
mDialog.setMessage(msg);
if (mSecurityController.isVpnEnabled() && !mSecurityController.isVpnRestricted()) {
@@ -235,7 +284,7 @@
}
}
- mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(isBranded), this);
+ mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
mDialog.show();
mDialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
@@ -244,13 +293,13 @@
return mContext.getString(R.string.status_bar_settings_settings_button);
}
- private String getPositiveButton(boolean isBranded) {
- return mContext.getString(isBranded ? android.R.string.ok : R.string.quick_settings_done);
+ private String getPositiveButton() {
+ return mContext.getString(R.string.quick_settings_done);
}
protected CharSequence getMessage(String deviceOwnerPackage, String profileOwnerPackage,
String primaryVpn, String profileVpn, CharSequence deviceOwnerOrganization,
- boolean hasProfileOwner, boolean isBranded) {
+ boolean hasProfileOwner) {
if (deviceOwnerPackage != null) {
final SpannableStringBuilder message = new SpannableStringBuilder();
if (deviceOwnerOrganization != null) {
@@ -273,13 +322,8 @@
return mContext.getString(R.string.monitoring_description_app_personal_work,
profileOwnerPackage, profileVpn, primaryVpn);
} else {
- if (isBranded) {
- return mContext.getString(R.string.branded_monitoring_description_app_personal,
- primaryVpn);
- } else {
- return mContext.getString(R.string.monitoring_description_app_personal,
- primaryVpn);
- }
+ return mContext.getString(R.string.monitoring_description_app_personal,
+ primaryVpn);
}
} else if (profileVpn != null) {
return mContext.getString(R.string.monitoring_description_app_work,
@@ -305,7 +349,6 @@
@Override
public void run() {
mFooterIcon.setImageResource(mFooterIconId);
- mFooterIcon2.setImageResource(mFooterIcon2Id);
}
};
@@ -316,8 +359,7 @@
mFooterText.setText(mFooterTextContent);
}
mRootView.setVisibility(mIsVisible ? View.VISIBLE : View.GONE);
- mFooterIcon.setVisibility(mIsIconVisible ? View.VISIBLE : View.INVISIBLE);
- mFooterIcon2.setVisibility(mIsIcon2Visible ? View.VISIBLE : View.INVISIBLE);
+ if (mDivider != null) mDivider.setVisibility(mIsVisible ? View.GONE : View.VISIBLE);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index 7518527a..e457d72 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -77,6 +77,9 @@
BatteryMeterView battery = findViewById(R.id.battery);
battery.setForceShowPercent(true);
+ // Don't show the Wi-Fi indicator here, because it is shown just below in the tile.
+ SignalClusterView signalCluster = findViewById(R.id.signal_cluster);
+ signalCluster.setForceBlockWifi();
mActivityStarter = Dependency.get(ActivityStarter.class);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 6781c16..976efb2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -340,12 +340,12 @@
switch (state) {
case Tile.STATE_UNAVAILABLE:
return Utils.getDisabled(context,
- Utils.getColorAttr(context, android.R.attr.textColorTertiary));
+ Utils.getColorAttr(context, android.R.attr.textColorPrimary));
case Tile.STATE_INACTIVE:
return Utils.getDisabled(context,
- Utils.getColorAttr(context, android.R.attr.textColorSecondary));
+ Utils.getColorAttr(context, android.R.attr.colorForeground));
case Tile.STATE_ACTIVE:
- return Utils.getColorAttr(context, attr.textColorSecondary);
+ return Utils.getColorAttr(context, android.R.attr.textColorPrimary);
default:
Log.e("QSTile", "Invalid state " + state);
return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index 92ff17a1..d74e3ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -128,7 +128,7 @@
state.value = mDataController.isMobileDataSupported()
&& mDataController.isMobileDataEnabled();
state.icon = ResourceIcon.get(R.drawable.ic_data_unavailable);
- state.state = cb.airplaneModeEnabled || !cb.enabled ? Tile.STATE_UNAVAILABLE
+ state.state = cb.airplaneModeEnabled || !cb.enabled || cb.noSim ? Tile.STATE_UNAVAILABLE
: state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
if (state.state == Tile.STATE_ACTIVE) {
state.icon = ResourceIcon.get(R.drawable.ic_data_on);
@@ -161,44 +161,27 @@
private static final class CallbackInfo {
boolean enabled;
- boolean wifiEnabled;
boolean airplaneModeEnabled;
- String signalContentDescription;
- int dataTypeIconId;
- String dataContentDescription;
boolean activityIn;
boolean activityOut;
- String enabledDesc;
boolean noSim;
- boolean isDataTypeIconWide;
boolean roaming;
}
private final class CellSignalCallback implements SignalCallback {
private final CallbackInfo mInfo = new CallbackInfo();
- @Override
- public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
- boolean activityIn, boolean activityOut, String description, boolean isTransient) {
- mInfo.wifiEnabled = enabled;
- refreshState(mInfo);
- }
@Override
- public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
- int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
- String description, boolean isWide, int subId, boolean roaming) {
- if (qsIcon == null) {
+ public void setMobileDataIndicators(IconState statusIcon, int statusType,
+ boolean activityIn, boolean activityOut, String typeContentDescription,
+ int subId, boolean roaming, boolean isEmergency) {
+ if (statusIcon == null) {
// Not data sim, don't display.
return;
}
- mInfo.enabled = qsIcon.visible;
- mInfo.signalContentDescription = qsIcon.contentDescription;
- mInfo.dataTypeIconId = qsType;
- mInfo.dataContentDescription = typeContentDescription;
+ mInfo.enabled = statusIcon.visible;
mInfo.activityIn = activityIn;
mInfo.activityOut = activityOut;
- mInfo.enabledDesc = description;
- mInfo.isDataTypeIconWide = qsType != 0 && isWide;
mInfo.roaming = roaming;
refreshState(mInfo);
}
@@ -206,15 +189,6 @@
@Override
public void setNoSims(boolean show) {
mInfo.noSim = show;
- if (mInfo.noSim) {
- // Make sure signal gets cleared out when no sims.
- mInfo.dataTypeIconId = 0;
- // Show a No SIMs description to avoid emergency calls message.
- mInfo.enabled = true;
- mInfo.enabledDesc = mContext.getString(
- R.string.keyguard_missing_sim_message_short);
- mInfo.signalContentDescription = mInfo.enabledDesc;
- }
refreshState(mInfo);
}
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 397b78d..429ace6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -65,7 +65,7 @@
import android.os.UserManager;
import android.provider.Settings;
import android.util.ArraySet;
-import android.util.LauncherIcons;
+import android.util.IconDrawableFactory;
import android.util.Log;
import android.util.MutableBoolean;
import android.view.Display;
@@ -82,7 +82,6 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.R;
import com.android.systemui.pip.tv.PipMenuActivity;
-import com.android.systemui.pip.tv.PipOnboardingActivity;
import com.android.systemui.recents.Recents;
import com.android.systemui.recents.RecentsDebugFlags;
import com.android.systemui.recents.RecentsImpl;
@@ -113,7 +112,6 @@
final static List<String> sRecentsBlacklist;
static {
sRecentsBlacklist = new ArrayList<>();
- sRecentsBlacklist.add(PipOnboardingActivity.class.getName());
sRecentsBlacklist.add(PipMenuActivity.class.getName());
}
@@ -123,6 +121,7 @@
ActivityManager mAm;
IActivityManager mIam;
PackageManager mPm;
+ IconDrawableFactory mDrawableFactory;
IPackageManager mIpm;
AssistUtils mAssistUtils;
WindowManager mWm;
@@ -141,7 +140,6 @@
int mDummyThumbnailHeight;
Paint mBgProtectionPaint;
Canvas mBgProtectionCanvas;
- LauncherIcons mLauncherIcons;
private final Handler mHandler = new H();
@@ -260,6 +258,7 @@
mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
mIam = ActivityManager.getService();
mPm = context.getPackageManager();
+ mDrawableFactory = IconDrawableFactory.newInstance(context);
mIpm = AppGlobals.getPackageManager();
mAssistUtils = new AssistUtils(context);
mWm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
@@ -298,8 +297,6 @@
Collections.addAll(sRecentsBlacklist,
res.getStringArray(R.array.recents_blacklist_array));
-
- mLauncherIcons = new LauncherIcons(context);
}
/**
@@ -810,13 +807,19 @@
* Returns the content description for a given task, badging it if necessary. The content
* description joins the app and activity labels.
*/
- public String getBadgedContentDescription(ActivityInfo info, int userId, Resources res) {
+ public String getBadgedContentDescription(ActivityInfo info, int userId,
+ ActivityManager.TaskDescription td, Resources res) {
// If we are mocking, then return a mock label
if (RecentsDebugFlags.Static.EnableMockTasks) {
return "Recent Task Content Description: " + userId;
}
- String activityLabel = info.loadLabel(mPm).toString();
+ String activityLabel;
+ if (td != null && td.getLabel() != null) {
+ activityLabel = td.getLabel();
+ } else {
+ activityLabel = info.loadLabel(mPm).toString();
+ }
String applicationLabel = info.applicationInfo.loadLabel(mPm).toString();
String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId);
return applicationLabel.equals(activityLabel) ? badgedApplicationLabel
@@ -836,8 +839,7 @@
return new ColorDrawable(0xFF666666);
}
- Drawable icon = mLauncherIcons.wrapIconDrawableWithShadow(info.loadIcon(mPm));
- return getBadgedIcon(icon, userId);
+ return mDrawableFactory.getBadgedIcon(info, info.applicationInfo, userId);
}
/**
@@ -852,8 +854,7 @@
return new ColorDrawable(0xFF666666);
}
- Drawable icon = mLauncherIcons.wrapIconDrawableWithShadow(appInfo.loadIcon(mPm));
- return getBadgedIcon(icon, userId);
+ return mDrawableFactory.getBadgedIcon(appInfo, userId);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 5c25bfd..78c71a1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -182,7 +182,8 @@
// Load the title, icon, and color
ActivityInfo info = loader.getAndUpdateActivityInfo(taskKey);
String title = loader.getAndUpdateActivityTitle(taskKey, t.taskDescription);
- String titleDescription = loader.getAndUpdateContentDescription(taskKey, res);
+ String titleDescription = loader.getAndUpdateContentDescription(taskKey,
+ t.taskDescription, res);
String dismissDescription = String.format(dismissDescFormat, titleDescription);
String appInfoDescription = String.format(appInfoDescFormat, titleDescription);
Drawable icon = isStackTask
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
index e8ffb91..5e78b61 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java
@@ -449,7 +449,8 @@
* Returns the cached task content description if the task key is not expired, updating the
* cache if it is.
*/
- String getAndUpdateContentDescription(Task.TaskKey taskKey, Resources res) {
+ String getAndUpdateContentDescription(Task.TaskKey taskKey, ActivityManager.TaskDescription td,
+ Resources res) {
SystemServicesProxy ssp = Recents.getSystemServices();
// Return the cached content description if it exists
@@ -461,8 +462,15 @@
// All short paths failed, load the label from the activity info and cache it
ActivityInfo activityInfo = getAndUpdateActivityInfo(taskKey);
if (activityInfo != null) {
- label = ssp.getBadgedContentDescription(activityInfo, taskKey.userId, res);
- mContentDescriptionCache.put(taskKey, label);
+ label = ssp.getBadgedContentDescription(activityInfo, taskKey.userId, td, res);
+ if (td == null) {
+ // Only add to the cache if the task description is null, otherwise, it is possible
+ // for the task description to change between calls without the last active time
+ // changing (ie. between preloading and Overview starting) which would lead to stale
+ // content descriptions
+ // TODO: Investigate improving this
+ mContentDescriptionCache.put(taskKey, label);
+ }
return label;
}
// If the content description does not exist, return an empty label for now, but do not
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 677642e..dff09bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -1115,6 +1115,10 @@
mNotificationInflater.setInflateExceptionHandler(inflateExceptionHandler);
}
+ public void setNeedsRedaction(boolean needsRedaction) {
+ mNotificationInflater.setRedactAmbient(needsRedaction);
+ }
+
public interface ExpansionLogger {
public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
@@ -1490,7 +1494,7 @@
return getActualHeight();
}
if (mGuts != null && mGuts.isExposed()) {
- return mGuts.getHeight();
+ return mGuts.getIntrinsicHeight();
} else if ((isChildInGroup() && !isGroupExpanded())) {
return mPrivateLayout.getMinHeight();
} else if (mShowAmbient) {
@@ -1831,7 +1835,9 @@
@Override
public int getMinHeight() {
- if (!mOnKeyguard && mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) {
+ if (mGuts != null && mGuts.isExposed()) {
+ return mGuts.getIntrinsicHeight();
+ } else if (!mOnKeyguard && mIsHeadsUp && mHeadsUpManager.isTrackingHeadsUp()) {
return getPinnedHeadsUpHeight(false /* atLeastMinHeight */);
} else if (mIsSummaryWithChildren && !isGroupExpanded() && !mShowingPublic) {
return mChildrenContainer.getMinHeight();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 8f160dc..6098565 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -428,6 +428,9 @@
mAmbientChild.animate().cancel();
removeView(mAmbientChild);
}
+ if (child == null) {
+ return;
+ }
addView(child);
mAmbientChild = child;
mAmbientWrapper = NotificationViewWrapper.wrap(getContext(), child,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
index 2713f58..1375310 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGuts.java
@@ -39,6 +39,7 @@
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -73,7 +74,8 @@
private Handler mHandler;
private Runnable mFalsingCheck;
private boolean mNeedsFalsingProtection;
- private OnGutsClosedListener mListener;
+ private OnGutsClosedListener mClosedListener;
+ private OnHeightChangedListener mHeightListener;
private GutsContent mGutsContent;
@@ -87,6 +89,11 @@
public View getContentView();
/**
+ * @return the actual height of the content.
+ */
+ public int getActualHeight();
+
+ /**
* Called when the guts view have been told to close, typically after an outside
* interaction. Returning {@code true} here will prevent the guts view to close.
*/
@@ -102,6 +109,10 @@
public void onGutsClosed(NotificationGuts guts);
}
+ public interface OnHeightChangedListener {
+ public void onHeightChanged(NotificationGuts guts);
+ }
+
interface OnSettingsClickListener {
void onClick(View v, int appUid);
}
@@ -125,7 +136,6 @@
public NotificationGuts(Context context) {
this(context, null);
-
}
public void setGutsContent(GutsContent content) {
@@ -189,8 +199,8 @@
public void closeControls(int x, int y, boolean save) {
if (getWindowToken() == null) {
- if (mListener != null) {
- mListener.onGutsClosed(this);
+ if (mClosedListener != null) {
+ mClosedListener.onGutsClosed(this);
}
return;
}
@@ -198,8 +208,8 @@
animateClose(x, y);
}
setExposed(false, mNeedsFalsingProtection);
- if (mListener != null) {
- mListener.onGutsClosed(this);
+ if (mClosedListener != null) {
+ mClosedListener.onGutsClosed(this);
}
}
@@ -234,6 +244,10 @@
return mActualHeight;
}
+ public int getIntrinsicHeight() {
+ return mGutsContent != null && mExposed ? mGutsContent.getActualHeight() : getHeight();
+ }
+
public void setClipTopAmount(int clipTopAmount) {
mClipTopAmount = clipTopAmount;
invalidate();
@@ -251,10 +265,21 @@
}
public void setClosedListener(OnGutsClosedListener listener) {
- mListener = listener;
+ mClosedListener = listener;
+ }
+
+ public void setHeightChangedListener(OnHeightChangedListener listener) {
+ mHeightListener = listener;
+ }
+
+ protected void onHeightChanged() {
+ if (mHeightListener != null) {
+ mHeightListener.onHeightChanged(this);
+ }
}
public void setExposed(boolean exposed, boolean needsFalsingProtection) {
+ final boolean wasExposed = mExposed;
mExposed = exposed;
mNeedsFalsingProtection = needsFalsingProtection;
if (mExposed && mNeedsFalsingProtection) {
@@ -262,6 +287,13 @@
} else {
mHandler.removeCallbacks(mFalsingCheck);
}
+ if (wasExposed != mExposed && mGutsContent != null) {
+ final View contentView = mGutsContent.getContentView();
+ contentView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ if (mExposed) {
+ contentView.requestAccessibilityFocus();
+ }
+ }
}
public boolean willBeRemoved() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
index 7928575..f3c7b84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java
@@ -35,6 +35,7 @@
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Switch;
@@ -57,6 +58,7 @@
private INotificationManager mINotificationManager;
private String mPkg;
+ private String mAppName;
private int mAppUid;
private List<NotificationChannel> mNotificationChannels;
private NotificationChannel mSingleNotificationChannel;
@@ -125,7 +127,7 @@
}
}
- String appName = mPkg;
+ mAppName = mPkg;
Drawable pkgicon = null;
CharSequence channelNameText = "";
ApplicationInfo info = null;
@@ -137,7 +139,7 @@
| PackageManager.MATCH_DIRECT_BOOT_AWARE);
if (info != null) {
mAppUid = info.uid;
- appName = String.valueOf(pm.getApplicationLabel(info));
+ mAppName = String.valueOf(pm.getApplicationLabel(info));
pkgicon = pm.getApplicationIcon(info);
}
} catch (PackageManager.NameNotFoundException e) {
@@ -189,7 +191,7 @@
} else {
channelNameText = mSingleNotificationChannel.getName();
}
- ((TextView) findViewById(R.id.pkgname)).setText(appName);
+ ((TextView) findViewById(R.id.pkgname)).setText(mAppName);
((TextView) findViewById(R.id.channel_name)).setText(channelNameText);
// Set group information if this channel has an associated group.
@@ -309,6 +311,21 @@
});
}
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ if (mGutsContainer != null &&
+ event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+ if (mGutsContainer.isExposed()) {
+ event.getText().add(mContext.getString(
+ R.string.notification_channel_controls_opened_accessibility, mAppName));
+ } else {
+ event.getText().add(mContext.getString(
+ R.string.notification_channel_controls_closed_accessibility, mAppName));
+ }
+ }
+ }
+
private void updateSecondaryText() {
final boolean disabled = mSingleNotificationChannel != null &&
getSelectedImportance() == IMPORTANCE_NONE;
@@ -386,4 +403,9 @@
}
return false;
}
+
+ @Override
+ public int getActualHeight() {
+ return getHeight();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
index fee24b7..7563fd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMenuRow.java
@@ -227,6 +227,7 @@
if (mShouldShowMenu
&& !NotificationStackScrollLayout.isPinnedHeadsUp(view)
&& !mParent.areGutsExposed()
+ && !mParent.isDark()
&& (mCheckForDrag == null || !mHandler.hasCallbacks(mCheckForDrag))) {
// Only show the menu if we're not a heads up view and guts aren't exposed.
mCheckForDrag = new CheckForDrag();
@@ -316,6 +317,10 @@
}
private void dismiss(View animView, float velocity) {
+ if (mFadeAnimator != null) {
+ mFadeAnimator.cancel();
+ }
+ mHandler.removeCallbacks(mCheckForDrag);
mMenuSnappedTo = false;
mDismissing = true;
mSwipeHelper.dismiss(animView, velocity);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
index 4a3f112..ccc99c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationSnooze.java
@@ -21,35 +21,47 @@
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Color;
+import android.graphics.Typeface;
import android.service.notification.SnoozeCriterion;
import android.service.notification.StatusBarNotification;
+import android.text.SpannableString;
+import android.text.style.StyleSpan;
import android.util.AttributeSet;
import android.util.Log;
-import android.util.TypedValue;
+import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
public class NotificationSnooze extends LinearLayout
implements NotificationGuts.GutsContent, View.OnClickListener {
- private static final int MAX_ASSISTANT_SUGGESTIONS = 2;
+ private static final int MAX_ASSISTANT_SUGGESTIONS = 1;
private NotificationGuts mGutsContainer;
private NotificationSwipeActionHelper mSnoozeListener;
private StatusBarNotification mSbn;
private TextView mSelectedOptionText;
private TextView mUndoButton;
- private ViewGroup mSnoozeOptionView;
+ private ImageView mExpandButton;
+ private View mDivider;
+ private ViewGroup mSnoozeOptionContainer;
private List<SnoozeOption> mSnoozeOptions;
- private boolean mSnoozing;
+ private int mCollapsedHeight;
private SnoozeOption mSelectedOption;
+ private boolean mSnoozing;
+ private boolean mExpanded;
+ private AnimatorSet mExpandAnimation;
public NotificationSnooze(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -58,16 +70,21 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+ mCollapsedHeight = getResources().getDimensionPixelSize(R.dimen.snooze_snackbar_min_height);
+ findViewById(R.id.notification_snooze).setOnClickListener(this);
+ mSelectedOptionText = (TextView) findViewById(R.id.snooze_option_default);
+ mUndoButton = (TextView) findViewById(R.id.undo);
+ mUndoButton.setOnClickListener(this);
+ mExpandButton = (ImageView) findViewById(R.id.expand_button);
+ mDivider = findViewById(R.id.divider);
+ mDivider.setAlpha(0f);
+ mSnoozeOptionContainer = (ViewGroup) findViewById(R.id.snooze_options);
+ mSnoozeOptionContainer.setAlpha(0f);
+
// Create the different options based on list
mSnoozeOptions = getDefaultSnoozeOptions();
createOptionViews();
- // Snackbar
- mSelectedOptionText = findViewById(R.id.snooze_option_default);
- mSelectedOptionText.setOnClickListener(this);
- mUndoButton = findViewById(R.id.undo);
- mUndoButton.setOnClickListener(this);
-
// Default to first option in list
setSelected(mSnoozeOptions.get(0));
}
@@ -96,52 +113,68 @@
private SnoozeOption createOption(int descriptionResId, int minutes) {
Resources res = getResources();
- String resultText = String.format(
- res.getString(R.string.snoozed_for_time), res.getString(descriptionResId));
- return new SnoozeOption(null, minutes, res.getString(descriptionResId), resultText);
+ final String description = res.getString(descriptionResId);
+ String resultText = String.format(res.getString(R.string.snoozed_for_time), description);
+ SpannableString string = new SpannableString(resultText);
+ string.setSpan(new StyleSpan(Typeface.BOLD),
+ resultText.length() - description.length(), resultText.length(), 0 /* flags */);
+ return new SnoozeOption(null, minutes, res.getString(descriptionResId), string);
}
private void createOptionViews() {
- mSnoozeOptionView = findViewById(R.id.snooze_options);
- mSnoozeOptionView.removeAllViews();
- mSnoozeOptionView.setVisibility(View.GONE);
- final Resources res = getResources();
- final int textSize = res.getDimensionPixelSize(R.dimen.snooze_option_text_size);
- final int p = res.getDimensionPixelSize(R.dimen.snooze_option_padding);
-
- // Add all the options
+ mSnoozeOptionContainer.removeAllViews();
+ LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
for (int i = 0; i < mSnoozeOptions.size(); i++) {
SnoozeOption option = mSnoozeOptions.get(i);
- TextView tv = new TextView(getContext());
- tv.setTextColor(Color.WHITE);
- tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
- tv.setPadding(p, p, p, p);
- mSnoozeOptionView.addView(tv);
+ TextView tv = (TextView) inflater.inflate(R.layout.notification_snooze_option,
+ mSnoozeOptionContainer, false);
+ mSnoozeOptionContainer.addView(tv);
tv.setText(option.description);
tv.setTag(option);
tv.setOnClickListener(this);
}
+ }
- // Add the undo option as final item
- TextView tv = new TextView(getContext());
- tv.setTextColor(Color.WHITE);
- tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize);
- tv.setPadding(p, p, p, p);
- mSnoozeOptionView.addView(tv);
- tv.setText(R.string.snooze_option_dont_snooze);
- tv.setOnClickListener(this);
+ private void hideSelectedOption() {
+ final int childCount = mSnoozeOptionContainer.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ final View child = mSnoozeOptionContainer.getChildAt(i);
+ child.setVisibility(child.getTag() == mSelectedOption ? View.GONE : View.VISIBLE);
+ }
}
private void showSnoozeOptions(boolean show) {
- mSelectedOptionText.setVisibility(show ? View.GONE : View.VISIBLE);
- mUndoButton.setVisibility(show ? View.GONE : View.VISIBLE);
- mSnoozeOptionView.setVisibility(show ? View.VISIBLE : View.GONE);
+ mExpanded = show;
+ animateSnoozeOptions(show);
+ int drawableId = show ? com.android.internal.R.drawable.ic_collapse_notification
+ : com.android.internal.R.drawable.ic_expand_notification;
+ mExpandButton.setImageResource(drawableId);
+ if (mGutsContainer != null) {
+ mGutsContainer.onHeightChanged();
+ }
+ }
+
+ private void animateSnoozeOptions(boolean show) {
+ if (mExpandAnimation != null) {
+ mExpandAnimation.cancel();
+ }
+ ObjectAnimator dividerAnim = ObjectAnimator.ofFloat(mDivider, View.ALPHA,
+ mDivider.getAlpha(), show ? 1f : 0f);
+ ObjectAnimator optionAnim = ObjectAnimator.ofFloat(mSnoozeOptionContainer, View.ALPHA,
+ mSnoozeOptionContainer.getAlpha(), show ? 1f : 0f);
+ mExpandAnimation = new AnimatorSet();
+ mExpandAnimation.playTogether(dividerAnim, optionAnim);
+ mExpandAnimation.setDuration(150);
+ mExpandAnimation.setInterpolator(show ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT);
+ mExpandAnimation.start();
}
private void setSelected(SnoozeOption option) {
mSelectedOption = option;
mSelectedOptionText.setText(option.confirmation);
showSnoozeOptions(false);
+ hideSelectedOption();
}
@Override
@@ -153,17 +186,28 @@
final SnoozeOption tag = (SnoozeOption) v.getTag();
if (tag != null) {
setSelected(tag);
- } else if (id == R.id.snooze_option_default) {
- // Show more snooze options
- showSnoozeOptions(true);
+ } else if (id == R.id.notification_snooze) {
+ // Toggle snooze options
+ showSnoozeOptions(!mExpanded);
} else {
- undoSnooze();
+ // Undo snooze was selected
+ mSelectedOption = null;
+ int[] parentLoc = new int[2];
+ int[] targetLoc = new int[2];
+ mGutsContainer.getLocationOnScreen(parentLoc);
+ v.getLocationOnScreen(targetLoc);
+ final int centerX = v.getWidth() / 2;
+ final int centerY = v.getHeight() / 2;
+ final int x = targetLoc[0] - parentLoc[0] + centerX;
+ final int y = targetLoc[1] - parentLoc[1] + centerY;
+ showSnoozeOptions(false);
+ mGutsContainer.closeControls(x, y, false /* save */);
}
}
- private void undoSnooze() {
- mSelectedOption = null;
- mGutsContainer.closeControls(-1 /* x */, -1 /* y */, true /* notify */);
+ @Override
+ public int getActualHeight() {
+ return mExpanded ? getHeight() : mCollapsedHeight;
}
@Override
@@ -173,6 +217,8 @@
@Override
public View getContentView() {
+ // Reset the view before use
+ setSelected(mSnoozeOptions.get(0));
return this;
}
@@ -197,11 +243,8 @@
mSnoozing = true;
mSnoozeListener.snooze(mSbn, mSelectedOption);
return true;
- } else {
- // Reset the view once it's closed
- setSelected(mSnoozeOptions.get(0));
- showSnoozeOptions(false);
}
+ // The view should be closed
return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index dc254f9..b01d9cc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -24,8 +24,6 @@
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Rect;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.telephony.SubscriptionInfo;
@@ -120,6 +118,7 @@
private boolean mBlockWifi;
private boolean mBlockEthernet;
private boolean mActivityEnabled;
+ private boolean mForceBlockWifi;
public SignalClusterView(Context context) {
this(context, null);
@@ -151,6 +150,16 @@
updateActivityEnabled();
}
+ public void setForceBlockWifi() {
+ mForceBlockWifi = true;
+ mBlockWifi = true;
+ if (isAttachedToWindow()) {
+ // Re-register to get new callbacks.
+ mNetworkController.removeCallback(this);
+ mNetworkController.addCallback(this);
+ }
+ }
+
@Override
public void onTuningChanged(String key, String newValue) {
if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
@@ -167,7 +176,7 @@
mBlockAirplane = blockAirplane;
mBlockMobile = blockMobile;
mBlockEthernet = blockEthernet;
- mBlockWifi = blockWifi;
+ mBlockWifi = blockWifi || mForceBlockWifi;
// Re-register to get new callbacks.
mNetworkController.removeCallback(this);
mNetworkController.addCallback(this);
@@ -288,9 +297,9 @@
}
@Override
- public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
- int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
- String description, boolean isWide, int subId, boolean roaming) {
+ public void setMobileDataIndicators(IconState statusIcon, int statusType,
+ boolean activityIn, boolean activityOut, String typeContentDescription,
+ int subId, boolean roaming, boolean isEmergency) {
PhoneState state = getState(subId);
if (state == null) {
return;
@@ -300,7 +309,6 @@
state.mMobileTypeId = statusType;
state.mMobileDescription = statusIcon.contentDescription;
state.mMobileTypeDescription = typeContentDescription;
- state.mIsMobileTypeIconWide = statusType != 0 && isWide;
state.mRoaming = roaming;
state.mActivityIn = activityIn && mActivityEnabled;
state.mActivityOut = activityOut && mActivityEnabled;
@@ -525,7 +533,7 @@
mWifiAirplaneSpacer.setVisibility(View.GONE);
}
- if (((anyMobileVisible && firstMobileTypeId != 0) || mNoSimsVisible) && mWifiVisible) {
+ if (((anyMobileVisible && firstMobileTypeId == 0) || mNoSimsVisible) && mWifiVisible) {
mWifiSignalSpacer.setVisibility(View.VISIBLE);
} else {
mWifiSignalSpacer.setVisibility(View.GONE);
@@ -636,7 +644,6 @@
private int mMobileStrengthId = 0, mMobileTypeId = 0;
private int mLastMobileStrengthId = -1;
private int mLastMobileTypeId = -1;
- private boolean mIsMobileTypeIconWide;
private String mMobileDescription, mMobileTypeDescription;
private ViewGroup mMobileGroup;
@@ -692,12 +699,8 @@
// When this isn't next to wifi, give it some extra padding between the signals.
mMobileGroup.setPaddingRelative(isSecondaryIcon ? mSecondaryTelephonyPadding : 0,
0, 0, 0);
- mMobile.setPaddingRelative(
- mIsMobileTypeIconWide ? mWideTypeIconStartPadding : mMobileDataIconStartPadding,
- 0, 0, 0);
- mMobileDark.setPaddingRelative(
- mIsMobileTypeIconWide ? mWideTypeIconStartPadding : mMobileDataIconStartPadding,
- 0, 0, 0);
+ mMobile.setPaddingRelative(mMobileDataIconStartPadding, 0, 0, 0);
+ mMobileDark.setPaddingRelative(mMobileDataIconStartPadding, 0, 0, 0);
if (DEBUG) Log.d(TAG, String.format("mobile: %s sig=%d typ=%d",
(mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 92bfae9..ec15d10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -499,12 +499,18 @@
}
private void setColorInternal(int color) {
- if (color != NO_COLOR) {
- setImageTintList(ColorStateList.valueOf(color));
+ mCurrentSetColor = color;
+ updateIconColor();
+ }
+
+ private void updateIconColor() {
+ if (mCurrentSetColor != NO_COLOR) {
+ setImageTintList(ColorStateList.valueOf(NotificationUtils.interpolateColors(
+ mCurrentSetColor, Color.WHITE, mDarkAmount)));
} else {
setImageTintList(null);
+ mDozer.updateGrayscale(this, mDarkAmount);
}
- mCurrentSetColor = color;
}
public void setIconColor(int iconColor, boolean animate) {
@@ -669,10 +675,10 @@
}
public void setDark(boolean dark, boolean fade, long delay) {
- mDozer.setImageDark(this, dark, fade, delay, mIconColor == NO_COLOR);
mDozer.setIntensityDark(f -> {
mDarkAmount = f;
updateDecorColor();
+ updateIconColor();
}, dark, fade, delay);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
index d592c5f..0b3b3cb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationDozeHelper.java
@@ -35,8 +35,7 @@
startIntensityAnimation(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
- updateGrayscaleMatrix((float) animation.getAnimatedValue());
- target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
+ updateGrayscale(target, (float) animation.getAnimatedValue());
}
}, dark, delay, new AnimatorListenerAdapter() {
@Override
@@ -49,8 +48,12 @@
}
public void updateGrayscale(ImageView target, boolean dark) {
- if (dark) {
- updateGrayscaleMatrix(1f);
+ updateGrayscale(target, dark ? 1 : 0);
+ }
+
+ public void updateGrayscale(ImageView target, float darkAmount) {
+ if (darkAmount > 0) {
+ updateGrayscaleMatrix(darkAmount);
target.setColorFilter(new ColorMatrixColorFilter(mGrayscaleColorMatrix));
} else {
target.setColorFilter(null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
index 73eecbb..2e34f24 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationInflater.java
@@ -21,6 +21,8 @@
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting;
@@ -49,6 +51,7 @@
private RemoteViews.OnClickHandler mRemoteViewClickHandler;
private boolean mIsChildInGroup;
private InflationExceptionHandler mInflateExceptionHandler;
+ private boolean mRedactAmbient;
public NotificationInflater(ExpandableNotificationRow row) {
mRow = row;
@@ -92,6 +95,21 @@
mRemoteViewClickHandler = remoteViewClickHandler;
}
+ public void setRedactAmbient(boolean redactAmbient) {
+ if (mRedactAmbient != redactAmbient) {
+ mRedactAmbient = redactAmbient;
+ if (mRow.getEntry() == null) {
+ return;
+ }
+ try {
+ inflateNotificationViews(FLAG_REINFLATE_AMBIENT_VIEW);
+ } catch (InflationException e) {
+ mInflateExceptionHandler.handleInflationException(
+ mRow.getStatusBarNotification(), e);
+ }
+ }
+ }
+
public void inflateNotificationViews() throws InflationException {
inflateNotificationViews(FLAG_REINFLATE_ALL);
}
@@ -123,6 +141,8 @@
Notification.Builder builder, Context packageContext) {
NotificationData.Entry entry = mRow.getEntry();
NotificationContentView privateLayout = mRow.getPrivateLayout();
+ NotificationContentView publicLayout = mRow.getPublicLayout();
+
boolean isLowPriority = mIsLowPriority && !mIsChildInGroup;
if ((reInflateFlags & FLAG_REINFLATE_CONTENT_VIEW) != 0) {
final RemoteViews newContentView = createContentView(builder,
@@ -190,7 +210,6 @@
}
if ((reInflateFlags & FLAG_REINFLATE_PUBLIC_VIEW) != 0) {
- NotificationContentView publicLayout = mRow.getPublicLayout();
final RemoteViews newPublicNotification
= builder.makePublicContentView();
if (!compareRemoteViews(newPublicNotification, entry.cachedPublicContentView)) {
@@ -209,18 +228,24 @@
}
if ((reInflateFlags & FLAG_REINFLATE_AMBIENT_VIEW) != 0) {
- final RemoteViews newAmbientNotification
- = builder.makeAmbientNotification();
- if (!compareRemoteViews(newAmbientNotification, entry.cachedAmbientContentView)) {
+ final RemoteViews newAmbientNotification = mRedactAmbient
+ ? builder.makePublicAmbientNotification()
+ : builder.makeAmbientNotification();
+ NotificationContentView newParent = mRedactAmbient ? publicLayout : privateLayout;
+ NotificationContentView otherParent = !mRedactAmbient ? publicLayout : privateLayout;
+
+ if (newParent.getAmbientChild() == null ||
+ !compareRemoteViews(newAmbientNotification, entry.cachedAmbientContentView)) {
View ambientContentView = newAmbientNotification.apply(
packageContext,
- privateLayout,
+ newParent,
mRemoteViewClickHandler);
ambientContentView.setIsRootNamespace(true);
- privateLayout.setAmbientChild(ambientContentView);
+ newParent.setAmbientChild(ambientContentView);
+ otherParent.setAmbientChild(null);
} else {
newAmbientNotification.reapply(packageContext,
- privateLayout.getAmbientChild(),
+ newParent.getAmbientChild(),
mRemoteViewClickHandler);
}
entry.cachedAmbientContentView = newAmbientNotification;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index be16266..dd99749 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -23,6 +23,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewStub;
import android.widget.LinearLayout;
import com.android.systemui.Dependency;
@@ -36,6 +37,7 @@
import com.android.systemui.statusbar.policy.EncryptionHelper;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
@@ -56,6 +58,13 @@
private DarkIconManager mDarkIconManager;
private SignalClusterView mSignalClusterView;
+ private SignalCallback mSignalCallback = new SignalCallback() {
+ @Override
+ public void setIsAirplaneMode(NetworkController.IconState icon) {
+ mStatusBarComponent.recomputeDisableFlags(true /* animate */);
+ }
+ };
+
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -84,6 +93,7 @@
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mSignalClusterView);
// Default to showing until we know otherwise.
showSystemIconArea(false);
+ initEmergencyCryptkeeperText();
}
@Override
@@ -109,6 +119,9 @@
super.onDestroyView();
Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mSignalClusterView);
Dependency.get(StatusBarIconController.class).removeIconGroup(mDarkIconManager);
+ if (mNetworkController.hasEmergencyCryptKeeperText()) {
+ mNetworkController.removeCallback(mSignalCallback);
+ }
}
public void initNotificationIconArea(NotificationIconAreaController
@@ -233,4 +246,17 @@
.start();
}
}
+
+ private void initEmergencyCryptkeeperText() {
+ View emergencyViewStub = mStatusBar.findViewById(R.id.emergency_cryptkeeper_text);
+ if (mNetworkController.hasEmergencyCryptKeeperText()) {
+ if (emergencyViewStub != null) {
+ ((ViewStub) emergencyViewStub).inflate();
+ }
+ mNetworkController.addCallback(mSignalCallback);
+ } else if (emergencyViewStub != null) {
+ ViewGroup parent = (ViewGroup) emergencyViewStub.getParent();
+ parent.removeView(emergencyViewStub);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index c3f8d97..40776ea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -158,6 +158,10 @@
return sPickupSubtypePerformsProxMatcher.isIn(subType);
}
+ public int getPulseVisibleDurationExtended() {
+ return 2 * getPulseVisibleDuration();
+ }
+
/**
* Parses a spec of the form `1,2,3,!5,*`. The resulting object will match numbers that are
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index b78f748..c5f23c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -30,6 +30,7 @@
import com.android.systemui.Interpolators;
import com.android.systemui.doze.DozeHost;
import com.android.systemui.doze.DozeLog;
+import com.android.systemui.doze.DozeTriggers;
/**
* Controller which handles all the doze animations of the scrims.
@@ -43,8 +44,6 @@
private final ScrimController mScrimController;
private final Context mContext;
- private final View mStackScroller;
- private final NotificationPanelView mNotificationPanelView;
private boolean mDozing;
private DozeHost.PulseCallback mPulseCallback;
@@ -53,24 +52,22 @@
private Animator mBehindAnimator;
private float mInFrontTarget;
private float mBehindTarget;
+ private boolean mDozingAborted;
- public DozeScrimController(ScrimController scrimController, Context context,
- View stackScroller, NotificationPanelView notificationPanelView) {
+ public DozeScrimController(ScrimController scrimController, Context context) {
mContext = context;
- mStackScroller = stackScroller;
mScrimController = scrimController;
mDozeParameters = new DozeParameters(context);
- mNotificationPanelView = notificationPanelView;
}
public void setDozing(boolean dozing, boolean animate) {
if (mDozing == dozing) return;
mDozing = dozing;
if (mDozing) {
+ mDozingAborted = false;
abortAnimations();
mScrimController.setDozeBehindAlpha(1f);
mScrimController.setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? 0f : 1f);
- mNotificationPanelView.setDark(true);
} else {
cancelPulsing();
if (animate) {
@@ -85,8 +82,6 @@
mScrimController.setDozeBehindAlpha(0f);
mScrimController.setDozeInFrontAlpha(0f);
}
- // TODO: animate
- mNotificationPanelView.setDark(false);
}
}
@@ -116,10 +111,19 @@
cancelPulsing();
if (mDozing) {
mScrimController.setDozeBehindAlpha(1f);
- mScrimController.setDozeInFrontAlpha(1f);
+ mScrimController.setDozeInFrontAlpha(
+ mDozeParameters.getAlwaysOn() && !mDozingAborted ? 0f : 1f);
}
}
+ /**
+ * Aborts dozing immediately.
+ */
+ public void abortDoze() {
+ mDozingAborted = true;
+ abortPulsing();
+ }
+
public void onScreenTurnedOn() {
if (isPulsing()) {
final boolean pickupOrDoubleTap = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP
@@ -139,12 +143,17 @@
return mDozing;
}
+ public void extendPulse() {
+ mHandler.removeCallbacks(mPulseOut);
+ }
+
private void cancelPulsing() {
if (DEBUG) Log.d(TAG, "Cancel pulsing");
if (mPulseCallback != null) {
mHandler.removeCallbacks(mPulseIn);
mHandler.removeCallbacks(mPulseOut);
+ mHandler.removeCallbacks(mPulseOutExtended);
pulseFinished();
}
}
@@ -271,12 +280,23 @@
if (DEBUG) Log.d(TAG, "Pulse in finished, mDozing=" + mDozing);
if (!mDozing) return;
mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration());
+ mHandler.postDelayed(mPulseOutExtended,
+ mDozeParameters.getPulseVisibleDurationExtended());
+ }
+ };
+
+ private final Runnable mPulseOutExtended = new Runnable() {
+ @Override
+ public void run() {
+ mHandler.removeCallbacks(mPulseOut);
+ mPulseOut.run();
}
};
private final Runnable mPulseOut = new Runnable() {
@Override
public void run() {
+ mHandler.removeCallbacks(mPulseOutExtended);
if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
if (!mDozing) return;
startScrimAnimation(true /* inFront */, mDozeParameters.getAlwaysOn() ? 0 : 1,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 2bb3cbc..9206914 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -221,7 +221,7 @@
case MODE_WAKE_AND_UNLOCK:
Trace.beginSection("MODE_WAKE_AND_UNLOCK");
mStatusBarWindowManager.setStatusBarFocusable(false);
- mDozeScrimController.abortPulsing();
+ mDozeScrimController.abortDoze();
mKeyguardViewMediator.onWakeAndUnlocking();
mScrimController.setWakeAndUnlocking();
if (mStatusBar.getNavigationBarView() != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index dadb749..67d5b6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -57,6 +57,7 @@
private static final int TAG_KEY_ANIM_TARGET = R.id.scrim_target;
private static final int TAG_START_ALPHA = R.id.scrim_alpha_start;
private static final int TAG_END_ALPHA = R.id.scrim_alpha_end;
+ private static final float NOT_INITIALIZED = -1;
private final LightBarController mLightBarController;
protected final ScrimView mScrimBehind;
@@ -87,9 +88,9 @@
private boolean mDozing;
private float mDozeInFrontAlpha;
private float mDozeBehindAlpha;
- private float mCurrentInFrontAlpha;
- private float mCurrentBehindAlpha;
- private float mCurrentHeadsUpAlpha = 1;
+ private float mCurrentInFrontAlpha = NOT_INITIALIZED;
+ private float mCurrentBehindAlpha = NOT_INITIALIZED;
+ private float mCurrentHeadsUpAlpha = NOT_INITIALIZED;
private int mPinnedHeadsUpCount;
private float mTopHeadsUpDragAmount;
private View mDraggedHeadsUpView;
@@ -111,6 +112,7 @@
mScrimBehindAlpha = context.getResources().getFloat(R.dimen.scrim_behind_alpha);
updateHeadsUpScrim(false);
+ updateScrims();
}
public void setKeyguardShowing(boolean showing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
index a9eb20b..6361eb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
@@ -57,12 +57,12 @@
private static final int LEVEL_MASK = 0xff;
private static final int NUM_LEVEL_SHIFT = 8;
private static final int NUM_LEVEL_MASK = 0xff << NUM_LEVEL_SHIFT;
- private static final int STATE_SHIFT = 16;
- private static final int STATE_MASK = 0xff << STATE_SHIFT;
- private static final int STATE_NONE = 0;
- private static final int STATE_EMPTY = 1;
- private static final int STATE_CUT = 2;
- private static final int STATE_CARRIER_CHANGE = 3;
+ public static final int STATE_SHIFT = 16;
+ public static final int STATE_MASK = 0xff << STATE_SHIFT;
+ public static final int STATE_NONE = 0;
+ public static final int STATE_EMPTY = 1;
+ public static final int STATE_CUT = 2;
+ public static final int STATE_CARRIER_CHANGE = 3;
private static final long DOT_DELAY = 1000;
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 d3cb6a4a8..46d6415 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -31,6 +31,7 @@
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT;
import static com.android.systemui.statusbar.phone.BarTransitions.MODE_WARNING;
+import android.R.style;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.NonNull;
@@ -48,10 +49,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
-import android.content.pm.ActivityInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -95,6 +94,7 @@
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
+import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -139,7 +139,6 @@
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.PluginFragmentListener;
import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.pip.phone.PipManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
@@ -1114,14 +1113,11 @@
}
mHeadsUpManager.addListener(mScrimController);
mStackScroller.setScrimController(mScrimController);
- mDozeScrimController = new DozeScrimController(mScrimController, context, mStackScroller,
- mNotificationPanel);
+ mDozeScrimController = new DozeScrimController(mScrimController, context);
// Other icons
mVolumeComponent = getComponent(VolumeComponent.class);
- initEmergencyCryptkeeperText();
-
mKeyguardBottomArea.setStatusBar(this);
mKeyguardBottomArea.setUserSetupComplete(mUserSetup);
if (UserManager.get(mContext).isUserSwitcherEnabled()) {
@@ -1231,24 +1227,6 @@
});
}
- private void initEmergencyCryptkeeperText() {
- View emergencyViewStub = mStatusBarWindow.findViewById(R.id.emergency_cryptkeeper_text);
- if (mNetworkController.hasEmergencyCryptKeeperText()) {
- if (emergencyViewStub != null) {
- ((ViewStub) emergencyViewStub).inflate();
- }
- mNetworkController.addCallback(new NetworkController.SignalCallback() {
- @Override
- public void setIsAirplaneMode(NetworkController.IconState icon) {
- recomputeDisableFlags(true /* animate */);
- }
- });
- } else if (emergencyViewStub != null) {
- ViewGroup parent = (ViewGroup) emergencyViewStub.getParent();
- parent.removeView(emergencyViewStub);
- }
- }
-
/**
* Returns the {@link android.view.View.OnTouchListener} that will be invoked when the
* background window of the status bar is clicked.
@@ -1358,7 +1336,10 @@
}
private void inflateDismissView() {
- mDismissView = (DismissView) LayoutInflater.from(mContext).inflate(
+ // Always inflate with a dark theme, since this sits on the scrim.
+ ContextThemeWrapper themedContext = new ContextThemeWrapper(mContext,
+ style.Theme_DeviceDefault);
+ mDismissView = (DismissView) LayoutInflater.from(themedContext).inflate(
R.layout.status_bar_notification_dismiss_all, mStackScroller, false);
mDismissView.setOnButtonClickListener(new View.OnClickListener() {
@Override
@@ -1830,6 +1811,7 @@
updatePublicContentView(ent, ent.notification);
}
ent.row.setSensitive(sensitive, deviceSensitive);
+ ent.row.setNeedsRedaction(needsRedaction(ent));
if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
ExpandableNotificationRow summary = mGroupManager.getGroupSummary(
ent.row.getStatusBarNotification());
@@ -1918,6 +1900,21 @@
mNotificationIconAreaController.updateNotificationIcons(mNotificationData);
}
+ /** @return true if the entry needs redaction when on the lockscreen. */
+ private boolean needsRedaction(Entry ent) {
+ int userId = ent.notification.getUserId();
+
+ boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
+ boolean notiUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(userId);
+ boolean redactedLockscreen = currentUserWantsRedaction || notiUserWantsRedaction;
+
+ boolean notificationRequestsRedaction =
+ ent.notification.getNotification().visibility == Notification.VISIBILITY_PRIVATE;
+ boolean userForcesRedaction = packageHasVisibilityOverride(ent.notification.getKey());
+
+ return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen;
+ }
+
/**
* Disable QS if device not provisioned.
* If the user switcher is simple then disable QS during setup because
@@ -1928,6 +1925,7 @@
&& (mUserSetup || mUserSwitcherController == null
|| !mUserSwitcherController.isSimpleUserSwitcher())
&& ((mDisabled2 & StatusBarManager.DISABLE2_QUICK_SETTINGS) == 0)
+ && !mDozing
&& !ONLY_CORE_APPS);
}
@@ -4332,6 +4330,8 @@
mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation);
mScrimController.setDozing(mDozing);
mKeyguardIndicationController.setDozing(mDozing);
+ mNotificationPanel.setDark(mDozing);
+ updateQsExpansionEnabled();
// Immediately abort the dozing from the doze scrim controller in case of wake-and-unlock
// for pulsing so the Keyguard fade-out animation scrim can take over.
@@ -4958,6 +4958,7 @@
mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD
|| mFingerprintUnlockController.getMode()
== FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
+ mStatusBarWindowManager.setDozing(mDozing);
updateDozingState();
Trace.endSection();
}
@@ -5065,6 +5066,16 @@
StatusBar.this.startPendingIntentDismissingKeyguard(intent);
}
+ @Override
+ public void abortPulsing() {
+ mDozeScrimController.abortPulsing();
+ }
+
+ @Override
+ public void extendPulse() {
+ mDozeScrimController.extendPulse();
+ }
+
}
// Begin Extra BaseStatusBar methods.
@@ -5763,6 +5774,9 @@
snoozeGuts.setSnoozeListener(mStackScroller.getSwipeActionHelper());
snoozeGuts.setStatusBarNotification(sbn);
snoozeGuts.setSnoozeOptions(row.getEntry().snoozeCriteria);
+ guts.setHeightChangedListener((NotificationGuts g) -> {
+ mStackScroller.onHeightChanged(row, row.isShown() /* needsAnimation */);
+ });
}
if (gutsView instanceof NotificationInfo) {
@@ -5864,6 +5878,9 @@
}
final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
+ if (row.isDark()) {
+ return false;
+ }
bindGuts(row, item);
NotificationGuts guts = row.getGuts();
@@ -5911,8 +5928,10 @@
}
});
a.start();
- guts.setExposed(true /* exposed */,
- mState == StatusBarState.KEYGUARD /* needsFalsingProtection */);
+ final boolean needsFalsingProtection =
+ (mState == StatusBarState.KEYGUARD &&
+ !mAccessibilityManager.isTouchExplorationEnabled());
+ guts.setExposed(true /* exposed */, needsFalsingProtection);
row.closeRemoteInput();
mStackScroller.onHeightChanged(row, true /* needsAnimation */);
mNotificationGutsExposed = guts;
@@ -6163,6 +6182,7 @@
}
}
+ row.setNeedsRedaction(needsRedaction(entry));
boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
row.setIsLowPriority(isLowPriority);
// bind the click event to the content area
@@ -6527,7 +6547,6 @@
NotificationData.Entry entry = new NotificationData.Entry(sbn);
Dependency.get(LeakDetector.class).trackInstance(entry);
entry.createIcons(mContext, sbn);
-
// Construct the expanded view.
inflateViews(entry, mStackScroller);
return entry;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index deb0070..0326e40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -118,7 +118,7 @@
mLpChanged.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
}
- if (state.keyguardShowing && !state.backdropShowing) {
+ if (state.keyguardShowing && !state.backdropShowing && !state.dozing) {
mLpChanged.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
} else {
mLpChanged.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -357,6 +357,11 @@
apply(mCurrentState);
}
+ public void setDozing(boolean dozing) {
+ mCurrentState.dozing = dozing;
+ apply(mCurrentState);
+ }
+
public void setBarHeight(int barHeight) {
mBarHeight = barHeight;
apply(mCurrentState);
@@ -404,6 +409,7 @@
boolean remoteInputActive;
boolean forcePluginOpen;
+ boolean dozing;
private boolean isKeyguardShowingAndNotOccluded() {
return keyguardShowing && !keyguardOccluded;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 1848d4e..1a09d75 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -255,6 +255,9 @@
if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
mStackScrollLayout.closeControlsIfOutsideTouch(ev);
}
+ if (mService.isDozing()) {
+ mService.mDozeScrimController.extendPulse();
+ }
return super.dispatchTouchEvent(ev);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index a456786..e98dc98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -110,30 +110,24 @@
public void setWifiIndicators(final boolean enabled, final IconState statusIcon,
final IconState qsIcon, final boolean activityIn, final boolean activityOut,
final String description, boolean isTransient) {
- post(new Runnable() {
- @Override
- public void run() {
- for (SignalCallback callback : mSignalCallbacks) {
- callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut,
- description, isTransient);
- }
+ post(() -> {
+ for (SignalCallback callback : mSignalCallbacks) {
+ callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut,
+ description, isTransient);
}
});
}
@Override
- public void setMobileDataIndicators(final IconState statusIcon, final IconState qsIcon,
- final int statusType, final int qsType,final boolean activityIn,
+ public void setMobileDataIndicators(final IconState statusIcon,
+ final int statusType, final boolean activityIn,
final boolean activityOut, final String typeContentDescription,
- final String description, final boolean isWide, final int subId, boolean roaming) {
- post(new Runnable() {
- @Override
- public void run() {
- for (SignalCallback signalCluster : mSignalCallbacks) {
- signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType,
- activityIn, activityOut, typeContentDescription, description, isWide,
- subId, roaming);
- }
+ final int subId, boolean roaming, boolean isEmergency) {
+ post(() -> {
+ for (SignalCallback signalCluster : mSignalCallbacks) {
+ signalCluster.setMobileDataIndicators(statusIcon, statusType,
+ activityIn, activityOut, typeContentDescription,
+ subId, roaming, isEmergency);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 67b5596..4421a6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -244,7 +244,8 @@
return SignalDrawable.getCarrierChangeState(getNumLevels());
} else if (mCurrentState.connected) {
return SignalDrawable.getState(mCurrentState.level, getNumLevels(),
- mCurrentState.inetCondition == 0);
+ mCurrentState.inetCondition == 0 ||
+ (mCurrentState.dataDisabled && mCurrentState.userSetup));
} else if (mCurrentState.enabled) {
return SignalDrawable.getEmptyState(getNumLevels());
} else {
@@ -263,24 +264,14 @@
String contentDescription = getStringIfExists(getContentDescription());
String dataContentDescription = getStringIfExists(icons.mDataContentDescription);
- final boolean dataDisabled = mCurrentState.iconGroup == TelephonyIcons.DATA_DISABLED
+ final boolean dataDisabled = mCurrentState.dataDisabled
&& mCurrentState.userSetup;
// Show icon in QS when we are connected or data is disabled.
- boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
+ boolean showDataIcon = mCurrentState.dataConnected;
IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode,
getCurrentIconId(), contentDescription);
- int qsTypeIcon = 0;
- IconState qsIcon = null;
- String description = null;
- // Only send data sim callbacks to QS.
- if (mCurrentState.dataSim) {
- qsTypeIcon = showDataIcon ? icons.mQsDataType : 0;
- qsIcon = new IconState(mCurrentState.enabled
- && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription);
- description = mCurrentState.isEmergency ? null : mCurrentState.networkName;
- }
boolean activityIn = mCurrentState.dataConnected
&& !mCurrentState.carrierNetworkChangeMode
&& mCurrentState.activityIn;
@@ -289,9 +280,10 @@
&& mCurrentState.activityOut;
showDataIcon &= mCurrentState.isDefault || dataDisabled;
int typeIcon = showDataIcon ? icons.mDataType : 0;
- callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
- activityIn, activityOut, dataContentDescription, description, icons.mIsWide,
- mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming);
+ callback.setMobileDataIndicators(statusIcon, typeIcon,
+ activityIn, activityOut, dataContentDescription,
+ mSubscriptionInfo.getSubscriptionId(), mCurrentState.roaming,
+ mCurrentState.isEmergency);
}
@Override
@@ -438,14 +430,14 @@
} else {
mCurrentState.iconGroup = mDefaultIcons;
}
+ mCurrentState.dataDisabled = isDataDisabled();
mCurrentState.dataConnected = mCurrentState.connected
- && mDataState == TelephonyManager.DATA_CONNECTED;
+ && mDataState == TelephonyManager.DATA_CONNECTED
+ && !mCurrentState.dataDisabled;
mCurrentState.roaming = isRoaming();
if (isCarrierNetworkChangeActive()) {
mCurrentState.iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
- } else if (isDataDisabled()) {
- mCurrentState.iconGroup = TelephonyIcons.DATA_DISABLED;
}
if (isEmergencyOnly() != mCurrentState.isEmergency) {
mCurrentState.isEmergency = isEmergencyOnly();
@@ -577,6 +569,7 @@
boolean isDefault;
boolean userSetup;
boolean roaming;
+ boolean dataDisabled;
@Override
public void copyFrom(State s) {
@@ -592,6 +585,7 @@
carrierNetworkChangeMode = state.carrierNetworkChangeMode;
userSetup = state.userSetup;
roaming = state.roaming;
+ dataDisabled = state.dataDisabled;
}
@Override
@@ -609,6 +603,7 @@
builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode)
.append(',');
builder.append("userSetup=").append(userSetup);
+ builder.append("dataDisabled=").append(dataDisabled);
}
@Override
@@ -623,6 +618,7 @@
&& ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode
&& ((MobileState) o).userSetup == userSetup
&& ((MobileState) o).isDefault == isDefault
+ && ((MobileState) o).dataDisabled == dataDisabled
&& ((MobileState) o).roaming == roaming;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index c02ce0e..ab4a8f2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -48,9 +48,9 @@
default void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
boolean activityIn, boolean activityOut, String description, boolean isTransient) {}
- default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
- int qsType, boolean activityIn, boolean activityOut, String typeContentDescription,
- String description, boolean isWide, int subId, boolean roaming) {}
+ default void setMobileDataIndicators(IconState statusIcon, int statusType,
+ boolean activityIn, boolean activityOut, String typeContentDescription,
+ int subId, boolean roaming, boolean isEmergency) {}
default void setSubs(List<SubscriptionInfo> subs) {}
default void setNoSims(boolean show) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index c21f444..60f4ab8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -867,7 +867,6 @@
datatype.equals("h") ? TelephonyIcons.H :
datatype.equals("lte") ? TelephonyIcons.LTE :
datatype.equals("lte+") ? TelephonyIcons.LTE_PLUS :
- datatype.equals("dis") ? TelephonyIcons.DATA_DISABLED :
TelephonyIcons.UNKNOWN;
}
if (args.containsKey("roam")) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index 3f8e41a..1fb9b69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -23,16 +23,20 @@
/** Whether the device has device owner, even if not on this user. */
boolean isDeviceManaged();
boolean hasProfileOwner();
+ boolean hasWorkProfile();
String getDeviceOwnerName();
String getProfileOwnerName();
CharSequence getDeviceOwnerOrganizationName();
+ CharSequence getWorkProfileOrganizationName();
boolean isNetworkLoggingEnabled();
boolean isVpnEnabled();
boolean isVpnRestricted();
/** Whether the VPN app should use branded VPN iconography. */
boolean isVpnBranded();
String getPrimaryVpnName();
- String getProfileVpnName();
+ String getWorkProfileVpnName();
+ boolean hasCACertInCurrentUser();
+ boolean hasCACertInWorkProfile();
void onUserSwitched(int newUserId);
public interface SecurityControllerCallback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 19ced23..fcb7289 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -138,6 +138,13 @@
}
@Override
+ public CharSequence getWorkProfileOrganizationName() {
+ final int profileId = getWorkProfileUserId(mCurrentUserId);
+ if (profileId == UserHandle.USER_NULL) return null;
+ return mDevicePolicyManager.getOrganizationNameForUser(profileId);
+ }
+
+ @Override
public String getPrimaryVpnName() {
VpnConfig cfg = mCurrentVpns.get(mVpnUserId);
if (cfg != null) {
@@ -147,16 +154,27 @@
}
}
+ private int getWorkProfileUserId(int userId) {
+ for (final UserInfo userInfo : mUserManager.getProfiles(userId)) {
+ if (userInfo.isManagedProfile()) {
+ return userInfo.id;
+ }
+ }
+ return UserHandle.USER_NULL;
+ }
+
@Override
- public String getProfileVpnName() {
- for (int profileId : mUserManager.getProfileIdsWithDisabled(mVpnUserId)) {
- if (profileId == mVpnUserId) {
- continue;
- }
- VpnConfig cfg = mCurrentVpns.get(profileId);
- if (cfg != null) {
- return getNameForVpnConfig(cfg, UserHandle.of(profileId));
- }
+ public boolean hasWorkProfile() {
+ return getWorkProfileUserId(mCurrentUserId) != UserHandle.USER_NULL;
+ }
+
+ @Override
+ public String getWorkProfileVpnName() {
+ final int profileId = getWorkProfileUserId(mVpnUserId);
+ if (profileId == UserHandle.USER_NULL) return null;
+ VpnConfig cfg = mCurrentVpns.get(profileId);
+ if (cfg != null) {
+ return getNameForVpnConfig(cfg, UserHandle.of(profileId));
}
return null;
}
@@ -199,6 +217,18 @@
}
@Override
+ public boolean hasCACertInCurrentUser() {
+ //TODO: implement
+ return false;
+ }
+
+ @Override
+ public boolean hasCACertInWorkProfile() {
+ //TODO: implement
+ return false;
+ }
+
+ @Override
public void removeCallback(SecurityControllerCallback callback) {
synchronized (mCallbacks) {
if (callback == null) return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
index aaa0568..ec7e557 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java
@@ -44,10 +44,6 @@
static final int ICON_4G_PLUS = R.drawable.stat_sys_data_fully_connected_4g_plus;
static final int ICON_1X = R.drawable.stat_sys_data_fully_connected_1x;
- static final int ICON_DATA_DISABLED = R.drawable.stat_sys_data_disabled;
-
- static final int QS_ICON_DATA_DISABLED = R.drawable.ic_qs_data_disabled;
-
static final MobileIconGroup CARRIER_NETWORK_CHANGE = new MobileIconGroup(
"CARRIER_NETWORK_CHANGE",
null,
@@ -221,20 +217,5 @@
true,
TelephonyIcons.QS_DATA_LTE_PLUS
);
-
- static final MobileIconGroup DATA_DISABLED = new MobileIconGroup(
- "DataDisabled",
- null,
- null,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH,
- 0, 0,
- 0,
- 0,
- AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0],
- R.string.accessibility_cell_data_off,
- TelephonyIcons.ICON_DATA_DISABLED,
- false,
- TelephonyIcons.QS_ICON_DATA_DISABLED
- );
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
index 374408d..dfc3591 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiIcons.java
@@ -47,6 +47,7 @@
static final int QS_WIFI_NO_NETWORK = R.drawable.ic_qs_wifi_no_network;
static final int WIFI_NO_NETWORK = R.drawable.stat_sys_wifi_signal_null;
+ static final int WIFI_DISCONNECTED = R.drawable.stat_sys_wifi_signal_disconnected;
static final int WIFI_LEVEL_COUNT = WIFI_SIGNAL_STRENGTH[0].length;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 2104cb1..a773acf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -80,7 +80,7 @@
AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH,
WifiIcons.WIFI_NO_NETWORK,
WifiIcons.QS_WIFI_NO_NETWORK,
- WifiIcons.WIFI_NO_NETWORK,
+ WifiIcons.WIFI_DISCONNECTED,
WifiIcons.QS_WIFI_NO_NETWORK,
AccessibilityContentDescriptions.WIFI_NO_CONNECTION
);
@@ -133,8 +133,7 @@
@Override
public void notifyListeners(SignalCallback callback) {
// only show wifi in the cluster if connected or if wifi-only
- boolean wifiVisible = mCurrentState.enabled
- && (mCurrentState.connected || !mHasMobileData);
+ boolean wifiVisible = true;
String wifiDesc = wifiVisible ? mCurrentState.ssid : null;
boolean ssidPresent = wifiVisible && mCurrentState.ssid != null;
String contentDescription = getStringIfExists(getContentDescription());
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 697cac9..b8b046b 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -433,7 +433,7 @@
return false;
}
});
- row.icon = (ImageButton) row.view.findViewById(R.id.volume_row_icon);
+ row.icon = row.view.findViewById(R.id.volume_row_icon);
row.icon.setImageResource(iconRes);
if (row.stream != AudioSystem.STREAM_ACCESSIBILITY) {
row.icon.setOnClickListener(new OnClickListener() {
@@ -465,6 +465,8 @@
row.userAttempt = 0; // reset the grace period, slider updates immediately
}
});
+ } else {
+ row.icon.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 1ff373c..ff644d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -40,6 +40,15 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+/*
+ * Compile and run the whole SystemUI test suite:
+ runtest --path frameworks/base/packages/SystemUI/tests
+ *
+ * Compile and run just this class:
+ runtest --path \
+ frameworks/base/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+*/
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class QSSecurityFooterTest extends SysuiTestCase {
@@ -47,11 +56,11 @@
private final String MANAGING_ORGANIZATION = "organization";
private final String DEVICE_OWNER_PACKAGE = "TestDPC";
private final String VPN_PACKAGE = "TestVPN";
+ private final String VPN_PACKAGE_2 = "TestVPN 2";
private ViewGroup mRootView;
private TextView mFooterText;
private TestableImageView mFooterIcon;
- private TestableImageView mFooterIcon2;
private QSSecurityFooter mFooter;
private SecurityController mSecurityController = mock(SecurityController.class);
@@ -69,15 +78,12 @@
mRootView = (ViewGroup) mFooter.getView();
mFooterText = (TextView) mRootView.findViewById(R.id.footer_text);
mFooterIcon = (TestableImageView) mRootView.findViewById(R.id.footer_icon);
- mFooterIcon2 = (TestableImageView) mRootView.findViewById(R.id.footer_icon2);
mFooter.setHostEnvironment(null);
}
@Test
public void testUnmanaged() {
when(mSecurityController.isDeviceManaged()).thenReturn(false);
- when(mSecurityController.isVpnEnabled()).thenReturn(false);
- when(mSecurityController.isVpnBranded()).thenReturn(false);
mFooter.refreshState();
waitForIdleSync(mFooter.mHandler);
@@ -91,8 +97,12 @@
mFooter.refreshState();
waitForIdleSync(mFooter.mHandler);
- assertEquals(mContext.getString(R.string.do_disclosure_generic), mFooterText.getText());
+ assertEquals(mContext.getString(R.string.quick_settings_disclosure_management),
+ mFooterText.getText());
assertEquals(View.VISIBLE, mRootView.getVisibility());
+ assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+ // -1 == never set.
+ assertEquals(-1, mFooterIcon.getLastImageResource());
}
@Test
@@ -103,37 +113,100 @@
mFooter.refreshState();
waitForIdleSync(mFooter.mHandler);
- assertEquals(mContext.getString(R.string.do_disclosure_with_name, MANAGING_ORGANIZATION),
+ assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_management,
+ MANAGING_ORGANIZATION),
mFooterText.getText());
assertEquals(View.VISIBLE, mRootView.getVisibility());
+ assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+ // -1 == never set.
+ assertEquals(-1, mFooterIcon.getLastImageResource());
}
@Test
public void testNetworkLoggingEnabled() {
when(mSecurityController.isDeviceManaged()).thenReturn(true);
when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
- when(mSecurityController.isVpnEnabled()).thenReturn(false);
mFooter.refreshState();
waitForIdleSync(mFooter.mHandler);
- assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
- assertEquals(R.drawable.ic_qs_network_logging, mFooterIcon.getLastImageResource());
- assertEquals(View.INVISIBLE, mFooterIcon2.getVisibility());
- }
-
- @Test
- public void testVpnEnabled() {
- when(mSecurityController.isDeviceManaged()).thenReturn(true);
- when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(false);
- when(mSecurityController.isVpnEnabled()).thenReturn(true);
- when(mSecurityController.isVpnBranded()).thenReturn(false);
- mFooter.refreshState();
-
- waitForIdleSync(mFooter.mHandler);
+ assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
+ mFooterText.getText());
assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
// -1 == never set.
assertEquals(-1, mFooterIcon.getLastImageResource());
- assertEquals(View.INVISIBLE, mFooterIcon2.getVisibility());
+
+ // Same situation, but with organization name set
+ when(mSecurityController.getDeviceOwnerOrganizationName())
+ .thenReturn(MANAGING_ORGANIZATION);
+ mFooter.refreshState();
+
+ waitForIdleSync(mFooter.mHandler);
+ assertEquals(mContext.getString(
+ R.string.quick_settings_disclosure_named_management_monitoring,
+ MANAGING_ORGANIZATION),
+ mFooterText.getText());
+ }
+
+ @Test
+ public void testManagedCACertsInstalled() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+ when(mSecurityController.hasCACertInCurrentUser()).thenReturn(true);
+ mFooter.refreshState();
+
+ waitForIdleSync(mFooter.mHandler);
+ assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
+ mFooterText.getText());
+ }
+
+ @Test
+ public void testManagedOneVpnEnabled() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+ when(mSecurityController.isVpnEnabled()).thenReturn(true);
+ when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE);
+ mFooter.refreshState();
+
+ waitForIdleSync(mFooter.mHandler);
+ assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_named_vpn,
+ VPN_PACKAGE),
+ mFooterText.getText());
+ assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+ assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+
+ // Same situation, but with organization name set
+ when(mSecurityController.getDeviceOwnerOrganizationName())
+ .thenReturn(MANAGING_ORGANIZATION);
+ mFooter.refreshState();
+
+ waitForIdleSync(mFooter.mHandler);
+ assertEquals(mContext.getString(
+ R.string.quick_settings_disclosure_named_management_named_vpn,
+ MANAGING_ORGANIZATION, VPN_PACKAGE),
+ mFooterText.getText());
+ }
+
+ @Test
+ public void testManagedTwoVpnsEnabled() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+ when(mSecurityController.isVpnEnabled()).thenReturn(true);
+ when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE);
+ when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2);
+ mFooter.refreshState();
+
+ waitForIdleSync(mFooter.mHandler);
+ assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_vpns),
+ mFooterText.getText());
+ assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+ assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+
+ // Same situation, but with organization name set
+ when(mSecurityController.getDeviceOwnerOrganizationName())
+ .thenReturn(MANAGING_ORGANIZATION);
+ mFooter.refreshState();
+
+ waitForIdleSync(mFooter.mHandler);
+ assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_management_vpns,
+ MANAGING_ORGANIZATION),
+ mFooterText.getText());
}
@Test
@@ -141,15 +214,101 @@
when(mSecurityController.isDeviceManaged()).thenReturn(true);
when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
when(mSecurityController.isVpnEnabled()).thenReturn(true);
- when(mSecurityController.isVpnBranded()).thenReturn(false);
+ when(mSecurityController.getPrimaryVpnName()).thenReturn("VPN Test App");
mFooter.refreshState();
waitForIdleSync(mFooter.mHandler);
assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
- assertEquals(View.VISIBLE, mFooterIcon2.getVisibility());
+ assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+ assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
+ mFooterText.getText());
+ }
+
+ @Test
+ public void testWorkProfileCACertsInstalled() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(false);
+ when(mSecurityController.hasCACertInWorkProfile()).thenReturn(true);
+ mFooter.refreshState();
+
+ waitForIdleSync(mFooter.mHandler);
// -1 == never set.
assertEquals(-1, mFooterIcon.getLastImageResource());
- assertEquals(-1, mFooterIcon2.getLastImageResource());
+ assertEquals(mContext.getString(
+ R.string.quick_settings_disclosure_managed_profile_monitoring),
+ mFooterText.getText());
+
+ // Same situation, but with organization name set
+ when(mSecurityController.getWorkProfileOrganizationName())
+ .thenReturn(MANAGING_ORGANIZATION);
+ mFooter.refreshState();
+
+ waitForIdleSync(mFooter.mHandler);
+ assertEquals(mContext.getString(
+ R.string.quick_settings_disclosure_named_managed_profile_monitoring,
+ MANAGING_ORGANIZATION),
+ mFooterText.getText());
+ }
+
+ @Test
+ public void testCACertsInstalled() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(false);
+ when(mSecurityController.hasCACertInCurrentUser()).thenReturn(true);
+ mFooter.refreshState();
+
+ waitForIdleSync(mFooter.mHandler);
+ // -1 == never set.
+ assertEquals(-1, mFooterIcon.getLastImageResource());
+ assertEquals(mContext.getString(R.string.quick_settings_disclosure_monitoring),
+ mFooterText.getText());
+ }
+
+ @Test
+ public void testTwoVpnsEnabled() {
+ when(mSecurityController.isVpnEnabled()).thenReturn(true);
+ when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE);
+ when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2);
+ mFooter.refreshState();
+
+ waitForIdleSync(mFooter.mHandler);
+ assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+ assertEquals(mContext.getString(R.string.quick_settings_disclosure_vpns),
+ mFooterText.getText());
+ }
+
+ @Test
+ public void testWorkProfileVpnEnabled() {
+ when(mSecurityController.isVpnEnabled()).thenReturn(true);
+ when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2);
+ mFooter.refreshState();
+
+ waitForIdleSync(mFooter.mHandler);
+ assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+ assertEquals(mContext.getString(
+ R.string.quick_settings_disclosure_managed_profile_named_vpn,
+ VPN_PACKAGE_2),
+ mFooterText.getText());
+ }
+
+ @Test
+ public void testVpnEnabled() {
+ when(mSecurityController.isVpnEnabled()).thenReturn(true);
+ when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE);
+ mFooter.refreshState();
+
+ waitForIdleSync(mFooter.mHandler);
+ assertEquals(R.drawable.ic_qs_vpn, mFooterIcon.getLastImageResource());
+ assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_vpn,
+ VPN_PACKAGE),
+ mFooterText.getText());
+
+ when(mSecurityController.hasWorkProfile()).thenReturn(true);
+ mFooter.refreshState();
+
+ waitForIdleSync(mFooter.mHandler);
+ assertEquals(mContext.getString(
+ R.string.quick_settings_disclosure_personal_profile_named_vpn,
+ VPN_PACKAGE),
+ mFooterText.getText());
}
@Test
@@ -160,8 +319,7 @@
null /* primaryVpn */,
null /* profileVpn */,
null /* deviceOwnerOrganization */,
- false /* hasProfileOwner */,
- false /* isBranded */));
+ false /* hasProfileOwner */));
}
@Test
@@ -172,8 +330,7 @@
VPN_PACKAGE,
null /* profileVpn */,
null /* deviceOwnerOrganization */,
- false /* hasProfileOwner */,
- false /* isBranded */));
+ false /* hasProfileOwner */));
}
@Test
@@ -184,8 +341,7 @@
null /* primaryVpn */,
null /* profileVpn */,
MANAGING_ORGANIZATION,
- false /* hasProfileOwner */,
- false /* isBranded */));
+ false /* hasProfileOwner */));
}
@Test
@@ -196,8 +352,7 @@
VPN_PACKAGE,
null /* profileVpn */,
MANAGING_ORGANIZATION,
- false /* hasProfileOwner */,
- false /* isBranded */));
+ false /* hasProfileOwner */));
}
private CharSequence getExpectedMessage(boolean hasDeviceOwnerOrganization, boolean hasVPN) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index 3ed1681..f6c75a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -114,33 +114,26 @@
boolean wide = true;
int subId = 5;
boolean roaming = true;
- mHandler.setMobileDataIndicators(status, qs, type, qsType, in, out, typeDescription,
- description, wide, subId, roaming);
+ boolean isEmergency = true;
+ mHandler.setMobileDataIndicators(status, type, in, out, typeDescription,
+ subId, roaming, isEmergency);
waitForCallbacks();
ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class);
- ArgumentCaptor<IconState> qsArg = ArgumentCaptor.forClass(IconState.class);
ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class);
- ArgumentCaptor<Integer> qsTypeIconArg = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Boolean> inArg = ArgumentCaptor.forClass(Boolean.class);
ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class);
ArgumentCaptor<String> typeContentArg = ArgumentCaptor.forClass(String.class);
- ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class);
- ArgumentCaptor<Boolean> wideArg = ArgumentCaptor.forClass(Boolean.class);
ArgumentCaptor<Integer> subIdArg = ArgumentCaptor.forClass(Integer.class);
Mockito.verify(mSignalCallback).setMobileDataIndicators(statusArg.capture(),
- qsArg.capture(), typeIconArg.capture(), qsTypeIconArg.capture(), inArg.capture(),
- outArg.capture(), typeContentArg.capture(), descArg.capture(), wideArg.capture(),
- subIdArg.capture(), eq(roaming));
+ typeIconArg.capture(), inArg.capture(),
+ outArg.capture(), typeContentArg.capture(),
+ subIdArg.capture(), eq(roaming), eq(isEmergency));
assertEquals(status, statusArg.getValue());
- assertEquals(qs, qsArg.getValue());
assertEquals(type, (int) typeIconArg.getValue());
- assertEquals(qsType, (int) qsTypeIconArg.getValue());
assertEquals(in, (boolean) inArg.getValue());
assertEquals(out, (boolean) outArg.getValue());
assertEquals(typeDescription, typeContentArg.getValue());
- assertEquals(description, descArg.getValue());
- assertEquals(wide, (boolean) wideArg.getValue());
assertEquals(subId, (int) subIdArg.getValue());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 505e1d8..b39171e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -29,6 +29,7 @@
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
+
import com.android.internal.telephony.cdma.EriInfo;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.statusbar.phone.SignalDrawable;
@@ -45,8 +46,6 @@
import org.junit.runner.Description;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -70,7 +69,7 @@
protected static final int DEFAULT_SIGNAL_STRENGTH = DEFAULT_LEVEL;
protected static final int DEFAULT_QS_SIGNAL_STRENGTH = DEFAULT_LEVEL;
protected static final int DEFAULT_ICON = TelephonyIcons.ICON_3G;
- protected static final int DEFAULT_QS_ICON = TelephonyIcons.QS_DATA_3G;
+ protected static final int DEFAULT_QS_ICON = DEFAULT_ICON;
protected NetworkControllerImpl mNetworkController;
protected MobileSignalController mMobileSignalController;
@@ -117,7 +116,7 @@
when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(true);
when(mMockCm.getDefaultNetworkCapabilitiesForUser(0)).thenReturn(
- new NetworkCapabilities[] { mNetCapabilities });
+ new NetworkCapabilities[]{mNetCapabilities});
mSignalStrength = mock(SignalStrength.class);
mServiceState = mock(ServiceState.class);
@@ -175,17 +174,17 @@
}
protected NetworkControllerImpl setUpNoMobileData() {
- when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
- NetworkControllerImpl networkControllerNoMobile
- = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager, mMockTm,
- mMockWm, mMockSm, mConfig, mContext.getMainLooper(), mCallbackHandler,
- mock(AccessPointControllerImpl.class),
- mock(DataUsageController.class), mMockSubDefaults,
- mock(DeviceProvisionedController.class));
+ when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false);
+ NetworkControllerImpl networkControllerNoMobile
+ = new NetworkControllerImpl(mContext, mMockCm, mMockNetworkScoreManager, mMockTm,
+ mMockWm, mMockSm, mConfig, mContext.getMainLooper(), mCallbackHandler,
+ mock(AccessPointControllerImpl.class),
+ mock(DataUsageController.class), mMockSubDefaults,
+ mock(DeviceProvisionedController.class));
- setupNetworkController();
+ setupNetworkController();
- return networkControllerNoMobile;
+ return networkControllerNoMobile;
}
@@ -308,11 +307,10 @@
ArgumentCaptor<Boolean> dataOutArg = ArgumentCaptor.forClass(Boolean.class);
Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
- any(),
- iconArg.capture(),
- anyInt(),
- typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(),
- anyString(), anyString(), anyBoolean(), anyInt(), anyBoolean());
+ iconArg.capture(),
+ typeIconArg.capture(),
+ dataInArg.capture(), dataOutArg.capture(),
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
IconState iconState = iconArg.getValue();
int state = SignalDrawable.getState(icon, SignalStrength.NUM_SIGNAL_STRENGTH_BINS,
false);
@@ -335,17 +333,16 @@
}
protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon,
- boolean roaming, boolean inet) {
+ boolean roaming, boolean inet) {
ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class);
// TODO: Verify all fields.
Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
iconArg.capture(),
- any(),
typeIconArg.capture(),
- anyInt(), anyBoolean(), anyBoolean(), anyString(), anyString(), anyBoolean(),
- anyInt(), eq(roaming));
+ anyBoolean(), anyBoolean(), anyString(),
+ anyInt(), eq(roaming), anyBoolean());
IconState iconState = iconArg.getValue();
int state = icon == -1 ? 0
@@ -356,22 +353,18 @@
}
protected void verifyLastMobileDataIndicators(boolean visible, int icon, int typeIcon,
- boolean qsVisible, int qsIcon, int qsTypeIcon, boolean dataIn, boolean dataOut) {
+ boolean qsVisible, boolean dataIn, boolean dataOut) {
ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class);
- ArgumentCaptor<IconState> qsIconArg = ArgumentCaptor.forClass(IconState.class);
- ArgumentCaptor<Integer> qsTypeIconArg = ArgumentCaptor.forClass(Integer.class);
ArgumentCaptor<Boolean> dataInArg = ArgumentCaptor.forClass(Boolean.class);
ArgumentCaptor<Boolean> dataOutArg = ArgumentCaptor.forClass(Boolean.class);
Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
iconArg.capture(),
- qsIconArg.capture(),
typeIconArg.capture(),
- qsTypeIconArg.capture(),
dataInArg.capture(),
dataOutArg.capture(),
- anyString(), anyString(), anyBoolean(), anyInt(), anyBoolean());
+ anyString(), anyInt(), anyBoolean(), anyBoolean());
IconState iconState = iconArg.getValue();
@@ -381,17 +374,15 @@
assertEquals("Signal icon in status bar", state, iconState.icon);
assertEquals("Visibility in status bar", visible, iconState.visible);
- iconState = qsIconArg.getValue();
assertEquals("Visibility in quick settings", qsVisible, iconState.visible);
assertEquals("Signal icon in quick settings", state, iconState.icon);
- assertEquals("Data icon in quick settings", qsTypeIcon, (int) qsTypeIconArg.getValue());
assertEquals("Data direction in in quick settings", dataIn,
(boolean) dataInArg.getValue());
assertEquals("Data direction out in quick settings", dataOut,
(boolean) dataOutArg.getValue());
}
- protected void assertNetworkNameEquals(String expected) {
- assertEquals("Network name", expected, mMobileSignalController.getState().networkName);
- }
+ protected void assertNetworkNameEquals(String expected) {
+ assertEquals("Network name", expected, mMobileSignalController.getState().networkName);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index dfe00f9..6470c11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -1,5 +1,9 @@
package com.android.systemui.statusbar.policy;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -11,10 +15,14 @@
import android.test.suitebuilder.annotation.SmallTest;
import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.statusbar.phone.SignalDrawable;
+import com.android.systemui.statusbar.policy.NetworkController.IconState;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -116,8 +124,11 @@
updateDataConnectionState(TelephonyManager.DATA_DISCONNECTED, 0);
setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
- verifyDataIndicators(TelephonyIcons.ICON_DATA_DISABLED,
- TelephonyIcons.QS_ICON_DATA_DISABLED);
+ ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
+ Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
+ iconArg.capture(), anyInt(), anyBoolean(), anyBoolean(), any(), anyInt(),
+ anyBoolean(), anyBoolean());
+ assertEquals(SignalDrawable.STATE_CUT, SignalDrawable.getState(iconArg.getValue().icon));
}
@Test
@@ -129,9 +140,14 @@
setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
when(mMockProvisionController.isUserSetup(anyInt())).thenReturn(false);
mUserCallback.onUserSetupChanged();
+ waitForIdleSync();
// Don't show the X until the device is setup.
- verifyDataIndicators(0, 0);
+ ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
+ Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators(
+ iconArg.capture(), anyInt(), anyBoolean(), anyBoolean(), any(), anyInt(),
+ anyBoolean(), anyBoolean());
+ assertNotEquals(SignalDrawable.STATE_CUT, SignalDrawable.getState(iconArg.getValue().icon));
}
@Test
@@ -181,12 +197,12 @@
updateDataActivity(direction);
verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, DEFAULT_ICON, true,
- DEFAULT_QS_SIGNAL_STRENGTH, DEFAULT_QS_ICON, in, out);
+ in, out);
}
private void verifyDataIndicators(int dataIcon, int qsDataIcon) {
verifyLastMobileDataIndicators(true, DEFAULT_SIGNAL_STRENGTH, dataIcon,
- true, DEFAULT_QS_SIGNAL_STRENGTH, qsDataIcon, false,
+ true, false,
false);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
index 1627925..e542c37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java
@@ -214,7 +214,7 @@
verifyLastQsMobileDataIndicators(true,
testStrength,
- TelephonyIcons.QS_DATA_1X, false, false);
+ TelephonyIcons.ICON_1X, false, false);
}
}
@@ -434,7 +434,7 @@
verifyLastQsMobileDataIndicators(true /* visible */,
DEFAULT_LEVEL /* icon */,
- DEFAULT_QS_ICON /* typeIcon */,
+ DEFAULT_ICON /* typeIcon */,
false /* dataIn */,
true /* dataOut */);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 73fa5aa1..edfa326 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -62,7 +62,7 @@
public void testWifiIcon() {
String testSsid = "Test SSID";
setWifiEnabled(true);
- verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
+ verifyLastWifiIcon(true, WifiIcons.WIFI_DISCONNECTED);
setWifiState(true, testSsid);
verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
index 157b8a0..fee5e32 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
@@ -36,6 +36,11 @@
}
@Override
+ public boolean hasWorkProfile() {
+ return false;
+ }
+
+ @Override
public String getDeviceOwnerName() {
return null;
}
@@ -51,6 +56,11 @@
}
@Override
+ public CharSequence getWorkProfileOrganizationName() {
+ return null;
+ }
+
+ @Override
public boolean isNetworkLoggingEnabled() {
return false;
}
@@ -76,11 +86,21 @@
}
@Override
- public String getProfileVpnName() {
+ public String getWorkProfileVpnName() {
return null;
}
@Override
+ public boolean hasCACertInCurrentUser() {
+ return false;
+ }
+
+ @Override
+ public boolean hasCACertInWorkProfile() {
+ return false;
+ }
+
+ @Override
public void onUserSwitched(int newUserId) {
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index d6f5256..1b5b2c6 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -48,6 +48,7 @@
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
+import android.service.autofill.FillEventHistory;
import android.util.LocalLog;
import android.util.Log;
import android.util.Slog;
@@ -184,7 +185,7 @@
}
@Override
- public void onStopUser(int userId) {
+ public void onCleanupUser(int userId) {
synchronized (mLock) {
removeCachedServiceLocked(userId);
}
@@ -211,7 +212,7 @@
/**
* Peeks the service instance for a user.
*
- * @return service instance or null if not already present
+ * @return service instance or {@code null} if not already present
*/
@Nullable
AutofillManagerServiceImpl peekServiceForUserLocked(int userId) {
@@ -398,6 +399,21 @@
}
@Override
+ public FillEventHistory getFillEventHistory() throws RemoteException {
+ UserHandle user = getCallingUserHandle();
+ int uid = getCallingUid();
+
+ synchronized (mLock) {
+ AutofillManagerServiceImpl service = peekServiceForUserLocked(user.getIdentifier());
+ if (service != null) {
+ return service.getFillEventHistory(uid);
+ }
+ }
+
+ return null;
+ }
+
+ @Override
public boolean restoreSession(int sessionId, IBinder activityToken, IBinder appCallback)
throws RemoteException {
activityToken = Preconditions.checkNotNull(activityToken, "activityToken");
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 63bf373..e274e18 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -48,6 +48,9 @@
import android.provider.Settings;
import android.service.autofill.AutofillService;
import android.service.autofill.AutofillServiceInfo;
+import android.service.autofill.FillEventHistory;
+import android.service.autofill.FillRequest;
+import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
import android.text.TextUtils;
import android.util.LocalLog;
@@ -121,6 +124,10 @@
@GuardedBy("mLock")
private final SparseArray<Session> mSessions = new SparseArray<>();
+ /** The last selection */
+ @GuardedBy("mLock")
+ private FillEventHistory mEventHistory;
+
/**
* Receiver of assist data from the app's {@link Activity}.
*/
@@ -169,7 +176,8 @@
structure.sanitizeForParceling(true);
// TODO(b/33197203): Need to pipe the bundle
- session.mRemoteFillService.onFillRequest(structure, null, session.mFlags);
+ FillRequest request = new FillRequest(structure, null, session.mFlags);
+ session.mRemoteFillService.onFillRequest(request);
}
};
@@ -498,6 +506,72 @@
return mInfo.getServiceInfo().loadLabel(mContext.getPackageManager());
}
+ /**
+ * Initializes the last fill selection after an autofill service returned a new
+ * {@link FillResponse}.
+ */
+ void setLastResponse(int serviceUid, @NonNull FillResponse response) {
+ synchronized (mLock) {
+ mEventHistory = new FillEventHistory(serviceUid, response.getClientState());
+ }
+ }
+
+ /**
+ * Updates the last fill selection when an authentication was selected.
+ */
+ void setAuthenticationSelected() {
+ synchronized (mLock) {
+ mEventHistory.addEvent(
+ new FillEventHistory.Event(FillEventHistory.Event.TYPE_AUTHENTICATION_SELECTED, null));
+ }
+ }
+
+ /**
+ * Updates the last fill selection when an dataset authentication was selected.
+ */
+ void setDatasetAuthenticationSelected(@Nullable String selectedDataset) {
+ synchronized (mLock) {
+ mEventHistory.addEvent(new FillEventHistory.Event(
+ FillEventHistory.Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset));
+ }
+ }
+
+ /**
+ * Updates the last fill selection when an save Ui is shown.
+ */
+ void setSaveShown() {
+ synchronized (mLock) {
+ mEventHistory.addEvent(new FillEventHistory.Event(FillEventHistory.Event.TYPE_SAVE_SHOWN, null));
+ }
+ }
+
+ /**
+ * Updates the last fill response when a dataset was selected.
+ */
+ void setDatasetSelected(@Nullable String selectedDataset) {
+ synchronized (mLock) {
+ mEventHistory.addEvent(
+ new FillEventHistory.Event(FillEventHistory.Event.TYPE_DATASET_SELECTED, selectedDataset));
+ }
+ }
+
+ /**
+ * Gets the fill event history.
+ *
+ * @param callingUid The calling uid
+ *
+ * @return The history or {@code null} if there is none.
+ */
+ FillEventHistory getFillEventHistory(int callingUid) {
+ synchronized (mLock) {
+ if (mEventHistory != null && mEventHistory.getServiceUid() == callingUid) {
+ return mEventHistory;
+ }
+ }
+
+ return null;
+ }
+
void dumpLocked(String prefix, PrintWriter pw) {
final String prefix2 = prefix + " ";
@@ -526,6 +600,20 @@
mSessions.valueAt(i).dumpLocked(prefix2, pw);
}
}
+
+ if (mEventHistory == null || mEventHistory.getEvents().size() == 0) {
+ pw.print(prefix); pw.println("No event on last fill response");
+ } else {
+ pw.print(prefix); pw.println("Events of last fill response:");
+ pw.print(prefix);
+
+ int numEvents = mEventHistory.getEvents().size();
+ for (int i = 0; i < numEvents; i++) {
+ FillEventHistory.Event event = mEventHistory.getEvents().get(i);
+ pw.println(" " + i + ": eventType=" + event.getType() + " datasetId="
+ + event.getDatasetId());
+ }
+ }
}
void destroySessionsLocked() {
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index dd520ac..4d0f380 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -20,12 +20,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.assist.AssistStructure;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.os.Bundle;
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.ICancellationSignal;
@@ -33,10 +31,12 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.service.autofill.AutofillService;
+import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.service.autofill.IAutoFillService;
import android.service.autofill.IFillCallback;
import android.service.autofill.ISaveCallback;
+import android.service.autofill.SaveRequest;
import android.text.format.DateUtils;
import android.util.Slog;
@@ -87,8 +87,8 @@
private PendingRequest mPendingRequest;
public interface FillServiceCallbacks {
- void onFillRequestSuccess(@Nullable FillResponse response,
- @NonNull String servicePackageName);
+ void onFillRequestSuccess(@Nullable FillResponse response, int serviceUid,
+ @NonNull String servicePackageName, int requestId);
void onFillRequestFailure(@Nullable CharSequence message,
@NonNull String servicePackageName);
void onSaveRequestSuccess(@NonNull String servicePackageName);
@@ -134,17 +134,16 @@
mCallbacks.onServiceDied(this);
}
- public void onFillRequest(@NonNull AssistStructure structure, @Nullable Bundle extras,
- int flags) {
+ public void onFillRequest(@NonNull FillRequest request) {
cancelScheduledUnbind();
- final PendingFillRequest request = new PendingFillRequest(structure, extras, this, flags);
- mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
+ final PendingFillRequest pendingRequest = new PendingFillRequest(request, this);
+ mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, pendingRequest).sendToTarget();
}
- public void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle extras) {
+ public void onSaveRequest(@NonNull SaveRequest request) {
cancelScheduledUnbind();
- final PendingSaveRequest request = new PendingSaveRequest(structure, extras, this);
- mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
+ final PendingSaveRequest pendingRequest = new PendingSaveRequest(request, this);
+ mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, pendingRequest).sendToTarget();
}
// Note: we are dumping without a lock held so this is a bit racy but
@@ -253,10 +252,11 @@
}
private void dispatchOnFillRequestSuccess(PendingRequest pendingRequest,
- FillResponse response) {
+ int callingUid, FillResponse response, int requestId) {
mHandler.getHandler().post(() -> {
if (handleResponseCallbackCommon(pendingRequest)) {
- mCallbacks.onFillRequestSuccess(response, mComponentName.getPackageName());
+ mCallbacks.onFillRequestSuccess(response, callingUid,
+ mComponentName.getPackageName(), requestId);
}
});
}
@@ -392,18 +392,13 @@
private static final class PendingFillRequest extends PendingRequest {
private final Object mLock = new Object();
private final WeakReference<RemoteFillService> mWeakService;
- private final AssistStructure mStructure;
- private final Bundle mExtras;
+ private final FillRequest mRequest;
private final IFillCallback mCallback;
private ICancellationSignal mCancellation;
private boolean mCancelled;
- private int mFlags;
- public PendingFillRequest(AssistStructure structure,
- Bundle extras, RemoteFillService service, int flags) {
- mStructure = structure;
- mExtras = extras;
- mFlags = flags;
+ public PendingFillRequest(FillRequest request, RemoteFillService service) {
+ mRequest = request;
mWeakService = new WeakReference<>(service);
mCallback = new IFillCallback.Stub() {
@Override
@@ -425,11 +420,11 @@
}
@Override
- public void onSuccess(FillResponse response) {
+ public void onSuccess(FillResponse response, int requestId) {
RemoteFillService remoteService = mWeakService.get();
if (remoteService != null) {
remoteService.dispatchOnFillRequestSuccess(
- PendingFillRequest.this, response);
+ PendingFillRequest.this, getCallingUid(), response, requestId);
}
}
@@ -449,8 +444,7 @@
RemoteFillService remoteService = mWeakService.get();
if (remoteService != null) {
try {
- remoteService.mAutoFillService.onFillRequest(mStructure,
- mExtras, mCallback, mFlags);
+ remoteService.mAutoFillService.onFillRequest(mRequest, mCallback);
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Error calling on fill request", e);
cancel();
@@ -481,14 +475,12 @@
private static final class PendingSaveRequest extends PendingRequest {
private final WeakReference<RemoteFillService> mWeakService;
- private final AssistStructure mStructure;
- private final Bundle mExtras;
+ private final SaveRequest mRequest;
private final ISaveCallback mCallback;
- public PendingSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle extras,
+ public PendingSaveRequest(@NonNull SaveRequest request,
@NonNull RemoteFillService service) {
- mStructure = structure;
- mExtras = extras;
+ mRequest = request;
mWeakService = new WeakReference<>(service);
mCallback = new ISaveCallback.Stub() {
@Override
@@ -516,7 +508,7 @@
final RemoteFillService service = mWeakService.get();
if (service != null) {
try {
- service.mAutoFillService.onSaveRequest(mStructure, mExtras, mCallback);
+ service.mAutoFillService.onSaveRequest(mRequest, mCallback);
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Error calling on save request", e);
}
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 0b1381e..2b99614 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -44,11 +44,15 @@
import android.os.RemoteException;
import android.service.autofill.AutofillService;
import android.service.autofill.Dataset;
+import android.service.autofill.FillContext;
+import android.service.autofill.FillRequest;
import android.service.autofill.FillResponse;
import android.service.autofill.SaveInfo;
+import android.service.autofill.SaveRequest;
import android.util.ArrayMap;
import android.util.DebugUtils;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
@@ -64,6 +68,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
@@ -125,7 +130,7 @@
RemoteFillService mRemoteFillService;
@GuardedBy("mLock")
- private ArrayList<FillResponse> mResponses;
+ private SparseArray<FillResponse> mResponses;
/**
* Response that requires a service authentitcation request.
@@ -156,7 +161,7 @@
* and used on subsequent {@code onFillRequest()} and {@code onSaveRequest()} calls.
*/
@GuardedBy("mLock")
- private Bundle mExtras;
+ private Bundle mClientState;
/**
* Flags used to start the session.
@@ -216,13 +221,16 @@
synchronized (mLock) {
mActivityToken = newActivity;
mClient = IAutoFillManagerClient.Stub.asInterface(newClient);
+
+ // The tracked id are not persisted in the client, hence update them
+ updateTrackedIdsLocked();
}
}
// FillServiceCallbacks
@Override
- public void onFillRequestSuccess(@Nullable FillResponse response,
- @NonNull String servicePackageName) {
+ public void onFillRequestSuccess(@Nullable FillResponse response, int serviceUid,
+ @NonNull String servicePackageName, int requestId) {
if (response == null) {
if ((mFlags & FLAG_MANUAL_REQUEST) != 0) {
getUiForShowing().showError(R.string.autofill_error_cannot_autofill);
@@ -233,6 +241,8 @@
return;
}
+ mService.setLastResponse(serviceUid, response);
+
if ((response.getDatasets() == null || response.getDatasets().isEmpty())
&& response.getAuthentication() == null) {
// Response is "empty" from an UI point of view, need to notify client.
@@ -243,7 +253,7 @@
// TODO(b/33197203 , b/35707731): make sure it's ignored if there is one already
mResponseWaitingAuth = response;
}
- processResponseLocked(response);
+ processResponseLocked(response, requestId);
}
final LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST))
@@ -306,6 +316,9 @@
synchronized (mLock) {
fillInIntent = createAuthFillInIntent(mStructure, extras);
}
+
+ mService.setAuthenticationSelected();
+
mHandlerCaller.getHandler().post(() -> startAuthentication(intent, fillInIntent));
}
@@ -343,9 +356,8 @@
if (id.equals(mCurrentViewId)) {
try {
final ViewState view = mViewStates.get(id);
- mClient.requestShowFillUi(mWindowToken, id, width, height,
- view.getVirtualBounds(),
- presenter);
+ mClient.requestShowFillUi(this.id, mWindowToken, id, width, height,
+ view.getVirtualBounds(), presenter);
} catch (RemoteException e) {
Slog.e(TAG, "Error requesting to show fill UI", e);
}
@@ -363,7 +375,7 @@
public void requestHideFillUi(AutofillId id) {
synchronized (mLock) {
try {
- mClient.requestHideFillUi(mWindowToken, id);
+ mClient.requestHideFillUi(this.id, mWindowToken, id);
} catch (RemoteException e) {
Slog.e(TAG, "Error requesting to hide fill UI", e);
}
@@ -395,12 +407,18 @@
AutofillManager.EXTRA_AUTHENTICATION_RESULT);
if (result instanceof FillResponse) {
mMetricsLogger.action(MetricsEvent.AUTOFILL_AUTHENTICATED, mPackageName);
+ final int requestIndex = mResponses.indexOfValue(mResponseWaitingAuth);
mResponseWaitingAuth = null;
- processResponseLocked((FillResponse) result);
+ if (requestIndex >= 0) {
+ final int requestId = mResponses.keyAt(requestIndex);
+ processResponseLocked((FillResponse) result, requestId);
+ } else {
+ Slog.e(TAG, "Error cannot find id for auth response");
+ }
} else if (result instanceof Dataset) {
final Dataset dataset = (Dataset) result;
for (int i = 0; i < mResponses.size(); i++) {
- final FillResponse response = mResponses.get(i);
+ final FillResponse response = mResponses.valueAt(i);
final int index = response.getDatasets().indexOf(mDatasetWaitingAuth);
if (index >= 0) {
response.getDatasets().set(index, dataset);
@@ -429,7 +447,7 @@
*/
public boolean showSaveLocked() {
if (mStructure == null) {
- Slog.wtf(TAG, "showSaveLocked(): no mStructure");
+ Slog.d(TAG, "showSaveLocked(): no mStructure");
return true;
}
if (mResponses == null) {
@@ -441,12 +459,18 @@
return true;
}
- final FillResponse response = mResponses.get(mResponses.size() - 1);
+ final int lastResponseIdx = getLastResponseIndex();
+ if (lastResponseIdx < 0) {
+ Slog.d(TAG, "showSaveLocked(): mResponses=" + mResponses
+ + ", mViewStates=" + mViewStates);
+ return true;
+ }
+ final FillResponse response = mResponses.valueAt(lastResponseIdx);
final SaveInfo saveInfo = response.getSaveInfo();
if (DEBUG) {
- Slog.d(TAG,
- "showSaveLocked(): mResponses=" + mResponses + ", mViewStates=" + mViewStates);
+ Slog.d(TAG, "showSaveLocked(): mResponses=" + mResponses
+ + ", mViewStates=" + mViewStates);
}
/*
@@ -523,6 +547,7 @@
}
}
if (atLeastOneChanged) {
+ mService.setSaveShown();
getUiForShowing().showSaveUi(mService.getServiceLabel(), saveInfo, mPackageName);
return false;
}
@@ -573,7 +598,15 @@
mStructure.dump();
}
- mRemoteFillService.onSaveRequest(mStructure, mExtras);
+ // TODO(b/33197203): Implement partitioning properly
+ final int lastResponseIdx = getLastResponseIndex();
+ final int requestId = mResponses.keyAt(lastResponseIdx);
+ final FillContext fillContext = new FillContext(requestId, mStructure);
+ final ArrayList fillContexts = new ArrayList(1);
+ fillContexts.add(fillContext);
+
+ final SaveRequest saveRequest = new SaveRequest(fillContexts, mClientState);
+ mRemoteFillService.onSaveRequest(saveRequest);
}
void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int flags) {
@@ -687,7 +720,9 @@
overlay.focused = id.equals(viewState.id);
node.setAutofillOverlay(overlay);
}
- mRemoteFillService.onFillRequest(mStructure, mExtras, 0);
+
+ FillRequest request = new FillRequest(mStructure, mClientState, 0);
+ mRemoteFillService.onFillRequest(request);
return newViewState;
}
@@ -716,7 +751,7 @@
}
if (!mHasCallback) return;
try {
- mClient.notifyNoFillUi(mWindowToken, mCurrentViewId);
+ mClient.notifyNoFillUi(id, mWindowToken, mCurrentViewId);
} catch (RemoteException e) {
Slog.e(TAG, "Error notifying client no fill UI: windowToken=" + mWindowToken
+ " id=" + mCurrentViewId, e);
@@ -724,20 +759,54 @@
}
}
- private void processResponseLocked(FillResponse response) {
+ private void updateTrackedIdsLocked() {
+ if (mResponses == null || mResponses.size() == 0) {
+ return;
+ }
+
+ // Only track the views of the last response as only those are reported back to the
+ // service, see #showSaveLocked
+ ArrayList<AutofillId> trackedViews = new ArrayList<>();
+ boolean saveOnAllViewsInvisible = false;
+ SaveInfo saveInfo = mResponses.valueAt(getLastResponseIndex()).getSaveInfo();
+ if (saveInfo != null) {
+ saveOnAllViewsInvisible =
+ (saveInfo.getFlags() & SaveInfo.FLAG_SAVE_ON_ALL_VIEWS_INVISIBLE) != 0;
+
+ // We only need to track views if we want to save once they become invisible.
+ if (saveOnAllViewsInvisible) {
+ if (saveInfo.getRequiredIds() != null) {
+ Collections.addAll(trackedViews, saveInfo.getRequiredIds());
+ }
+
+ if (saveInfo.getOptionalIds() != null) {
+ Collections.addAll(trackedViews, saveInfo.getOptionalIds());
+ }
+ }
+ }
+
+ try {
+ mClient.setTrackedViews(id, trackedViews, saveOnAllViewsInvisible);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Cannot set tracked ids", e);
+ }
+ }
+
+ private void processResponseLocked(FillResponse response, int requestId) {
if (DEBUG) {
Slog.d(TAG, "processResponseLocked(mCurrentViewId=" + mCurrentViewId + "):" + response);
}
if (mResponses == null) {
- mResponses = new ArrayList<>(4);
+ mResponses = new SparseArray<>(4);
}
- mResponses.add(response);
+ mResponses.put(requestId, response);
if (response != null) {
- mExtras = response.getExtras();
+ mClientState = response.getClientState();
}
setViewStatesLocked(response, ViewState.STATE_FILLABLE);
+ updateTrackedIdsLocked();
if (mCurrentViewId == null) {
return;
@@ -819,12 +888,15 @@
synchronized (mLock) {
// Autofill it directly...
if (dataset.getAuthentication() == null) {
+ mService.setDatasetSelected(dataset.getId());
+
autoFillApp(dataset);
return;
}
// ...or handle authentication.
// TODO(b/33197203 , b/35707731): make sure it's ignored if there is one already
+ mService.setDatasetAuthenticationSelected(dataset.getId());
mDatasetWaitingAuth = dataset;
setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH);
final Intent fillInIntent = createAuthFillInIntent(mStructure, null);
@@ -852,7 +924,7 @@
private void startAuthentication(IntentSender intent, Intent fillInIntent) {
try {
synchronized (mLock) {
- mClient.authenticate(intent, fillInIntent);
+ mClient.authenticate(id, intent, fillInIntent);
}
} catch (RemoteException e) {
Slog.e(TAG, "Error launching auth intent", e);
@@ -885,7 +957,8 @@
}
}
pw.print(prefix); pw.print("mHasCallback: "); pw.println(mHasCallback);
- pw.print(prefix); pw.print("mExtras: "); pw.println(Helper.bundleToString(mExtras));
+ pw.print(prefix); pw.print("mClientState: "); pw.println(
+ Helper.bundleToString(mClientState));
mRemoteFillService.dump(prefix, pw);
}
@@ -895,7 +968,7 @@
if (DEBUG) {
Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
}
- mClient.autofill(mWindowToken, dataset.getFieldIds(), dataset.getFieldValues());
+ mClient.autofill(id, mWindowToken, dataset.getFieldIds(), dataset.getFieldValues());
setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED);
} catch (RemoteException e) {
Slog.w(TAG, "Error autofilling activity: " + e);
@@ -962,4 +1035,20 @@
destroyLocked();
mService.removeSessionLocked(id);
}
+
+ private int getLastResponseIndex() {
+ // The response ids are monotonically increasing so
+ // we just find the largest id which is the last. We
+ // do not rely on the internal ordering in sparse
+ // array to avoid - wow this stopped working!?
+ int lastResponseIdx = -1;
+ int lastResponseId = -1;
+ final int responseCount = mResponses.size();
+ for (int i = 0; i < responseCount; i++) {
+ if (mResponses.keyAt(i) > lastResponseId) {
+ lastResponseIdx = i;
+ }
+ }
+ return lastResponseIdx;
+ }
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 4449da9..ab6a3a7 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -161,7 +161,8 @@
log.setType(MetricsProto.MetricsEvent.TYPE_DETAIL);
hideFillUiUiThread();
if (mCallback != null) {
- mCallback.authenticate(response.getAuthentication(), response.getExtras());
+ mCallback.authenticate(response.getAuthentication(),
+ response.getClientState());
}
}
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index d647c63..aa5083d 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -798,6 +798,19 @@
return ((app.flags & ApplicationInfo.FLAG_STOPPED) != 0);
}
+ // We also avoid backups of 'disabled' apps
+ private static boolean appIsDisabled(ApplicationInfo app, PackageManager pm) {
+ switch (pm.getApplicationEnabledSetting(app.packageName)) {
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER:
+ case PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
/* does *not* check overall backup eligibility policy! */
private static boolean appGetsFullBackup(PackageInfo pkg) {
if (pkg.applicationInfo.backupAgentName != null) {
@@ -10774,7 +10787,8 @@
PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName,
PackageManager.GET_SIGNATURES);
if (!appIsEligibleForBackup(packageInfo.applicationInfo) ||
- appIsStopped(packageInfo.applicationInfo)) {
+ appIsStopped(packageInfo.applicationInfo) ||
+ appIsDisabled(packageInfo.applicationInfo, mPackageManager)) {
return false;
}
IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 50c0a12..d5adf48 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -55,6 +55,7 @@
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
+import android.net.MatchAllNetworkSpecifier;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
@@ -89,7 +90,6 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -203,6 +203,8 @@
// See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
private final int mReleasePendingIntentDelayMs;
+ private MockableSystemProperties mSystemProperties;
+
private Tethering mTethering;
private final PermissionMonitor mPermissionMonitor;
@@ -675,6 +677,8 @@
IpConnectivityLog logger) {
if (DBG) log("ConnectivityService starting up");
+ mSystemProperties = getSystemProperties();
+
mMetricsLog = logger;
mDefaultRequest = createInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder());
@@ -692,7 +696,7 @@
mReleasePendingIntentDelayMs = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS, 5_000);
- mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
+ mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
mContext = checkNotNull(context, "missing Context");
mNetd = checkNotNull(netManager, "missing INetworkManagementService");
@@ -722,7 +726,7 @@
mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1];
// TODO: What is the "correct" way to do determine if this is a wifi only device?
- boolean wifiOnly = SystemProperties.getBoolean("ro.radio.noril", false);
+ boolean wifiOnly = mSystemProperties.getBoolean("ro.radio.noril", false);
log("wifiOnly=" + wifiOnly);
String[] naStrings = context.getResources().getStringArray(
com.android.internal.R.array.networkAttributes);
@@ -775,8 +779,8 @@
}
}
- mTestMode = SystemProperties.get("cm.test.mode").equals("true")
- && SystemProperties.get("ro.build.type").equals("eng");
+ mTestMode = mSystemProperties.get("cm.test.mode").equals("true")
+ && mSystemProperties.get("ro.build.type").equals("eng");
mTethering = new Tethering(mContext, mNetd, statsService, mPolicyManager,
IoThread.get().getLooper(), new MockableSystemProperties());
@@ -1732,8 +1736,8 @@
// Overridden for testing purposes to avoid writing to SystemProperties.
@VisibleForTesting
- protected int getDefaultTcpRwnd() {
- return SystemProperties.getInt(DEFAULT_TCP_RWND_KEY, 0);
+ protected MockableSystemProperties getSystemProperties() {
+ return new MockableSystemProperties();
}
private void updateTcpBufferSizes(NetworkAgentInfo nai) {
@@ -1771,10 +1775,11 @@
}
Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.TCP_DEFAULT_INIT_RWND, getDefaultTcpRwnd());
+ Settings.Global.TCP_DEFAULT_INIT_RWND,
+ mSystemProperties.getInt("net.tcp.default_init_rwnd", 0));
final String sysctlKey = "sys.sysctl.tcp_def_init_rwnd";
if (rwndValue != 0) {
- SystemProperties.set(sysctlKey, rwndValue.toString());
+ mSystemProperties.set(sysctlKey, rwndValue.toString());
}
}
@@ -1798,7 +1803,7 @@
@Override
public int getRestoreDefaultNetworkDelay(int networkType) {
- String restoreDefaultNetworkDelayStr = SystemProperties.get(
+ String restoreDefaultNetworkDelayStr = mSystemProperties.get(
NETWORK_RESTORE_DELAY_PROP_NAME);
if(restoreDefaultNetworkDelayStr != null &&
restoreDefaultNetworkDelayStr.length() != 0) {
@@ -2965,7 +2970,7 @@
@Override
public boolean isTetheringSupported() {
enforceTetherAccessPermission();
- int defaultVal = (SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1);
+ int defaultVal = (mSystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1);
boolean tetherEnabledInSettings = (Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.TETHER_SUPPORTED, defaultVal) != 0)
&& !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
@@ -4044,11 +4049,8 @@
throw new IllegalArgumentException("Bad timeout specified");
}
- if (NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER
- .equals(networkCapabilities.getNetworkSpecifier())) {
- throw new IllegalArgumentException("Invalid network specifier - must not be '"
- + NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER + "'");
- }
+ MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(
+ networkCapabilities.getNetworkSpecifier());
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
nextNetworkRequestId(), type);
@@ -4117,6 +4119,9 @@
enforceMeteredApnPolicy(networkCapabilities);
ensureRequestableCapabilities(networkCapabilities);
+ MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(
+ networkCapabilities.getNetworkSpecifier());
+
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
NetworkRequestInfo nri = new NetworkRequestInfo(networkRequest, operation);
@@ -4178,6 +4183,9 @@
nc.addCapability(NET_CAPABILITY_FOREGROUND);
}
+ MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(
+ networkCapabilities.getNetworkSpecifier());
+
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
NetworkRequestInfo nri = new NetworkRequestInfo(messenger, networkRequest, binder);
@@ -4195,6 +4203,9 @@
enforceAccessPermission();
}
+ MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(
+ networkCapabilities.getNetworkSpecifier());
+
NetworkRequest networkRequest = new NetworkRequest(
new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
@@ -4466,17 +4477,24 @@
int last = 0;
for (InetAddress dns : dnses) {
++last;
- String key = "net.dns" + last;
- String value = dns.getHostAddress();
- SystemProperties.set(key, value);
+ setNetDnsProperty(last, dns.getHostAddress());
}
for (int i = last + 1; i <= mNumDnsEntries; ++i) {
- String key = "net.dns" + i;
- SystemProperties.set(key, "");
+ setNetDnsProperty(i, "");
}
mNumDnsEntries = last;
}
+ private void setNetDnsProperty(int which, String value) {
+ final String key = "net.dns" + which;
+ // Log and forget errors setting unsupported properties.
+ try {
+ mSystemProperties.set(key, value);
+ } catch (Exception e) {
+ Log.e(TAG, "Error setting unsupported net.dns property: ", e);
+ }
+ }
+
private String getNetworkPermission(NetworkCapabilities nc) {
// TODO: make these permission strings AIDL constants instead.
if (!nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 2e61550..6502c01 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -25,7 +25,7 @@
# This is logged when the screen on broadcast has completed
2727 power_screen_broadcast_stop (which|1|5),(wakelockCount|1|1)
# This is logged when the screen is turned on or off.
-2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1)
+2728 power_screen_state (offOrOn|1|5),(becauseOfUser|1|5),(totalTouchDownTime|2|3),(touchCycles|1|1),(latency|1|3)
# This is logged when the partial wake lock (keeping the device awake
# regardless of whether the screen is off) is acquired or released.
2729 power_partial_wake_state (releasedorAcquired|1|5),(tag|3)
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 39bfeda..8ad3d23 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -49,6 +49,7 @@
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import android.annotation.BinderThread;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -2146,6 +2147,7 @@
return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
}
+ @BinderThread
@SuppressWarnings("deprecation")
@Override
public void setImeWindowStatus(IBinder token, IBinder startInputToken, int vis,
@@ -2161,9 +2163,23 @@
mBackDisposition = backDisposition;
updateSystemUiLocked(token, vis, backDisposition);
}
+
+ final boolean dismissImeOnBackKeyPressed;
+ switch (backDisposition) {
+ case InputMethodService.BACK_DISPOSITION_WILL_DISMISS:
+ dismissImeOnBackKeyPressed = true;
+ break;
+ case InputMethodService.BACK_DISPOSITION_WILL_NOT_DISMISS:
+ dismissImeOnBackKeyPressed = false;
+ break;
+ default:
+ case InputMethodService.BACK_DISPOSITION_DEFAULT:
+ dismissImeOnBackKeyPressed = ((vis & InputMethodService.IME_VISIBLE) != 0);
+ break;
+ }
mWindowManagerInternal.updateInputMethodWindowStatus(token,
(vis & InputMethodService.IME_VISIBLE) != 0,
- info != null ? info.mTargetWindow : null);
+ dismissImeOnBackKeyPressed, info != null ? info.mTargetWindow : null);
}
private void updateSystemUi(IBinder token, int vis, int backDisposition) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index d796098..452fe1d 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3272,6 +3272,8 @@
try {
return mContext.getSystemService(StorageStatsManager.class)
.queryStatsForUid(volumeUuid, uid).getCacheBytes();
+ } catch (IOException e) {
+ throw new ParcelableException(e);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -3312,6 +3314,8 @@
return Math.max(0, path.getUsableSpace() - storage.getStorageLowBytes(path));
}
}
+ } catch (IOException e) {
+ throw new ParcelableException(e);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -3329,13 +3333,13 @@
+ " because only " + allocatableBytes + " allocatable"));
}
- // Free up enough disk space to satisfy both the requested allocation
- // and our low disk warning space.
- final File path = storage.findPathForUuid(volumeUuid);
- bytes += storage.getStorageLowBytes(path);
-
final long token = Binder.clearCallingIdentity();
try {
+ // Free up enough disk space to satisfy both the requested allocation
+ // and our low disk warning space.
+ final File path = storage.findPathForUuid(volumeUuid);
+ bytes += storage.getStorageLowBytes(path);
+
mPms.freeStorage(volumeUuid, bytes, flags);
} catch (IOException e) {
throw new ParcelableException(e);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index c77820b..2cd14e9 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -312,8 +312,7 @@
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
- int id, Notification notification, int callingPid, int callingUid,
- boolean fgRequired, String callingPackage, final int userId)
+ int callingPid, int callingUid, boolean fgRequired, String callingPackage, final int userId)
throws TransactionTooLargeException {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
+ " type=" + resolvedType + " args=" + service.getExtras());
@@ -464,10 +463,6 @@
}
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
- // STOPSHIP deprecated; remove when NotificationManager.startServiceInForeground is retired
- if (notification != null) {
- setServiceForegroundInnerLocked(r, id, notification, 0);
- }
return cmp;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2be5313..8f1afa8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1230,6 +1230,20 @@
*/
int[] mDeviceIdleTempWhitelist = new int[0];
+ static final class PendingTempWhitelist {
+ final int targetUid;
+ final long duration;
+ final String tag;
+
+ PendingTempWhitelist(int _targetUid, long _duration, String _tag) {
+ targetUid = _targetUid;
+ duration = _duration;
+ tag = _tag;
+ }
+ }
+
+ final SparseArray<PendingTempWhitelist> mPendingTempWhitelist = new SparseArray<>();
+
/**
* Information about and control over application operations
*/
@@ -1688,6 +1702,7 @@
static final int NOTIFY_VR_SLEEPING_MSG = 65;
static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67;
+ static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
static final int START_USER_SWITCH_FG_MSG = 712;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
@@ -1921,6 +1936,9 @@
case DISPATCH_UIDS_CHANGED_UI_MSG: {
dispatchUidsChanged();
} break;
+ case PUSH_TEMP_WHITELIST_UI_MSG: {
+ pushTempWhitelist();
+ } break;
}
}
}
@@ -6493,7 +6511,8 @@
// This is the first appearance of the uid, report it now!
if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
"Creating new process uid: " + uidRec);
- if (Arrays.binarySearch(mDeviceIdleTempWhitelist, UserHandle.getAppId(proc.uid)) >= 0) {
+ if (Arrays.binarySearch(mDeviceIdleTempWhitelist, UserHandle.getAppId(proc.uid)) >= 0
+ || mPendingTempWhitelist.indexOfKey(proc.uid) >= 0) {
uidRec.setWhitelist = uidRec.curWhitelist = true;
}
uidRec.updateHasInternetPermission();
@@ -7487,43 +7506,6 @@
}
}
- /**
- * Whitelists {@code targetUid} to temporarily bypass Power Save mode.
- */
- void tempWhitelistAppForPowerSave(int callerPid, int callerUid, int targetUid, long duration) {
- if (DEBUG_WHITELISTS) {
- Slog.d(TAG, "tempWhitelistAppForPowerSave(" + callerPid + ", " + callerUid + ", "
- + targetUid + ", " + duration + ")");
- }
-
- if (checkPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, callerPid, callerUid)
- != PackageManager.PERMISSION_GRANTED) {
- synchronized (mPidsSelfLocked) {
- final ProcessRecord pr = mPidsSelfLocked.get(callerPid);
- if (pr == null) {
- Slog.w(TAG, "tempWhitelistAppForPowerSave() no ProcessRecord for pid "
- + callerPid);
- return;
- }
- if (!pr.whitelistManager) {
- if (DEBUG_WHITELISTS) {
- Slog.d(TAG, "tempWhitelistAppForPowerSave() for target " + targetUid
- + ": pid " + callerPid + " is not allowed");
- }
- return;
- }
- }
- }
-
- final long token = Binder.clearCallingIdentity();
- try {
- mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(targetUid, duration,
- true, "pe from uid:" + callerUid);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
@Override
public void cancelIntentSender(IIntentSender sender) {
if (!(sender instanceof PendingIntentRecord)) {
@@ -7863,7 +7845,14 @@
r.pictureInPictureArgs.copyOnlySet(args);
final float aspectRatio = r.pictureInPictureArgs.getAspectRatio();
final List<RemoteAction> actions = r.pictureInPictureArgs.getActions();
- final Rect sourceBounds = r.pictureInPictureArgs.getSourceRectHint();
+ // Adjust the source bounds by the insets for the transition down
+ final Rect sourceBounds = new Rect(r.pictureInPictureArgs.getSourceRectHint());
+ final Rect insets = r.pictureInPictureArgs.getSourceRectHintInsets();
+ if (insets != null) {
+ sourceBounds.offsetTo(Math.max(0, sourceBounds.left - insets.left),
+ Math.max(0, sourceBounds.top - insets.top));
+ }
+
mStackSupervisor.moveActivityToPinnedStackLocked(r, sourceBounds, aspectRatio,
true /* moveHomeStackToFront */, "enterPictureInPictureMode");
final PinnedActivityStack stack = mStackSupervisor.getStack(PINNED_STACK_ID);
@@ -8412,7 +8401,8 @@
boolean isOnDeviceIdleWhitelistLocked(int uid) {
final int appId = UserHandle.getAppId(uid);
return Arrays.binarySearch(mDeviceIdleWhitelist, appId) >= 0
- || Arrays.binarySearch(mDeviceIdleTempWhitelist, appId) >= 0;
+ || Arrays.binarySearch(mDeviceIdleTempWhitelist, appId) >= 0
+ || mPendingTempWhitelist.indexOfKey(uid) >= 0;
}
private ProviderInfo getProviderInfoLocked(String authority, int userHandle, int pmFlags) {
@@ -10535,8 +10525,9 @@
final PinnedActivityStack pinnedStack =
mStackSupervisor.getStack(PINNED_STACK_ID);
if (pinnedStack != null) {
- pinnedStack.animateResizePinnedStack(null /* sourceBounds */,
- destBounds, animationDuration);
+ pinnedStack.animateResizePinnedStack(null /* sourceHintBounds */,
+ destBounds, animationDuration,
+ false /* schedulePipModeChangedOnAnimationEnd */);
}
} else {
throw new IllegalArgumentException("Stack: " + stackId
@@ -10694,6 +10685,13 @@
return;
}
+ // When a task is locked, dismiss the pinned stack if it exists
+ final PinnedActivityStack pinnedStack = mStackSupervisor.getStack(
+ PINNED_STACK_ID);
+ if (pinnedStack != null) {
+ mStackSupervisor.removeStackLocked(PINNED_STACK_ID);
+ }
+
// isSystemInitiated is used to distinguish between locked and pinned mode, as pinned mode
// is initiated by system after the pinning request was shown and locked mode is initiated
// by an authorized app directly
@@ -15587,6 +15585,18 @@
}
pw.println(" mDeviceIdleWhitelist=" + Arrays.toString(mDeviceIdleWhitelist));
pw.println(" mDeviceIdleTempWhitelist=" + Arrays.toString(mDeviceIdleTempWhitelist));
+ if (mPendingTempWhitelist.size() > 0) {
+ pw.println(" mPendingTempWhitelist:");
+ for (int i = 0; i < mPendingTempWhitelist.size(); i++) {
+ PendingTempWhitelist ptw = mPendingTempWhitelist.valueAt(i);
+ pw.print(" ");
+ UserHandle.formatUid(pw, ptw.targetUid);
+ pw.print(": ");
+ TimeUtils.formatDuration(ptw.duration, pw);
+ pw.print(" ");
+ pw.println(ptw.tag);
+ }
+ }
}
if (dumpPackage == null) {
pw.println(" mWakefulness="
@@ -17928,8 +17938,7 @@
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
- String resolvedType, int id, Notification notification, boolean requireForeground,
- String callingPackage, int userId)
+ String resolvedType, boolean requireForeground, String callingPackage, int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
// Refuse possible leaked file descriptors
@@ -17950,7 +17959,7 @@
ComponentName res;
try {
res = mServices.startServiceLocked(caller, service,
- resolvedType, id, notification, callingPid, callingUid,
+ resolvedType, callingPid, callingUid,
requireForeground, callingPackage, userId);
} finally {
Binder.restoreCallingIdentity(origId);
@@ -17969,7 +17978,7 @@
ComponentName res;
try {
res = mServices.startServiceLocked(null, service,
- resolvedType, 0, null, -1, uid, fgRequired, callingPackage, userId);
+ resolvedType, -1, uid, fgRequired, callingPackage, userId);
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -22699,6 +22708,80 @@
enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE);
}
+ /**
+ * Whitelists {@code targetUid} to temporarily bypass Power Save mode.
+ */
+ void tempWhitelistForPendingIntentLocked(int callerPid, int callerUid, int targetUid,
+ long duration, String tag) {
+ if (DEBUG_WHITELISTS) {
+ Slog.d(TAG, "tempWhitelistForPendingIntentLocked(" + callerPid + ", " + callerUid + ", "
+ + targetUid + ", " + duration + ")");
+ }
+
+ synchronized (mPidsSelfLocked) {
+ final ProcessRecord pr = mPidsSelfLocked.get(callerPid);
+ if (pr == null) {
+ Slog.w(TAG, "tempWhitelistForPendingIntentLocked() no ProcessRecord for pid "
+ + callerPid);
+ return;
+ }
+ if (!pr.whitelistManager) {
+ if (checkPermission(CHANGE_DEVICE_IDLE_TEMP_WHITELIST, callerPid, callerUid)
+ != PackageManager.PERMISSION_GRANTED) {
+ if (DEBUG_WHITELISTS) {
+ Slog.d(TAG, "tempWhitelistForPendingIntentLocked() for target " + targetUid
+ + ": pid " + callerPid + " is not allowed");
+ }
+ return;
+ }
+ }
+ }
+
+ tempWhitelistUidLocked(targetUid, duration, tag);
+ }
+
+ /**
+ * Whitelists {@code targetUid} to temporarily bypass Power Save mode.
+ */
+ void tempWhitelistUidLocked(int targetUid, long duration, String tag) {
+ mPendingTempWhitelist.put(targetUid, new PendingTempWhitelist(targetUid, duration, tag));
+ setUidTempWhitelistStateLocked(targetUid, true);
+ mUiHandler.obtainMessage(PUSH_TEMP_WHITELIST_UI_MSG).sendToTarget();
+ }
+
+ void pushTempWhitelist() {
+ final int N;
+ final PendingTempWhitelist[] list;
+
+ // First copy out the pending changes... we need to leave them in the map for now,
+ // in case someone needs to check what is coming up while we don't have the lock held.
+ synchronized(this) {
+ N = mPendingTempWhitelist.size();
+ list = new PendingTempWhitelist[N];
+ for (int i = 0; i < N; i++) {
+ list[i] = mPendingTempWhitelist.valueAt(i);
+ }
+ }
+
+ // Now safely dispatch changes to device idle controller.
+ for (int i = 0; i < N; i++) {
+ PendingTempWhitelist ptw = list[i];
+ mLocalDeviceIdleController.addPowerSaveTempWhitelistAppDirect(ptw.targetUid,
+ ptw.duration, true, ptw.tag);
+ }
+
+ // And now we can safely remove them from the map.
+ synchronized(this) {
+ for (int i = 0; i < N; i++) {
+ PendingTempWhitelist ptw = list[i];
+ int index = mPendingTempWhitelist.indexOfKey(ptw.targetUid);
+ if (index >= 0 && mPendingTempWhitelist.valueAt(index) == ptw) {
+ mPendingTempWhitelist.removeAt(index);
+ }
+ }
+ }
+ }
+
final void setAppIdTempWhitelistStateLocked(int appId, boolean onWhitelist) {
boolean changed = false;
for (int i=mActiveUids.size()-1; i>=0; i--) {
@@ -22713,6 +22796,15 @@
}
}
+ final void setUidTempWhitelistStateLocked(int uid, boolean onWhitelist) {
+ boolean changed = false;
+ final UidRecord uidRec = mActiveUids.get(uid);
+ if (uidRec != null && uidRec.curWhitelist != onWhitelist) {
+ uidRec.curWhitelist = onWhitelist;
+ updateOomAdjLocked();
+ }
+ }
+
final void trimApplications() {
synchronized (this) {
int i;
@@ -23885,6 +23977,15 @@
}
}
+ @Override
+ public long getActivityStartInitiatedTime(IBinder token) {
+ final ActivityRecord r = ActivityRecord.forTokenLocked(token);
+ if (r != null) {
+ return r.mStartInitiatedTimeMs;
+ }
+ return 0;
+ }
+
void updateApplicationInfoLocked(@NonNull List<String> packagesToUpdate, int userId) {
final PackageManagerInternal packageManager = getPackageManagerInternalLocked();
final boolean updateFrameworkRes = packagesToUpdate.contains("android");
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 0fcf3e6..b6bfb00 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -524,7 +524,7 @@
pw.println("Starting service: " + intent);
pw.flush();
ComponentName cn = mInterface.startService(null, intent, intent.getType(),
- -1, null, asForeground, SHELL_PACKAGE_NAME, mUserId);
+ asForeground, SHELL_PACKAGE_NAME, mUserId);
if (cn == null) {
err.println("Error: Not found; no service started.");
return -1;
diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
index 2881787..494aaa7 100644
--- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java
@@ -314,7 +314,8 @@
builder.setPackageName(info.launchedActivity.packageName);
builder.setType(type);
builder.addTaggedData(FIELD_CLASS_NAME, info.launchedActivity.info.name);
- if (info.launchedActivity.launchedFromPackage != null) {
+ final boolean isInstantApp = info.launchedActivity.info.applicationInfo.isInstantApp();
+ if (isInstantApp && info.launchedActivity.launchedFromPackage != null) {
builder.addTaggedData(APP_TRANSITION_CALLING_PACKAGE_NAME,
info.launchedActivity.launchedFromPackage);
}
@@ -323,8 +324,7 @@
info.launchedActivity.info.launchToken);
info.launchedActivity.info.launchToken = null;
}
- builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL,
- info.launchedActivity.info.applicationInfo.isInstantApp() ? 1 : 0);
+ builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0);
builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS,
mCurrentTransitionDeviceUptime);
builder.addTaggedData(APP_TRANSITION_DELAY_MS, mCurrentTransitionDelayMs);
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 276b267..7cdddc0 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -341,6 +341,12 @@
private final Rect mBounds = new Rect();
/**
+ * Denotes the timestamp at which this activity start was last initiated in the
+ * {@link SystemClock#uptimeMillis()} time base.
+ */
+ long mStartInitiatedTimeMs;
+
+ /**
* Temp configs used in {@link #ensureActivityConfigurationLocked(int, boolean)}
*/
private final Configuration mTmpConfig1 = new Configuration();
@@ -498,6 +504,8 @@
pw.print(" forceNewConfig="); pw.println(forceNewConfig);
pw.print(prefix); pw.print("mActivityType=");
pw.println(activityTypeToString(mActivityType));
+ pw.print(prefix); pw.print("mStartInitiatedTimeMs=");
+ TimeUtils.formatDuration(mStartInitiatedTimeMs, now, pw);
if (requestedVrComponent != null) {
pw.print(prefix);
pw.print("requestedVrComponent=");
@@ -1170,6 +1178,10 @@
* the activity is not currently visible and {@param noThrow} is not set.
*/
boolean checkEnterPictureInPictureState(String caller, boolean noThrow, boolean beforeStopping) {
+ if (!supportsPictureInPicture()) {
+ return false;
+ }
+
// Check app-ops and see if PiP is supported for this package
if (!checkEnterPictureInPictureAppOpsState()) {
return false;
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 4c84d98..85c5c64 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -65,6 +65,8 @@
import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.ASSISTANT_ACTIVITY_TYPE;
import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPED;
+import static com.android.server.am.ActivityStack.ActivityState.STOPPING;
import static com.android.server.am.ActivityStackSupervisor.FindTaskResult;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PAUSE_IMMEDIATELY;
@@ -589,6 +591,13 @@
}
/**
+ * Returns whether to defer the scheduling of the multi-window mode.
+ */
+ boolean deferScheduleMultiWindowModeChanged() {
+ return false;
+ }
+
+ /**
* Defers updating the bounds of the stack. If the stack was resized/repositioned while
* deferring, the bounds will update in {@link #continueUpdateBounds()}.
*/
@@ -1172,7 +1181,7 @@
final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = activities.get(activityNdx);
- if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED
+ if (r.state == STOPPING || r.state == STOPPED
|| r.state == ActivityState.PAUSED || r.state == ActivityState.PAUSING) {
r.setSleeping(true);
}
@@ -1355,7 +1364,7 @@
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Complete pause: " + prev);
if (prev != null) {
- final boolean wasStopping = prev.state == ActivityState.STOPPING;
+ final boolean wasStopping = prev.state == STOPPING;
prev.state = ActivityState.PAUSED;
if (prev.finishing) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Executing finish of activity: " + prev);
@@ -1376,7 +1385,7 @@
// We are also stopping, the stop request must have gone soon after the pause.
// We can't clobber it, because the stop confirmation will not be handled.
// We don't need to schedule another stop, we only need to let it happen.
- prev.state = ActivityState.STOPPING;
+ prev.state = STOPPING;
} else if ((!prev.visible && !hasVisibleBehindActivity())
|| mService.isSleepingOrShuttingDownLocked()) {
// If we were visible then resumeTopActivities will release resources before
@@ -1995,10 +2004,17 @@
// keeping the screen frozen.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Making invisible: " + r + " " + r.state);
try {
+ final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState(
+ "makeInvisible", true /* noThrow */, true /* beforeStopping */);
+ // We don't want to call setVisible(false) to avoid notifying the client of this
+ // intermittent invisible state if it can enter Pip and isn't stopped or stopping.
+ if (!canEnterPictureInPicture || r.state == STOPPING || r.state == STOPPED) {
+ r.setVisible(false);
+ }
+
switch (r.state) {
case STOPPING:
case STOPPED:
- r.setVisible(false);
if (r.app != null && r.app.thread != null) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Scheduling invisibility: " + r);
@@ -2017,25 +2033,16 @@
// This case created for transitioning activities from
// translucent to opaque {@link Activity#convertToOpaque}.
if (visibleBehind == r) {
- r.setVisible(false);
releaseBackgroundResources(r);
} else {
// If this activity is in a state where it can currently enter
// picture-in-picture, then don't immediately schedule the idle now in case
// the activity tries to enterPictureInPictureMode() later. Otherwise,
// we will try and stop the activity next time idle is processed.
- final boolean canEnterPictureInPicture = r.checkEnterPictureInPictureState(
- "makeInvisible", true /* noThrow */, true /* beforeStopping */);
if (canEnterPictureInPicture) {
- // We set r.visible=false so that Stop will later
- // call setVisible for us. In this case
- // we don't want to call setVisible(false) to avoid
- // notifying the client of this intermittent invisible
- // state.
+ // We set r.visible=false so that Stop will later call setVisible for us
r.visible = false;
- } else {
- r.setVisible(false);
}
addToStopping(r, true /* scheduleIdle */,
canEnterPictureInPicture /* idleDelayed */);
@@ -2311,9 +2318,20 @@
mStackSupervisor.setLaunchSource(next.info.applicationInfo.uid);
+ final boolean prevCanPip = prev != null && prev.checkEnterPictureInPictureState(
+ "resumeTopActivity", true /* noThrow */, userLeaving /* beforeStopping */);
// If the flag RESUME_WHILE_PAUSING is set, then continue to schedule the previous activity
- // to be paused, while at the same time resuming the new resume activity
+ // to be paused, while at the same time resuming the new resume activity only if the
+ // previous activity can't go into Pip since we want to give Pip activities a chance to
+ // enter Pip before resuming the next activity.
final boolean resumeWhilePausing = (next.info.flags & FLAG_RESUME_WHILE_PAUSING) != 0;
+ // TODO: This would be go to have however, the various call points that pass in
+ // prev need to be corrected first. In some cases the prev is equal to the next e.g. launch
+ // an app from home. And, is come other cases it is null e.g. press home button after
+ // launching an app. The doc on the method says prev. is null expect for the case we are
+ // coming from pause. We need to see if that is a valid thing and also if all the code in
+ // this method using prev. are setup to function like that.
+ //&& !prevCanPip;
boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false);
if (mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
@@ -3353,11 +3371,11 @@
r.stopped = false;
if (DEBUG_STATES) Slog.v(TAG_STATES,
"Moving to STOPPING: " + r + " (stop requested)");
- r.state = ActivityState.STOPPING;
+ r.state = STOPPING;
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Stopping visible=" + r.visible + " for " + r);
if (!r.visible) {
- r.setVisibility(false);
+ r.setVisible(false);
}
EventLogTags.writeAmStopActivity(
r.userId, System.identityHashCode(r), r.shortComponentName);
@@ -3375,7 +3393,7 @@
// Just in case, assume it to be stopped.
r.stopped = true;
if (DEBUG_STATES) Slog.v(TAG_STATES, "Stop failed; moving to STOPPED: " + r);
- r.state = ActivityState.STOPPED;
+ r.state = STOPPED;
if (r.deferRelaunchUntilPaused) {
destroyActivityLocked(r, true, "stop-except");
}
@@ -3680,7 +3698,7 @@
}
if (DEBUG_STATES) Slog.v(TAG_STATES,
"Moving to STOPPING: "+ r + " (finish requested)");
- r.state = ActivityState.STOPPING;
+ r.state = STOPPING;
if (oomAdj) {
mService.updateOomAdjLocked();
}
@@ -3705,8 +3723,8 @@
|| (prevState == ActivityState.PAUSED
&& (mode == FINISH_AFTER_PAUSE || mStackId == PINNED_STACK_ID))
|| finishingActivityInNonFocusedStack
- || prevState == ActivityState.STOPPING
- || prevState == ActivityState.STOPPED
+ || prevState == STOPPING
+ || prevState == STOPPED
|| prevState == ActivityState.INITIALIZING) {
r.makeFinishingLocked();
boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm");
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 4d16e33..152d5f4 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -2504,7 +2504,7 @@
// incorrect if AMS.resizeStackWithBoundsFromWindowManager() is already called while waiting
// for the AMS lock to be freed. So check and make sure these bounds are still good.
final PinnedStackWindowController stackController = stack.getWindowContainerController();
- if (stackController.pinnedStackResizeAllowed()) {
+ if (stackController.pinnedStackResizeDisallowed()) {
return;
}
@@ -2873,11 +2873,15 @@
return true;
}
- void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceBounds, float aspectRatio,
+ void moveActivityToPinnedStackLocked(ActivityRecord r, Rect sourceHintBounds, float aspectRatio,
boolean moveHomeStackToFront, String reason) {
mWindowManager.deferSurfaceLayout();
+ // This will clear the pinned stack by moving an existing task to the full screen stack,
+ // ensuring only one task is present.
+ moveTasksToFullscreenStackLocked(PINNED_STACK_ID, !ON_TOP);
+
// Need to make sure the pinned stack exist so we can resize it below...
final PinnedActivityStack stack = getStack(PINNED_STACK_ID, CREATE_IF_NEEDED, ON_TOP);
@@ -2948,11 +2952,8 @@
final Rect destBounds = mWindowManager.getPictureInPictureBounds(DEFAULT_DISPLAY,
aspectRatio, false /* useExistingStackBounds */);
- // TODO(b/36099777): Schedule the PiP mode change here immediately until we can defer all
- // callbacks until after the bounds animation
- scheduleUpdatePictureInPictureModeIfNeeded(r.getTask(), destBounds, true /* immediate */);
-
- stack.animateResizePinnedStack(sourceBounds, destBounds, -1 /* animationDuration */);
+ stack.animateResizePinnedStack(sourceHintBounds, destBounds, -1 /* animationDuration */,
+ true /* schedulePipModeChangedOnAnimationEnd */);
mService.mTaskChangeNotificationController.notifyActivityPinned(r.packageName);
}
@@ -4179,6 +4180,12 @@
}
void scheduleUpdateMultiWindowMode(TaskRecord task) {
+ // If the stack is animating in a way where we will be forcing a multi-mode change at the
+ // end, then ensure that we defer all in between multi-window mode changes
+ if (task.getStack().deferScheduleMultiWindowModeChanged()) {
+ return;
+ }
+
for (int i = task.mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = task.mActivities.get(i);
if (r.app != null && r.app.thread != null) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 56594d3..8f1c203 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -244,6 +244,7 @@
ActivityOptions options, boolean ignoreTargetSecurity, boolean componentSpecified,
ActivityRecord[] outActivity, ActivityStackSupervisor.ActivityContainer container,
TaskRecord inTask) {
+ final long activityStartTime = SystemClock.uptimeMillis();
int err = ActivityManager.START_SUCCESS;
ProcessRecord callerApp = null;
@@ -478,6 +479,7 @@
callingPackage, intent, resolvedType, aInfo, mService.getGlobalConfiguration(),
resultRecord, resultWho, requestCode, componentSpecified, voiceSession != null,
mSupervisor, container, options, sourceRecord);
+ r.mStartInitiatedTimeMs = activityStartTime;
if (outActivity != null) {
outActivity[0] = r;
}
@@ -1029,6 +1031,7 @@
// so make sure the task now has the identity of the new intent.
top.getTask().setIntent(mStartActivity);
}
+ top.mStartInitiatedTimeMs = mStartActivity.mStartInitiatedTimeMs;
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity, top.getTask());
top.deliverNewIntentLocked(mCallingUid, mStartActivity.intent,
mStartActivity.launchedFromPackage);
@@ -1052,6 +1055,7 @@
setTaskFromIntentActivity(reusedActivity);
if (!mAddingToTask && mReuseTask == null) {
+ reusedActivity.mStartInitiatedTimeMs = mStartActivity.mStartInitiatedTimeMs;
// We didn't do anything... but it was needed (a.k.a., client don't use that
// intent!) And for paranoia, make sure we have correctly resumed the top activity.
resumeTargetStackIfNeeded();
@@ -1084,6 +1088,7 @@
|| mLaunchSingleTop || mLaunchSingleTask);
if (dontStart) {
ActivityStack.logStartActivity(AM_NEW_INTENT, top, top.getTask());
+ top.mStartInitiatedTimeMs = mStartActivity.mStartInitiatedTimeMs;
// For paranoia, make sure we have correctly resumed the top activity.
topStack.mLastPausedActivity = null;
if (mDoResume) {
@@ -1664,6 +1669,7 @@
// desires.
if (((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0 || mLaunchSingleTop)
&& intentActivity.realActivity.equals(mStartActivity.realActivity)) {
+ intentActivity.mStartInitiatedTimeMs = mStartActivity.mStartInitiatedTimeMs;
ActivityStack.logStartActivity(AM_NEW_INTENT, mStartActivity,
intentActivity.getTask());
if (intentActivity.frontOfTask) {
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index 02ec075..c9c1d00 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -148,18 +148,7 @@
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
- final int result = msg.what;
-
- synchronized (mService) {
- if (mProc != null && mProc.crashDialog == AppErrorDialog.this) {
- mProc.crashDialog = null;
- }
- }
- mResult.set(result);
-
- // Make sure we don't have time timeout still hanging around.
- removeMessages(TIMEOUT);
-
+ setResult(msg.what);
dismiss();
}
};
@@ -168,11 +157,23 @@
public void dismiss() {
if (!mResult.mHasResult) {
// We are dismissing and the result has not been set...go ahead and set.
- mResult.set(FORCE_QUIT);
+ setResult(FORCE_QUIT);
}
super.dismiss();
}
+ private void setResult(int result) {
+ synchronized (mService) {
+ if (mProc != null && mProc.crashDialog == AppErrorDialog.this) {
+ mProc.crashDialog = null;
+ }
+ }
+ mResult.set(result);
+
+ // Make sure we don't have time timeout still hanging around.
+ mHandler.removeMessages(TIMEOUT);
+ }
+
@Override
public void onClick(View v) {
switch (v.getId()) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index baa71d7..349180f 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -155,8 +155,6 @@
static final int BROADCAST_INTENT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG;
static final int BROADCAST_TIMEOUT_MSG = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 1;
- static final int SCHEDULE_TEMP_WHITELIST_MSG
- = ActivityManagerService.FIRST_BROADCAST_QUEUE_MSG + 2;
final BroadcastHandler mHandler;
@@ -178,13 +176,6 @@
broadcastTimeoutLocked(true);
}
} break;
- case SCHEDULE_TEMP_WHITELIST_MSG: {
- DeviceIdleController.LocalService dic = mService.mLocalDeviceIdleController;
- if (dic != null) {
- dic.addPowerSaveTempWhitelistAppDirect(UserHandle.getAppId(msg.arg1),
- msg.arg2, true, (String)msg.obj);
- }
- } break;
}
}
}
@@ -789,12 +780,11 @@
if (r.intent.getAction() != null) {
b.append(r.intent.getAction());
} else if (r.intent.getComponent() != null) {
- b.append(r.intent.getComponent().flattenToShortString());
+ r.intent.getComponent().appendShortString(b);
} else if (r.intent.getData() != null) {
b.append(r.intent.getData());
}
- mHandler.obtainMessage(SCHEDULE_TEMP_WHITELIST_MSG, uid, (int)duration, b.toString())
- .sendToTarget();
+ mService.tempWhitelistUidLocked(uid, duration, b.toString());
}
/**
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index c697f28..a580d4b 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -237,14 +237,6 @@
if (intent != null) intent.setDefusable(true);
if (options != null) options.setDefusable(true);
- if (whitelistDuration > 0 && !canceled) {
- // Must call before acquiring the lock. It's possible the method return before sending
- // the intent due to some validations inside the lock, in which case the UID shouldn't
- // be whitelisted, but since the whitelist is temporary, that would be ok.
- owner.tempWhitelistAppForPowerSave(Binder.getCallingPid(), Binder.getCallingUid(), uid,
- whitelistDuration);
- }
-
synchronized (owner) {
final ActivityContainer activityContainer = (ActivityContainer)container;
if (activityContainer != null && activityContainer.mParentActivity != null &&
@@ -279,6 +271,22 @@
resolvedType = key.requestResolvedType;
}
+ if (whitelistDuration > 0) {
+ StringBuilder tag = new StringBuilder(64);
+ tag.append("pendingintent:");
+ UserHandle.formatUid(tag, Binder.getCallingUid());
+ tag.append(":");
+ if (finalIntent.getAction() != null) {
+ tag.append(finalIntent.getAction());
+ } else if (finalIntent.getComponent() != null) {
+ finalIntent.getComponent().appendShortString(tag);
+ } else if (finalIntent.getData() != null) {
+ tag.append(finalIntent.getData());
+ }
+ owner.tempWhitelistForPendingIntentLocked(Binder.getCallingPid(),
+ Binder.getCallingUid(), uid, whitelistDuration, tag.toString());
+ }
+
final long origId = Binder.clearCallingIdentity();
boolean sendFinish = finishedReceiver != null;
diff --git a/services/core/java/com/android/server/am/PinnedActivityStack.java b/services/core/java/com/android/server/am/PinnedActivityStack.java
index cd9c42c..a4932bb 100644
--- a/services/core/java/com/android/server/am/PinnedActivityStack.java
+++ b/services/core/java/com/android/server/am/PinnedActivityStack.java
@@ -43,9 +43,10 @@
return new PinnedStackWindowController(mStackId, this, displayId, onTop, outBounds);
}
- void animateResizePinnedStack(Rect sourceBounds, Rect destBounds, int animationDuration) {
- getWindowContainerController().animateResizePinnedStack(sourceBounds, destBounds,
- animationDuration);
+ void animateResizePinnedStack(Rect sourceHintBounds, Rect toBounds, int animationDuration,
+ boolean schedulePipModeChangedOnAnimationEnd) {
+ getWindowContainerController().animateResizePinnedStack(toBounds, sourceHintBounds,
+ animationDuration, schedulePipModeChangedOnAnimationEnd);
}
void setPictureInPictureAspectRatio(float aspectRatio) {
@@ -60,7 +61,18 @@
return getWindowContainerController().isAnimatingBoundsToFullscreen();
}
- @Override
+ /**
+ * Returns whether to defer the scheduling of the multi-window mode.
+ */
+ boolean deferScheduleMultiWindowModeChanged() {
+ // For the pinned stack, the deferring of the multi-window mode changed is tied to the
+ // transition animation into picture-in-picture, and is called once the animation completes,
+ // or is interrupted in a way that would leave the stack in a non-fullscreen state.
+ // @see BoundsAnimationController
+ // @see BoundsAnimationControllerTests
+ return mWindowContainerController.deferScheduleMultiWindowModeChanged();
+ }
+
public void updatePictureInPictureModeForPinnedStackAnimation(Rect targetStackBounds) {
// It is guaranteed that the activities requiring the update will be in the pinned stack at
// this point (either reparented before the animation into PiP, or before reparenting after
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index f8a4d4b..d42b6a7 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -48,6 +48,7 @@
import android.util.DisplayMetrics;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.util.XmlUtils;
@@ -445,10 +446,23 @@
final Rect bounds = updateOverrideConfigurationFromLaunchBounds();
final Configuration overrideConfig = getOverrideConfiguration();
- mWindowContainerController = new TaskWindowContainerController(taskId, this,
+ setWindowContainerController(new TaskWindowContainerController(taskId, this,
getStack().getWindowContainerController(), userId, bounds, overrideConfig,
mResizeMode, mSupportsPictureInPicture, isHomeTask(), onTop, showForAllUsers,
- lastTaskDescription);
+ lastTaskDescription));
+ }
+
+ /**
+ * Should only be invoked from {@link #createWindowContainer(boolean, boolean)}.
+ */
+ @VisibleForTesting
+ protected void setWindowContainerController(TaskWindowContainerController controller) {
+ if (mWindowContainerController != null) {
+ throw new IllegalArgumentException("Window container=" + mWindowContainerController
+ + " already created for task=" + this);
+ }
+
+ mWindowContainerController = controller;
}
void removeWindowContainer() {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 2687242..aa1b74c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -520,7 +520,11 @@
private int mPrevVolDirection = AudioManager.ADJUST_SAME;
// mVolumeControlStream is set by VolumePanel to temporarily force the stream type which volume
// is controlled by Vol keys.
- private int mVolumeControlStream = -1;
+ private int mVolumeControlStream = -1;
+ // interpretation of whether the volume stream has been selected by the user by clicking on a
+ // volume slider to change which volume is controlled by the volume keys. Is false
+ // when mVolumeControlStream is -1.
+ private boolean mUserSelectedVolumeControlStream = false;
private final Object mForceControlStreamLock = new Object();
// VolumePanel is currently the only client of forceVolumeControlStream() and runs in system
// server process so in theory it is not necessary to monitor the client death.
@@ -929,11 +933,8 @@
synchronized (VolumeStreamState.class) {
int numStreamTypes = AudioSystem.getNumStreamTypes();
for (int streamType = 0; streamType < numStreamTypes; streamType++) {
- if (streamType != mStreamVolumeAlias[streamType]) {
- mStreamStates[streamType].
- setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]],
- TAG);
- }
+ mStreamStates[streamType]
+ .setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], TAG);
// apply stream volume
if (!mStreamStates[streamType].mIsMuted) {
mStreamStates[streamType].applyAllVolumes();
@@ -1022,20 +1023,21 @@
}
mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
- final int oldStreamA11yAlias = mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY];
- if (oldStreamA11yAlias != a11yStreamAlias) {
- mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY] = a11yStreamAlias;
- mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName =
- System.VOLUME_SETTINGS_INT[a11yStreamAlias];
- // restore the value from the settings when the alias changes
- mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].readSettings();
- }
+ mStreamVolumeAlias[AudioSystem.STREAM_ACCESSIBILITY] = a11yStreamAlias;
if (updateVolumes) {
mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
caller);
+
+ mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].mVolumeIndexSettingName =
+ System.VOLUME_SETTINGS_INT[a11yStreamAlias];
mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].setAllIndexes(
mStreamStates[a11yStreamAlias], caller);
+ if (sIndependentA11yVolume) {
+ // restore the a11y values from the settings
+ mStreamStates[AudioSystem.STREAM_ACCESSIBILITY].readSettings();
+ }
+
// apply stream mute states according to new value of mRingerModeAffectedStreams
setRingerModeInt(getRingerModeInternal(), false);
sendMsg(mAudioHandler,
@@ -1226,14 +1228,29 @@
private void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
String callingPackage, String caller, int uid) {
if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream=" + suggestedStreamType
- + ", flags=" + flags + ", caller=" + caller);
- int streamType;
- boolean isMute = isMuteAdjust(direction);
- if (mVolumeControlStream != -1) {
+ + ", flags=" + flags + ", caller=" + caller
+ + ", volControlStream=" + mVolumeControlStream
+ + ", userSelect=" + mUserSelectedVolumeControlStream);
+ final int streamType;
+ if (mUserSelectedVolumeControlStream) { // implies mVolumeControlStream != -1
streamType = mVolumeControlStream;
} else {
- streamType = getActiveStreamType(suggestedStreamType);
+ final int maybeActiveStreamType = getActiveStreamType(suggestedStreamType);
+ final boolean activeForReal;
+ if (maybeActiveStreamType == AudioSystem.STREAM_MUSIC) {
+ activeForReal = isAfMusicActiveRecently(0);
+ } else {
+ activeForReal = AudioSystem.isStreamActive(maybeActiveStreamType, 0);
+ }
+ if (activeForReal || mVolumeControlStream == -1) {
+ streamType = maybeActiveStreamType;
+ } else {
+ streamType = mVolumeControlStream;
+ }
}
+
+ final boolean isMute = isMuteAdjust(direction);
+
ensureValidStreamType(streamType);
final int resolvedStream = mStreamVolumeAlias[streamType];
@@ -1709,13 +1726,18 @@
/** @see AudioManager#forceVolumeControlStream(int) */
public void forceVolumeControlStream(int streamType, IBinder cb) {
+ if (DEBUG_VOL) { Log.d(TAG, String.format("forceVolumeControlStream(%d)", streamType)); }
synchronized(mForceControlStreamLock) {
+ if (mVolumeControlStream != -1 && streamType != -1) {
+ mUserSelectedVolumeControlStream = true;
+ }
mVolumeControlStream = streamType;
if (mVolumeControlStream == -1) {
if (mForceControlStreamClient != null) {
mForceControlStreamClient.release();
mForceControlStreamClient = null;
}
+ mUserSelectedVolumeControlStream = false;
} else {
mForceControlStreamClient = new ForceControlStreamClient(cb);
}
@@ -1746,6 +1768,7 @@
} else {
mForceControlStreamClient = null;
mVolumeControlStream = -1;
+ mUserSelectedVolumeControlStream = false;
}
}
}
@@ -4228,7 +4251,17 @@
return mIndexMin;
}
+ /**
+ * Copies all device/index pairs from the given VolumeStreamState after initializing
+ * them with the volume for DEVICE_OUT_DEFAULT. No-op if the source VolumeStreamState
+ * has the same stream type as this instance.
+ * @param srcStream
+ * @param caller
+ */
public void setAllIndexes(VolumeStreamState srcStream, String caller) {
+ if (mStreamType == srcStream.mStreamType) {
+ return;
+ }
synchronized (VolumeStreamState.class) {
int srcStreamType = srcStream.getStreamType();
// apply default device volume from source stream to all devices first in case
diff --git a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
index 4f68652..77b86d8 100644
--- a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
+++ b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java
@@ -19,7 +19,20 @@
import android.os.SystemProperties;
public class MockableSystemProperties {
+
+ public String get(String key) {
+ return SystemProperties.get(key);
+ }
+
+ public int getInt(String key, int def) {
+ return SystemProperties.getInt(key, def);
+ }
+
public boolean getBoolean(String key, boolean def) {
return SystemProperties.getBoolean(key, def);
}
+
+ public void set(String key, String value) {
+ SystemProperties.set(key, value);
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 1ffa864..601ed01 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -78,7 +78,6 @@
public static final int CMD_IPV6_TETHER_UPDATE = BASE_IFACE + 13;
private final State mInitialState;
- private final State mServingState;
private final State mLocalHotspotState;
private final State mTetheredState;
private final State mUnavailableState;
@@ -107,14 +106,12 @@
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
mInitialState = new InitialState();
- mServingState = new ServingState();
mLocalHotspotState = new LocalHotspotState();
mTetheredState = new TetheredState();
mUnavailableState = new UnavailableState();
addState(mInitialState);
- addState(mServingState);
- addState(mLocalHotspotState, mServingState);
- addState(mTetheredState, mServingState);
+ addState(mLocalHotspotState);
+ addState(mTetheredState);
addState(mUnavailableState);
setInitialState(mInitialState);
@@ -222,12 +219,11 @@
}
}
- class ServingState extends State {
+ class BaseServingState extends State {
@Override
public void enter() {
if (!configureIfaceIp(true)) {
mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
- transitionTo(mInitialState);
return;
}
@@ -236,12 +232,13 @@
} catch (Exception e) {
Log.e(TAG, "Error Tethering: " + e.toString());
mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
- transitionTo(mInitialState);
return;
}
if (!mIPv6TetherSvc.start()) {
Log.e(TAG, "Failed to start IPv6TetheringInterfaceServices");
+ // TODO: Make this a fatal error once Bluetooth IPv6 is sorted.
+ return;
}
}
@@ -254,9 +251,9 @@
try {
mNMService.untetherInterface(mIfaceName);
- } catch (Exception ee) {
+ } catch (Exception e) {
mLastError = ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR;
- Log.e(TAG, "Failed to untether interface: " + ee.toString());
+ Log.e(TAG, "Failed to untether interface: " + e.toString());
}
configureIfaceIp(false);
@@ -293,15 +290,27 @@
}
}
- class LocalHotspotState extends State {
+ // Handling errors in BaseServingState.enter() by transitioning is
+ // problematic because transitioning during a multi-state jump yields
+ // a Log.wtf(). Ultimately, there should be only one ServingState,
+ // and forwarding and NAT rules should be handled by a coordinating
+ // functional element outside of TetherInterfaceStateMachine.
+ class LocalHotspotState extends BaseServingState {
@Override
public void enter() {
+ super.enter();
+ if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ transitionTo(mInitialState);
+ }
+
if (DBG) Log.d(TAG, "Local hotspot " + mIfaceName);
sendInterfaceState(IControlsTethering.STATE_LOCAL_HOTSPOT);
}
@Override
public boolean processMessage(Message message) {
+ if (super.processMessage(message)) return true;
+
maybeLogMessage(this, message.what);
switch (message.what) {
case CMD_TETHER_REQUESTED:
@@ -317,9 +326,19 @@
}
}
- class TetheredState extends State {
+ // Handling errors in BaseServingState.enter() by transitioning is
+ // problematic because transitioning during a multi-state jump yields
+ // a Log.wtf(). Ultimately, there should be only one ServingState,
+ // and forwarding and NAT rules should be handled by a coordinating
+ // functional element outside of TetherInterfaceStateMachine.
+ class TetheredState extends BaseServingState {
@Override
public void enter() {
+ super.enter();
+ if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ transitionTo(mInitialState);
+ }
+
if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
sendInterfaceState(IControlsTethering.STATE_TETHERED);
}
@@ -327,6 +346,7 @@
@Override
public void exit() {
cleanupUpstream();
+ super.exit();
}
private void cleanupUpstream() {
@@ -361,6 +381,8 @@
@Override
public boolean processMessage(Message message) {
+ if (super.processMessage(message)) return true;
+
maybeLogMessage(this, message.what);
boolean retValue = true;
switch (message.what) {
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
index 552f0d1..fe49813 100644
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
@@ -36,6 +36,7 @@
public abstract boolean handleFailedAttempt();
public abstract void resetFailedAttempts();
+ private boolean mAlreadyCancelled;
public AuthenticationClient(Context context, long halDeviceId, IBinder token,
IFingerprintServiceReceiver receiver, int targetUserId, int groupId, long opId,
@@ -129,6 +130,10 @@
@Override
public int stop(boolean initiatedByClient) {
+ if (mAlreadyCancelled) {
+ Slog.w(TAG, "stopAuthentication: already cancelled!");
+ return 0;
+ }
IBiometricsFingerprint daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "stopAuthentication: no fingerprint HAL!");
@@ -145,6 +150,7 @@
Slog.e(TAG, "stopAuthentication failed", e);
return ERROR_ESRCH;
}
+ mAlreadyCancelled = true;
return 0; // success
}
diff --git a/services/core/java/com/android/server/job/GrantedUriPermissions.java b/services/core/java/com/android/server/job/GrantedUriPermissions.java
new file mode 100644
index 0000000..e413d8d
--- /dev/null
+++ b/services/core/java/com/android/server/job/GrantedUriPermissions.java
@@ -0,0 +1,156 @@
+/*
+ * 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.server.job;
+
+import android.app.IActivityManager;
+import android.content.ClipData;
+import android.content.ContentProvider;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public class GrantedUriPermissions {
+ private final int mGrantFlags;
+ private final int mSourceUserId;
+ private final String mTag;
+ private final IBinder mPermissionOwner;
+ private final ArrayList<Uri> mUris = new ArrayList<>();
+
+ private GrantedUriPermissions(IActivityManager am, int grantFlags, int uid, String tag)
+ throws RemoteException {
+ mGrantFlags = grantFlags;
+ mSourceUserId = UserHandle.getUserId(uid);
+ mTag = tag;
+ mPermissionOwner = am.newUriPermissionOwner("job: " + tag);
+ }
+
+ public void revoke(IActivityManager am) {
+ for (int i = mUris.size()-1; i >= 0; i--) {
+ try {
+ am.revokeUriPermissionFromOwner(mPermissionOwner, mUris.get(i),
+ mGrantFlags, mSourceUserId);
+ } catch (RemoteException e) {
+ }
+ }
+ mUris.clear();
+ }
+
+ public static boolean checkGrantFlags(int grantFlags) {
+ return (grantFlags & (Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ |Intent.FLAG_GRANT_READ_URI_PERMISSION)) != 0;
+ }
+
+ public static GrantedUriPermissions createFromIntent(IActivityManager am, Intent intent,
+ int sourceUid, String targetPackage, int targetUserId, String tag) {
+ int grantFlags = intent.getFlags();
+ if (!checkGrantFlags(grantFlags)) {
+ return null;
+ }
+
+ GrantedUriPermissions perms = null;
+
+ Uri data = intent.getData();
+ if (data != null) {
+ perms = grantUri(am, data, sourceUid, targetPackage, targetUserId, grantFlags, tag,
+ perms);
+ }
+
+ ClipData clip = intent.getClipData();
+ if (clip != null) {
+ perms = grantClip(am, clip, sourceUid, targetPackage, targetUserId, grantFlags, tag,
+ perms);
+ }
+
+ return perms;
+ }
+
+ public static GrantedUriPermissions createFromClip(IActivityManager am, ClipData clip,
+ int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag) {
+ if (!checkGrantFlags(grantFlags)) {
+ return null;
+ }
+ GrantedUriPermissions perms = null;
+ if (clip != null) {
+ perms = grantClip(am, clip, sourceUid, targetPackage, targetUserId, grantFlags,
+ tag, perms);
+ }
+ return perms;
+ }
+
+ private static GrantedUriPermissions grantClip(IActivityManager am, ClipData clip,
+ int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag,
+ GrantedUriPermissions curPerms) {
+ final int N = clip.getItemCount();
+ for (int i = 0; i < N; i++) {
+ curPerms = grantItem(am, clip.getItemAt(i), sourceUid, targetPackage, targetUserId,
+ grantFlags, tag, curPerms);
+ }
+ return curPerms;
+ }
+
+ private static GrantedUriPermissions grantUri(IActivityManager am, Uri uri,
+ int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag,
+ GrantedUriPermissions curPerms) {
+ try {
+ int sourceUserId = ContentProvider.getUserIdFromUri(uri,
+ UserHandle.getUserId(sourceUid));
+ uri = ContentProvider.getUriWithoutUserId(uri);
+ if (curPerms == null) {
+ curPerms = new GrantedUriPermissions(am, grantFlags, sourceUid, tag);
+ }
+ am.grantUriPermissionFromOwner(curPerms.mPermissionOwner, sourceUid, targetPackage,
+ uri, grantFlags, sourceUserId, targetUserId);
+ curPerms.mUris.add(uri);
+ } catch (RemoteException e) {
+ Slog.e("JobScheduler", "AM dead");
+ }
+ return curPerms;
+ }
+
+ private static GrantedUriPermissions grantItem(IActivityManager am, ClipData.Item item,
+ int sourceUid, String targetPackage, int targetUserId, int grantFlags, String tag,
+ GrantedUriPermissions curPerms) {
+ if (item.getUri() != null) {
+ curPerms = grantUri(am, item.getUri(), sourceUid, targetPackage, targetUserId,
+ grantFlags, tag, curPerms);
+ }
+ Intent intent = item.getIntent();
+ if (intent != null && intent.getData() != null) {
+ curPerms = grantUri(am, intent.getData(), sourceUid, targetPackage, targetUserId,
+ grantFlags, tag, curPerms);
+ }
+ return curPerms;
+ }
+
+ // Dumpsys infrastructure
+ public void dump(PrintWriter pw, String prefix) {
+ pw.print(prefix); pw.print("mGrantFlags=0x"); pw.print(Integer.toHexString(mGrantFlags));
+ pw.print(" mSourceUserId="); pw.println(mSourceUserId);
+ pw.print(prefix); pw.print("mTag="); pw.println(mTag);
+ pw.print(prefix); pw.print("mPermissionOwner="); pw.println(mPermissionOwner);
+ for (int i = 0; i < mUris.size(); i++) {
+ pw.print(prefix); pw.print("#"); pw.print(i); pw.print(": ");
+ pw.println(mUris.get(i));
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index d01de3c..c8bfa34 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -601,7 +601,7 @@
// Fast path: we are adding work to an existing job, and the JobInfo is not
// changing. We can just directly enqueue this work in to the job.
if (toCancel.getJob().equals(job)) {
- toCancel.enqueueWorkLocked(work);
+ toCancel.enqueueWorkLocked(ActivityManager.getService(), work);
return JobScheduler.RESULT_SUCCESS;
}
}
@@ -625,7 +625,7 @@
}
if (work != null) {
// If work has been supplied, enqueue it into the new job.
- jobStatus.enqueueWorkLocked(work);
+ jobStatus.enqueueWorkLocked(ActivityManager.getService(), work);
}
startTrackingJobLocked(jobStatus, toCancel);
mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
@@ -758,7 +758,7 @@
final JobStatus executing = jsc.getRunningJob();
if (executing != null
&& (executing.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) == 0) {
- jsc.cancelExecutingJob(JobParameters.REASON_DEVICE_IDLE);
+ jsc.cancelExecutingJobLocked(JobParameters.REASON_DEVICE_IDLE);
}
}
} else {
@@ -921,7 +921,7 @@
private boolean stopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
boolean writeBack) {
// Deal with any remaining work items in the old job.
- jobStatus.stopTrackingJobLocked(incomingJob);
+ jobStatus.stopTrackingJobLocked(ActivityManager.getService(), incomingJob);
// Remove from store as well as controllers.
final boolean removed = mJobs.remove(jobStatus, writeBack);
@@ -939,7 +939,7 @@
JobServiceContext jsc = mActiveServices.get(i);
final JobStatus executing = jsc.getRunningJob();
if (executing != null && executing.matches(job.getUid(), job.getJobId())) {
- jsc.cancelExecutingJob(reason);
+ jsc.cancelExecutingJobLocked(reason);
return true;
}
}
@@ -1071,9 +1071,16 @@
if (DEBUG) {
Slog.d(TAG, "Completed " + jobStatus + ", reschedule=" + needsReschedule);
}
+
+ // If the job wants to be rescheduled, we first need to make the next upcoming
+ // job so we can transfer any appropriate state over from the previous job when
+ // we stop it.
+ final JobStatus rescheduledJob = needsReschedule
+ ? getRescheduleJobForFailureLocked(jobStatus) : null;
+
// Do not write back immediately if this is a periodic job. The job may get lost if system
// shuts down before it is added back.
- if (!stopTrackingJobLocked(jobStatus, null, !jobStatus.getJob().isPeriodic())) {
+ if (!stopTrackingJobLocked(jobStatus, rescheduledJob, !jobStatus.getJob().isPeriodic())) {
if (DEBUG) {
Slog.d(TAG, "Could not find job to remove. Was job removed while executing?");
}
@@ -1082,18 +1089,14 @@
mHandler.obtainMessage(MSG_CHECK_JOB_GREEDY).sendToTarget();
return;
}
- // Note: there is a small window of time in here where, when rescheduling a job,
- // we will stop monitoring its content providers. This should be fixed by stopping
- // the old job after scheduling the new one, but since we have no lock held here
- // that may cause ordering problems if the app removes jobStatus while in here.
- if (needsReschedule) {
- JobStatus rescheduled = getRescheduleJobForFailureLocked(jobStatus);
+
+ if (rescheduledJob != null) {
try {
- rescheduled.prepareLocked(ActivityManager.getService());
+ rescheduledJob.prepareLocked(ActivityManager.getService());
} catch (SecurityException e) {
- Slog.w(TAG, "Unable to regrant job permissions for " + rescheduled);
+ Slog.w(TAG, "Unable to regrant job permissions for " + rescheduledJob);
}
- startTrackingJobLocked(rescheduled, jobStatus);
+ startTrackingJobLocked(rescheduledJob, jobStatus);
} else if (jobStatus.getJob().isPeriodic()) {
JobStatus rescheduledPeriodic = getRescheduleJobForPeriodic(jobStatus);
try {
@@ -1561,7 +1564,7 @@
Slog.d(TAG, "preempting job: " + mActiveServices.get(i).getRunningJob());
}
// preferredUid will be set to uid of currently running job.
- mActiveServices.get(i).preemptExecutingJob();
+ mActiveServices.get(i).preemptExecutingJobLocked();
preservePreferredUid = true;
} else {
final JobStatus pendingJob = contextIdToJobMap[i];
diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java
index c7ef0e2..9144966 100644
--- a/services/core/java/com/android/server/job/JobServiceContext.java
+++ b/services/core/java/com/android/server/job/JobServiceContext.java
@@ -44,8 +44,6 @@
import com.android.internal.app.IBatteryStats;
import com.android.server.job.controllers.JobStatus;
-import java.util.concurrent.atomic.AtomicBoolean;
-
/**
* Handles client binding and lifecycle of a job. Jobs execute one at a time on an instance of this
* class.
@@ -56,19 +54,15 @@
* job lands, and again when it is complete.
* - Cancelling is trickier, because there are also interactions from the client. It's possible
* the {@link com.android.server.job.JobServiceContext.JobServiceHandler} tries to process a
- * {@link #MSG_CANCEL} after the client has already finished. This is handled by having
- * {@link com.android.server.job.JobServiceContext.JobServiceHandler#handleCancelH} check whether
+ * {@link #doCancelLocked(int)} after the client has already finished. This is handled by having
+ * {@link com.android.server.job.JobServiceContext.JobServiceHandler#handleCancelLocked} check whether
* the context is still valid.
- * To mitigate this, tearing down the context removes all messages from the handler, including any
- * tardy {@link #MSG_CANCEL}s. Additionally, we avoid sending duplicate onStopJob()
+ * To mitigate this, we avoid sending duplicate onStopJob()
* calls to the client after they've specified jobFinished().
*/
public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection {
private static final boolean DEBUG = JobSchedulerService.DEBUG;
private static final String TAG = "JobServiceContext";
- /** Define the maximum # of jobs allowed to run on a service at once. */
- private static final int defaultMaxActiveJobsPerService =
- ActivityManager.isLowRamDeviceStatic() ? 1 : 3;
/** Amount of time a job is allowed to execute for before being considered timed-out. */
private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins.
/** Amount of time the JobScheduler waits for the initial service launch+bind. */
@@ -90,14 +84,6 @@
// Messages that result from interactions with the client service.
/** System timed out waiting for a response. */
private static final int MSG_TIMEOUT = 0;
- /** Received a callback from client. */
- private static final int MSG_CALLBACK = 1;
- /** Run through list and start any ready jobs.*/
- private static final int MSG_SERVICE_BOUND = 2;
- /** Cancel a job. */
- private static final int MSG_CANCEL = 3;
- /** Shutdown the job. Used when the client crashes and we can't die gracefully.*/
- private static final int MSG_SHUTDOWN_EXECUTION = 4;
public static final int NO_PREFERRED_UID = -1;
@@ -115,7 +101,7 @@
private JobParameters mParams;
@VisibleForTesting
int mVerb;
- private AtomicBoolean mCancelled = new AtomicBoolean();
+ private boolean mCancelled;
/**
* All the information maintained about the job currently being executed.
@@ -245,14 +231,12 @@
}
/** Called externally when a job that was scheduled for execution should be cancelled. */
- void cancelExecutingJob(int reason) {
- mCallbackHandler.obtainMessage(MSG_CANCEL, reason, 0 /* unused */).sendToTarget();
+ void cancelExecutingJobLocked(int reason) {
+ doCancelLocked(reason);
}
- void preemptExecutingJob() {
- Message m = mCallbackHandler.obtainMessage(MSG_CANCEL);
- m.arg1 = JobParameters.REASON_PREEMPT;
- m.sendToTarget();
+ void preemptExecutingJobLocked() {
+ doCancelLocked(JobParameters.REASON_PREEMPT);
}
int getPreferredUid() {
@@ -273,59 +257,54 @@
@Override
public void jobFinished(int jobId, boolean reschedule) {
- if (!verifyCallingUid()) {
- return;
- }
- mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0)
- .sendToTarget();
+ doCallback(reschedule);
}
@Override
public void acknowledgeStopMessage(int jobId, boolean reschedule) {
- if (!verifyCallingUid()) {
- return;
- }
- mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, reschedule ? 1 : 0)
- .sendToTarget();
+ doCallback(reschedule);
}
@Override
public void acknowledgeStartMessage(int jobId, boolean ongoing) {
- if (!verifyCallingUid()) {
- return;
- }
- mCallbackHandler.obtainMessage(MSG_CALLBACK, jobId, ongoing ? 1 : 0).sendToTarget();
+ doCallback(ongoing);
}
@Override
public JobWorkItem dequeueWork(int jobId) {
- if (!verifyCallingUid()) {
- throw new SecurityException("Bad calling uid: " + Binder.getCallingUid());
- }
- JobWorkItem work = null;
- boolean stillWorking = false;
- synchronized (mLock) {
- if (mRunningJob != null) {
- work = mRunningJob.dequeueWorkLocked();
- stillWorking = mRunningJob.hasExecutingWorkLocked();
+ final int callingUid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ if (!verifyCallingUidLocked(callingUid)) {
+ throw new SecurityException("Bad calling uid: " + callingUid);
+ }
+
+ final JobWorkItem work = mRunningJob.dequeueWorkLocked();
+ if (work == null && !mRunningJob.hasExecutingWorkLocked()) {
+ // This will finish the job.
+ doCallbackLocked(false);
+ }
+ return work;
}
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- if (work == null && !stillWorking) {
- jobFinished(jobId, false);
- }
- return work;
}
@Override
public boolean completeWork(int jobId, int workId) {
- if (!verifyCallingUid()) {
- throw new SecurityException("Bad calling uid: " + Binder.getCallingUid());
- }
- synchronized (mLock) {
- if (mRunningJob != null) {
- return mRunningJob.completeWorkLocked(workId);
+ final int callingUid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ if (!verifyCallingUidLocked(callingUid)) {
+ throw new SecurityException("Bad calling uid: " + callingUid);
+ }
+ return mRunningJob.completeWorkLocked(ActivityManager.getService(), workId);
}
- return false;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -344,20 +323,20 @@
// looper and at this point we can't get any binder callbacks from the client. Better
// safe than sorry.
runningJob = mRunningJob;
- }
- if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
- mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
- return;
- }
- this.service = IJobService.Stub.asInterface(service);
- final PowerManager pm =
- (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
- runningJob.getTag());
- wl.setWorkSource(new WorkSource(runningJob.getSourceUid()));
- wl.setReferenceCounted(false);
- wl.acquire();
- synchronized (mLock) {
+
+ if (runningJob == null || !name.equals(runningJob.getServiceComponent())) {
+ closeAndCleanupJobLocked(true /* needsReschedule */);
+ return;
+ }
+ this.service = IJobService.Stub.asInterface(service);
+ final PowerManager pm =
+ (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ runningJob.getTag());
+ wl.setWorkSource(new WorkSource(runningJob.getSourceUid()));
+ wl.setReferenceCounted(false);
+ wl.acquire();
+
// We use a new wakelock instance per job. In rare cases there is a race between
// teardown following job completion/cancellation and new job service spin-up
// such that if we simply assign mWakeLock to be the new instance, we orphan
@@ -369,14 +348,16 @@
mWakeLock.release();
}
mWakeLock = wl;
+ doServiceBoundLocked();
}
- mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget();
}
/** If the client service crashes we reschedule this job and clean up. */
@Override
public void onServiceDisconnected(ComponentName name) {
- mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget();
+ synchronized (mLock) {
+ closeAndCleanupJobLocked(true /* needsReschedule */);
+ }
}
/**
@@ -384,22 +365,18 @@
* whether the client exercising the callback is the client we expect.
* @return True if the binder calling is coming from the client we expect.
*/
- private boolean verifyCallingUid() {
- synchronized (mLock) {
- if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) {
- if (DEBUG) {
- Slog.d(TAG, "Stale callback received, ignoring.");
- }
- return false;
+ private boolean verifyCallingUidLocked(final int callingUid) {
+ if (mRunningJob == null || callingUid != mRunningJob.getUid()) {
+ if (DEBUG) {
+ Slog.d(TAG, "Stale callback received, ignoring.");
}
- return true;
+ return false;
}
+ return true;
}
/**
- * Handles the lifecycle of the JobService binding/callbacks, etc. The convention within this
- * class is to append 'H' to each function name that can only be called on this handler. This
- * isn't strictly necessary because all of these functions are private, but helps clarity.
+ * Scheduling of async messages (basically timeouts at this point).
*/
private class JobServiceHandler extends Handler {
JobServiceHandler(Looper looper) {
@@ -409,296 +386,280 @@
@Override
public void handleMessage(Message message) {
switch (message.what) {
- case MSG_SERVICE_BOUND:
- doServiceBound();
- break;
- case MSG_CALLBACK:
- doCallback(message.arg2);
- break;
- case MSG_CANCEL:
- doCancel(message.arg1);
- break;
case MSG_TIMEOUT:
synchronized (mLock) {
- handleOpTimeoutH();
+ handleOpTimeoutLocked();
}
break;
- case MSG_SHUTDOWN_EXECUTION:
- closeAndCleanupJobH(true /* needsReschedule */);
- break;
default:
Slog.e(TAG, "Unrecognised message: " + message);
}
}
+ }
- void doServiceBound() {
+ void doServiceBoundLocked() {
+ removeOpTimeOutLocked();
+ handleServiceBoundLocked();
+ }
+
+ void doCallback(boolean reschedule) {
+ final int callingUid = Binder.getCallingUid();
+ final long ident = Binder.clearCallingIdentity();
+ try {
synchronized (mLock) {
- removeOpTimeOutLocked();
- handleServiceBoundH();
- }
- }
-
- void doCallback(int arg2) {
- synchronized (mLock) {
- if (DEBUG) {
- Slog.d(TAG, "MSG_CALLBACK of : " + mRunningJob
- + " v:" + VERB_STRINGS[mVerb]);
- }
- removeOpTimeOutLocked();
-
- if (mVerb == VERB_STARTING) {
- final boolean workOngoing = arg2 == 1;
- handleStartedH(workOngoing);
- } else if (mVerb == VERB_EXECUTING ||
- mVerb == VERB_STOPPING) {
- final boolean reschedule = arg2 == 1;
- handleFinishedH(reschedule);
- } else {
- if (DEBUG) {
- Slog.d(TAG, "Unrecognised callback: " + mRunningJob);
- }
- }
- }
- }
-
- void doCancel(int arg1) {
- synchronized (mLock) {
- if (mVerb == VERB_FINISHED) {
- if (DEBUG) {
- Slog.d(TAG,
- "Trying to process cancel for torn-down context, ignoring.");
- }
+ if (!verifyCallingUidLocked(callingUid)) {
return;
}
- mParams.setStopReason(arg1);
- if (arg1 == JobParameters.REASON_PREEMPT) {
- mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
- NO_PREFERRED_UID;
- }
- handleCancelH();
+ doCallbackLocked(reschedule);
}
-
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
+ }
- /** Start the job on the service. */
- private void handleServiceBoundH() {
+ void doCallbackLocked(boolean reschedule) {
+ if (DEBUG) {
+ Slog.d(TAG, "doCallback of : " + mRunningJob
+ + " v:" + VERB_STRINGS[mVerb]);
+ }
+ removeOpTimeOutLocked();
+
+ if (mVerb == VERB_STARTING) {
+ handleStartedLocked(reschedule);
+ } else if (mVerb == VERB_EXECUTING ||
+ mVerb == VERB_STOPPING) {
+ handleFinishedLocked(reschedule);
+ } else {
if (DEBUG) {
- Slog.d(TAG, "MSG_SERVICE_BOUND for " + mRunningJob.toShortString());
- }
- if (mVerb != VERB_BINDING) {
- Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
- + VERB_STRINGS[mVerb]);
- closeAndCleanupJobH(false /* reschedule */);
- return;
- }
- if (mCancelled.get()) {
- if (DEBUG) {
- Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
- + mRunningJob);
- }
- closeAndCleanupJobH(true /* reschedule */);
- return;
- }
- try {
- mVerb = VERB_STARTING;
- scheduleOpTimeOutLocked();
- service.startJob(mParams);
- } catch (Exception e) {
- // We catch 'Exception' because client-app malice or bugs might induce a wide
- // range of possible exception-throw outcomes from startJob() and its handling
- // of the client's ParcelableBundle extras.
- Slog.e(TAG, "Error sending onStart message to '" +
- mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
+ Slog.d(TAG, "Unrecognised callback: " + mRunningJob);
}
}
+ }
- /**
- * State behaviours.
- * VERB_STARTING -> Successful start, change job to VERB_EXECUTING and post timeout.
- * _PENDING -> Error
- * _EXECUTING -> Error
- * _STOPPING -> Error
- */
- private void handleStartedH(boolean workOngoing) {
- switch (mVerb) {
- case VERB_STARTING:
- mVerb = VERB_EXECUTING;
- if (!workOngoing) {
- // Job is finished already so fast-forward to handleFinished.
- handleFinishedH(false);
- return;
- }
- if (mCancelled.get()) {
- if (DEBUG) {
- Slog.d(TAG, "Job cancelled while waiting for onStartJob to complete.");
- }
- // Cancelled *while* waiting for acknowledgeStartMessage from client.
- handleCancelH();
- return;
- }
- scheduleOpTimeOutLocked();
- break;
- default:
- Slog.e(TAG, "Handling started job but job wasn't starting! Was "
- + VERB_STRINGS[mVerb] + ".");
- return;
+ void doCancelLocked(int arg1) {
+ if (mVerb == VERB_FINISHED) {
+ if (DEBUG) {
+ Slog.d(TAG,
+ "Trying to process cancel for torn-down context, ignoring.");
}
+ return;
}
-
- /**
- * VERB_EXECUTING -> Client called jobFinished(), clean up and notify done.
- * _STOPPING -> Successful finish, clean up and notify done.
- * _STARTING -> Error
- * _PENDING -> Error
- */
- private void handleFinishedH(boolean reschedule) {
- switch (mVerb) {
- case VERB_EXECUTING:
- case VERB_STOPPING:
- closeAndCleanupJobH(reschedule);
- break;
- default:
- Slog.e(TAG, "Got an execution complete message for a job that wasn't being" +
- "executed. Was " + VERB_STRINGS[mVerb] + ".");
- }
+ mParams.setStopReason(arg1);
+ if (arg1 == JobParameters.REASON_PREEMPT) {
+ mPreferredUid = mRunningJob != null ? mRunningJob.getUid() :
+ NO_PREFERRED_UID;
}
+ handleCancelLocked();
+ }
- /**
- * A job can be in various states when a cancel request comes in:
- * VERB_BINDING -> Cancelled before bind completed. Mark as cancelled and wait for
- * {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)}
- * _STARTING -> Mark as cancelled and wait for
- * {@link JobServiceContext#acknowledgeStartMessage(int, boolean)}
- * _EXECUTING -> call {@link #sendStopMessageH}}, but only if there are no callbacks
- * in the message queue.
- * _ENDING -> No point in doing anything here, so we ignore.
- */
- private void handleCancelH() {
- if (JobSchedulerService.DEBUG) {
- Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " "
- + VERB_STRINGS[mVerb]);
- }
- switch (mVerb) {
- case VERB_BINDING:
- case VERB_STARTING:
- mCancelled.set(true);
- break;
- case VERB_EXECUTING:
- if (hasMessages(MSG_CALLBACK)) {
- // If the client has called jobFinished, ignore this cancel.
- return;
- }
- sendStopMessageH();
- break;
- case VERB_STOPPING:
- // Nada.
- break;
- default:
- Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb);
- break;
- }
+ /** Start the job on the service. */
+ private void handleServiceBoundLocked() {
+ if (DEBUG) {
+ Slog.d(TAG, "handleServiceBound for " + mRunningJob.toShortString());
}
-
- /** Process MSG_TIMEOUT here. */
- private void handleOpTimeoutH() {
- switch (mVerb) {
- case VERB_BINDING:
- Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
- ", dropping.");
- closeAndCleanupJobH(false /* needsReschedule */);
- break;
- case VERB_STARTING:
- // Client unresponsive - wedged or failed to respond in time. We don't really
- // know what happened so let's log it and notify the JobScheduler
- // FINISHED/NO-RETRY.
- Slog.e(TAG, "No response from client for onStartJob '" +
- mRunningJob.toShortString());
- closeAndCleanupJobH(false /* needsReschedule */);
- break;
- case VERB_STOPPING:
- // At least we got somewhere, so fail but ask the JobScheduler to reschedule.
- Slog.e(TAG, "No response from client for onStopJob, '" +
- mRunningJob.toShortString());
- closeAndCleanupJobH(true /* needsReschedule */);
- break;
- case VERB_EXECUTING:
- // Not an error - client ran out of time.
- Slog.i(TAG, "Client timed out while executing (no jobFinished received)." +
- " sending onStop. " + mRunningJob.toShortString());
- mParams.setStopReason(JobParameters.REASON_TIMEOUT);
- sendStopMessageH();
- break;
- default:
- Slog.e(TAG, "Handling timeout for an invalid job state: " +
- mRunningJob.toShortString() + ", dropping.");
- closeAndCleanupJobH(false /* needsReschedule */);
- }
+ if (mVerb != VERB_BINDING) {
+ Slog.e(TAG, "Sending onStartJob for a job that isn't pending. "
+ + VERB_STRINGS[mVerb]);
+ closeAndCleanupJobLocked(false /* reschedule */);
+ return;
}
-
- /**
- * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
- * VERB_STOPPING.
- */
- private void sendStopMessageH() {
- removeOpTimeOutLocked();
- if (mVerb != VERB_EXECUTING) {
- Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob);
- closeAndCleanupJobH(false /* reschedule */);
- return;
+ if (mCancelled) {
+ if (DEBUG) {
+ Slog.d(TAG, "Job cancelled while waiting for bind to complete. "
+ + mRunningJob);
}
- try {
- mVerb = VERB_STOPPING;
- scheduleOpTimeOutLocked();
- service.stopJob(mParams);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error sending onStopJob to client.", e);
- // The job's host app apparently crashed during the job, so we should reschedule.
- closeAndCleanupJobH(true /* reschedule */);
- }
+ closeAndCleanupJobLocked(true /* reschedule */);
+ return;
}
+ try {
+ mVerb = VERB_STARTING;
+ scheduleOpTimeOutLocked();
+ service.startJob(mParams);
+ } catch (Exception e) {
+ // We catch 'Exception' because client-app malice or bugs might induce a wide
+ // range of possible exception-throw outcomes from startJob() and its handling
+ // of the client's ParcelableBundle extras.
+ Slog.e(TAG, "Error sending onStart message to '" +
+ mRunningJob.getServiceComponent().getShortClassName() + "' ", e);
+ }
+ }
- /**
- * The provided job has finished, either by calling
- * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
- * or from acknowledging the stop message we sent. Either way, we're done tracking it and
- * we want to clean up internally.
- */
- private void closeAndCleanupJobH(boolean reschedule) {
- final JobStatus completedJob;
- synchronized (mLock) {
- if (mVerb == VERB_FINISHED) {
+ /**
+ * State behaviours.
+ * VERB_STARTING -> Successful start, change job to VERB_EXECUTING and post timeout.
+ * _PENDING -> Error
+ * _EXECUTING -> Error
+ * _STOPPING -> Error
+ */
+ private void handleStartedLocked(boolean workOngoing) {
+ switch (mVerb) {
+ case VERB_STARTING:
+ mVerb = VERB_EXECUTING;
+ if (!workOngoing) {
+ // Job is finished already so fast-forward to handleFinished.
+ handleFinishedLocked(false);
return;
}
- completedJob = mRunningJob;
- mJobPackageTracker.noteInactive(completedJob);
- try {
- mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
- mRunningJob.getSourceUid());
- } catch (RemoteException e) {
- // Whatever.
+ if (mCancelled) {
+ if (DEBUG) {
+ Slog.d(TAG, "Job cancelled while waiting for onStartJob to complete.");
+ }
+ // Cancelled *while* waiting for acknowledgeStartMessage from client.
+ handleCancelLocked();
+ return;
}
- if (mWakeLock != null) {
- mWakeLock.release();
- }
- mContext.unbindService(JobServiceContext.this);
- mWakeLock = null;
- mRunningJob = null;
- mParams = null;
- mVerb = VERB_FINISHED;
- mCancelled.set(false);
- service = null;
- mAvailable = true;
- removeOpTimeOutLocked();
- removeMessages(MSG_CALLBACK);
- removeMessages(MSG_SERVICE_BOUND);
- removeMessages(MSG_CANCEL);
- removeMessages(MSG_SHUTDOWN_EXECUTION);
- mCompletedListener.onJobCompletedLocked(completedJob, reschedule);
- }
+ scheduleOpTimeOutLocked();
+ break;
+ default:
+ Slog.e(TAG, "Handling started job but job wasn't starting! Was "
+ + VERB_STRINGS[mVerb] + ".");
+ return;
}
}
/**
+ * VERB_EXECUTING -> Client called jobFinished(), clean up and notify done.
+ * _STOPPING -> Successful finish, clean up and notify done.
+ * _STARTING -> Error
+ * _PENDING -> Error
+ */
+ private void handleFinishedLocked(boolean reschedule) {
+ switch (mVerb) {
+ case VERB_EXECUTING:
+ case VERB_STOPPING:
+ closeAndCleanupJobLocked(reschedule);
+ break;
+ default:
+ Slog.e(TAG, "Got an execution complete message for a job that wasn't being" +
+ "executed. Was " + VERB_STRINGS[mVerb] + ".");
+ }
+ }
+
+ /**
+ * A job can be in various states when a cancel request comes in:
+ * VERB_BINDING -> Cancelled before bind completed. Mark as cancelled and wait for
+ * {@link #onServiceConnected(android.content.ComponentName, android.os.IBinder)}
+ * _STARTING -> Mark as cancelled and wait for
+ * {@link JobServiceContext#acknowledgeStartMessage(int, boolean)}
+ * _EXECUTING -> call {@link #sendStopMessageLocked}}, but only if there are no callbacks
+ * in the message queue.
+ * _ENDING -> No point in doing anything here, so we ignore.
+ */
+ private void handleCancelLocked() {
+ if (JobSchedulerService.DEBUG) {
+ Slog.d(TAG, "Handling cancel for: " + mRunningJob.getJobId() + " "
+ + VERB_STRINGS[mVerb]);
+ }
+ switch (mVerb) {
+ case VERB_BINDING:
+ case VERB_STARTING:
+ mCancelled = true;
+ break;
+ case VERB_EXECUTING:
+ sendStopMessageLocked();
+ break;
+ case VERB_STOPPING:
+ // Nada.
+ break;
+ default:
+ Slog.e(TAG, "Cancelling a job without a valid verb: " + mVerb);
+ break;
+ }
+ }
+
+ /** Process MSG_TIMEOUT here. */
+ private void handleOpTimeoutLocked() {
+ switch (mVerb) {
+ case VERB_BINDING:
+ Slog.e(TAG, "Time-out while trying to bind " + mRunningJob.toShortString() +
+ ", dropping.");
+ closeAndCleanupJobLocked(false /* needsReschedule */);
+ break;
+ case VERB_STARTING:
+ // Client unresponsive - wedged or failed to respond in time. We don't really
+ // know what happened so let's log it and notify the JobScheduler
+ // FINISHED/NO-RETRY.
+ Slog.e(TAG, "No response from client for onStartJob '" +
+ mRunningJob.toShortString());
+ closeAndCleanupJobLocked(false /* needsReschedule */);
+ break;
+ case VERB_STOPPING:
+ // At least we got somewhere, so fail but ask the JobScheduler to reschedule.
+ Slog.e(TAG, "No response from client for onStopJob, '" +
+ mRunningJob.toShortString());
+ closeAndCleanupJobLocked(true /* needsReschedule */);
+ break;
+ case VERB_EXECUTING:
+ // Not an error - client ran out of time.
+ Slog.i(TAG, "Client timed out while executing (no jobFinished received)." +
+ " sending onStop. " + mRunningJob.toShortString());
+ mParams.setStopReason(JobParameters.REASON_TIMEOUT);
+ sendStopMessageLocked();
+ break;
+ default:
+ Slog.e(TAG, "Handling timeout for an invalid job state: " +
+ mRunningJob.toShortString() + ", dropping.");
+ closeAndCleanupJobLocked(false /* needsReschedule */);
+ }
+ }
+
+ /**
+ * Already running, need to stop. Will switch {@link #mVerb} from VERB_EXECUTING ->
+ * VERB_STOPPING.
+ */
+ private void sendStopMessageLocked() {
+ removeOpTimeOutLocked();
+ if (mVerb != VERB_EXECUTING) {
+ Slog.e(TAG, "Sending onStopJob for a job that isn't started. " + mRunningJob);
+ closeAndCleanupJobLocked(false /* reschedule */);
+ return;
+ }
+ try {
+ mVerb = VERB_STOPPING;
+ scheduleOpTimeOutLocked();
+ service.stopJob(mParams);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending onStopJob to client.", e);
+ // The job's host app apparently crashed during the job, so we should reschedule.
+ closeAndCleanupJobLocked(true /* reschedule */);
+ }
+ }
+
+ /**
+ * The provided job has finished, either by calling
+ * {@link android.app.job.JobService#jobFinished(android.app.job.JobParameters, boolean)}
+ * or from acknowledging the stop message we sent. Either way, we're done tracking it and
+ * we want to clean up internally.
+ */
+ private void closeAndCleanupJobLocked(boolean reschedule) {
+ final JobStatus completedJob;
+ if (mVerb == VERB_FINISHED) {
+ return;
+ }
+ completedJob = mRunningJob;
+ mJobPackageTracker.noteInactive(completedJob);
+ try {
+ mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(),
+ mRunningJob.getSourceUid());
+ } catch (RemoteException e) {
+ // Whatever.
+ }
+ if (mWakeLock != null) {
+ mWakeLock.release();
+ }
+ mContext.unbindService(JobServiceContext.this);
+ mWakeLock = null;
+ mRunningJob = null;
+ mParams = null;
+ mVerb = VERB_FINISHED;
+ mCancelled = false;
+ service = null;
+ mAvailable = true;
+ removeOpTimeOutLocked();
+ mCompletedListener.onJobCompletedLocked(completedJob, reschedule);
+ }
+
+ /**
* Called when sending a message to the client, over whose execution we have no control. If
* we haven't received a response in a certain amount of time, we want to give up and carry
* on with life.
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index e8cc078..1ab66b9 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -26,7 +26,6 @@
import android.content.Intent;
import android.net.Uri;
import android.os.Binder;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
@@ -35,6 +34,8 @@
import android.util.Slog;
import android.util.TimeUtils;
+import com.android.server.job.GrantedUriPermissions;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -103,7 +104,7 @@
final String tag;
- private IBinder permissionOwner;
+ private GrantedUriPermissions uriPerms;
private boolean prepared;
/**
@@ -284,12 +285,17 @@
earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis);
}
- public void enqueueWorkLocked(JobWorkItem work) {
+ public void enqueueWorkLocked(IActivityManager am, JobWorkItem work) {
if (pendingWork == null) {
pendingWork = new ArrayList<>();
}
work.setWorkId(nextPendingWorkId);
nextPendingWorkId++;
+ if (work.getIntent() != null
+ && GrantedUriPermissions.checkGrantFlags(work.getIntent().getFlags())) {
+ work.setGrants(GrantedUriPermissions.createFromIntent(am, work.getIntent(), sourceUid,
+ sourcePackageName, sourceUserId, toShortString()));
+ }
pendingWork.add(work);
}
@@ -311,12 +317,20 @@
return executingWork != null && executingWork.size() > 0;
}
- public boolean completeWorkLocked(int workId) {
+ private static void ungrantWorkItem(IActivityManager am, JobWorkItem work) {
+ if (work.getGrants() != null) {
+ ((GrantedUriPermissions)work.getGrants()).revoke(am);
+ }
+ }
+
+ public boolean completeWorkLocked(IActivityManager am, int workId) {
if (executingWork != null) {
final int N = executingWork.size();
for (int i = 0; i < N; i++) {
- if (executingWork.get(i).getWorkId() == workId) {
+ JobWorkItem work = executingWork.get(i);
+ if (work.getWorkId() == workId) {
executingWork.remove(i);
+ ungrantWorkItem(am, work);
return true;
}
}
@@ -324,15 +338,36 @@
return false;
}
- public void stopTrackingJobLocked(JobStatus incomingJob) {
+ private static void ungrantWorkList(IActivityManager am, ArrayList<JobWorkItem> list) {
+ if (list != null) {
+ final int N = list.size();
+ for (int i = 0; i < N; i++) {
+ ungrantWorkItem(am, list.get(i));
+ }
+ }
+ }
+
+ public void stopTrackingJobLocked(IActivityManager am, JobStatus incomingJob) {
if (incomingJob != null) {
- // We are replacing with a new job -- transfer the work!
- incomingJob.pendingWork = pendingWork;
+ // We are replacing with a new job -- transfer the work! We do any executing
+ // work first, since that was originally at the front of the pending work.
+ if (executingWork != null && executingWork.size() > 0) {
+ incomingJob.pendingWork = executingWork;
+ }
+ if (incomingJob.pendingWork == null) {
+ incomingJob.pendingWork = pendingWork;
+ } else if (pendingWork != null && pendingWork.size() > 0) {
+ incomingJob.pendingWork.addAll(pendingWork);
+ }
pendingWork = null;
+ executingWork = null;
incomingJob.nextPendingWorkId = nextPendingWorkId;
} else {
// We are completely stopping the job... need to clean up work.
- // XXX remove perms when that is impl.
+ ungrantWorkList(am, pendingWork);
+ pendingWork = null;
+ ungrantWorkList(am, executingWork);
+ executingWork = null;
}
}
@@ -344,10 +379,8 @@
prepared = true;
final ClipData clip = job.getClipData();
if (clip != null) {
- final int N = clip.getItemCount();
- for (int i = 0; i < N; i++) {
- grantItemLocked(am, clip.getItemAt(i), sourceUid, sourcePackageName, sourceUserId);
- }
+ uriPerms = GrantedUriPermissions.createFromClip(am, clip, sourceUid, sourcePackageName,
+ sourceUserId, job.getClipGrantFlags(), toShortString());
}
}
@@ -357,14 +390,9 @@
return;
}
prepared = false;
- if (permissionOwner != null) {
- final ClipData clip = job.getClipData();
- if (clip != null) {
- final int N = clip.getItemCount();
- for (int i = 0; i < N; i++) {
- revokeItemLocked(am, clip.getItemAt(i));
- }
- }
+ if (uriPerms != null) {
+ uriPerms.revoke(am);
+ uriPerms = null;
}
}
@@ -372,57 +400,6 @@
return prepared;
}
- private final void grantUriLocked(IActivityManager am, Uri uri, int sourceUid,
- String targetPackage, int targetUserId) {
- try {
- int sourceUserId = ContentProvider.getUserIdFromUri(uri,
- UserHandle.getUserId(sourceUid));
- uri = ContentProvider.getUriWithoutUserId(uri);
- if (permissionOwner == null) {
- permissionOwner = am.newUriPermissionOwner("job: " + toShortString());
- }
- am.grantUriPermissionFromOwner(permissionOwner, sourceUid, targetPackage,
- uri, job.getClipGrantFlags(), sourceUserId, targetUserId);
- } catch (RemoteException e) {
- Slog.e("JobScheduler", "AM dead");
- }
- }
-
- private final void grantItemLocked(IActivityManager am, ClipData.Item item, int sourceUid,
- String targetPackage, int targetUserId) {
- if (item.getUri() != null) {
- grantUriLocked(am, item.getUri(), sourceUid, targetPackage, targetUserId);
- }
- Intent intent = item.getIntent();
- if (intent != null && intent.getData() != null) {
- grantUriLocked(am, intent.getData(), sourceUid, targetPackage, targetUserId);
- }
- }
-
- private final void revokeUriLocked(IActivityManager am, Uri uri) {
- int userId = ContentProvider.getUserIdFromUri(uri,
- UserHandle.getUserId(Binder.getCallingUid()));
- long ident = Binder.clearCallingIdentity();
- try {
- uri = ContentProvider.getUriWithoutUserId(uri);
- am.revokeUriPermissionFromOwner(permissionOwner, uri,
- job.getClipGrantFlags(), userId);
- } catch (RemoteException e) {
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
-
- private final void revokeItemLocked(IActivityManager am, ClipData.Item item) {
- if (item.getUri() != null) {
- revokeUriLocked(am, item.getUri());
- }
- Intent intent = item.getIntent();
- if (intent != null && intent.getData() != null) {
- revokeUriLocked(am, intent.getData());
- }
- }
-
public JobInfo getJob() {
return job;
}
@@ -833,6 +810,15 @@
}
}
+ private void dumpJobWorkItem(PrintWriter pw, String prefix, JobWorkItem work, int index) {
+ pw.print(prefix); pw.print(" #"); pw.print(index); pw.print(": #");
+ pw.print(work.getWorkId()); pw.print(" "); pw.println(work.getIntent());
+ if (work.getGrants() != null) {
+ pw.print(prefix); pw.println(" URI grants:");
+ ((GrantedUriPermissions)work.getGrants()).dump(pw, prefix + " ");
+ }
+ }
+
// Dumpsys infrastructure
public void dump(PrintWriter pw, String prefix, boolean full) {
pw.print(prefix); UserHandle.formatUid(pw, callingUid);
@@ -898,6 +884,10 @@
job.getClipData().toShortString(b);
pw.println(b);
}
+ if (uriPerms != null) {
+ pw.print(prefix); pw.println(" Granted URI permissions:");
+ uriPerms.dump(pw, prefix + " ");
+ }
if (job.getNetworkType() != JobInfo.NETWORK_TYPE_NONE) {
pw.print(prefix); pw.print(" Network type: "); pw.println(job.getNetworkType());
}
@@ -950,17 +940,13 @@
if (pendingWork != null && pendingWork.size() > 0) {
pw.print(prefix); pw.println("Pending work:");
for (int i = 0; i < pendingWork.size(); i++) {
- JobWorkItem work = pendingWork.get(i);
- pw.print(prefix); pw.print(" #"); pw.print(i); pw.print(": #");
- pw.print(work.getWorkId()); pw.print(" "); pw.println(work.getIntent());
+ dumpJobWorkItem(pw, prefix, pendingWork.get(i), i);
}
}
if (executingWork != null && executingWork.size() > 0) {
pw.print(prefix); pw.println("Executing work:");
for (int i = 0; i < executingWork.size(); i++) {
- JobWorkItem work = executingWork.get(i);
- pw.print(prefix); pw.print(" #"); pw.print(i); pw.print(": #");
- pw.print(work.getWorkId()); pw.print(" "); pw.println(work.getIntent());
+ dumpJobWorkItem(pw, prefix, executingWork.get(i), i);
}
}
pw.print(prefix); pw.print("Earliest run time: ");
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 3a1ddd7..fdaa599 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -288,6 +288,9 @@
// current setting - 4 hours
private static final long MAX_RETRY_INTERVAL = 4*60*60*1000;
+ // Timeout when holding wakelocks for downloading XTRA data.
+ private static final long DOWNLOAD_XTRA_DATA_TIMEOUT_MS = 60 * 1000;
+
private BackOff mNtpBackOff = new BackOff(RETRY_INTERVAL, MAX_RETRY_INTERVAL);
private BackOff mXtraBackOff = new BackOff(RETRY_INTERVAL, MAX_RETRY_INTERVAL);
@@ -986,7 +989,7 @@
mDownloadXtraDataPending = STATE_DOWNLOADING;
// hold wake lock while task runs
- mWakeLock.acquire();
+ mWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS);
Log.i(TAG, "WakeLock acquired by handleDownloadXtraData()");
AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
@@ -1009,7 +1012,11 @@
}
// release wake lock held by task
- mWakeLock.release();
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ } else {
+ Log.e(TAG, "WakeLock expired before release in handleDownloadXtraData()");
+ }
Log.i(TAG, "WakeLock released by handleDownloadXtraData()");
}
});
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 53a8092..ee348cf 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -101,8 +101,6 @@
private ParceledListSlice mQueue;
private CharSequence mQueueTitle;
private int mRatingType;
- private int mRepeatMode;
- private boolean mShuffleModeEnabled;
// End TransportPerformer fields
// Volume handling fields
@@ -622,47 +620,6 @@
}
}
- private void pushRepeatModeUpdate() {
- synchronized (mLock) {
- if (mDestroyed) {
- return;
- }
- for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
- try {
- holder.mCallback.onRepeatModeChanged(mRepeatMode);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removed dead callback in pushRepeatModeUpdate",
- holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushRepeatModeUpdate", holder, e);
- }
- }
- }
- }
-
- private void pushShuffleModeUpdate() {
- synchronized (mLock) {
- if (mDestroyed) {
- return;
- }
- for (int i = mControllerCallbackHolders.size() - 1; i >= 0; i--) {
- ISessionControllerCallbackHolder holder = mControllerCallbackHolders.get(i);
- try {
- holder.mCallback.onShuffleModeChanged(mShuffleModeEnabled);
- } catch (DeadObjectException e) {
- mControllerCallbackHolders.remove(i);
- logCallbackException("Removed dead callback in pushShuffleModeUpdate",
- holder, e);
- } catch (RemoteException e) {
- logCallbackException("unexpected exception in pushShuffleModeUpdate",
- holder, e);
- }
- }
- }
- }
-
private void pushSessionDestroyed() {
synchronized (mLock) {
// This is the only method that may be (and can only be) called
@@ -887,30 +844,6 @@
}
@Override
- public void setRepeatMode(int repeatMode) {
- boolean changed;
- synchronized (mLock) {
- changed = mRepeatMode != repeatMode;
- mRepeatMode = repeatMode;
- }
- if (changed) {
- mHandler.post(MessageHandler.MSG_UPDATE_REPEAT_MODE);
- }
- }
-
- @Override
- public void setShuffleModeEnabled(boolean enabled) {
- boolean changed;
- synchronized (mLock) {
- changed = mShuffleModeEnabled != enabled;
- mShuffleModeEnabled = enabled;
- }
- if (changed) {
- mHandler.post(MessageHandler.MSG_UPDATE_SHUFFLE_MODE);
- }
- }
-
- @Override
public void setCurrentVolume(int volume) {
mCurrentVolume = volume;
mHandler.post(MessageHandler.MSG_UPDATE_VOLUME);
@@ -1126,54 +1059,6 @@
}
}
- public void repeatMode(int repeatMode) {
- try {
- mCb.onRepeatMode(repeatMode);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in repeatMode.", e);
- }
- }
-
- public void shuffleMode(boolean enabled) {
- try {
- mCb.onShuffleMode(enabled);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in shuffleMode.", e);
- }
- }
-
- public void addQueueItem(MediaDescription description) {
- try {
- mCb.onAddQueueItem(description);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in addQueueItem.", e);
- }
- }
-
- public void addQueueItemAt(MediaDescription description, int index) {
- try {
- mCb.onAddQueueItemAt(description, index);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in addQueueItemAt.", e);
- }
- }
-
- public void removeQueueItem(MediaDescription description) {
- try {
- mCb.onRemoveQueueItem(description);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in removeQueueItem.", e);
- }
- }
-
- public void removeQueueItemAt(int index) {
- try {
- mCb.onRemoveQueueItemAt(index);
- } catch (RemoteException e) {
- Slog.e(TAG, "Remote failure in removeQueueItem.", e);
- }
- }
-
public void adjustVolume(int direction) {
try {
mCb.onAdjustVolume(direction);
@@ -1410,25 +1295,13 @@
}
@Override
- public void repeatMode(int repeatMode) {
- updateCallingPackage();
- mSessionCb.repeatMode(repeatMode);
- }
-
- @Override
- public void shuffleMode(boolean enabled) throws RemoteException {
- updateCallingPackage();
- mSessionCb.shuffleMode(enabled);
- }
-
-
- @Override
public void sendCustomAction(String action, Bundle args)
throws RemoteException {
updateCallingPackage();
mSessionCb.sendCustomAction(action, args);
}
+
@Override
public MediaMetadata getMetadata() {
synchronized (mLock) {
@@ -1449,30 +1322,6 @@
}
@Override
- public void addQueueItem(MediaDescription description) {
- updateCallingPackage();
- mSessionCb.addQueueItem(description);
- }
-
- @Override
- public void addQueueItemAt(MediaDescription description, int index) {
- updateCallingPackage();
- mSessionCb.addQueueItemAt(description, index);
- }
-
- @Override
- public void removeQueueItem(MediaDescription description) {
- updateCallingPackage();
- mSessionCb.removeQueueItem(description);
- }
-
- @Override
- public void removeQueueItemAt(int index) {
- updateCallingPackage();
- mSessionCb.removeQueueItemAt(index);
- }
-
- @Override
public CharSequence getQueueTitle() {
return mQueueTitle;
}
@@ -1490,16 +1339,6 @@
}
@Override
- public int getRepeatMode() {
- return mRepeatMode;
- }
-
- @Override
- public boolean isShuffleModeEnabled() {
- return mShuffleModeEnabled;
- }
-
- @Override
public boolean isTransportControlEnabled() {
return MediaSessionRecord.this.isTransportControlEnabled();
}
@@ -1524,9 +1363,7 @@
private static final int MSG_SEND_EVENT = 6;
private static final int MSG_UPDATE_SESSION_STATE = 7;
private static final int MSG_UPDATE_VOLUME = 8;
- private static final int MSG_UPDATE_REPEAT_MODE = 9;
- private static final int MSG_UPDATE_SHUFFLE_MODE = 10;
- private static final int MSG_DESTROYED = 11;
+ private static final int MSG_DESTROYED = 9;
public MessageHandler(Looper looper) {
super(looper);
@@ -1558,12 +1395,6 @@
case MSG_UPDATE_VOLUME:
pushVolumeUpdate();
break;
- case MSG_UPDATE_REPEAT_MODE:
- pushRepeatModeUpdate();
- break;
- case MSG_UPDATE_SHUFFLE_MODE:
- pushShuffleModeUpdate();
- break;
case MSG_DESTROYED:
pushSessionDestroyed();
}
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index b0d8adc..0e69bca 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -23,6 +23,7 @@
import android.os.UserHandle;
import android.util.IntArray;
import android.util.Log;
+import android.util.SparseArray;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -38,7 +39,7 @@
private static final String TAG = "MediaSessionStack";
/**
- * Listens the change in the media button session.
+ * Listen the change in the media button session.
*/
interface OnMediaButtonSessionChangedListener {
/**
@@ -85,7 +86,12 @@
private MediaSessionRecord mCachedDefault;
private MediaSessionRecord mCachedVolumeDefault;
- private ArrayList<MediaSessionRecord> mCachedActiveList;
+
+ /**
+ * Cache the result of the {@link #getActiveSessions} per user.
+ */
+ private final SparseArray<ArrayList<MediaSessionRecord>> mCachedActiveLists =
+ new SparseArray<>();
MediaSessionStack(AudioPlaybackMonitor monitor, OnMediaButtonSessionChangedListener listener) {
mAudioPlaybackMonitor = monitor;
@@ -99,7 +105,7 @@
*/
public void addSession(MediaSessionRecord record) {
mSessions.add(record);
- clearCache();
+ clearCache(record.getUserId());
// Update the media button session.
// The added session could be the session from the package with the audio playback.
@@ -115,11 +121,14 @@
public void removeSession(MediaSessionRecord record) {
mSessions.remove(record);
if (mMediaButtonSession == record) {
- // When the media button session is gone, try to find the alternative media session
- // in the media button session app.
- onMediaSessionChangeInMediaButtonSessionApp();
+ // When the media button session is removed, nullify the media button session and do not
+ // search for the alternative media session within the app. It's because the alternative
+ // media session might be a dummy which isn't able to handle the media key events.
+ mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged(
+ mMediaButtonSession, null);
+ mMediaButtonSession = null;
}
- clearCache();
+ clearCache(record.getUserId());
}
/**
@@ -140,7 +149,7 @@
if (shouldUpdatePriority(oldState, newState)) {
mSessions.remove(record);
mSessions.add(0, record);
- clearCache();
+ clearCache(record.getUserId());
} else if (!MediaSession.isActiveState(newState)) {
// Just clear the volume cache when a state goes inactive
mCachedVolumeDefault = null;
@@ -151,7 +160,13 @@
// In that case, we pick the media session whose PlaybackState matches
// the audio playback configuration.
if (mMediaButtonSession != null && mMediaButtonSession.getUid() == record.getUid()) {
- onMediaSessionChangeInMediaButtonSessionApp();
+ MediaSessionRecord newMediaButtonSession =
+ findMediaButtonSession(mMediaButtonSession.getUid());
+ if (newMediaButtonSession != mMediaButtonSession) {
+ mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged(
+ mMediaButtonSession, newMediaButtonSession);
+ mMediaButtonSession = newMediaButtonSession;
+ }
}
}
@@ -163,7 +178,7 @@
public void onSessionStateChange(MediaSessionRecord record) {
// For now just clear the cache. Eventually we'll selectively clear
// depending on what changed.
- clearCache();
+ clearCache(record.getUserId());
}
/**
@@ -194,22 +209,6 @@
}
/**
- * Handle the change in a media session in the media button session app.
- * <p>If the app has multiple media sessions, change in a media sesion in the app may change
- * the media button session.
- * @see #findMediaButtonSession
- */
- private void onMediaSessionChangeInMediaButtonSessionApp() {
- MediaSessionRecord newMediaButtonSession =
- findMediaButtonSession(mMediaButtonSession.getUid());
- if (newMediaButtonSession != mMediaButtonSession) {
- mOnMediaButtonSessionChangedListener.onMediaButtonSessionChanged(mMediaButtonSession,
- newMediaButtonSession);
- mMediaButtonSession = newMediaButtonSession;
- }
- }
-
- /**
* Find the media button session with the given {@param uid}.
* If the app has multiple media sessions, the media session matches the audio playback state
* becomes the media button session.
@@ -245,14 +244,17 @@
* Get the current priority sorted list of active sessions. The most
* important session is at index 0 and the least important at size - 1.
*
- * @param userId The user to check.
+ * @param userId The user to check. It can be {@link UserHandle#USER_ALL} to get all sessions
+ * for all users in this {@link MediaSessionStack}.
* @return All the active sessions in priority order.
*/
public ArrayList<MediaSessionRecord> getActiveSessions(int userId) {
- if (mCachedActiveList == null) {
- mCachedActiveList = getPriorityList(true, userId);
+ ArrayList<MediaSessionRecord> cachedActiveList = mCachedActiveLists.get(userId);
+ if (cachedActiveList == null) {
+ cachedActiveList = getPriorityList(true, userId);
+ mCachedActiveLists.put(userId, cachedActiveList);
}
- return mCachedActiveList;
+ return cachedActiveList;
}
/**
@@ -382,9 +384,12 @@
return false;
}
- private void clearCache() {
+ private void clearCache(int userId) {
mCachedDefault = null;
mCachedVolumeDefault = null;
- mCachedActiveList = null;
+ mCachedActiveLists.remove(userId);
+ // mCachedActiveLists may also include the list of sessions for UserHandle.USER_ALL,
+ // so they also need to be cleared.
+ mCachedActiveLists.remove(UserHandle.USER_ALL);
}
}
diff --git a/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java b/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java
index ce976d2..acedafc 100644
--- a/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java
+++ b/services/core/java/com/android/server/notification/GlobalSortKeyComparator.java
@@ -15,19 +15,25 @@
*/
package com.android.server.notification;
+import android.util.Slog;
+
import java.util.Comparator;
/**
* Sorts notifications by their global sort key.
*/
public class GlobalSortKeyComparator implements Comparator<NotificationRecord> {
+ private final static String TAG = "GlobalSortComp";
+
@Override
public int compare(NotificationRecord left, NotificationRecord right) {
if (left.getGlobalSortKey() == null) {
- throw new IllegalStateException("Missing left global sort key: " + left);
+ Slog.wtf(TAG, "Missing left global sort key: " + left);
+ return 1;
}
if (right.getGlobalSortKey() == null) {
- throw new IllegalStateException("Missing right global sort key: " + right);
+ Slog.wtf(TAG, "Missing right global sort key: " + right);
+ return -1;
}
return left.getGlobalSortKey().compareTo(right.getGlobalSortKey());
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 90e9b92..3cb2f35 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -356,7 +356,6 @@
private void rebuildRestoredPackages() {
mRestoredPackages.clear();
- mSnoozingForCurrentProfiles.clear();
String secureSettingName = restoredSettingName(mConfig.secureSettingName);
String secondarySettingName = mConfig.secondarySettingName == null
? null : restoredSettingName(mConfig.secondarySettingName);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f334ba4..e13aeaf 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -97,6 +97,7 @@
import android.media.IRingtonePlayer;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -1322,7 +1323,8 @@
if (!fromListener) {
mListeners.notifyNotificationChannelChanged(
- pkg, modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+ pkg, UserHandle.getUserHandleForUid(uid),
+ modifiedChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
}
synchronized (mNotificationLock) {
@@ -1647,7 +1649,8 @@
Preconditions.checkNotNull(group, "group in list is null");
mRankingHelper.createNotificationChannelGroup(pkg, Binder.getCallingUid(), group,
true /* fromTargetApp */);
- mListeners.notifyNotificationChannelGroupChanged(pkg, group,
+ mListeners.notifyNotificationChannelGroupChanged(pkg,
+ UserHandle.of(UserHandle.getCallingUserId()), group,
NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
}
savePolicyFile();
@@ -1663,6 +1666,7 @@
mRankingHelper.createNotificationChannel(pkg, uid, channel,
true /* fromTargetApp */);
mListeners.notifyNotificationChannelChanged(pkg,
+ UserHandle.getUserHandleForUid(uid),
mRankingHelper.getNotificationChannel(pkg, uid, channel.getId(), false),
NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
}
@@ -1708,6 +1712,7 @@
UserHandle.getUserId(callingUid), REASON_CHANNEL_BANNED, null);
mRankingHelper.deleteNotificationChannel(pkg, callingUid, channelId);
mListeners.notifyNotificationChannelChanged(pkg,
+ UserHandle.getUserHandleForUid(callingUid),
mRankingHelper.getNotificationChannel(pkg, callingUid, channelId, true),
NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
savePolicyFile();
@@ -1737,11 +1742,14 @@
true,
UserHandle.getUserId(Binder.getCallingUid()), REASON_CHANNEL_BANNED,
null);
- mListeners.notifyNotificationChannelChanged(pkg, deletedChannel,
+ mListeners.notifyNotificationChannelChanged(pkg,
+ UserHandle.getUserHandleForUid(callingUid),
+ deletedChannel,
NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
}
mListeners.notifyNotificationChannelGroupChanged(
- pkg, groupToDelete, NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
+ pkg, UserHandle.getUserHandleForUid(callingUid), groupToDelete,
+ NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
savePolicyFile();
}
}
@@ -1866,10 +1874,9 @@
int userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
Binder.getCallingUid(), incomingUserId, true, false,
"getAppActiveNotifications", pkg);
- final ArrayMap<String, StatusBarNotification> map
- = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
-
synchronized (mNotificationLock) {
+ final ArrayMap<String, StatusBarNotification> map
+ = new ArrayMap<>(mNotificationList.size() + mEnqueuedNotifications.size());
final int N = mNotificationList.size();
for (int i = 0; i < N; i++) {
StatusBarNotification sbn = sanitizeSbn(pkg, userId,
@@ -1892,11 +1899,10 @@
map.put(sbn.getKey(), sbn); // pending update overwrites existing post here
}
}
+ final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
+ list.addAll(map.values());
+ return new ParceledListSlice<StatusBarNotification>(list);
}
-
- final ArrayList<StatusBarNotification> list = new ArrayList<>(map.size());
- list.addAll(map.values());
- return new ParceledListSlice<StatusBarNotification>(list);
}
private StatusBarNotification sanitizeSbn(String pkg, int userId,
@@ -2028,8 +2034,10 @@
long identity = Binder.clearCallingIdentity();
try {
// allow bound services to disable themselves
- final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- info.getOwner().setComponentState(info.component, false);
+ synchronized (mNotificationLock) {
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ info.getOwner().setComponentState(info.component, false);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2093,8 +2101,10 @@
String key, String snoozeCriterionId) {
long identity = Binder.clearCallingIdentity();
try {
- final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
+ synchronized (mNotificationLock) {
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ snoozeNotificationInt(key, SNOOZE_UNTIL_UNSPECIFIED, snoozeCriterionId, info);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2110,8 +2120,10 @@
long duration) {
long identity = Binder.clearCallingIdentity();
try {
- final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- snoozeNotificationInt(key, duration, null, info);
+ synchronized (mNotificationLock) {
+ final ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ snoozeNotificationInt(key, duration, null, info);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2126,9 +2138,11 @@
public void unsnoozeNotificationFromAssistant(INotificationListener token, String key) {
long identity = Binder.clearCallingIdentity();
try {
- final ManagedServiceInfo info =
- mNotificationAssistants.checkServiceTokenLocked(token);
- unsnoozeNotificationInt(key, info);
+ synchronized (mNotificationLock) {
+ final ManagedServiceInfo info =
+ mNotificationAssistants.checkServiceTokenLocked(token);
+ unsnoozeNotificationInt(key, info);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2691,43 +2705,62 @@
@Override
public void updateNotificationChannelFromPrivilegedListener(INotificationListener token,
- String pkg, NotificationChannel channel) throws RemoteException {
+ String pkg, UserHandle user, NotificationChannel channel) throws RemoteException {
Preconditions.checkNotNull(channel);
+ Preconditions.checkNotNull(pkg);
+ Preconditions.checkNotNull(user);
- ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- if (!hasCompanionDevice(info)) {
- throw new SecurityException(info + " does not have access");
- }
-
- int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
- updateNotificationChannelInt(pkg, uid, channel, true);
+ verifyPrivilegedListener(token, user);
+ updateNotificationChannelInt(pkg, getUidForPackageAndUser(pkg, user), channel, true);
}
@Override
public ParceledListSlice<NotificationChannel> getNotificationChannelsFromPrivilegedListener(
- INotificationListener token, String pkg) throws RemoteException {
- ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
- if (!hasCompanionDevice(info)) {
- throw new SecurityException(info + " does not have access");
- }
+ INotificationListener token, String pkg, UserHandle user) throws RemoteException {
+ Preconditions.checkNotNull(pkg);
+ Preconditions.checkNotNull(user);
+ verifyPrivilegedListener(token, user);
- int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
- return mRankingHelper.getNotificationChannels(pkg, uid, false /* includeDeleted */);
+ return mRankingHelper.getNotificationChannels(pkg, getUidForPackageAndUser(pkg, user),
+ false /* includeDeleted */);
}
@Override
public ParceledListSlice<NotificationChannelGroup>
getNotificationChannelGroupsFromPrivilegedListener(
- INotificationListener token, String pkg) throws RemoteException {
- ManagedServiceInfo info = mListeners.checkServiceTokenLocked(token);
+ INotificationListener token, String pkg, UserHandle user) throws RemoteException {
+ Preconditions.checkNotNull(pkg);
+ Preconditions.checkNotNull(user);
+ verifyPrivilegedListener(token, user);
+
+ List<NotificationChannelGroup> groups = new ArrayList<>();
+ groups.addAll(mRankingHelper.getNotificationChannelGroups(
+ pkg, getUidForPackageAndUser(pkg, user)));
+ return new ParceledListSlice<>(groups);
+ }
+
+ private void verifyPrivilegedListener(INotificationListener token, UserHandle user) {
+ ManagedServiceInfo info;
+ synchronized (mNotificationLock) {
+ info = mListeners.checkServiceTokenLocked(token);
+ }
if (!hasCompanionDevice(info)) {
throw new SecurityException(info + " does not have access");
}
+ if (!info.enabledAndUserMatches(user.getIdentifier())) {
+ throw new SecurityException(info + " does not have access");
+ }
+ }
- List<NotificationChannelGroup> groups = new ArrayList<>();
- int uid = mPackageManager.getPackageUid(pkg, 0, info.userid);
- groups.addAll(mRankingHelper.getNotificationChannelGroups(pkg, uid));
- return new ParceledListSlice<>(groups);
+ private int getUidForPackageAndUser(String pkg, UserHandle user) throws RemoteException {
+ int uid = 0;
+ long identity = Binder.clearCallingIdentity();
+ try {
+ uid = mPackageManager.getPackageUid(pkg, 0, user.getIdentifier());
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ return uid;
}
};
@@ -3075,8 +3108,10 @@
@Override
public void run() {
synchronized (mNotificationLock) {
- removeForegroundServiceFlagByListLocked(mEnqueuedNotifications, pkg, notificationId, userId);
- removeForegroundServiceFlagByListLocked(mNotificationList, pkg, notificationId, userId);
+ removeForegroundServiceFlagByListLocked(
+ mEnqueuedNotifications, pkg, notificationId, userId);
+ removeForegroundServiceFlagByListLocked(
+ mNotificationList, pkg, notificationId, userId);
}
}
});
@@ -3162,16 +3197,16 @@
// STOPSHIP TODO: should throw instead of logging or toasting.
// throw new IllegalArgumentException(noChannelStr);
Log.e(TAG, noChannelStr);
-
- final String noChannelToastStr =
- "Developer warning for package \"" + pkg + "\"\n" +
+ doDebugOnlyToast("Developer warning for package \"" + pkg + "\"\n" +
"Failed to post notification on channel \"" + channelId + "\"\n" +
- "See log for more details";
- Toast noChannelToast =
- Toast.makeText(getContext(), noChannelToastStr, Toast.LENGTH_LONG);
- noChannelToast.show();
+ "See log for more details");
return;
+ } else if (channelId == null && shouldWarnUseChannels(pkg, notificationUid)) {
+ // STOPSHIP TODO: remove once default channel is removed for all apps that target O.
+ doDebugOnlyToast("Developer warning for package \"" + pkg + "\"\n" +
+ "Posted notification should specify a channel");
}
+
final StatusBarNotification n = new StatusBarNotification(
pkg, opPkg, id, tag, notificationUid, callingPid, notification,
user, null, System.currentTimeMillis());
@@ -3203,6 +3238,30 @@
idOut[0] = id;
}
+ private void doDebugOnlyToast(CharSequence toastText) {
+ if (Build.IS_DEBUGGABLE) {
+ try {
+ Toast toast = Toast.makeText(getContext(), toastText, Toast.LENGTH_LONG);
+ toast.show();
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Unable to toast with text: " + toastText, e);
+ }
+ }
+ }
+
+ // STOPSHIP - Remove once RankingHelper deletes default channel for all apps targeting O.
+ private boolean shouldWarnUseChannels(String pkg, int uid) {
+ try {
+ final int userId = UserHandle.getUserId(uid);
+ final ApplicationInfo applicationInfo =
+ mPackageManagerClient.getApplicationInfoAsUser(pkg, 0, userId);
+ return applicationInfo.targetSdkVersion > Build.VERSION_CODES.N_MR1;
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, e.toString());
+ return false;
+ }
+ }
+
private int resolveNotificationUid(String opPackageName, int callingUid, int userId) {
// The system can post notifications on behalf of any package it wants
if (isCallerSystem() && opPackageName != null && !"android".equals(opPackageName)) {
@@ -3552,7 +3611,7 @@
final boolean aboveThreshold =
record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
final boolean canInterrupt = aboveThreshold && !record.isIntercepted();
- if (DBG || record.isIntercepted())
+ if (DBG)
Slog.v(TAG,
"pkg=" + record.sbn.getPackageName() + " canInterrupt=" + canInterrupt +
" intercept=" + record.isIntercepted()
@@ -5019,7 +5078,7 @@
}
}
- protected void notifyNotificationChannelChanged(final String pkg,
+ protected void notifyNotificationChannelChanged(final String pkg, final UserHandle user,
final NotificationChannel channel, final int modificationType) {
if (channel == null) {
return;
@@ -5034,15 +5093,16 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyNotificationChannelChanged(serviceInfo, pkg, channel,
- modificationType);
+ notifyNotificationChannelChanged(
+ serviceInfo, pkg, user, channel, modificationType);
}
});
}
}
- protected void notifyNotificationChannelGroupChanged(final String pkg,
- final NotificationChannelGroup group, final int modificationType) {
+ protected void notifyNotificationChannelGroupChanged(
+ final String pkg, final UserHandle user, final NotificationChannelGroup group,
+ final int modificationType) {
if (group == null) {
return;
}
@@ -5056,8 +5116,8 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- notifyNotificationChannelGroupChanged(serviceInfo, pkg, group,
- modificationType);
+ notifyNotificationChannelGroupChanged(
+ serviceInfo, pkg, user, group, modificationType);
}
});
}
@@ -5118,22 +5178,22 @@
}
void notifyNotificationChannelChanged(ManagedServiceInfo info,
- final String pkg, final NotificationChannel channel,
+ final String pkg, final UserHandle user, final NotificationChannel channel,
final int modificationType) {
final INotificationListener listener = (INotificationListener) info.service;
try {
- listener.onNotificationChannelModification(pkg, channel, modificationType);
+ listener.onNotificationChannelModification(pkg, user, channel, modificationType);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (channel changed): " + listener, ex);
}
}
private void notifyNotificationChannelGroupChanged(ManagedServiceInfo info,
- final String pkg, final NotificationChannelGroup group,
+ final String pkg, final UserHandle user, final NotificationChannelGroup group,
final int modificationType) {
final INotificationListener listener = (INotificationListener) info.service;
try {
- listener.onNotificationChannelGroupModification(pkg, group, modificationType);
+ listener.onNotificationChannelGroupModification(pkg, user, group, modificationType);
} catch (RemoteException ex) {
Log.e(TAG, "unable to notify listener (channel group changed): " + listener, ex);
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index b7d3173..b48fd5c 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -172,7 +172,7 @@
final boolean useDefaultSound = (n.defaults & Notification.DEFAULT_SOUND) != 0;
if (useDefaultSound) {
sound = Settings.System.DEFAULT_NOTIFICATION_URI;
- } else if (n.sound != null) {
+ } else {
sound = n.sound;
}
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 7b86542..2e4b49a 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -274,23 +274,56 @@
// Intercept and collect dexopt requests
final List<String> commands = new ArrayList<String>();
final Installer collectingInstaller = new Installer(mContext, true) {
+ /**
+ * Encode the dexopt command into a string.
+ *
+ * Note: If you have to change the signature of this function, increase the version
+ * number, and update the counterpart in
+ * frameworks/native/cmds/installd/otapreopt.cpp.
+ */
@Override
public void dexopt(String apkPath, int uid, @Nullable String pkgName,
String instructionSet, int dexoptNeeded, @Nullable String outputPath,
int dexFlags, String compilerFilter, @Nullable String volumeUuid,
@Nullable String sharedLibraries, @Nullable String seInfo) throws InstallerException {
- commands.add(buildCommand("dexopt",
- apkPath,
- uid,
- pkgName,
- instructionSet,
- dexoptNeeded,
- outputPath,
- dexFlags,
- compilerFilter,
- volumeUuid,
- sharedLibraries,
- seInfo));
+ final StringBuilder builder = new StringBuilder();
+
+ // The version. Right now it's 2.
+ builder.append("2 ");
+
+ builder.append("dexopt");
+
+ encodeParameter(builder, apkPath);
+ encodeParameter(builder, uid);
+ encodeParameter(builder, pkgName);
+ encodeParameter(builder, instructionSet);
+ encodeParameter(builder, dexoptNeeded);
+ encodeParameter(builder, outputPath);
+ encodeParameter(builder, dexFlags);
+ encodeParameter(builder, compilerFilter);
+ encodeParameter(builder, volumeUuid);
+ encodeParameter(builder, sharedLibraries);
+ encodeParameter(builder, seInfo);
+
+ commands.add(builder.toString());
+ }
+
+ /**
+ * Encode a parameter as necessary for the commands string.
+ */
+ private void encodeParameter(StringBuilder builder, Object arg) {
+ builder.append(' ');
+
+ if (arg == null) {
+ builder.append('!');
+ }
+
+ String txt = String.valueOf(arg);
+ if (txt.indexOf('\0') != -1 || txt.indexOf(' ') != -1 || "!".equals(txt)) {
+ throw new IllegalArgumentException(
+ "Invalid argument while executing " + arg);
+ }
+ builder.append(txt);
}
};
@@ -430,28 +463,4 @@
super(installer, installLock, context, "*otadexopt*");
}
}
-
- /**
- * Cook up argument list in the format that {@code installd} expects.
- */
- private static String buildCommand(Object... args) {
- final StringBuilder builder = new StringBuilder();
- for (Object arg : args) {
- String escaped;
- if (arg == null) {
- escaped = "";
- } else {
- escaped = String.valueOf(arg);
- }
- if (escaped.indexOf('\0') != -1 || escaped.indexOf(' ') != -1 || "!".equals(escaped)) {
- throw new IllegalArgumentException(
- "Invalid argument while executing " + Arrays.toString(args));
- }
- if (TextUtils.isEmpty(escaped)) {
- escaped = "!";
- }
- builder.append(' ').append(escaped);
- }
- return builder.toString();
- }
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 650d2f6..5535d17 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -364,7 +364,8 @@
* $ cts-tradefed run commandAndExit cts -m CtsAppSecurityHostTestCases
* </pre>
*/
-public class PackageManagerService extends IPackageManager.Stub {
+public class PackageManagerService extends IPackageManager.Stub
+ implements PackageSender {
static final String TAG = "PackageManager";
static final boolean DEBUG_SETTINGS = false;
static final boolean DEBUG_PREFERRED = false;
@@ -4217,8 +4218,10 @@
}
SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getName(),
- libInfo.getVersion(), libInfo.getType(), libInfo.getDeclaringPackage(),
- getPackagesUsingSharedLibraryLPr(libInfo, flags, userId));
+ // TODO: Remove cast for lib version once internally we support longs.
+ (int) libInfo.getVersion(), libInfo.getType(),
+ libInfo.getDeclaringPackage(), getPackagesUsingSharedLibraryLPr(libInfo,
+ flags, userId));
if (result == null) {
result = new ArrayList<>();
@@ -13083,7 +13086,7 @@
}
};
- final void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
+ public void sendPackageBroadcast(final String action, final String pkg, final Bundle extras,
final int flags, final String targetPkg, final IIntentReceiver finishedReceiver,
final int[] userIds) {
mHandler.post(new Runnable() {
@@ -13201,7 +13204,7 @@
if (dcsUid > 0) {
am.backgroundWhitelistUid(dcsUid);
}
- am.startService(null, intent, null, -1, null, false, mContext.getOpPackageName(),
+ am.startService(null, intent, null, false, mContext.getOpPackageName(),
UserHandle.USER_SYSTEM);
} catch (RemoteException e) {
}
@@ -13386,8 +13389,7 @@
sendPackageAddedForNewUsers(packageName, isSystem, pkgSetting.appId, userId);
}
- private void sendPackageAddedForNewUsers(String packageName, boolean isSystem,
- int appId, int... userIds) {
+ public void sendPackageAddedForNewUsers(String packageName, boolean isSystem, int appId, int... userIds) {
if (ArrayUtils.isEmpty(userIds)) {
return;
}
@@ -13514,7 +13516,7 @@
private void sendApplicationHiddenForUser(String packageName, PackageSetting pkgSetting,
int userId) {
- final PackageRemovedInfo info = new PackageRemovedInfo();
+ final PackageRemovedInfo info = new PackageRemovedInfo(this);
info.removedPackage = packageName;
info.removedUsers = new int[] {userId};
info.broadcastUsers = new int[] {userId};
@@ -16150,7 +16152,7 @@
}
// Update what is removed
- res.removedInfo = new PackageRemovedInfo();
+ res.removedInfo = new PackageRemovedInfo(this);
res.removedInfo.uid = oldPackage.applicationInfo.uid;
res.removedInfo.removedPackage = oldPackage.packageName;
res.removedInfo.isStaticSharedLib = pkg.staticSharedLibName != null;
@@ -16180,7 +16182,7 @@
}
}
if (!childPackageUpdated) {
- PackageRemovedInfo childRemovedRes = new PackageRemovedInfo();
+ PackageRemovedInfo childRemovedRes = new PackageRemovedInfo(this);
childRemovedRes.removedPackage = childPkg.packageName;
childRemovedRes.isUpdate = false;
childRemovedRes.dataRemoved = true;
@@ -16870,7 +16872,7 @@
sUserManager.getUserIds(), true);
}
if ((mPackages.containsKey(childPkg.packageName))) {
- childRes.removedInfo = new PackageRemovedInfo();
+ childRes.removedInfo = new PackageRemovedInfo(this);
childRes.removedInfo.removedPackage = childPkg.packageName;
}
if (res.addedChildPackages == null) {
@@ -17593,7 +17595,8 @@
for (int i = 0; i < versionCount; i++) {
SharedLibraryEntry libEntry = versionedLib.valueAt(i);
if (versionsCallerCanSee != null && versionsCallerCanSee.indexOfKey(
- libEntry.info.getVersion()) < 0) {
+ // TODO: Remove cast for lib version once internally we support longs.
+ (int) libEntry.info.getVersion()) < 0) {
continue;
}
// TODO: We will change version code to long, so in the new API it is long
@@ -17716,7 +17719,7 @@
* sending a broadcast if necessary
*/
private int deletePackageX(String packageName, int versionCode, int userId, int deleteFlags) {
- final PackageRemovedInfo info = new PackageRemovedInfo();
+ final PackageRemovedInfo info = new PackageRemovedInfo(this);
final boolean res;
final int removeUser = (deleteFlags & PackageManager.DELETE_ALL_USERS) != 0
@@ -17816,7 +17819,8 @@
return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;
}
- class PackageRemovedInfo {
+ static class PackageRemovedInfo {
+ final PackageSender packageSender;
String removedPackage;
int uid = -1;
int removedAppId = -1;
@@ -17834,6 +17838,10 @@
ArrayMap<String, PackageRemovedInfo> removedChildPackages;
ArrayMap<String, PackageInstalledInfo> appearedChildPackages;
+ PackageRemovedInfo(PackageSender packageSender) {
+ this.packageSender = packageSender;
+ }
+
void sendPackageRemovedBroadcasts(boolean killApp) {
sendPackageRemovedBroadcastInternal(killApp);
final int childCount = removedChildPackages != null ? removedChildPackages.size() : 0;
@@ -17862,8 +17870,9 @@
? appearedChildPackages.size() : 0;
for (int i = 0; i < packageCount; i++) {
PackageInstalledInfo installedInfo = appearedChildPackages.valueAt(i);
- sendPackageAddedForNewUsers(installedInfo.name, true,
- UserHandle.getAppId(installedInfo.uid), installedInfo.newUsers);
+ packageSender.sendPackageAddedForNewUsers(installedInfo.name,
+ true, UserHandle.getAppId(installedInfo.uid),
+ installedInfo.newUsers);
}
}
@@ -17871,12 +17880,12 @@
Bundle extras = new Bundle(2);
extras.putInt(Intent.EXTRA_UID, removedAppId >= 0 ? removedAppId : uid);
extras.putBoolean(Intent.EXTRA_REPLACING, true);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, removedPackage,
- extras, 0, null, null, null);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, removedPackage,
- extras, 0, null, null, null);
- sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
- null, 0, removedPackage, null, null);
+ packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+ removedPackage, extras, 0, null, null, null);
+ packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ removedPackage, extras, 0, null, null, null);
+ packageSender.sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
+ null, null, 0, removedPackage, null, null);
}
private void sendPackageRemovedBroadcastInternal(boolean killApp) {
@@ -17895,17 +17904,35 @@
}
extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, removedForAllUsers);
if (removedPackage != null) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
- extras, 0, null, null, broadcastUsers);
+ packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED,
+ removedPackage, extras, 0, null, null, broadcastUsers);
if (dataRemoved && !isRemovedPackageSystemUpdate) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
- removedPackage, extras, Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
- null, null, broadcastUsers);
+ packageSender.sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED,
+ removedPackage, extras,
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND,
+ null, null, broadcastUsers);
}
}
if (removedAppId >= 0) {
- sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, 0, null, null,
- broadcastUsers);
+ packageSender.sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras,
+ Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND, null, null, broadcastUsers);
+ }
+ }
+
+ void populateUsers(int[] userIds, PackageSetting deletedPackageSetting) {
+ removedUsers = userIds;
+ if (removedUsers == null) {
+ broadcastUsers = null;
+ return;
+ }
+
+ broadcastUsers = EMPTY_INT_ARRAY;
+ for (int i = userIds.length - 1; i >= 0; --i) {
+ final int userId = userIds[i];
+ if (deletedPackageSetting.getInstantApp(userId)) {
+ continue;
+ }
+ broadcastUsers = ArrayUtils.appendInt(broadcastUsers, userId);
}
}
}
@@ -17931,23 +17958,8 @@
outInfo.removedPackage = packageName;
outInfo.isStaticSharedLib = deletedPkg != null
&& deletedPkg.staticSharedLibName != null;
- outInfo.removedUsers = deletedPs != null
- ? deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true)
- : null;
- if (outInfo.removedUsers == null) {
- outInfo.broadcastUsers = null;
- } else {
- outInfo.broadcastUsers = EMPTY_INT_ARRAY;
- int[] allUsers = outInfo.removedUsers;
- for (int i = allUsers.length - 1; i >= 0; --i) {
- final int userId = allUsers[i];
- if (deletedPs.getInstantApp(userId)) {
- continue;
- }
- outInfo.broadcastUsers =
- ArrayUtils.appendInt(outInfo.broadcastUsers, userId);
- }
- }
+ outInfo.populateUsers(deletedPs == null ? null
+ : deletedPs.queryInstalledUsers(sUserManager.getUserIds(), true), deletedPs);
}
}
@@ -18430,7 +18442,7 @@
outInfo.removedChildPackages = new ArrayMap<>(childCount);
for (int i = 0; i < childCount; i++) {
String childPackageName = ps.childPackageNames.get(i);
- PackageRemovedInfo childInfo = new PackageRemovedInfo();
+ PackageRemovedInfo childInfo = new PackageRemovedInfo(this);
childInfo.removedPackage = childPackageName;
outInfo.removedChildPackages.put(childPackageName, childInfo);
PackageSetting childPs = mSettings.getPackageLPr(childPackageName);
@@ -21692,7 +21704,7 @@
if (DEBUG_SD_INSTALL)
Log.i(TAG, "Trying to unload pkg : " + pkgName);
// Delete package internally
- PackageRemovedInfo outInfo = new PackageRemovedInfo();
+ PackageRemovedInfo outInfo = new PackageRemovedInfo(this);
synchronized (mInstallLock) {
final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
final boolean res;
@@ -21859,7 +21871,7 @@
final ApplicationInfo info = ps.pkg.applicationInfo;
final int deleteFlags = PackageManager.DELETE_KEEP_DATA;
- final PackageRemovedInfo outInfo = new PackageRemovedInfo();
+ final PackageRemovedInfo outInfo = new PackageRemovedInfo(this);
try (PackageFreezer freezer = freezePackageForDelete(ps.name, deleteFlags,
"unloadPrivatePackagesInner")) {
@@ -23636,4 +23648,18 @@
public ComponentName getInstantAppResolverSettingsComponent() {
return mInstantAppResolverSettingsComponent;
}
+
+ @Override
+ public ComponentName getInstantAppInstallerComponent() {
+ return mInstantAppInstallerActivity == null
+ ? null : mInstantAppInstallerActivity.getComponentName();
+ }
+}
+
+interface PackageSender {
+ void sendPackageBroadcast(final String action, final String pkg,
+ final Bundle extras, final int flags, final String targetPkg,
+ final IIntentReceiver finishedReceiver, final int[] userIds);
+ void sendPackageAddedForNewUsers(String packageName, boolean isSystem,
+ int appId, int... userIds);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 4f29bfa..5c9f749 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -222,7 +222,7 @@
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
-import android.widget.ImageView;
+import android.view.inputmethod.InputMethodManagerInternal;
import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
@@ -279,6 +279,7 @@
static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP = 2;
static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME = 3;
static final int SHORT_PRESS_POWER_GO_HOME = 4;
+ static final int SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME = 5;
static final int LONG_PRESS_POWER_NOTHING = 0;
static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1;
@@ -407,6 +408,7 @@
PowerManager mPowerManager;
ActivityManagerInternal mActivityManagerInternal;
InputManagerInternal mInputManagerInternal;
+ InputMethodManagerInternal mInputMethodManagerInternal;
DreamManagerInternal mDreamManagerInternal;
PowerManagerInternal mPowerManagerInternal;
IStatusBarService mStatusBarService;
@@ -513,6 +515,7 @@
volatile boolean mPictureInPictureVisible;
// Written by vr manager thread, only read in this class
volatile boolean mPersistentVrModeEnabled;
+ volatile private boolean mDismissImeOnBackKeyPressed;
// Used to hold the last user key used to wake the device. This helps us prevent up events
// from being passed to the foregrounded app without a corresponding down event
@@ -1396,6 +1399,21 @@
case SHORT_PRESS_POWER_GO_HOME:
launchHomeFromHotKey(true /* awakenFromDreams */, false /*respectKeyguard*/);
break;
+ case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: {
+ if (mDismissImeOnBackKeyPressed) {
+ if (mInputMethodManagerInternal == null) {
+ mInputMethodManagerInternal =
+ LocalServices.getService(InputMethodManagerInternal.class);
+ }
+ if (mInputMethodManagerInternal != null) {
+ mInputMethodManagerInternal.hideCurrentInputMethod();
+ }
+ } else {
+ launchHomeFromHotKey(true /* awakenFromDreams */,
+ false /*respectKeyguard*/);
+ }
+ break;
+ }
}
}
}
@@ -7955,6 +7973,11 @@
}
@Override
+ public void setDismissImeOnBackKeyPressed(boolean newValue) {
+ mDismissImeOnBackKeyPressed = newValue;
+ }
+
+ @Override
public int getInputMethodWindowVisibleHeightLw() {
return mDockBottom - mCurBottom;
}
@@ -8170,6 +8193,8 @@
pw.print(prefix); pw.print("mLastInputMethodTargetWindow=");
pw.println(mLastInputMethodTargetWindow);
}
+ pw.print(prefix); pw.print("mDismissImeOnBackKeyPressed=");
+ pw.println(mDismissImeOnBackKeyPressed);
if (mStatusBar != null) {
pw.print(prefix); pw.print("mStatusBar=");
pw.print(mStatusBar); pw.print(" isStatusBarKeyguard=");
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 8f11436..f5bb082 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -406,11 +406,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
- LogMaker log = new LogMaker(MetricsEvent.SCREEN);
- log.setType(MetricsEvent.TYPE_OPEN);
- log.setSubtype(0); // not user initiated
- MetricsLogger.action(log);
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 1, 0, 0, 0);
+ // Note a SCREEN tron event is logged in PowerManagerService.
mPolicy.startedWakingUp();
}
});
@@ -470,7 +466,7 @@
log.setType(MetricsEvent.TYPE_CLOSE);
log.setSubtype(why);
MetricsLogger.action(log);
- EventLog.writeEvent(EventLogTags.POWER_SCREEN_STATE, 0, why, 0, 0);
+ EventLogTags.writePowerScreenState(0, why, 0, 0, 0);
mPolicy.finishedGoingToSleep(why);
}
});
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index cf597b05..4f239a5 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -32,6 +32,7 @@
import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.hardware.power.V1_0.PowerHint;
+import android.metrics.LogMaker;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
@@ -75,6 +76,8 @@
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
@@ -188,6 +191,11 @@
// System property indicating that the screen should remain off until an explicit user action
private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
+ private static final String TRACE_SCREEN_ON = "Screen turning on";
+
+ /** If turning screen on takes more than this long, we show a warning on logcat. */
+ private static final int SCREEN_ON_LATENCY_WARNING_MS = 200;
+
/** Constants for {@link #shutdownOrRebootInternal} */
@Retention(RetentionPolicy.SOURCE)
@IntDef({HALT_MODE_SHUTDOWN, HALT_MODE_REBOOT, HALT_MODE_REBOOT_SAFE_MODE})
@@ -1369,6 +1377,8 @@
return false;
}
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0);
+
Trace.traceBegin(Trace.TRACE_TAG_POWER, "wakeUp");
try {
switch (mWakefulness) {
@@ -1551,6 +1561,23 @@
}
}
+ private void logScreenOn() {
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, TRACE_SCREEN_ON, 0);
+
+ final int latencyMs = (int) (SystemClock.uptimeMillis() - mLastWakeTime);
+
+ LogMaker log = new LogMaker(MetricsEvent.SCREEN);
+ log.setType(MetricsEvent.TYPE_OPEN);
+ log.setSubtype(0); // not user initiated
+ log.setLatency(latencyMs); // How long it took.
+ MetricsLogger.action(log);
+ EventLogTags.writePowerScreenState(1, 0, 0, 0, latencyMs);
+
+ if (latencyMs >= SCREEN_ON_LATENCY_WARNING_MS) {
+ Slog.w(TAG, "Screen on took " + latencyMs+ " ms");
+ }
+ }
+
private void finishWakefulnessChangeIfNeededLocked() {
if (mWakefulnessChanging && mDisplayReady) {
if (mWakefulness == WAKEFULNESS_DOZING
@@ -1560,6 +1587,9 @@
if (mWakefulness == WAKEFULNESS_DOZING || mWakefulness == WAKEFULNESS_ASLEEP) {
logSleepTimeoutRecapturedLocked();
}
+ if (mWakefulness == WAKEFULNESS_AWAKE) {
+ logScreenOn();
+ }
mWakefulnessChanging = false;
mNotifier.onWakefulnessChangeFinished();
}
diff --git a/services/core/java/com/android/server/storage/AppCollector.java b/services/core/java/com/android/server/storage/AppCollector.java
index a77d33f..03b754f 100644
--- a/services/core/java/com/android/server/storage/AppCollector.java
+++ b/services/core/java/com/android/server/storage/AppCollector.java
@@ -21,22 +21,21 @@
import android.app.usage.StorageStatsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageStats;
import android.content.pm.UserInfo;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.os.Process;
-import android.os.RemoteException;
import android.os.UserManager;
import android.os.storage.VolumeInfo;
import android.util.Log;
+
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.Preconditions;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -44,7 +43,6 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* AppCollector asynchronously collects package sizes.
@@ -132,7 +130,7 @@
try {
StorageStats storageStats =
- mStorageStatsManager.queryStatsForPackage(app.volumeUuid,
+ mStorageStatsManager.queryStatsForPackage(app.storageUuid,
app.packageName, user.getUserHandle());
PackageStats packageStats = new PackageStats(app.packageName,
user.id);
@@ -140,7 +138,7 @@
packageStats.codeSize = storageStats.getCodeBytes();
packageStats.dataSize = storageStats.getDataBytes();
stats.add(packageStats);
- } catch (IllegalStateException e) {
+ } catch (NameNotFoundException | IOException e) {
Log.e(TAG, "An exception occurred while fetching app size", e);
}
}
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index f41eed5..e634552 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -22,6 +22,7 @@
import android.animation.Animator;
import android.animation.ValueAnimator;
+import android.annotation.IntDef;
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
@@ -35,6 +36,9 @@
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Enables animating bounds of objects.
*
@@ -43,7 +47,7 @@
* relaunching it would cause poorer experience), these class provides a way to directly animate
* the bounds of the resized object.
*
- * The object that is resized needs to implement {@link AnimateBoundsUser} interface.
+ * The object that is resized needs to implement {@link BoundsAnimationTarget} interface.
*
* NOTE: All calls to methods in this class should be done on the UI thread
*/
@@ -56,8 +60,19 @@
private static final int DEFAULT_TRANSITION_DURATION = 425;
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({NO_PIP_MODE_CHANGED_CALLBACKS, SCHEDULE_PIP_MODE_CHANGED_ON_START,
+ SCHEDULE_PIP_MODE_CHANGED_ON_END})
+ public @interface SchedulePipModeChangedState {}
+ /** Do not schedule any PiP mode changed callbacks as a part of this animation. */
+ public static final int NO_PIP_MODE_CHANGED_CALLBACKS = 0;
+ /** Schedule a PiP mode changed callback when this animation starts. */
+ public static final int SCHEDULE_PIP_MODE_CHANGED_ON_START = 1;
+ /** Schedule a PiP mode changed callback when this animation ends. */
+ public static final int SCHEDULE_PIP_MODE_CHANGED_ON_END = 2;
+
// Only accessed on UI thread.
- private ArrayMap<AnimateBoundsUser, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
+ private ArrayMap<BoundsAnimationTarget, BoundsAnimator> mRunningAnimations = new ArrayMap<>();
private final class AppTransitionNotifier
extends WindowManagerInternal.AppTransitionListener implements Runnable {
@@ -108,40 +123,42 @@
@VisibleForTesting
final class BoundsAnimator extends ValueAnimator
implements ValueAnimator.AnimatorUpdateListener, ValueAnimator.AnimatorListener {
- private final AnimateBoundsUser mTarget;
+ private final BoundsAnimationTarget mTarget;
private final Rect mFrom = new Rect();
private final Rect mTo = new Rect();
private final Rect mTmpRect = new Rect();
private final Rect mTmpTaskBounds = new Rect();
- private final boolean mMoveToFullScreen;
- // True if this this animation was cancelled and will be replaced the another animation from
- // the same {@link #AnimateBoundsUser} target.
+
+ // True if this this animation was canceled and will be replaced the another animation from
+ // the same {@link #BoundsAnimationTarget} target.
private boolean mSkipFinalResize;
// True if this animation replaced a previous animation of the same
- // {@link #AnimateBoundsUser} target.
+ // {@link #BoundsAnimationTarget} target.
private final boolean mSkipAnimationStart;
- // True if this animation was cancelled by the user, not as a part of a replacing animation
+ // True if this animation was canceled by the user, not as a part of a replacing animation
private boolean mSkipAnimationEnd;
- // True if this animation is not replacing a previous animation, or if the previous
- // animation is animating to a different fullscreen state than the current animation.
- // We use this to ensure that we always provide a consistent set/order of callbacks when we
- // transition to/from PiP.
- private final boolean mAnimatingToNewFullscreenState;
+ // True if the animation target should be moved to the fullscreen stack at the end of this
+ // animation
+ private boolean mMoveToFullscreen;
+
+ // Whether to schedule PiP mode changes on animation start/end
+ private @SchedulePipModeChangedState int mSchedulePipModeChangedState;
// Depending on whether we are animating from
// a smaller to a larger size
private final int mFrozenTaskWidth;
private final int mFrozenTaskHeight;
- BoundsAnimator(AnimateBoundsUser target, Rect from, Rect to, boolean moveToFullScreen,
- boolean replacingExistingAnimation, boolean animatingToNewFullscreenState) {
+ BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to,
+ @SchedulePipModeChangedState int schedulePipModeChangedState,
+ boolean moveToFullscreen, boolean replacingExistingAnimation) {
super();
mTarget = target;
mFrom.set(from);
mTo.set(to);
- mMoveToFullScreen = moveToFullScreen;
mSkipAnimationStart = replacingExistingAnimation;
- mAnimatingToNewFullscreenState = animatingToNewFullscreenState;
+ mSchedulePipModeChangedState = schedulePipModeChangedState;
+ mMoveToFullscreen = moveToFullscreen;
addUpdateListener(this);
addListener(this);
@@ -161,7 +178,8 @@
@Override
public void onAnimationStart(Animator animation) {
if (DEBUG) Slog.d(TAG, "onAnimationStart: mTarget=" + mTarget
- + " mSkipAnimationStart=" + mSkipAnimationStart);
+ + " mSkipAnimationStart=" + mSkipAnimationStart
+ + " mSchedulePipModeChangedState=" + mSchedulePipModeChangedState);
mFinishAnimationAfterTransition = false;
mTmpRect.set(mFrom.left, mFrom.top, mFrom.left + mFrozenTaskWidth,
mFrom.top + mFrozenTaskHeight);
@@ -170,13 +188,8 @@
// we trigger any size changes, so it can swap surfaces
// in to appropriate modes, or do as it wishes otherwise.
if (!mSkipAnimationStart) {
- mTarget.onAnimationStart(mMoveToFullScreen);
- }
-
- // If we are animating to a new fullscreen state (either to/from fullscreen), then
- // notify the target of the change with the new frozen task bounds
- if (mAnimatingToNewFullscreenState && mMoveToFullScreen) {
- mTarget.updatePictureInPictureMode(null);
+ mTarget.onAnimationStart(mSchedulePipModeChangedState ==
+ SCHEDULE_PIP_MODE_CHANGED_ON_START);
}
// Immediately update the task bounds if they have to become larger, but preserve
@@ -206,20 +219,26 @@
// any further animation.
if (DEBUG) Slog.d(TAG, "animateUpdate: cancelled");
+ // If we have already scheduled a PiP mode changed at the start of the animation,
+ // then we need to clean up and schedule one at the end, since we have canceled the
+ // animation to the final state.
+ if (mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
+ mSchedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
+ }
+
// Since we are cancelling immediately without a replacement animation, send the
// animation end to maintain callback parity, but also skip any further resizes
- prepareCancel(false /* skipAnimationEnd */, true /* skipFinalResize */);
- cancel();
+ cancelAndCallAnimationEnd();
}
}
@Override
public void onAnimationEnd(Animator animation) {
if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
- + " mMoveToFullScreen=" + mMoveToFullScreen
+ " mSkipFinalResize=" + mSkipFinalResize
+ " mFinishAnimationAfterTransition=" + mFinishAnimationAfterTransition
- + " mAppTransitionIsRunning=" + mAppTransition.isRunning());
+ + " mAppTransitionIsRunning=" + mAppTransition.isRunning()
+ + " callers=" + Debug.getCallers(2));
// There could be another animation running. For example in the
// move to fullscreen case, recents will also be closing while the
@@ -231,58 +250,57 @@
return;
}
- if (!mSkipFinalResize) {
- // If not cancelled, resize the pinned stack to the final size. All calls to
- // setPinnedStackSize() must be done between onAnimationStart() and onAnimationEnd()
- mTarget.setPinnedStackSize(mTo, null);
+ if (!mSkipAnimationEnd) {
+ // If this animation has already scheduled the picture-in-picture mode on start, and
+ // we are not skipping the final resize due to being canceled, then move the PiP to
+ // fullscreen once the animation ends
+ if (DEBUG) Slog.d(TAG, "onAnimationEnd: mTarget=" + mTarget
+ + " moveToFullscreen=" + mMoveToFullscreen);
+ mTarget.onAnimationEnd(mSchedulePipModeChangedState ==
+ SCHEDULE_PIP_MODE_CHANGED_ON_END, !mSkipFinalResize ? mTo : null,
+ mMoveToFullscreen);
}
- finishAnimation();
-
- if (mMoveToFullScreen && !mSkipFinalResize) {
- mTarget.moveToFullscreen();
- }
+ // Clean up this animation
+ removeListener(this);
+ removeUpdateListener(this);
+ mRunningAnimations.remove(mTarget);
}
@Override
public void onAnimationCancel(Animator animation) {
- finishAnimation();
+ // Always skip the final resize when the animation is canceled
+ mSkipFinalResize = true;
+ mMoveToFullscreen = false;
}
- public void prepareCancel(boolean skipAnimationEnd, boolean skipFinalResize) {
- if (DEBUG) Slog.d(TAG, "prepareCancel: skipAnimationEnd=" + skipAnimationEnd
- + " skipFinalResize=" + skipFinalResize);
- mSkipAnimationEnd = skipAnimationEnd;
- mSkipFinalResize = skipFinalResize;
+ private void cancelAndCallAnimationEnd() {
+ if (DEBUG) Slog.d(TAG, "cancelAndCallAnimationEnd: mTarget=" + mTarget);
+ mSkipAnimationEnd = false;
+ super.cancel();
}
@Override
public void cancel() {
if (DEBUG) Slog.d(TAG, "cancel: mTarget=" + mTarget);
+ mSkipAnimationEnd = true;
super.cancel();
}
- /** Returns true if the animation target is the same as the input bounds. */
+ /**
+ * @return true if the animation target is the same as the input bounds.
+ */
boolean isAnimatingTo(Rect bounds) {
return mTo.equals(bounds);
}
- private boolean animatingToLargerSize() {
- if (mFrom.width() * mFrom.height() > mTo.width() * mTo.height()) {
- return false;
- }
- return true;
- }
-
- private void finishAnimation() {
- if (DEBUG) Slog.d(TAG, "finishAnimation: mTarget=" + mTarget
- + " callers" + Debug.getCallers(2));
- if (!mSkipAnimationEnd) {
- mTarget.onAnimationEnd();
- }
- removeListener(this);
- removeUpdateListener(this);
- mRunningAnimations.remove(mTarget);
+ /**
+ * @return true if we are animating to a larger surface size
+ */
+ @VisibleForTesting
+ boolean animatingToLargerSize() {
+ // TODO: Fix this check for aspect ratio changes
+ return (mFrom.width() * mFrom.height() <= mTo.width() * mTo.height());
}
@Override
@@ -291,63 +309,23 @@
}
}
- public interface AnimateBoundsUser {
- /**
- * Sets the size of the target (without any intermediate steps, like scheduling animation)
- * but freezes the bounds of any tasks in the target at taskBounds,
- * to allow for more flexibility during resizing. Only works for the pinned stack at the
- * moment.
- *
- * @return Whether the target should continue to be animated and this call was successful.
- * If false, the animation will be cancelled because the user has determined that the
- * animation is now invalid and not required. In such a case, the cancel will trigger the
- * animation end callback as well, but will not send any further size changes.
- */
- boolean setPinnedStackSize(Rect bounds, Rect taskBounds);
-
- /**
- * Callback for the target to inform it that the animation has started, so it can do some
- * necessary preparation.
- */
- void onAnimationStart(boolean toFullscreen);
-
- /**
- * Callback for the target to inform it that the animation is going to a new fullscreen
- * state and should update the picture-in-picture mode accordingly.
- *
- * @param targetStackBounds the target stack bounds we are animating to, can be null if
- * we are animating to fullscreen
- */
- void updatePictureInPictureMode(Rect targetStackBounds);
-
- /**
- * Callback for the target to inform it that the animation has ended, so it can do some
- * necessary cleanup.
- */
- void onAnimationEnd();
-
- /**
- * Callback for the target to inform it to reparent to the fullscreen stack.
- */
- void moveToFullscreen();
- }
-
- public void animateBounds(final AnimateBoundsUser target, Rect from, Rect to,
- int animationDuration, boolean moveToFullscreen) {
- animateBoundsImpl(target, from, to, animationDuration, moveToFullscreen);
+ public void animateBounds(final BoundsAnimationTarget target, Rect from, Rect to,
+ int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
+ boolean moveToFullscreen) {
+ animateBoundsImpl(target, from, to, animationDuration, schedulePipModeChangedState,
+ moveToFullscreen);
}
@VisibleForTesting
- BoundsAnimator animateBoundsImpl(final AnimateBoundsUser target, Rect from, Rect to,
- int animationDuration, boolean moveToFullscreen) {
+ BoundsAnimator animateBoundsImpl(final BoundsAnimationTarget target, Rect from, Rect to,
+ int animationDuration, @SchedulePipModeChangedState int schedulePipModeChangedState,
+ boolean moveToFullscreen) {
final BoundsAnimator existing = mRunningAnimations.get(target);
final boolean replacing = existing != null;
- final boolean animatingToNewFullscreenState = (existing == null) ||
- (existing.mMoveToFullScreen != moveToFullscreen);
if (DEBUG) Slog.d(TAG, "animateBounds: target=" + target + " from=" + from + " to=" + to
- + " moveToFullscreen=" + moveToFullscreen + " replacing=" + replacing
- + " animatingToNewFullscreenState=" + animatingToNewFullscreenState);
+ + " schedulePipModeChangedState=" + schedulePipModeChangedState
+ + " replacing=" + replacing);
if (replacing) {
if (existing.isAnimatingTo(to)) {
@@ -355,15 +333,36 @@
// one we are trying to start.
if (DEBUG) Slog.d(TAG, "animateBounds: same destination as existing=" + existing
+ " ignoring...");
+
return existing;
}
- // Since we are replacing, we skip both animation start and end callbacks, and don't
- // animate to the final bounds when cancelling
- existing.prepareCancel(true /* skipAnimationEnd */, true /* skipFinalResize */);
+
+ // Update the PiP callback states if we are replacing the animation
+ if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
+ if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
+ if (DEBUG) Slog.d(TAG, "animateBounds: still animating to fullscreen, keep"
+ + " existing deferred state");
+ } else {
+ if (DEBUG) Slog.d(TAG, "animateBounds: fullscreen animation canceled, callback"
+ + " on start already processed, schedule deferred update on end");
+ schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
+ }
+ } else if (existing.mSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END) {
+ if (schedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_START) {
+ if (DEBUG) Slog.d(TAG, "animateBounds: non-fullscreen animation canceled,"
+ + " callback on start will be processed");
+ } else {
+ if (DEBUG) Slog.d(TAG, "animateBounds: still animating from fullscreen, keep"
+ + " existing deferred state");
+ schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
+ }
+ }
+
+ // Since we are replacing, we skip both animation start and end callbacks
existing.cancel();
}
- final BoundsAnimator animator = new BoundsAnimator(target, from, to, moveToFullscreen,
- replacing, animatingToNewFullscreenState);
+ final BoundsAnimator animator = new BoundsAnimator(target, from, to,
+ schedulePipModeChangedState, moveToFullscreen, replacing);
mRunningAnimations.put(target, animator);
animator.setFloatValues(0f, 1f);
animator.setDuration((animationDuration != -1 ? animationDuration
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
new file mode 100644
index 0000000..8b1bf7b
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java
@@ -0,0 +1,62 @@
+/*
+ * 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.server.wm;
+
+import android.graphics.Rect;
+
+/**
+ * The target for a BoundsAnimation.
+ * @see BoundsAnimationController
+ */
+interface BoundsAnimationTarget {
+
+ /**
+ * Callback for the target to inform it that the animation has started, so it can do some
+ * necessary preparation.
+ *
+ * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
+ * callbacks
+ */
+ void onAnimationStart(boolean schedulePipModeChangedCallback);
+
+ /**
+ * Sets the size of the target (without any intermediate steps, like scheduling animation)
+ * but freezes the bounds of any tasks in the target at taskBounds, to allow for more
+ * flexibility during resizing. Only works for the pinned stack at the moment. This will
+ * only be called between onAnimationStart() and onAnimationEnd().
+ *
+ * @return Whether the target should continue to be animated and this call was successful.
+ * If false, the animation will be cancelled because the user has determined that the
+ * animation is now invalid and not required. In such a case, the cancel will trigger the
+ * animation end callback as well, but will not send any further size changes.
+ */
+ boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds);
+
+ /**
+ * Callback for the target to inform it that the animation has ended, so it can do some
+ * necessary cleanup.
+ *
+ * @param schedulePipModeChangedCallback whether or not to schedule the PiP mode changed
+ * callbacks
+ * @param finalStackSize the final stack bounds to set on the target (can be to indicate that
+ * the animation was cancelled and the target does not need to update to the final stack bounds)
+ * @param moveToFullscreen whether or the target should reparent itself to the fullscreen stack
+ * when the animation completes
+ */
+ void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
+ boolean moveToFullscreen);
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 058fdae..1823610 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -125,6 +125,7 @@
import android.view.SurfaceControl;
import android.view.WindowManagerPolicy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.view.IInputMethodClient;
@@ -1396,6 +1397,22 @@
return null;
}
+ @VisibleForTesting
+ int getStackCount() {
+ return mTaskStackContainers.size();
+ }
+
+ @VisibleForTesting
+ int getStaskPosById(int stackId) {
+ for (int i = mTaskStackContainers.size() - 1; i >= 0; --i) {
+ final TaskStack stack = mTaskStackContainers.get(i);
+ if (stack.mStackId == stackId) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
@Override
void onConfigurationChanged(Configuration newParentConfig) {
super.onConfigurationChanged(newParentConfig);
@@ -3274,8 +3291,9 @@
: requestedPosition >= topChildPosition;
int targetPosition = requestedPosition;
- if (toTop && isStackVisible(PINNED_STACK_ID) && stack.mStackId != PINNED_STACK_ID) {
- // The pinned stack is always the top most stack (always-on-top) when it is visible.
+ if (toTop && stack.mStackId != PINNED_STACK_ID
+ && getStackById(PINNED_STACK_ID) != null) {
+ // The pinned stack is always the top most stack (always-on-top) when it is present.
TaskStack topStack = mChildren.get(topChildPosition);
if (topStack.mStackId != PINNED_STACK_ID) {
throw new IllegalStateException("Pinned stack isn't top stack??? " + mChildren);
diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java
index 1684878..82416ec 100644
--- a/services/core/java/com/android/server/wm/PinnedStackController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackController.java
@@ -26,7 +26,6 @@
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
import android.graphics.Point;
-import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Handler;
import android.os.IBinder;
@@ -123,6 +122,13 @@
mSnapAlgorithm.setMinimized(isMinimized);
});
}
+
+ @Override
+ public int getDisplayRotation() {
+ synchronized (mService.mWindowMap) {
+ return mDisplayInfo.rotation;
+ }
+ }
}
/**
@@ -221,22 +227,26 @@
* @return the size of the PIP based on the given {@param aspectRatio}.
*/
Size getSize(float aspectRatio) {
- return mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mMinSize,
- mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+ synchronized (mService.mWindowMap) {
+ return mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, mMinSize,
+ mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+ }
}
/**
* @return the default bounds to show the PIP when there is no active PIP.
*/
Rect getDefaultBounds() {
- final Rect insetBounds = new Rect();
- getInsetBounds(insetBounds);
+ synchronized (mService.mWindowMap) {
+ final Rect insetBounds = new Rect();
+ getInsetBounds(insetBounds);
- final Rect defaultBounds = new Rect();
- final Size size = getSize(mDefaultAspectRatio);
- Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
- 0, mIsImeShowing ? mImeHeight : 0, defaultBounds);
- return defaultBounds;
+ final Rect defaultBounds = new Rect();
+ final Size size = getSize(mDefaultAspectRatio);
+ Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
+ 0, mIsImeShowing ? mImeHeight : 0, defaultBounds);
+ return defaultBounds;
+ }
}
/**
@@ -254,42 +264,44 @@
* new orientation of the device if necessary.
*/
boolean onTaskStackBoundsChanged(Rect targetBounds, Rect outBounds) {
- final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
- if (mDisplayInfo.equals(displayInfo)) {
- // We are already in the right orientation, ignore
- outBounds.setEmpty();
- return false;
- } else if (targetBounds.isEmpty()) {
- // The stack is null, we are just initializing the stack, so just store the display info
- // and ignore
+ synchronized (mService.mWindowMap) {
+ final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo();
+ if (mDisplayInfo.equals(displayInfo)) {
+ // We are already in the right orientation, ignore
+ outBounds.setEmpty();
+ return false;
+ } else if (targetBounds.isEmpty()) {
+ // The stack is null, we are just initializing the stack, so just store the display
+ // info and ignore
+ mDisplayInfo.copyFrom(displayInfo);
+ outBounds.setEmpty();
+ return false;
+ }
+
+ mTmpRect.set(targetBounds);
+ final Rect postChangeStackBounds = mTmpRect;
+
+ // Calculate the snap fraction of the current stack along the old movement bounds
+ final Rect preChangeMovementBounds = getMovementBounds(postChangeStackBounds);
+ final float snapFraction = mSnapAlgorithm.getSnapFraction(postChangeStackBounds,
+ preChangeMovementBounds);
mDisplayInfo.copyFrom(displayInfo);
- outBounds.setEmpty();
- return false;
+
+ // Calculate the stack bounds in the new orientation to the same same fraction along the
+ // rotated movement bounds.
+ final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds,
+ false /* adjustForIme */);
+ mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
+ snapFraction);
+ if (mIsMinimized) {
+ applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds);
+ }
+
+ notifyMovementBoundsChanged(false /* fromImeAdjustment */);
+
+ outBounds.set(postChangeStackBounds);
+ return true;
}
-
- mTmpRect.set(targetBounds);
- final Rect postChangeStackBounds = mTmpRect;
-
- // Calculate the snap fraction of the current stack along the old movement bounds
- final Rect preChangeMovementBounds = getMovementBounds(postChangeStackBounds);
- final float snapFraction = mSnapAlgorithm.getSnapFraction(postChangeStackBounds,
- preChangeMovementBounds);
- mDisplayInfo.copyFrom(displayInfo);
-
- // Calculate the stack bounds in the new orientation to the same same fraction along the
- // rotated movement bounds.
- final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds,
- false /* adjustForIme */);
- mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
- snapFraction);
- if (mIsMinimized) {
- applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds);
- }
-
- notifyMovementBoundsChanged(false /* fromImeAdjustment */);
-
- outBounds.set(postChangeStackBounds);
- return true;
}
/**
@@ -378,25 +390,27 @@
* Notifies listeners that the PIP movement bounds have changed.
*/
private void notifyMovementBoundsChanged(boolean fromImeAdjustement) {
- if (mPinnedStackListener != null) {
- try {
- final Rect insetBounds = new Rect();
- getInsetBounds(insetBounds);
- final Rect normalBounds = getDefaultBounds();
- if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
- transformBoundsToAspectRatio(normalBounds, mAspectRatio);
+ synchronized (mService.mWindowMap) {
+ if (mPinnedStackListener != null) {
+ try {
+ final Rect insetBounds = new Rect();
+ getInsetBounds(insetBounds);
+ final Rect normalBounds = getDefaultBounds();
+ if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
+ transformBoundsToAspectRatio(normalBounds, mAspectRatio);
+ }
+ final Rect animatingBounds = mTmpAnimatingBoundsRect;
+ final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID);
+ if (pinnedStack != null) {
+ pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
+ } else {
+ animatingBounds.set(normalBounds);
+ }
+ mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds,
+ animatingBounds, fromImeAdjustement, mDisplayInfo.rotation);
+ } catch (RemoteException e) {
+ Slog.e(TAG_WM, "Error delivering actions changed event.", e);
}
- final Rect animatingBounds = mTmpAnimatingBoundsRect;
- final TaskStack pinnedStack = mDisplayContent.getStackById(PINNED_STACK_ID);
- if (pinnedStack != null) {
- pinnedStack.getAnimationOrCurrentBounds(animatingBounds);
- } else {
- animatingBounds.set(normalBounds);
- }
- mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds,
- animatingBounds, fromImeAdjustement);
- } catch (RemoteException e) {
- Slog.e(TAG_WM, "Error delivering actions changed event.", e);
}
}
}
@@ -405,11 +419,13 @@
* @return the bounds on the screen that the PIP can be visible in.
*/
private void getInsetBounds(Rect outRect) {
- mService.mPolicy.getStableInsetsLw(mDisplayInfo.rotation, mDisplayInfo.logicalWidth,
- mDisplayInfo.logicalHeight, mTmpInsets);
- outRect.set(mTmpInsets.left + mScreenEdgeInsets.x, mTmpInsets.top + mScreenEdgeInsets.y,
- mDisplayInfo.logicalWidth - mTmpInsets.right - mScreenEdgeInsets.x,
- mDisplayInfo.logicalHeight - mTmpInsets.bottom - mScreenEdgeInsets.y);
+ synchronized (mService.mWindowMap) {
+ mService.mPolicy.getStableInsetsLw(mDisplayInfo.rotation, mDisplayInfo.logicalWidth,
+ mDisplayInfo.logicalHeight, mTmpInsets);
+ outRect.set(mTmpInsets.left + mScreenEdgeInsets.x, mTmpInsets.top + mScreenEdgeInsets.y,
+ mDisplayInfo.logicalWidth - mTmpInsets.right - mScreenEdgeInsets.x,
+ mDisplayInfo.logicalHeight - mTmpInsets.bottom - mScreenEdgeInsets.y);
+ }
}
/**
@@ -417,7 +433,9 @@
* controller.
*/
private Rect getMovementBounds(Rect stackBounds) {
- return getMovementBounds(stackBounds, true /* adjustForIme */);
+ synchronized (mService.mWindowMap) {
+ return getMovementBounds(stackBounds, true /* adjustForIme */);
+ }
}
/**
@@ -425,23 +443,27 @@
* controller.
*/
private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme) {
- final Rect movementBounds = new Rect();
- getInsetBounds(movementBounds);
+ synchronized (mService.mWindowMap) {
+ final Rect movementBounds = new Rect();
+ getInsetBounds(movementBounds);
- // Apply the movement bounds adjustments based on the current state
- mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
- (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
- return movementBounds;
+ // Apply the movement bounds adjustments based on the current state
+ mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
+ (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
+ return movementBounds;
+ }
}
/**
* Applies the minimized offsets to the given stack bounds.
*/
private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) {
- mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
- mService.getStableInsetsLocked(mDisplayContent.getDisplayId(), mStableInsets);
- mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize,
- mStableInsets);
+ synchronized (mService.mWindowMap) {
+ mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+ mService.getStableInsetsLocked(mDisplayContent.getDisplayId(), mStableInsets);
+ mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize,
+ mStableInsets);
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/PinnedStackWindowController.java b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
index 135832e..5b9c16c 100644
--- a/services/core/java/com/android/server/wm/PinnedStackWindowController.java
+++ b/services/core/java/com/android/server/wm/PinnedStackWindowController.java
@@ -17,6 +17,10 @@
package com.android.server.wm;
import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
+import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
+import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
+import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
import android.app.RemoteAction;
import android.graphics.Rect;
@@ -40,36 +44,53 @@
/**
* Animates the pinned stack.
*/
- public void animateResizePinnedStack(Rect sourceBounds, Rect destBounds,
- int animationDuration) {
+ public void animateResizePinnedStack(Rect toBounds, Rect sourceHintBounds,
+ int animationDuration, boolean schedulePipModeChangedOnAnimationEnd) {
synchronized (mWindowMap) {
if (mContainer == null) {
throw new IllegalArgumentException("Pinned stack container not found :(");
}
- // Get non-null fullscreen bounds if the bounds are null
- final boolean moveToFullscreen = destBounds == null;
- destBounds = getPinnedStackAnimationBounds(destBounds);
+ // Get the from-bounds
+ final Rect fromBounds = new Rect();
+ mContainer.getBounds(fromBounds);
- // If the bounds are truly null, then there was no fullscreen stack at this time, so
- // animate this to the full display bounds
- final Rect toBounds;
- if (destBounds == null) {
- toBounds = new Rect();
- mContainer.getDisplayContent().getLogicalDisplayRect(toBounds);
- } else {
- toBounds = destBounds;
+ // Get non-null fullscreen to-bounds for animating if the bounds are null
+ @SchedulePipModeChangedState int schedulePipModeChangedState =
+ NO_PIP_MODE_CHANGED_CALLBACKS;
+ final boolean toFullscreen = toBounds == null;
+ if (toFullscreen) {
+ if (schedulePipModeChangedOnAnimationEnd) {
+ throw new IllegalArgumentException("Should not defer scheduling PiP mode"
+ + " change on animation to fullscreen.");
+ }
+ schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_START;
+
+ mService.getStackBounds(FULLSCREEN_WORKSPACE_STACK_ID, mTmpBoundsRect);
+ if (!mTmpBoundsRect.isEmpty()) {
+ // If there is a fullscreen bounds, use that
+ toBounds = new Rect(mTmpBoundsRect);
+ } else {
+ // Otherwise, use the display bounds
+ toBounds = new Rect();
+ mContainer.getDisplayContent().getLogicalDisplayRect(toBounds);
+ }
+ } else if (schedulePipModeChangedOnAnimationEnd) {
+ schedulePipModeChangedState = SCHEDULE_PIP_MODE_CHANGED_ON_END;
}
- final Rect originalBounds = new Rect();
- mContainer.getBounds(originalBounds);
- mContainer.setAnimationFinalBounds(sourceBounds, toBounds);
+ mContainer.setAnimationFinalBounds(sourceHintBounds, toBounds, toFullscreen);
+
+ final Rect finalToBounds = toBounds;
+ final @SchedulePipModeChangedState int finalSchedulePipModeChangedState =
+ schedulePipModeChangedState;
UiThread.getHandler().post(() -> {
if (mContainer == null) {
return;
}
- mService.mBoundsAnimationController.animateBounds(mContainer, originalBounds,
- toBounds, animationDuration, moveToFullscreen);
+ mService.mBoundsAnimationController.animateBounds(mContainer, fromBounds,
+ finalToBounds, animationDuration, finalSchedulePipModeChangedState,
+ toFullscreen);
});
}
}
@@ -93,7 +114,8 @@
if (Float.compare(aspectRatio, pinnedStackController.getAspectRatio()) != 0) {
if (!toBounds.equals(targetBounds)) {
- animateResizePinnedStack(null /* sourceBounds */, toBounds, -1 /* duration */);
+ animateResizePinnedStack(toBounds, null /* sourceHintBounds */,
+ -1 /* duration */, false /* schedulePipModeChangedOnAnimationEnd */);
}
pinnedStackController.setAspectRatio(
pinnedStackController.isValidPictureInPictureAspectRatio(aspectRatio)
@@ -116,26 +138,31 @@
}
/**
- * @return whether the bounds are currently animating to fullscreen.
+ * @return whether the multi-window mode change should be deferred as a part of a transition
+ * from fullscreen to non-fullscreen bounds.
*/
- public boolean isAnimatingBoundsToFullscreen() {
- return mContainer.isAnimatingBoundsToFullscreen();
- }
-
- public boolean pinnedStackResizeAllowed() {
- return mContainer.pinnedStackResizeAllowed();
+ public boolean deferScheduleMultiWindowModeChanged() {
+ synchronized(mWindowMap) {
+ return mContainer.deferScheduleMultiWindowModeChanged();
+ }
}
/**
- * Checks the {@param bounds} and retirms non-null fullscreen bounds for the pinned stack
- * animation if necessary.
+ * @return whether the bounds are currently animating to fullscreen.
*/
- private Rect getPinnedStackAnimationBounds(Rect bounds) {
- mService.getStackBounds(FULLSCREEN_WORKSPACE_STACK_ID, mTmpBoundsRect);
- if (bounds == null && !mTmpBoundsRect.isEmpty()) {
- bounds = new Rect(mTmpBoundsRect);
+ public boolean isAnimatingBoundsToFullscreen() {
+ synchronized (mWindowMap) {
+ return mContainer.isAnimatingBoundsToFullscreen();
}
- return bounds;
+ }
+
+ /**
+ * @return whether the stack can be resized from the bounds animation.
+ */
+ public boolean pinnedStackResizeDisallowed() {
+ synchronized (mWindowMap) {
+ return mContainer.pinnedStackResizeDisallowed();
+ }
}
/**
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index d7c41d3..d141f7c 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -56,7 +56,7 @@
import java.io.PrintWriter;
public class TaskStack extends WindowContainer<Task> implements DimLayer.DimLayerUser,
- BoundsAnimationController.AnimateBoundsUser {
+ BoundsAnimationTarget {
/** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
* restrict IME adjustment so that a min portion of top stack remains visible.*/
private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
@@ -132,7 +132,7 @@
private boolean mBoundsAnimatingToFullscreen = false;
private boolean mCancelCurrentBoundsAnimation = false;
private Rect mBoundsAnimationTarget = new Rect();
- private Rect mBoundsAnimationSourceBounds = new Rect();
+ private Rect mBoundsAnimationSourceHintBounds = new Rect();
// Temporary storage for the new bounds that should be used after the configuration change.
// Will be cleared once the client retrieves the new bounds via getBoundsForNewConfiguration().
@@ -322,18 +322,19 @@
* Sets the bounds animation target bounds ahead of an animation. This can't currently be done
* in onAnimationStart() since that is started on the UiThread.
*/
- void setAnimationFinalBounds(Rect sourceBounds, Rect destBounds) {
+ void setAnimationFinalBounds(Rect sourceHintBounds, Rect destBounds, boolean toFullscreen) {
mBoundsAnimatingRequested = true;
- if (sourceBounds != null) {
- mBoundsAnimationSourceBounds.set(sourceBounds);
- } else {
- mBoundsAnimationSourceBounds.setEmpty();
- }
+ mBoundsAnimatingToFullscreen = toFullscreen;
if (destBounds != null) {
mBoundsAnimationTarget.set(destBounds);
} else {
mBoundsAnimationTarget.setEmpty();
}
+ if (sourceHintBounds != null) {
+ mBoundsAnimationSourceHintBounds.set(sourceHintBounds);
+ } else {
+ mBoundsAnimationSourceHintBounds.setEmpty();
+ }
}
/**
@@ -346,8 +347,8 @@
/**
* @return the final source bounds for the bounds animation.
*/
- void getFinalAnimationSourceBounds(Rect outBounds) {
- outBounds.set(mBoundsAnimationSourceBounds);
+ void getFinalAnimationSourceHintBounds(Rect outBounds) {
+ outBounds.set(mBoundsAnimationSourceHintBounds);
}
/**
@@ -413,7 +414,7 @@
// orientation, clear the animation target bounds since they are obsolete, and
// cancel any currently running animations
mBoundsAnimationTarget.setEmpty();
- mBoundsAnimationSourceBounds.setEmpty();
+ mBoundsAnimationSourceHintBounds.setEmpty();
mCancelCurrentBoundsAnimation = true;
return true;
}
@@ -1458,13 +1459,16 @@
}
}
- public boolean setPinnedStackSize(Rect bounds, Rect tempTaskBounds) {
- if (mCancelCurrentBoundsAnimation) {
- return false;
+ public boolean setPinnedStackSize(Rect stackBounds, Rect tempTaskBounds) {
+ // Hold the lock since this is called from the BoundsAnimator running on the UiThread
+ synchronized (mService.mWindowMap) {
+ if (mCancelCurrentBoundsAnimation) {
+ return false;
+ }
}
try {
- mService.mActivityManager.resizePinnedStack(bounds, tempTaskBounds);
+ mService.mActivityManager.resizePinnedStack(stackBounds, tempTaskBounds);
} catch (RemoteException e) {
// I don't believe you.
}
@@ -1472,11 +1476,11 @@
}
@Override // AnimatesBounds
- public void onAnimationStart(boolean toFullscreen) {
+ public void onAnimationStart(boolean schedulePipModeChangedCallback) {
+ // Hold the lock since this is called from the BoundsAnimator running on the UiThread
synchronized (mService.mWindowMap) {
mBoundsAnimatingRequested = false;
mBoundsAnimating = true;
- mBoundsAnimatingToFullscreen = toFullscreen;
mCancelCurrentBoundsAnimation = false;
}
@@ -1486,41 +1490,63 @@
} catch (RemoteException e) {
// I don't believe you...
}
+
+ final PinnedStackWindowController controller =
+ (PinnedStackWindowController) getController();
+ if (schedulePipModeChangedCallback && controller != null) {
+ // We need to schedule the PiP mode change after the animation down, so use the
+ // final bounds
+ controller.updatePictureInPictureModeForPinnedStackAnimation(null);
+ }
}
}
@Override // AnimatesBounds
- public void updatePictureInPictureMode(Rect targetStackBounds) {
- final PinnedStackWindowController controller =
- (PinnedStackWindowController) getController();
- if (controller != null) {
- controller.updatePictureInPictureModeForPinnedStackAnimation(targetStackBounds);
- }
- }
-
- @Override // AnimatesBounds
- public void onAnimationEnd() {
+ public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackSize,
+ boolean moveToFullscreen) {
+ // Hold the lock since this is called from the BoundsAnimator running on the UiThread
synchronized (mService.mWindowMap) {
mBoundsAnimating = false;
mService.requestTraversal();
}
if (mStackId == PINNED_STACK_ID) {
+ final PinnedStackWindowController controller =
+ (PinnedStackWindowController) getController();
+ if (schedulePipModeChangedCallback && controller != null) {
+ // We need to schedule the PiP mode change after the animation down, so use the
+ // final bounds
+ controller.updatePictureInPictureModeForPinnedStackAnimation(
+ mBoundsAnimationTarget);
+ }
+
+ // Update to the final bounds if requested. This is done here instead of in the bounds
+ // animator to allow us to coordinate this after we notify the PiP mode changed
+ if (finalStackSize != null) {
+ setPinnedStackSize(finalStackSize, null);
+ }
+
try {
mService.mActivityManager.notifyPinnedStackAnimationEnded();
+ if (moveToFullscreen) {
+ mService.mActivityManager.moveTasksToFullscreenStack(mStackId,
+ true /* onTop */);
+ }
} catch (RemoteException e) {
// I don't believe you...
}
}
}
- @Override
- public void moveToFullscreen() {
- try {
- mService.mActivityManager.moveTasksToFullscreenStack(mStackId, true);
- } catch (RemoteException e) {
- e.printStackTrace();
+ /**
+ * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen
+ * bounds and we have a deferred PiP mode changed callback set with the animation.
+ */
+ public boolean deferScheduleMultiWindowModeChanged() {
+ if (mStackId == PINNED_STACK_ID) {
+ return (mBoundsAnimatingRequested || mBoundsAnimating);
}
+ return false;
}
public boolean hasMovementAnimations() {
@@ -1539,7 +1565,7 @@
return mBoundsAnimating && mBoundsAnimatingToFullscreen;
}
- public boolean pinnedStackResizeAllowed() {
+ public boolean pinnedStackResizeDisallowed() {
if (mBoundsAnimating && mCancelCurrentBoundsAnimation) {
return true;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0049585..252b4d4 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7267,13 +7267,16 @@
@Override
public void updateInputMethodWindowStatus(@NonNull IBinder imeToken,
- boolean imeWindowVisible, @Nullable IBinder targetWindowToken) {
+ boolean imeWindowVisible, boolean dismissImeOnBackKeyPressed,
+ @Nullable IBinder targetWindowToken) {
// TODO (b/34628091): Use this method to address the window animation issue.
if (DEBUG_INPUT_METHOD) {
Slog.w(TAG_WM, "updateInputMethodWindowStatus: imeToken=" + imeToken
+ + " dismissImeOnBackKeyPressed=" + dismissImeOnBackKeyPressed
+ " imeWindowVisible=" + imeWindowVisible
+ " targetWindowToken=" + targetWindowToken);
}
+ mPolicy.setDismissImeOnBackKeyPressed(dismissImeOnBackKeyPressed);
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0b96f3f..9555c8d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4365,9 +4365,12 @@
// When we change the Surface size, in scenarios which may require changing
// the surface position in sync with the resize, we use a preserved surface
// so we can freeze it while waiting for the client to report draw on the newly
- // sized surface.
+ // sized surface. Don't preserve surfaces if the insets change while animating the pinned
+ // stack since it can lead to issues if a new surface is created while calculating the
+ // scale for the animation using the source hint rect
+ // (see WindowStateAnimator#setSurfaceBoundariesLocked()).
if (isDragResizeChanged() || isResizedWhileNotDragResizing()
- || surfaceInsetsChanging()) {
+ || (surfaceInsetsChanging() && !inPinnedWorkspace())) {
mLastSurfaceInsets.set(mAttrs.surfaceInsets);
setDragResizing();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index fa35336..a2889b1 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1360,7 +1360,7 @@
int posX = mTmpSize.left;
int posY = mTmpSize.top;
task.mStack.getDimBounds(mTmpStackBounds);
- task.mStack.getFinalAnimationSourceBounds(mTmpSourceBounds);
+ task.mStack.getFinalAnimationSourceHintBounds(mTmpSourceBounds);
if (!mTmpSourceBounds.isEmpty()) {
// Get the final target stack bounds, if we are not animating, this is just the
// current stack bounds
diff --git a/services/core/jni/com_android_server_location_ContextHubService.cpp b/services/core/jni/com_android_server_location_ContextHubService.cpp
index d834e25..20466eb 100644
--- a/services/core/jni/com_android_server_location_ContextHubService.cpp
+++ b/services/core/jni/com_android_server_location_ContextHubService.cpp
@@ -1129,6 +1129,8 @@
if (appInstanceHandle == OS_APP_ID) {
if (msgType == CONTEXT_HUB_LOAD_APP) {
result = sendLoadNanoAppRequest(hubId, data, dataBufferLength);
+ } else if (msgType == CONTEXT_HUB_QUERY_APPS) {
+ result = db.hubInfo.contextHub->queryApps(hubId);
} else {
ALOGD("Dropping OS addresses message of type - %" PRIu32, msgType);
result = Result::BAD_PARAMS;
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 86662b9..74ecd11 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -68,12 +68,15 @@
using android::OK;
using android::sp;
+using android::wp;
using android::status_t;
using android::String16;
using android::hardware::Return;
using android::hardware::Void;
using android::hardware::hidl_vec;
+using android::hardware::hidl_death_recipient;
+using android::hidl::base::V1_0::IBase;
using android::hardware::gnss::V1_0::IAGnss;
using android::hardware::gnss::V1_0::IAGnssCallback;
@@ -97,7 +100,18 @@
using android::hardware::gnss::V1_0::IGnssXtra;
using android::hardware::gnss::V1_0::IGnssXtraCallback;
+struct GnssDeathRecipient : virtual public hidl_death_recipient
+{
+ // hidl_death_recipient interface
+ virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) override {
+ // TODO(gomo): implement a better death recovery mechanism without
+ // crashing system server process as described in go//treble-gnss-death
+ LOG_ALWAYS_FATAL("Abort due to IGNSS hidl service failure,"
+ " restarting system server");
+ }
+};
+sp<GnssDeathRecipient> gnssHalDeathRecipient = nullptr;
sp<IGnss> gnssHal = nullptr;
sp<IGnssXtra> gnssXtraIface = nullptr;
sp<IAGnssRil> agnssRilIface = nullptr;
@@ -1038,6 +1052,18 @@
// TODO(b/31632518)
gnssHal = IGnss::getService();
if (gnssHal != nullptr) {
+ gnssHalDeathRecipient = new GnssDeathRecipient();
+ hardware::Return<bool> linked = gnssHal->linkToDeath(
+ gnssHalDeathRecipient, /*cookie*/ 0);
+ if (!linked.isOk()) {
+ ALOGE("Transaction error in linking to GnssHAL death: %s",
+ linked.description().c_str());
+ } else if (!linked) {
+ ALOGW("Unable to link to GnssHal death notifications");
+ } else {
+ ALOGD("Link to death notification successful");
+ }
+
auto gnssXtra = gnssHal->getExtensionXtra();
if (!gnssXtra.isOk()) {
ALOGD("Unable to get a handle to Xtra");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bfa1b99..e82ba9c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -742,8 +742,8 @@
boolean forceEphemeralUsers = false; // Can only be set by a device owner.
boolean isNetworkLoggingEnabled = false; // Can only be set by a device owner.
- // one notification after enabling + 3 more after reboots
- static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 4;
+ // one notification after enabling + one more after reboots
+ static final int DEF_MAXIMUM_NETWORK_LOGGING_NOTIFICATIONS_SHOWN = 2;
int numNetworkLoggingNotifications = 0;
long lastNetworkLoggingNotificationTimeMs = 0; // Time in milliseconds since epoch
diff --git a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java
index d0dfc6c..122a954 100644
--- a/services/print/java/com/android/server/print/CompanionDeviceManagerService.java
+++ b/services/print/java/com/android/server/print/CompanionDeviceManagerService.java
@@ -17,12 +17,15 @@
package com.android.server.print;
+import static com.android.internal.util.CollectionUtils.size;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.Preconditions.checkState;
import android.Manifest;
import android.annotation.CheckResult;
import android.annotation.Nullable;
+import android.app.PendingIntent;
import android.companion.AssociationRequest;
import android.companion.CompanionDeviceManager;
import android.companion.ICompanionDeviceDiscoveryService;
@@ -46,13 +49,18 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.SettingsStringUtil.ComponentNameSet;
+import android.text.BidiFormatter;
import android.util.AtomicFile;
import android.util.ExceptionUtils;
+import android.util.Log;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.app.IAppOpsService;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.notification.NotificationAccessConfirmationActivityContract;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.server.FgThread;
@@ -79,6 +87,7 @@
//TODO schedule stopScan on activity destroy(except if configuration change)
//TODO on associate called again after configuration change -> replace old callback with new
//TODO avoid leaking calling activity in IFindDeviceCallback (see PrintManager#print for example)
+//TODO check user-feature present in manifest on API calls
/** @hide */
public class CompanionDeviceManagerService extends SystemService implements Binder.DeathRecipient {
@@ -140,10 +149,10 @@
@Override
public void binderDied() {
- Handler.getMain().post(this::handleBinderDied);
+ Handler.getMain().post(this::cleanup);
}
- private void handleBinderDied() {
+ private void cleanup() {
mServiceConnection = unbind(mServiceConnection);
mFindDeviceCallback = unlinkToDeath(mFindDeviceCallback, this, 0);
}
@@ -207,7 +216,6 @@
}
}
-
@Override
public List<String> getAssociations(String callingPackage, int userId)
throws RemoteException {
@@ -217,12 +225,13 @@
a -> a.deviceAddress);
}
+ //TODO also revoke notification access
@Override
public void disassociate(String deviceMacAddress, String callingPackage)
throws RemoteException {
checkNotNull(deviceMacAddress);
checkCallerIsSystemOr(callingPackage);
- updateAssociations(associations -> ArrayUtils.remove(associations,
+ updateAssociations(associations -> CollectionUtils.remove(associations,
new Association(getCallingUserId(), deviceMacAddress, callingPackage)));
}
@@ -237,11 +246,49 @@
checkArgument(getCallingUserId() == userId,
"Must be called by either same user or system");
-
mAppOpsManager.checkPackage(Binder.getCallingUid(), pkg);
}
+
+ @Override
+ public PendingIntent requestNotificationAccess(ComponentName component)
+ throws RemoteException {
+ String callingPackage = component.getPackageName();
+ checkCanCallNotificationApi(callingPackage);
+ int userId = getCallingUserId();
+ String packageTitle = BidiFormatter.getInstance().unicodeWrap(
+ getPackageInfo(callingPackage, userId)
+ .applicationInfo
+ .loadSafeLabel(getContext().getPackageManager())
+ .toString());
+ long identity = Binder.clearCallingIdentity();
+ try {
+ return PendingIntent.getActivity(getContext(),
+ 0 /* request code */,
+ NotificationAccessConfirmationActivityContract.launcherIntent(
+ userId, component, packageTitle),
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_ONE_SHOT
+ | PendingIntent.FLAG_CANCEL_CURRENT);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public boolean hasNotificationAccess(ComponentName component) throws RemoteException {
+ checkCanCallNotificationApi(component.getPackageName());
+ String setting = Settings.Secure.getString(getContext().getContentResolver(),
+ Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
+ return new ComponentNameSet(setting).contains(component);
+ }
+
+ private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
+ checkCallerIsSystemOr(callingPackage);
+ checkState(!ArrayUtils.isEmpty(readAllAssociations(getCallingUserId(), callingPackage)),
+ "App must have an association before calling this API");
+ }
}
+
private int getCallingUserId() {
return UserHandle.getUserId(Binder.getCallingUid());
}
@@ -263,7 +310,7 @@
mFindDeviceCallback.asBinder().linkToDeath(
CompanionDeviceManagerService.this, 0);
} catch (RemoteException e) {
- handleBinderDied();
+ cleanup();
return;
}
try {
@@ -291,10 +338,26 @@
return new ICompanionDeviceDiscoveryServiceCallback.Stub() {
@Override
+ public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+ try {
+ return super.onTransact(code, data, reply, flags);
+ } catch (Throwable e) {
+ Slog.e(LOG_TAG, "Error during IPC", e);
+ throw ExceptionUtils.propagate(e, RemoteException.class);
+ }
+ }
+
+ @Override
public void onDeviceSelected(String packageName, int userId, String deviceAddress) {
- //TODO unbind
updateSpecialAccessPermissionForAssociatedPackage(packageName, userId);
recordAssociation(packageName, deviceAddress);
+ cleanup();
+ }
+
+ @Override
+ public void onDeviceSelectionCancel() {
+ cleanup();
}
};
}
@@ -345,22 +408,29 @@
}
private void recordAssociation(String priviledgedPackage, String deviceAddress) {
- updateAssociations((associations) -> ArrayUtils.add(associations,
- new Association(getCallingUserId(), deviceAddress, priviledgedPackage)));
+ if (DEBUG) {
+ Log.i(LOG_TAG, "recordAssociation(priviledgedPackage = " + priviledgedPackage
+ + ", deviceAddress = " + deviceAddress + ")");
+ }
+ int userId = getCallingUserId();
+ updateAssociations(associations -> CollectionUtils.add(associations,
+ new Association(userId, deviceAddress, priviledgedPackage)));
}
- private void updateAssociations(Function<ArrayList<Association>, List<Association>> update) {
+ private void updateAssociations(Function<List<Association>, List<Association>> update) {
updateAssociations(update, getCallingUserId());
}
- private void updateAssociations(Function<ArrayList<Association>, List<Association>> update,
+ private void updateAssociations(Function<List<Association>, List<Association>> update,
int userId) {
final AtomicFile file = getStorageFileForUser(userId);
synchronized (file) {
- final ArrayList<Association> old = readAllAssociations(userId);
- final List<Association> associations = update.apply(old);
- if (Objects.equals(old, associations)) return;
+ List<Association> associations = readAllAssociations(userId);
+ final List<Association> old = CollectionUtils.copyOf(associations);
+ associations = update.apply(associations);
+ if (size(old) == size(associations)) return;
+ List<Association> finalAssociations = associations;
file.write((out) -> {
XmlSerializer xml = Xml.newSerializer();
try {
@@ -369,8 +439,8 @@
xml.startDocument(null, true);
xml.startTag(null, XML_TAG_ASSOCIATIONS);
- for (int i = 0; i < CollectionUtils.size(associations); i++) {
- Association association = associations.get(i);
+ for (int i = 0; i < size(finalAssociations); i++) {
+ Association association = finalAssociations.get(i);
xml.startTag(null, XML_TAG_ASSOCIATION)
.attribute(null, XML_ATTR_PACKAGE, association.companionAppPackage)
.attribute(null, XML_ATTR_DEVICE, association.deviceAddress)
@@ -386,15 +456,6 @@
});
}
-
-
- //TODO Show dialog before recording notification access
-// final SettingStringHelper setting =
-// new SettingStringHelper(
-// getContext().getContentResolver(),
-// Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
-// getUserId());
-// setting.write(ColonDelimitedSet.OfStrings.add(setting.read(), priviledgedPackage));
}
private AtomicFile getStorageFileForUser(int uid) {
diff --git a/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
new file mode 100644
index 0000000..24cb72e
--- /dev/null
+++ b/services/tests/notification/src/com/android/server/notification/GlobalSortKeyComparatorTest.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.eq;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class GlobalSortKeyComparatorTest {
+
+ private final String PKG = "PKG";
+ private final int UID = 1111111;
+ private static final String TEST_CHANNEL_ID = "test_channel_id";
+
+ @Test
+ public void testComparator() throws Exception {
+ Notification n = new Notification.Builder(
+ InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+ .build();
+ NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+ new StatusBarNotification(PKG,
+ PKG, 1, "media", UID, UID, n,
+ new UserHandle(UserHandle.myUserId()),
+ "", 1499), getDefaultChannel());
+ left.setGlobalSortKey("first");
+
+ NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+ new StatusBarNotification(PKG,
+ PKG, 1, "media", UID, UID, n,
+ new UserHandle(UserHandle.myUserId()),
+ "", 1499), getDefaultChannel());
+ right.setGlobalSortKey("second");
+
+ NotificationRecord last = new NotificationRecord(InstrumentationRegistry.getContext(),
+ new StatusBarNotification(PKG,
+ PKG, 1, "media", UID, UID, n,
+ new UserHandle(UserHandle.myUserId()),
+ "", 1499), getDefaultChannel());
+
+
+ final List<NotificationRecord> expected = new ArrayList<>();
+ expected.add(left);
+ expected.add(right);
+ expected.add(last);
+
+ List<NotificationRecord> actual = new ArrayList<>();
+ actual.addAll(expected);
+ Collections.shuffle(actual);
+
+ Collections.sort(actual, new GlobalSortKeyComparator());
+
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testNoCrash_leftNull() throws Exception {
+ Notification n = new Notification.Builder(
+ InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+ .build();
+ NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+ new StatusBarNotification(PKG,
+ PKG, 1, "media", UID, UID, n,
+ new UserHandle(UserHandle.myUserId()),
+ "", 1499), getDefaultChannel());
+
+ NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+ new StatusBarNotification(PKG,
+ PKG, 1, "media", UID, UID, n,
+ new UserHandle(UserHandle.myUserId()),
+ "", 1499), getDefaultChannel());
+ right.setGlobalSortKey("not null");
+
+ final List<NotificationRecord> expected = new ArrayList<>();
+ expected.add(right);
+ expected.add(left);
+
+ List<NotificationRecord> actual = new ArrayList<>();
+ actual.addAll(expected);
+ Collections.shuffle(actual);
+
+ Collections.sort(actual, new GlobalSortKeyComparator());
+
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testNoCrash_rightNull() throws Exception {
+ Notification n = new Notification.Builder(
+ InstrumentationRegistry.getContext(), TEST_CHANNEL_ID)
+ .build();
+ NotificationRecord left = new NotificationRecord(InstrumentationRegistry.getContext(),
+ new StatusBarNotification(PKG,
+ PKG, 1, "media", UID, UID, n,
+ new UserHandle(UserHandle.myUserId()),
+ "", 1499), getDefaultChannel());
+ left.setGlobalSortKey("not null");
+
+ NotificationRecord right = new NotificationRecord(InstrumentationRegistry.getContext(),
+ new StatusBarNotification(PKG,
+ PKG, 1, "media", UID, UID, n,
+ new UserHandle(UserHandle.myUserId()),
+ "", 1499), getDefaultChannel());
+
+ final List<NotificationRecord> expected = new ArrayList<>();
+ expected.add(left);
+ expected.add(right);
+
+ List<NotificationRecord> actual = new ArrayList<>();
+ actual.addAll(expected);
+ Collections.shuffle(actual);
+
+ Collections.sort(actual, new GlobalSortKeyComparator());
+
+ assertEquals(expected, actual);
+ }
+
+ private NotificationChannel getDefaultChannel() {
+ return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name",
+ NotificationManager.IMPORTANCE_LOW);
+ }
+}
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
index f666727..57ee928 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -49,6 +49,7 @@
import android.content.pm.ParceledListSlice;
import android.graphics.Color;
import android.os.Binder;
+import android.os.Process;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
@@ -460,10 +461,10 @@
mBinderService.createNotificationChannels(PKG,
new ParceledListSlice(Arrays.asList(mTestNotificationChannel, channel2)));
verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
- eq(mTestNotificationChannel),
+ eq(Process.myUserHandle()), eq(mTestNotificationChannel),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED));
verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
- eq(channel2),
+ eq(Process.myUserHandle()), eq(channel2),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED));
}
@@ -481,10 +482,10 @@
mBinderService.createNotificationChannelGroups(PKG,
new ParceledListSlice(Arrays.asList(group1, group2)));
verify(mNotificationListeners, times(1)).notifyNotificationChannelGroupChanged(eq(PKG),
- eq(group1),
+ eq(Process.myUserHandle()), eq(group1),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED));
verify(mNotificationListeners, times(1)).notifyNotificationChannelGroupChanged(eq(PKG),
- eq(group2),
+ eq(Process.myUserHandle()), eq(group2),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED));
}
@@ -503,7 +504,7 @@
reset(mNotificationListeners);
mBinderService.updateNotificationChannelForPackage(PKG, 0, mTestNotificationChannel);
verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
- eq(mTestNotificationChannel),
+ eq(Process.myUserHandle()), eq(mTestNotificationChannel),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
}
@@ -520,7 +521,7 @@
reset(mNotificationListeners);
mBinderService.deleteNotificationChannel(PKG, mTestNotificationChannel.getId());
verify(mNotificationListeners, times(1)).notifyNotificationChannelChanged(eq(PKG),
- eq(mTestNotificationChannel),
+ eq(Process.myUserHandle()), eq(mTestNotificationChannel),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED));
}
@@ -537,7 +538,7 @@
reset(mNotificationListeners);
mBinderService.deleteNotificationChannelGroup(PKG, ncg.getId());
verify(mNotificationListeners, times(1)).notifyNotificationChannelGroupChanged(eq(PKG),
- eq(ncg),
+ eq(Process.myUserHandle()), eq(ncg),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED));
}
@@ -550,12 +551,12 @@
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
mBinderService.updateNotificationChannelFromPrivilegedListener(
- null, PKG, mTestNotificationChannel);
+ null, PKG, Process.myUserHandle(), mTestNotificationChannel);
verify(mRankingHelper, times(1)).updateNotificationChannel(anyString(), anyInt(), any());
verify(mNotificationListeners, never()).notifyNotificationChannelChanged(eq(PKG),
- eq(mTestNotificationChannel),
+ eq(Process.myUserHandle()), eq(mTestNotificationChannel),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
}
@@ -568,7 +569,7 @@
try {
mBinderService.updateNotificationChannelFromPrivilegedListener(
- null, PKG, mTestNotificationChannel);
+ null, PKG, Process.myUserHandle(), mTestNotificationChannel);
fail("listeners that don't have a companion device shouldn't be able to call this");
} catch (SecurityException e) {
// pass
@@ -577,7 +578,33 @@
verify(mRankingHelper, never()).updateNotificationChannel(anyString(), anyInt(), any());
verify(mNotificationListeners, never()).notifyNotificationChannelChanged(eq(PKG),
- eq(mTestNotificationChannel),
+ eq(Process.myUserHandle()), eq(mTestNotificationChannel),
+ eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
+ }
+
+ @Test
+ @UiThreadTest
+ public void testUpdateNotificationChannelFromPrivilegedListener_badUser() throws Exception {
+ mNotificationManagerService.setRankingHelper(mRankingHelper);
+ List<String> associations = new ArrayList<>();
+ associations.add("a");
+ when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
+ mListener = mock(ManagedServices.ManagedServiceInfo.class);
+ when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
+ when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+
+ try {
+ mBinderService.updateNotificationChannelFromPrivilegedListener(
+ null, PKG, UserHandle.ALL, mTestNotificationChannel);
+ fail("incorrectly allowed a change to a user listener cannot see");
+ } catch (SecurityException e) {
+ // pass
+ }
+
+ verify(mRankingHelper, never()).updateNotificationChannel(anyString(), anyInt(), any());
+
+ verify(mNotificationListeners, never()).notifyNotificationChannelChanged(eq(PKG),
+ eq(Process.myUserHandle()), eq(mTestNotificationChannel),
eq(NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED));
}
@@ -589,7 +616,8 @@
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
- mBinderService.getNotificationChannelsFromPrivilegedListener(null, PKG);
+ mBinderService.getNotificationChannelsFromPrivilegedListener(
+ null, PKG, Process.myUserHandle());
verify(mRankingHelper, times(1)).getNotificationChannels(
anyString(), anyInt(), anyBoolean());
@@ -603,7 +631,8 @@
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
try {
- mBinderService.getNotificationChannelsFromPrivilegedListener(null, PKG);
+ mBinderService.getNotificationChannelsFromPrivilegedListener(
+ null, PKG, Process.myUserHandle());
fail("listeners that don't have a companion device shouldn't be able to call this");
} catch (SecurityException e) {
// pass
@@ -615,13 +644,37 @@
@Test
@UiThreadTest
+ public void testGetNotificationChannelFromPrivilegedListener_badUser() throws Exception {
+ mNotificationManagerService.setRankingHelper(mRankingHelper);
+ List<String> associations = new ArrayList<>();
+ associations.add("a");
+ when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
+ mListener = mock(ManagedServices.ManagedServiceInfo.class);
+ when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
+ when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+
+ try {
+ mBinderService.getNotificationChannelsFromPrivilegedListener(
+ null, PKG, Process.myUserHandle());
+ fail("listener getting channels from a user they cannot see");
+ } catch (SecurityException e) {
+ // pass
+ }
+
+ verify(mRankingHelper, never()).getNotificationChannels(
+ anyString(), anyInt(), anyBoolean());
+ }
+
+ @Test
+ @UiThreadTest
public void testGetNotificationChannelGroupsFromPrivilegedListener_success() throws Exception {
mNotificationManagerService.setRankingHelper(mRankingHelper);
List<String> associations = new ArrayList<>();
associations.add("a");
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
- mBinderService.getNotificationChannelGroupsFromPrivilegedListener(null, PKG);
+ mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
+ null, PKG, Process.myUserHandle());
verify(mRankingHelper, times(1)).getNotificationChannelGroups(anyString(), anyInt());
}
@@ -634,7 +687,29 @@
when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
try {
- mBinderService.getNotificationChannelGroupsFromPrivilegedListener(null, PKG);
+ mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
+ null, PKG, Process.myUserHandle());
+ fail("listeners that don't have a companion device shouldn't be able to call this");
+ } catch (SecurityException e) {
+ // pass
+ }
+
+ verify(mRankingHelper, never()).getNotificationChannelGroups(anyString(), anyInt());
+ }
+
+ @Test
+ @UiThreadTest
+ public void testGetNotificationChannelGroupsFromPrivilegedListener_badUser() throws Exception {
+ mNotificationManagerService.setRankingHelper(mRankingHelper);
+ List<String> associations = new ArrayList<>();
+ when(mCompanionMgr.getAssociations(PKG, uid)).thenReturn(associations);
+ mListener = mock(ManagedServices.ManagedServiceInfo.class);
+ when(mListener.enabledAndUserMatches(anyInt())).thenReturn(false);
+ when(mNotificationListeners.checkServiceTokenLocked(any())).thenReturn(mListener);
+
+ try {
+ mBinderService.getNotificationChannelGroupsFromPrivilegedListener(
+ null, PKG, Process.myUserHandle());
fail("listeners that don't have a companion device shouldn't be able to call this");
} catch (SecurityException e) {
// pass
diff --git a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
index 946044d..b2e6ef9 100644
--- a/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/notification/src/com/android/server/notification/NotificationRecordTest.java
@@ -124,6 +124,9 @@
builder.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
channel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
}
+ } else {
+ channel.setSound(null, null);
+ builder.setSound(null, null);
}
if (buzzy) {
if (defaultVibration) {
@@ -206,6 +209,18 @@
}
@Test
+ public void testSound_noSound_preUpgrade() throws Exception {
+ // pre upgrade, default sound.
+ StatusBarNotification sbn = getNotification(true /*preO */, false /* noisy */,
+ false /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+ false /* lights */, false /*defaultLights */);
+
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
+ assertEquals(null, record.getSound());
+ assertEquals(Notification.AUDIO_ATTRIBUTES_DEFAULT, record.getAudioAttributes());
+ }
+
+ @Test
public void testSound_default_upgradeUsesChannel() throws Exception {
channel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
// post upgrade, default sound.
diff --git a/services/tests/servicestests/src/com/android/server/BootReceiverFixFsckFsStatTest.java b/services/tests/servicestests/src/com/android/server/BootReceiverFixFsckFsStatTest.java
new file mode 100644
index 0000000..362c47ad
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/BootReceiverFixFsckFsStatTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.server;
+
+import static junit.framework.Assert.*;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BootReceiverFixFsckFsStatTest {
+
+ private static final String PARTITION = "userdata";
+
+ @Test
+ public void testTreeOptimization() {
+ final String[] logs = {
+ "e2fsck 1.43.3 (04-Sep-2016)",
+ "Pass 1: Checking inodes, blocks, and sizes",
+ "Inode 877141 extent tree (at level 1) could be shorter. Fix? yes",
+ " ",
+ "Pass 1E: Optimizing extent trees",
+ "Pass 2: Checking directory structure",
+ "Pass 3: Checking directory connectivity",
+ "Pass 4: Checking reference counts",
+ "Pass 5: Checking group summary information",
+ "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (71667712, 1000) != expected (71671808, 1000)",
+ "Update quota info for quota type 0? yes",
+ " ",
+ "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (59555840, 953) != expected (59559936, 953)",
+ "Update quota info for quota type 1? yes",
+ " ",
+ "/dev/block/platform/soc/624000.ufshc/by-name/userdata: ***** FILE SYSTEM WAS MODIFIED *****"
+ };
+ doTestFsckFsStat(logs, 0x405, 5, 0, logs.length);
+
+ final String[] doubleLogs = new String[logs.length * 2];
+ System.arraycopy(logs, 0, doubleLogs, 0, logs.length);
+ System.arraycopy(logs, 0, doubleLogs, logs.length, logs.length);
+ doTestFsckFsStat(doubleLogs, 0x401, 1, 0, logs.length);
+ doTestFsckFsStat(doubleLogs, 0x402, 2, logs.length, logs.length * 2);
+ }
+
+ @Test
+ public void testQuotaOnly() {
+ final String[] logs = {
+ "e2fsck 1.43.3 (04-Sep-2016)",
+ "Pass 1: Checking inodes, blocks, and sizes",
+ "Pass 1E: Optimizing extent trees",
+ "Pass 2: Checking directory structure",
+ "Pass 3: Checking directory connectivity",
+ "Pass 4: Checking reference counts",
+ "Pass 5: Checking group summary information",
+ "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (71667712, 1000) != expected (71671808, 1000)",
+ "Update quota info for quota type 0? yes",
+ " ",
+ "[QUOTA WARNING] Usage inconsistent for ID 10038:actual (59555840, 953) != expected (59559936, 953)",
+ "Update quota info for quota type 1? yes",
+ " ",
+ "/dev/block/platform/soc/624000.ufshc/by-name/userdata: ***** FILE SYSTEM WAS MODIFIED *****"
+ };
+ doTestFsckFsStat(logs, 0x405, 0x405, 0, logs.length);
+ }
+
+ @Test
+ public void testOrphaned() {
+ final String[] logs = {
+ "e2fsck 1.43.3 (04-Sep-2016)",
+ "Pass 1: Checking inodes, blocks, and sizes",
+ "Inodes that were part of a corrupted orphan linked list found. Fix? yes",
+ " ",
+ "Inode 589877 was part of the orphaned inode list. FIXED.",
+ " ",
+ "Inode 589878 was part of the orphaned inode list. FIXED.",
+ " ",
+ "Pass 2: Checking directory structure",
+ "Pass 3: Checking directory connectivity",
+ "Pass 4: Checking reference counts",
+ "Pass 5: Checking group summary information",
+ " ",
+ "/dev/block/platform/soc/624000.ufshc/by-name/userdata: ***** FILE SYSTEM WAS MODIFIED *****"
+ };
+ doTestFsckFsStat(logs, 0x405, 0x405, 0, logs.length);
+ }
+
+ private void doTestFsckFsStat(String[] lines, int statOrg, int statUpdated, int startLineNumber,
+ int endLineNumber) {
+ assertEquals(statUpdated, BootReceiver.fixFsckFsStat(PARTITION, statOrg, lines,
+ startLineNumber, endLineNumber));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
index 54ecab3..f75d49c 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityRecordTests.java
@@ -16,7 +16,8 @@
package com.android.server.am;
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import android.content.ComponentName;
import android.platform.test.annotations.Presubmit;
@@ -36,50 +37,61 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityRecordTests extends ActivityTestsBase {
+ private static final int TEST_STACK_ID = 100;
+
private final ComponentName testActivityComponent =
ComponentName.unflattenFromString("com.foo/.BarActivity");
@Test
public void testStackCleanupOnClearingTask() throws Exception {
final ActivityManagerService service = createActivityManagerService();
- final TestActivityStack testStack = new ActivityStackBuilder(service).build();
- final TaskRecord task = createTask(service, testActivityComponent, testStack);
+ final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
final ActivityRecord record = createActivity(service, testActivityComponent, task);
record.setTask(null);
- assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1);
+ assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 1);
}
@Test
public void testStackCleanupOnActivityRemoval() throws Exception {
final ActivityManagerService service = createActivityManagerService();
- final TestActivityStack testStack = new ActivityStackBuilder(service).build();
- final TaskRecord task = createTask(service, testActivityComponent, testStack);
+ final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
final ActivityRecord record = createActivity(service, testActivityComponent, task);
task.removeActivity(record);
- assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1);
+ assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 1);
}
@Test
public void testStackCleanupOnTaskRemoval() throws Exception {
final ActivityManagerService service = createActivityManagerService();
- final TestActivityStack testStack = new ActivityStackBuilder(service).build();
- final TaskRecord task = createTask(service, testActivityComponent, testStack);
+ final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
final ActivityRecord record = createActivity(service, testActivityComponent, task);
- testStack.removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING);
- assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 1);
+ service.mStackSupervisor.getStack(TEST_STACK_ID)
+ .removeTask(task, null /*reason*/, ActivityStack.REMOVE_TASK_MODE_MOVING);
+
+ // Stack should be gone on task removal.
+ assertNull(service.mStackSupervisor.getStack(TEST_STACK_ID));
}
@Test
public void testNoCleanupMovingActivityInSameStack() throws Exception {
final ActivityManagerService service = createActivityManagerService();
- final TestActivityStack testStack = new ActivityStackBuilder(service).build();
- final TaskRecord oldTask = createTask(service, testActivityComponent, testStack);
+ final TaskRecord oldTask = createTask(service, testActivityComponent, TEST_STACK_ID);
final ActivityRecord record = createActivity(service, testActivityComponent, oldTask);
- final TaskRecord newTask = createTask(service, testActivityComponent, testStack);
+ final TaskRecord newTask = createTask(service, testActivityComponent, TEST_STACK_ID);
record.reparent(newTask, 0, null /*reason*/);
- assertTrue(testStack.onActivityRemovedFromStackInvocationCount() == 0);
+ assertEquals(getActivityRemovedFromStackCount(service, TEST_STACK_ID), 0);
+ }
+
+ private static int getActivityRemovedFromStackCount(ActivityManagerService service,
+ int stackId) {
+ final ActivityStack stack = service.mStackSupervisor.getStack(stackId);
+ if (stack instanceof ActivityStackReporter) {
+ return ((ActivityStackReporter) stack).onActivityRemovedFromStackInvocationCount();
+ }
+
+ return -1;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 8423aff..fc9ab96 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -16,8 +16,14 @@
package com.android.server.am;
+import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import android.content.ComponentName;
+import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
@@ -25,6 +31,10 @@
import org.junit.runner.RunWith;
import org.junit.Test;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
/**
@@ -37,6 +47,9 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackSupervisorTests extends ActivityTestsBase {
+ private final ComponentName testActivityComponent =
+ ComponentName.unflattenFromString("com.foo/.BarActivity");
+
/**
* This test ensures that we do not try to restore a task based off an invalid task id. The
* stack supervisor is a test version so there will be no tasks present. We should expect
@@ -49,4 +62,59 @@
MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, 0 /*stackId*/);
assertNull(task);
}
+
+ /**
+ * This test ensures that an existing task in the pinned stack is moved to the fullscreen
+ * activity stack when a new task is added.
+ */
+ @Test
+ public void testReplacingTaskInPinnedStack() throws Exception {
+ final ActivityManagerService service = createActivityManagerService();
+ final TaskRecord firstTask = createTask(service, testActivityComponent,
+ FULLSCREEN_WORKSPACE_STACK_ID);
+ final ActivityRecord firstActivity = createActivity(service, testActivityComponent,
+ firstTask);
+ // Create a new task on the full screen stack
+ final TaskRecord secondTask = createTask(service, testActivityComponent,
+ FULLSCREEN_WORKSPACE_STACK_ID);
+ final ActivityRecord secondActivity = createActivity(service, testActivityComponent,
+ secondTask);
+ service.mStackSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack",
+ service.mStackSupervisor.getStack(FULLSCREEN_WORKSPACE_STACK_ID));
+
+ // Ensure full screen stack has both tasks.
+ ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, firstTask,
+ secondTask);
+
+ // Move first activity to pinned stack.
+ service.mStackSupervisor.moveActivityToPinnedStackLocked(firstActivity,
+ new Rect() /*sourceBounds*/, 0f /*aspectRatio*/, false, "initialMove");
+
+ // Ensure a task has moved over.
+ ensureStackPlacement(service.mStackSupervisor, PINNED_STACK_ID, firstTask);
+ ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, secondTask);
+
+ // Move second activity to pinned stack.
+ service.mStackSupervisor.moveActivityToPinnedStackLocked(secondActivity,
+ new Rect() /*sourceBounds*/, 0f /*aspectRatio*/ /*destBounds*/, false, "secondMove");
+
+ // Ensure stacks have swapped tasks.
+ ensureStackPlacement(service.mStackSupervisor, PINNED_STACK_ID, secondTask);
+ ensureStackPlacement(service.mStackSupervisor, FULLSCREEN_WORKSPACE_STACK_ID, firstTask);
+ }
+
+ private static void ensureStackPlacement(ActivityStackSupervisor supervisor, int stackId,
+ TaskRecord... tasks) {
+ final ActivityStack stack = supervisor.getStack(stackId);
+ final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
+ assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
+
+ if (tasks == null) {
+ return;
+ }
+
+ for (TaskRecord task : tasks) {
+ assertTrue(stackTasks.contains(task));
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index 1d80b33..f42abf1 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -37,28 +37,27 @@
@Presubmit
@RunWith(AndroidJUnit4.class)
public class ActivityStackTests extends ActivityTestsBase {
- private final ComponentName testActivityComponent =
+ private static final int TEST_STACK_ID = 100;
+ private static final ComponentName testActivityComponent =
ComponentName.unflattenFromString("com.foo/.BarActivity");
@Test
public void testEmptyTaskCleanupOnRemove() throws Exception {
final ActivityManagerService service = createActivityManagerService();
- final TestActivityStack testStack = new ActivityStackBuilder(service).build();
- final TaskRecord task = createTask(service, testActivityComponent, testStack);
+ final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
assertNotNull(task.getWindowContainerController());
- testStack.removeTask(task, "testEmptyTaskCleanupOnRemove",
- ActivityStack.REMOVE_TASK_MODE_DESTROYING);
+ service.mStackSupervisor.getStack(TEST_STACK_ID).removeTask(task,
+ "testEmptyTaskCleanupOnRemove", ActivityStack.REMOVE_TASK_MODE_DESTROYING);
assertNull(task.getWindowContainerController());
}
@Test
public void testOccupiedTaskCleanupOnRemove() throws Exception {
final ActivityManagerService service = createActivityManagerService();
- final TestActivityStack testStack = new ActivityStackBuilder(service).build();
- final TaskRecord task = createTask(service, testActivityComponent, testStack);
+ final TaskRecord task = createTask(service, testActivityComponent, TEST_STACK_ID);
final ActivityRecord activityRecord = createActivity(service, testActivityComponent, task);
assertNotNull(task.getWindowContainerController());
- testStack.removeTask(task, "testOccupiedTaskCleanupOnRemove",
- ActivityStack.REMOVE_TASK_MODE_DESTROYING);
+ service.mStackSupervisor.getStack(TEST_STACK_ID).removeTask(task,
+ "testOccupiedTaskCleanupOnRemove", ActivityStack.REMOVE_TASK_MODE_DESTROYING);
assertNotNull(task.getWindowContainerController());
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 3bf0e5f..0827084 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -17,6 +17,11 @@
package com.android.server.am;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doAnswer;
+
+import org.mockito.invocation.InvocationOnMock;
import android.app.ActivityManager;
import android.content.ComponentName;
@@ -33,6 +38,7 @@
import com.android.server.wm.AppWindowContainerController;
import com.android.server.wm.StackWindowController;
+import com.android.server.wm.TaskWindowContainerController;
import com.android.server.wm.WindowManagerService;
import com.android.server.wm.WindowTestUtils;
import org.junit.After;
@@ -64,16 +70,15 @@
protected ActivityManagerService createActivityManagerService() {
final ActivityManagerService service = new TestActivityManagerService(mContext);
- service.mWindowManager = WindowTestUtils.getWindowManagerService(mContext);
+ service.mWindowManager = WindowTestUtils.getMockWindowManagerService();
return service;
}
- protected static TestActivityStack createActivityStack(ActivityManagerService service,
+ protected static ActivityStack createActivityStack(ActivityManagerService service,
int stackId, int displayId, boolean onTop) {
if (service.mStackSupervisor instanceof TestActivityStackSupervisor) {
- final TestActivityStack stack = ((TestActivityStackSupervisor) service.mStackSupervisor)
+ return ((TestActivityStackSupervisor) service.mStackSupervisor)
.createTestStack(service, stackId, onTop);
- return stack;
}
return null;
@@ -103,7 +108,7 @@
}
protected static TaskRecord createTask(ActivityManagerService service,
- ComponentName component, ActivityStack stack) {
+ ComponentName component, int stackId) {
final ActivityInfo aInfo = new ActivityInfo();
aInfo.applicationInfo = new ApplicationInfo();
aInfo.applicationInfo.packageName = component.getPackageName();
@@ -113,13 +118,16 @@
final TaskRecord task = new TaskRecord(service, 0, aInfo, intent /*intent*/,
null /*_taskDescription*/, new ActivityManager.TaskThumbnailInfo());
+ final ActivityStack stack = service.mStackSupervisor.getStack(stackId,
+ true /*createStaticStackIfNeeded*/, true /*onTop*/);
stack.addTask(task, true, "creating test task");
task.setStack(stack);
- task.createWindowContainer(true, true);
+ task.setWindowContainerController(mock(TaskWindowContainerController.class));
return task;
}
+
/**
* An {@link ActivityManagerService} subclass which provides a test
* {@link ActivityStackSupervisor}.
@@ -127,6 +135,9 @@
protected static class TestActivityManagerService extends ActivityManagerService {
public TestActivityManagerService(Context context) {
super(context);
+ mSupportsMultiWindow = true;
+ mSupportsMultiDisplay = true;
+ mWindowManager = WindowTestUtils.getWindowManagerService(context);
}
@Override
@@ -142,6 +153,12 @@
protected static class TestActivityStackSupervisor extends ActivityStackSupervisor {
public TestActivityStackSupervisor(ActivityManagerService service, Looper looper) {
super(service, looper);
+ mWindowManager = prepareMockWindowManager();
+ }
+
+ // No home stack is set.
+ @Override
+ void moveHomeStackToFront(String reason) {
}
// Invoked during {@link ActivityStack} creation.
@@ -149,18 +166,45 @@
void updateUIDsPresentOnDisplay() {
}
- public TestActivityStack createTestStack(ActivityManagerService service, int stackId,
- boolean onTop) {
+ // Just return the current front task.
+ @Override
+ ActivityStack getNextFocusableStackLocked(ActivityStack currentFocus) {
+ return mFocusedStack;
+ }
+
+ // Called when moving activity to pinned stack.
+ @Override
+ void ensureActivitiesVisibleLocked(ActivityRecord starting, int configChanges,
+ boolean preserveWindows) {
+ }
+
+ public <T extends ActivityStack> T createTestStack(ActivityManagerService service,
+ int stackId, boolean onTop) {
final ActivityDisplay display = new ActivityDisplay();
final TestActivityContainer container =
new TestActivityContainer(service, stackId, display, onTop);
- return container.getStack();
+ mActivityContainers.put(stackId, container);
+ return (T) container.getStack();
+ }
+
+ @Override
+ protected <T extends ActivityStack> T getStack(int stackId,
+ boolean createStaticStackIfNeeded, boolean createOnTop) {
+ final T stack = super.getStack(stackId, createStaticStackIfNeeded, createOnTop);
+
+ if (stack != null || !createStaticStackIfNeeded) {
+ return stack;
+ }
+
+ return createTestStack(mService, stackId, createOnTop);
}
private class TestActivityContainer extends ActivityContainer {
- private ActivityManagerService mService;
- private TestActivityStack mStack;
+ private final ActivityManagerService mService;
+
private boolean mOnTop;
+ private int mStackId;
+ private ActivityStack mStack;
TestActivityContainer(ActivityManagerService service, int stackId,
ActivityDisplay activityDisplay, boolean onTop) {
@@ -174,12 +218,16 @@
// we cannot set {@link mService} by the time the super constructor calling this
// method is invoked.
mOnTop = onTop;
+ mStackId = stackId;
}
- public TestActivityStack getStack() {
+ public ActivityStack getStack() {
if (mStack == null) {
- mStack = new TestActivityStack(this,
- new RecentTasks(mService, mService.mStackSupervisor), mOnTop);
+ final RecentTasks recents =
+ new RecentTasks(mService, mService.mStackSupervisor);
+ mStack = mStackId == ActivityManager.StackId.PINNED_STACK_ID
+ ? new PinnedActivityStack(this, recents, mOnTop)
+ : new TestActivityStack(this, recents, mOnTop);
}
return mStack;
@@ -187,13 +235,31 @@
}
}
+ private static WindowManagerService prepareMockWindowManager() {
+ final WindowManagerService service = mock(WindowManagerService.class);
+
+ doAnswer((InvocationOnMock invocationOnMock) -> {
+ final Runnable runnable = invocationOnMock.<Runnable>getArgument(0);
+ if (runnable != null) {
+ runnable.run();
+ }
+ return null;
+ }).when(service).inSurfaceTransaction(any());
+
+ return service;
+ }
+
+ protected interface ActivityStackReporter {
+ int onActivityRemovedFromStackInvocationCount();
+ }
+
/**
* Override of {@link ActivityStack} that tracks test metrics, such as the number of times a
* method is called. Note that its functionality depends on the implementations of the
* construction arguments.
*/
protected static class TestActivityStack<T extends StackWindowController>
- extends ActivityStack<T> {
+ extends ActivityStack<T> implements ActivityStackReporter {
private int mOnActivityRemovedFromStackCount = 0;
private T mContainerController;
TestActivityStack(ActivityStackSupervisor.ActivityContainer activityContainer,
@@ -208,6 +274,7 @@
}
// Returns the number of times {@link #onActivityRemovedFromStack} has been called
+ @Override
public int onActivityRemovedFromStackInvocationCount() {
return mOnActivityRemovedFromStackCount;
}
@@ -225,6 +292,7 @@
}
}
+
protected static class ActivityStackBuilder {
private boolean mOnTop = true;
private int mStackId = 0;
@@ -251,7 +319,7 @@
return this;
}
- public TestActivityStack build() {
+ public ActivityStack build() {
return createActivityStack(mService, mStackId, mDisplayId, mOnTop);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
new file mode 100644
index 0000000..243c1b3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/AppErrorDialogTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.server.am;
+
+import android.content.Context;
+import android.os.Handler;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.AppOpsService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+/**
+ * runtest -c com.android.server.am.AppErrorDialogTest frameworks-services
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AppErrorDialogTest {
+
+ private Context mContext;
+ private ActivityManagerService mService;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getTargetContext();
+ mService = new ActivityManagerService(new ActivityManagerService.Injector() {
+ @Override
+ public AppOpsService getAppOpsService(File file, Handler handler) {
+ return null;
+ }
+
+ @Override
+ public Handler getUiHandler(ActivityManagerService service) {
+ return null;
+ }
+
+ @Override
+ public boolean isNetworkRestrictedForUid(int uid) {
+ return false;
+ }
+ });
+ }
+
+ @Test
+ @UiThreadTest
+ public void testCreateWorks() throws Exception {
+ AppErrorDialog.Data data = new AppErrorDialog.Data();
+ data.proc = new ProcessRecord(null, mContext.getApplicationInfo(), "name", 12345);
+ data.result = new AppErrorResult();
+
+ AppErrorDialog dialog = new AppErrorDialog(mContext, mService, data);
+
+ dialog.create();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
new file mode 100644
index 0000000..8f2f34e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -0,0 +1,89 @@
+/*
+ * 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.server.pm;
+
+import android.content.IIntentReceiver;
+
+import android.os.Bundle;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.File;
+
+// runtest -c com.android.server.pm.PackageManagerServiceTest frameworks-services
+
+@SmallTest
+public class PackageManagerServiceTest extends AndroidTestCase {
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testPackageRemoval() throws Exception {
+ class PackageSenderImpl implements PackageSender {
+ public void sendPackageBroadcast(final String action, final String pkg,
+ final Bundle extras, final int flags, final String targetPkg,
+ final IIntentReceiver finishedReceiver, final int[] userIds) {
+ }
+
+ public void sendPackageAddedForNewUsers(String packageName,
+ boolean isSystem, int appId, int... userIds) {
+ }
+ }
+
+ PackageSenderImpl sender = new PackageSenderImpl();
+ PackageSetting setting = null;
+ PackageManagerService.PackageRemovedInfo pri =
+ new PackageManagerService.PackageRemovedInfo(sender);
+
+ // Initial conditions: nothing there
+ assertNull(pri.removedUsers);
+ assertNull(pri.broadcastUsers);
+
+ // populateUsers with nothing leaves nothing
+ pri.populateUsers(null, setting);
+ assertNull(pri.broadcastUsers);
+
+ // Create a real (non-null) PackageSetting and confirm that the removed
+ // users are copied properly
+ setting = new PackageSetting("name", "realName", new File("codePath"),
+ new File("resourcePath"), "legacyNativeLibraryPathString",
+ "primaryCpuAbiString", "secondaryCpuAbiString",
+ "cpuAbiOverrideString", 0, 0, 0, "parentPackageName", null, 0,
+ null, null);
+ pri.populateUsers(new int[] {1, 2, 3, 4, 5}, setting);
+ assertNotNull(pri.broadcastUsers);
+ assertEquals(5, pri.broadcastUsers.length);
+
+ // Exclude a user
+ pri.broadcastUsers = null;
+ final int EXCLUDED_USER_ID = 4;
+ setting.setInstantApp(true, EXCLUDED_USER_ID);
+ pri.populateUsers(new int[] {1, 2, 3, EXCLUDED_USER_ID, 5}, setting);
+ assertNotNull(pri.broadcastUsers);
+ assertEquals(5 - 1, pri.broadcastUsers.length);
+
+ // TODO: test that sendApplicationHiddenForUser() actually fills in
+ // broadcastUsers
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
index 929a73d..c314de4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
@@ -27,6 +27,7 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
+import android.util.IconDrawableFactory;
import com.android.server.LocalServices;
@@ -155,9 +156,9 @@
public void testNumberOfBadges() {
assertTrue("Max profiles greater than number of badges",
UserManagerService.MAX_MANAGED_PROFILES
- <= ApplicationPackageManager.CORP_BADGE_COLORS.length);
+ <= IconDrawableFactory.CORP_BADGE_COLORS.length);
assertEquals("Num colors doesn't match number of badge labels",
- ApplicationPackageManager.CORP_BADGE_COLORS.length,
+ IconDrawableFactory.CORP_BADGE_COLORS.length,
ApplicationPackageManager.CORP_BADGE_LABEL_RES_ID.length);
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index 85dc712..7f150a2 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -16,6 +16,11 @@
package com.android.server.wm;
+import static com.android.server.wm.BoundsAnimationController.NO_PIP_MODE_CHANGED_CALLBACKS;
+import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_END;
+import static com.android.server.wm.BoundsAnimationController.SCHEDULE_PIP_MODE_CHANGED_ON_START;
+import static com.android.server.wm.BoundsAnimationController.SchedulePipModeChangedState;
+
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
@@ -47,6 +52,12 @@
* Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks
* depending on the various interactions.
*
+ * We are really concerned about only three of the transition states [F = fullscreen, !F = floating]
+ * F->!F, !F->!F, and !F->F. Each animation can only be cancelled from the target mid-transition,
+ * or if a new animation starts on the same target. The tests below verifies that the target is
+ * notified of all the cases where it is animating and cancelled so that it can respond
+ * appropriately.
+ *
* Build/Install/Run:
* bit FrameworksServicesTests:com.android.server.wm.BoundsAnimationControllerTests
*/
@@ -109,47 +120,46 @@
/**
* A test animate bounds user to track callbacks from the bounds animation.
*/
- private class TestAnimateBoundsUser implements BoundsAnimationController.AnimateBoundsUser {
+ private class TestBoundsAnimationTarget implements BoundsAnimationTarget {
+ boolean mAwaitingAnimationStart;
boolean mMovedToFullscreen;
boolean mAnimationStarted;
- boolean mAnimationStartedToFullscreen;
+ boolean mSchedulePipModeChangedOnStart;
boolean mAnimationEnded;
- boolean mUpdatedPictureInPictureModeWithBounds;
+ Rect mAnimationEndFinalStackBounds;
+ boolean mSchedulePipModeChangedOnEnd;
boolean mBoundsUpdated;
+ boolean mCancelRequested;
Rect mStackBounds;
Rect mTaskBounds;
- boolean mRequestCancelAnimation = false;
-
- void reinitialize(Rect stackBounds, Rect taskBounds) {
+ void initialize(Rect from) {
+ mAwaitingAnimationStart = true;
mMovedToFullscreen = false;
mAnimationStarted = false;
- mAnimationStartedToFullscreen = false;
mAnimationEnded = false;
- mUpdatedPictureInPictureModeWithBounds = false;
- mStackBounds = stackBounds;
- mTaskBounds = taskBounds;
+ mAnimationEndFinalStackBounds = null;
+ mSchedulePipModeChangedOnStart = false;
+ mSchedulePipModeChangedOnEnd = false;
+ mStackBounds = from;
+ mTaskBounds = null;
mBoundsUpdated = false;
- mRequestCancelAnimation = false;
}
@Override
- public void onAnimationStart(boolean toFullscreen) {
+ public void onAnimationStart(boolean schedulePipModeChangedCallback) {
+ mAwaitingAnimationStart = false;
mAnimationStarted = true;
- mAnimationStartedToFullscreen = toFullscreen;
- }
-
- @Override
- public void updatePictureInPictureMode(Rect targetStackBounds) {
- mUpdatedPictureInPictureModeWithBounds = true;
+ mSchedulePipModeChangedOnStart = schedulePipModeChangedCallback;
}
@Override
public boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds) {
// TODO: Once we break the runs apart, we should fail() here if this is called outside
// of onAnimationStart() and onAnimationEnd()
- if (mRequestCancelAnimation) {
+ if (mCancelRequested) {
+ mCancelRequested = false;
return false;
} else {
mBoundsUpdated = true;
@@ -160,32 +170,206 @@
}
@Override
- public void onAnimationEnd() {
+ public void onAnimationEnd(boolean schedulePipModeChangedCallback, Rect finalStackBounds,
+ boolean moveToFullscreen) {
mAnimationEnded = true;
+ mAnimationEndFinalStackBounds = finalStackBounds;
+ mSchedulePipModeChangedOnEnd = schedulePipModeChangedCallback;
+ mMovedToFullscreen = moveToFullscreen;
+ mTaskBounds = null;
+ }
+ }
+
+ /**
+ * Drives the animations, makes common assertions along the way.
+ */
+ private class BoundsAnimationDriver {
+
+ private BoundsAnimationController mController;
+ private TestBoundsAnimationTarget mTarget;
+ private BoundsAnimator mAnimator;
+
+ private Rect mFrom;
+ private Rect mTo;
+ private Rect mLargerBounds;
+ private Rect mExpectedFinalBounds;
+
+ BoundsAnimationDriver(BoundsAnimationController controller,
+ TestBoundsAnimationTarget target) {
+ mController = controller;
+ mTarget = target;
}
- @Override
- public void moveToFullscreen() {
- mMovedToFullscreen = true;
+ BoundsAnimationDriver start(Rect from, Rect to) {
+ if (mAnimator != null) {
+ throw new IllegalArgumentException("Call restart() to restart an animation");
+ }
+
+ mTarget.initialize(from);
+
+ // Started, not running
+ assertTrue(mTarget.mAwaitingAnimationStart);
+ assertTrue(!mTarget.mAnimationStarted);
+
+ startImpl(from, to);
+
+ // Started and running
+ assertTrue(!mTarget.mAwaitingAnimationStart);
+ assertTrue(mTarget.mAnimationStarted);
+
+ return this;
+ }
+
+ BoundsAnimationDriver restart(Rect to) {
+ if (mAnimator == null) {
+ throw new IllegalArgumentException("Call start() to start a new animation");
+ }
+
+ BoundsAnimator oldAnimator = mAnimator;
+ boolean toSameBounds = mAnimator.isStarted() && to.equals(mTo);
+
+ // Reset the animation start state
+ mTarget.mAnimationStarted = false;
+
+ // Start animation
+ startImpl(mTarget.mStackBounds, to);
+
+ if (toSameBounds) {
+ // Same animator if same final bounds
+ assertSame(oldAnimator, mAnimator);
+ }
+
+ // No animation start for replacing animation
+ assertTrue(!mTarget.mAnimationStarted);
+ mTarget.mAnimationStarted = true;
+ return this;
+ }
+
+ private BoundsAnimationDriver startImpl(Rect from, Rect to) {
+ boolean fromFullscreen = from.equals(BOUNDS_FULL);
+ boolean toFullscreen = to.equals(BOUNDS_FULL);
+ mFrom = new Rect(from);
+ mTo = new Rect(to);
+ mExpectedFinalBounds = new Rect(to);
+ mLargerBounds = getLargerBounds(mFrom, mTo);
+
+ // Start animation
+ final @SchedulePipModeChangedState int schedulePipModeChangedState = toFullscreen
+ ? SCHEDULE_PIP_MODE_CHANGED_ON_START
+ : fromFullscreen
+ ? SCHEDULE_PIP_MODE_CHANGED_ON_END
+ : NO_PIP_MODE_CHANGED_CALLBACKS;
+ mAnimator = mController.animateBoundsImpl(mTarget, from, to, DURATION,
+ schedulePipModeChangedState, toFullscreen);
+
+ // Original stack bounds, frozen task bounds
+ assertEquals(mFrom, mTarget.mStackBounds);
+ assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds);
+
+ // Animating to larger size
+ if (mFrom.equals(mLargerBounds)) {
+ assertTrue(!mAnimator.animatingToLargerSize());
+ } else if (mTo.equals(mLargerBounds)) {
+ assertTrue(mAnimator.animatingToLargerSize());
+ }
+
+ return this;
+ }
+
+ BoundsAnimationDriver expectStarted(boolean schedulePipModeChanged) {
+ // Callback made
+ assertTrue(mTarget.mAnimationStarted);
+
+ assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnStart);
+ return this;
+ }
+
+ BoundsAnimationDriver update(float t) {
+ mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(t));
+
+ // Temporary stack bounds, frozen task bounds
+ if (t == 0f) {
+ assertEquals(mFrom, mTarget.mStackBounds);
+ } else if (t == 1f) {
+ assertEquals(mTo, mTarget.mStackBounds);
+ } else {
+ assertNotEquals(mFrom, mTarget.mStackBounds);
+ assertNotEquals(mTo, mTarget.mStackBounds);
+ }
+ assertEqualSizeAtOffset(mLargerBounds, mTarget.mTaskBounds);
+ return this;
+ }
+
+ BoundsAnimationDriver cancel() {
+ // Cancel
+ mTarget.mCancelRequested = true;
+ mTarget.mBoundsUpdated = false;
+ mExpectedFinalBounds = null;
+
+ // Update
+ mAnimator.onAnimationUpdate(mMockAnimator.getWithValue(0.5f));
+
+ // Not started, not running, cancel reset
+ assertTrue(!mTarget.mCancelRequested);
+
+ // Stack/task bounds not updated
+ assertTrue(!mTarget.mBoundsUpdated);
+
+ // Callback made
+ assertTrue(mTarget.mAnimationEnded);
+ assertNull(mTarget.mAnimationEndFinalStackBounds);
+
+ return this;
+ }
+
+ BoundsAnimationDriver end() {
+ mAnimator.end();
+
+ // Final stack bounds
+ assertEquals(mTo, mTarget.mStackBounds);
+ assertEquals(mExpectedFinalBounds, mTarget.mAnimationEndFinalStackBounds);
+ assertNull(mTarget.mTaskBounds);
+
+ return this;
+ }
+
+ BoundsAnimationDriver expectEnded(boolean schedulePipModeChanged,
+ boolean moveToFullscreen) {
+ // Callback made
+ assertTrue(mTarget.mAnimationEnded);
+
+ assertEquals(schedulePipModeChanged, mTarget.mSchedulePipModeChangedOnEnd);
+ assertEquals(moveToFullscreen, mTarget.mMovedToFullscreen);
+ return this;
+ }
+
+ private Rect getLargerBounds(Rect r1, Rect r2) {
+ int r1Area = r1.width() * r1.height();
+ int r2Area = r2.width() * r2.height();
+ if (r1Area <= r2Area) {
+ return r2;
+ } else {
+ return r1;
+ }
}
}
// Constants
+ private static final boolean SCHEDULE_PIP_MODE_CHANGED = true;
private static final boolean MOVE_TO_FULLSCREEN = true;
+ private static final int DURATION = 100;
// Some dummy bounds to represent fullscreen and floating bounds
private static final Rect BOUNDS_FULL = new Rect(0, 0, 100, 100);
- private static final Rect BOUNDS_FLOATING = new Rect(80, 80, 95, 95);
- private static final Rect BOUNDS_ALT_FLOATING = new Rect(60, 60, 95, 95);
-
- // Some dummy duration
- private static final int DURATION = 100;
+ private static final Rect BOUNDS_FLOATING = new Rect(60, 60, 95, 95);
+ private static final Rect BOUNDS_SMALLER_FLOATING = new Rect(80, 80, 95, 95);
// Common
- private MockAppTransition mAppTransition;
- private MockValueAnimator mAnimator;
- private TestAnimateBoundsUser mTarget;
+ private MockAppTransition mMockAppTransition;
+ private MockValueAnimator mMockAnimator;
+ private TestBoundsAnimationTarget mTarget;
private BoundsAnimationController mController;
+ private BoundsAnimationDriver mDriver;
// Temp
private Rect mTmpRect = new Rect();
@@ -196,153 +380,184 @@
final Context context = InstrumentationRegistry.getTargetContext();
final Handler handler = new Handler(Looper.getMainLooper());
- mAppTransition = new MockAppTransition(context);
- mAnimator = new MockValueAnimator();
- mTarget = new TestAnimateBoundsUser();
- mController = new BoundsAnimationController(context, mAppTransition, handler);
+ mMockAppTransition = new MockAppTransition(context);
+ mMockAnimator = new MockValueAnimator();
+ mTarget = new TestBoundsAnimationTarget();
+ mController = new BoundsAnimationController(context, mMockAppTransition, handler);
+ mDriver = new BoundsAnimationDriver(mController, mTarget);
}
+ /** BASE TRANSITIONS **/
+
@UiThreadTest
@Test
public void testFullscreenToFloatingTransition() throws Exception {
- // Create and start the animation
- mTarget.reinitialize(BOUNDS_FULL, null);
- final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
- BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
-
- // Assert that when we are started, and that we are not going to fullscreen
- assertTrue(mTarget.mAnimationStarted);
- assertFalse(mTarget.mAnimationStartedToFullscreen);
- // Ensure we are not triggering a PiP mode change
- assertFalse(mTarget.mUpdatedPictureInPictureModeWithBounds);
- // Ensure that the task stack bounds are already frozen to the larger source stack bounds
- assertEquals(BOUNDS_FULL, mTarget.mStackBounds);
- assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
-
- // Drive some animation updates, ensure that only the stack bounds change and the task
- // bounds are frozen to the original stack bounds (adjusted for the offset)
- boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
- assertNotEquals(BOUNDS_FULL, mTarget.mStackBounds);
- assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
- boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(1f));
- assertNotEquals(BOUNDS_FULL, mTarget.mStackBounds);
- assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
-
- // Finish the animation, ensure that it reaches the final bounds with the given state
- boundsAnimator.end();
- assertTrue(mTarget.mAnimationEnded);
- assertEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
- assertNull(mTarget.mTaskBounds);
+ mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
+ .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
+ .update(0f)
+ .update(0.5f)
+ .update(1f)
+ .end()
+ .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
}
@UiThreadTest
@Test
public void testFloatingToFullscreenTransition() throws Exception {
- // Create and start the animation
- mTarget.reinitialize(BOUNDS_FULL, null);
- final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FLOATING,
- BOUNDS_FULL, DURATION, MOVE_TO_FULLSCREEN);
-
- // Assert that when we are started, and that we are going to fullscreen
- assertTrue(mTarget.mAnimationStarted);
- assertTrue(mTarget.mAnimationStartedToFullscreen);
- // Ensure that we update the PiP mode change with the new fullscreen bounds
- assertTrue(mTarget.mUpdatedPictureInPictureModeWithBounds);
- // Ensure that the task stack bounds are already frozen to the larger target stack bounds
- assertEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
- assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
-
- // Drive some animation updates, ensure that only the stack bounds change and the task
- // bounds are frozen to the original stack bounds (adjusted for the offset)
- boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
- assertNotEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
- assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
- boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(1f));
- assertNotEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
- assertEquals(BOUNDS_FULL, offsetToZero(mTarget.mTaskBounds));
-
- // Finish the animation, ensure that it reaches the final bounds with the given state
- boundsAnimator.end();
- assertTrue(mTarget.mAnimationEnded);
- assertEquals(BOUNDS_FULL, mTarget.mStackBounds);
- assertNull(mTarget.mTaskBounds);
+ mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
+ .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
+ .update(0f)
+ .update(0.5f)
+ .update(1f)
+ .end()
+ .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
}
@UiThreadTest
@Test
- public void testInterruptAnimationFromUser() throws Exception {
- // Create and start the animation
- mTarget.reinitialize(BOUNDS_FULL, null);
- final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
- BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
-
- // Cancel the animation on the next update from the user
- mTarget.mRequestCancelAnimation = true;
- mTarget.mBoundsUpdated = false;
- boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
- // Ensure that we got no more updates after returning false and the bounds are not updated
- // to the end value
- assertFalse(mTarget.mBoundsUpdated);
- assertNotEquals(BOUNDS_FLOATING, mTarget.mStackBounds);
- assertNotEquals(BOUNDS_FLOATING, mTarget.mTaskBounds);
- // Ensure that we received the animation end call
- assertTrue(mTarget.mAnimationEnded);
+ public void testFloatingToSmallerFloatingTransition() throws Exception {
+ mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING)
+ .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
+ .update(0f)
+ .update(0.5f)
+ .update(1f)
+ .end()
+ .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
}
@UiThreadTest
@Test
- public void testCancelAnimationFromNewAnimationToExistingBounds() throws Exception {
- // Create and start the animation
- mTarget.reinitialize(BOUNDS_FULL, null);
- final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
- BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
+ public void testFloatingToLargerFloatingTransition() throws Exception {
+ mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING)
+ .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
+ .update(0f)
+ .update(0.5f)
+ .update(1f)
+ .end()
+ .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
+ }
- // Drive some animation updates
- boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
+ /** F->!F w/ CANCEL **/
- // Cancel the animation as a restart to the same bounds
- mTarget.reinitialize(null, null);
- final BoundsAnimator altBoundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
- BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
- // Ensure the animator is the same
- assertSame(boundsAnimator, altBoundsAnimator);
- // Ensure we haven't restarted or finished the animation
- assertFalse(mTarget.mAnimationStarted);
- assertFalse(mTarget.mAnimationEnded);
- // Ensure that we haven't tried to update the PiP mode
- assertFalse(mTarget.mUpdatedPictureInPictureModeWithBounds);
+ @UiThreadTest
+ @Test
+ public void testFullscreenToFloatingCancelFromTarget() throws Exception {
+ mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
+ .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
+ .update(0.25f)
+ .cancel()
+ .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
}
@UiThreadTest
@Test
- public void testCancelAnimationFromNewAnimationToNewBounds() throws Exception {
- // Create and start the animation
- mTarget.reinitialize(BOUNDS_FULL, null);
- final BoundsAnimator boundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
- BOUNDS_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
+ public void testFullscreenToFloatingCancelFromAnimationToSameBounds() throws Exception {
+ mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
+ .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
+ .update(0.25f)
+ .restart(BOUNDS_FLOATING)
+ .end()
+ .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
+ }
- // Drive some animation updates
- boundsAnimator.onAnimationUpdate(mAnimator.getWithValue(0.5f));
+ @UiThreadTest
+ @Test
+ public void testFullscreenToFloatingCancelFromAnimationToFloatingBounds() throws Exception {
+ mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
+ .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
+ .update(0.25f)
+ .restart(BOUNDS_SMALLER_FLOATING)
+ .end()
+ .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
+ }
- // Cancel the animation as a restart to new bounds
- mTarget.reinitialize(null, null);
- final BoundsAnimator altBoundsAnimator = mController.animateBoundsImpl(mTarget, BOUNDS_FULL,
- BOUNDS_ALT_FLOATING, DURATION, !MOVE_TO_FULLSCREEN);
- // Ensure the animator is not the same
- assertNotSame(boundsAnimator, altBoundsAnimator);
- // Ensure that we did not get an animation start/end callback
- assertFalse(mTarget.mAnimationStarted);
- assertFalse(mTarget.mAnimationEnded);
- // Ensure that we haven't tried to update the PiP mode
- assertFalse(mTarget.mUpdatedPictureInPictureModeWithBounds);
+ @UiThreadTest
+ @Test
+ public void testFullscreenToFloatingCancelFromAnimationToFullscreenBounds() throws Exception {
+ mDriver.start(BOUNDS_FULL, BOUNDS_FLOATING)
+ .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
+ .update(0.25f)
+ .restart(BOUNDS_FULL)
+ .end()
+ .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
+ }
+
+ /** !F->F w/ CANCEL **/
+
+ @UiThreadTest
+ @Test
+ public void testFloatingToFullscreenCancelFromTarget() throws Exception {
+ mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
+ .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
+ .update(0.25f)
+ .cancel()
+ .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testFloatingToFullscreenCancelFromAnimationToSameBounds() throws Exception {
+ mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
+ .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
+ .update(0.25f)
+ .restart(BOUNDS_FULL)
+ .end()
+ .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testFloatingToFullscreenCancelFromAnimationToFloatingBounds() throws Exception {
+ mDriver.start(BOUNDS_FLOATING, BOUNDS_FULL)
+ .expectStarted(SCHEDULE_PIP_MODE_CHANGED)
+ .update(0.25f)
+ .restart(BOUNDS_SMALLER_FLOATING)
+ .end()
+ .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
+ }
+
+ /** !F->!F w/ CANCEL **/
+
+ @UiThreadTest
+ @Test
+ public void testFloatingToSmallerFloatingCancelFromTarget() throws Exception {
+ mDriver.start(BOUNDS_FLOATING, BOUNDS_SMALLER_FLOATING)
+ .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
+ .update(0.25f)
+ .cancel()
+ .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testFloatingToLargerFloatingCancelFromTarget() throws Exception {
+ mDriver.start(BOUNDS_SMALLER_FLOATING, BOUNDS_FLOATING)
+ .expectStarted(!SCHEDULE_PIP_MODE_CHANGED)
+ .update(0.25f)
+ .cancel()
+ .expectEnded(!SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
+ }
+
+ /** MISC **/
+
+ @UiThreadTest
+ @Test
+ public void testBoundsAreCopied() throws Exception {
+ Rect from = new Rect(0, 0, 100, 100);
+ Rect to = new Rect(25, 25, 75, 75);
+ mDriver.start(from, to)
+ .update(0.25f)
+ .end();
+ assertEquals(new Rect(0, 0, 100, 100), from);
+ assertEquals(new Rect(25, 25, 75, 75), to);
}
/**
- * @return the bounds offset to zero/zero.
+ * @return whether the task and stack bounds would be the same if they were at the same offset.
*/
- private Rect offsetToZero(Rect bounds) {
- mTmpRect.set(bounds);
- mTmpRect.offsetTo(0, 0);
- return mTmpRect;
+ private boolean assertEqualSizeAtOffset(Rect stackBounds, Rect taskBounds) {
+ mTmpRect.set(taskBounds);
+ mTmpRect.offsetTo(stackBounds.left, stackBounds.top);
+ return stackBounds.equals(mTmpRect);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 0270bb9..837ce56 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -289,6 +290,24 @@
verifySizes(sDisplayContent, smallerWidth, smallerHeight, smallerDensity);
}
+ /**
+ * This test enforces that the pinned stack is always kept as the top stack.
+ */
+ @Test
+ public void testPinnedStackLocation() {
+ createStackControllerOnStackOnDisplay(PINNED_STACK_ID, sDisplayContent);
+ final int initialStackCount = sDisplayContent.getStackCount();
+ // Ensure that the pinned stack was placed at the end
+ assertEquals(initialStackCount - 1, sDisplayContent.getStaskPosById(PINNED_STACK_ID));
+ // By default, this should try to create a new stack on top
+ createTaskStackOnDisplay(sDisplayContent);
+ final int afterStackCount = sDisplayContent.getStackCount();
+ // Make sure the stack count has increased
+ assertEquals(initialStackCount + 1, afterStackCount);
+ // Ensure that the pinned stack is still on top
+ assertEquals(afterStackCount - 1, sDisplayContent.getStaskPosById(PINNED_STACK_ID));
+ }
+
private static void verifySizes(DisplayContent displayContent, int expectedBaseWidth,
int expectedBaseHeight, int expectedBaseDensity) {
assertEquals(displayContent.mBaseDisplayWidth, expectedBaseWidth);
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index fbeda0a..9392e8e 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -90,6 +90,7 @@
return null;
}).when(am).notifyKeyguardFlagsChanged(any());
}
+
sWm = WindowManagerService.main(context, mock(InputManagerService.class), true, false,
false, new TestWindowManagerPolicy());
}
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
index 3a44357..ae3eb52 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestUtils.java
@@ -48,6 +48,13 @@
}
/**
+ * Retrieves an instance of a mock {@link WindowManagerService}.
+ */
+ public static WindowManagerService getMockWindowManagerService() {
+ return mock(WindowManagerService.class);
+ }
+
+ /**
* Creates a mock instance of {@link StackWindowController}.
*/
public static StackWindowController createMockStackWindowContainerController() {
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 5ad7f80..094c7bd 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -35,6 +35,7 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelableException;
import android.os.StatFs;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -150,7 +151,7 @@
try {
return mInstaller.isQuotaSupported(volumeUuid);
} catch (InstallerException e) {
- throw new IllegalStateException(e);
+ throw new ParcelableException(new IOException(e.getMessage()));
}
}
@@ -163,7 +164,8 @@
} else {
final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid);
if (vol == null) {
- throw new IllegalStateException("Volume was unexpected null");
+ throw new ParcelableException(
+ new IOException("Failed to find storage device for UUID " + volumeUuid));
}
return FileUtils.roundStorageSize(vol.disk.size);
}
@@ -189,7 +191,8 @@
} else {
final VolumeInfo vol = mStorage.findVolumeByUuid(volumeUuid);
if (vol == null) {
- throw new IllegalStateException("Volume was unexpected null");
+ throw new ParcelableException(
+ new IOException("Failed to find storage device for UUID " + volumeUuid));
}
return vol.getPath().getUsableSpace() + cacheBytes;
}
@@ -221,7 +224,7 @@
appInfo = mPackage.getApplicationInfoAsUser(packageName,
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
} catch (NameNotFoundException e) {
- throw new IllegalStateException(e);
+ throw new ParcelableException(e);
}
if (mPackage.getPackagesForUid(appInfo.uid).length == 1) {
@@ -239,7 +242,7 @@
mInstaller.getAppSize(volumeUuid, packageNames, userId, 0,
appId, ceDataInodes, codePaths, stats);
} catch (InstallerException e) {
- throw new IllegalStateException(e);
+ throw new ParcelableException(new IOException(e.getMessage()));
}
return translate(stats);
}
@@ -265,7 +268,7 @@
codePaths[i] = mPackage.getApplicationInfoAsUser(packageNames[i],
PackageManager.MATCH_UNINSTALLED_PACKAGES, userId).getCodePath();
} catch (NameNotFoundException e) {
- throw new IllegalStateException(e);
+ throw new ParcelableException(e);
}
}
@@ -281,7 +284,7 @@
checkEquals("UID " + uid, manualStats, stats);
}
} catch (InstallerException e) {
- throw new IllegalStateException(e);
+ throw new ParcelableException(new IOException(e.getMessage()));
}
return translate(stats);
}
@@ -313,7 +316,7 @@
checkEquals("User " + userId, manualStats, stats);
}
} catch (InstallerException e) {
- throw new IllegalStateException(e);
+ throw new ParcelableException(new IOException(e.getMessage()));
}
return translate(stats);
}
@@ -336,7 +339,7 @@
checkEquals("External " + userId, manualStats, stats);
}
} catch (InstallerException e) {
- throw new IllegalStateException(e);
+ throw new ParcelableException(new IOException(e.getMessage()));
}
final ExternalStorageStats res = new ExternalStorageStats();
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 1874d8d..0342da7 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -112,20 +112,26 @@
"android.telecom.action.CHANGE_PHONE_ACCOUNTS";
/**
- * The {@link android.content.Intent} action used indicate that a new phone account was
- * just registered.
- * @hide
+ * {@link android.content.Intent} action used indicate that a new phone account was just
+ * registered.
+ * <p>
+ * The Intent {@link Intent#getExtras() extras} will contain {@link #EXTRA_PHONE_ACCOUNT_HANDLE}
+ * to indicate which {@link PhoneAccount} was registered.
+ * <p>
+ * Will only be sent to the default dialer app (see {@link #getDefaultDialerPackage()}).
*/
- @SystemApi
public static final String ACTION_PHONE_ACCOUNT_REGISTERED =
"android.telecom.action.PHONE_ACCOUNT_REGISTERED";
/**
- * The {@link android.content.Intent} action used indicate that a phone account was
- * just unregistered.
- * @hide
+ * {@link android.content.Intent} action used indicate that a phone account was just
+ * unregistered.
+ * <p>
+ * The Intent {@link Intent#getExtras() extras} will contain {@link #EXTRA_PHONE_ACCOUNT_HANDLE}
+ * to indicate which {@link PhoneAccount} was unregistered.
+ * <p>
+ * Will only be sent to the default dialer app (see {@link #getDefaultDialerPackage()}).
*/
- @SystemApi
public static final String ACTION_PHONE_ACCOUNT_UNREGISTERED =
"android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/telephony/Telephony.java
index 943a6ca..eeaf2c1 100644
--- a/telephony/java/android/telephony/Telephony.java
+++ b/telephony/java/android/telephony/Telephony.java
@@ -19,18 +19,21 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.TestApi;
+import android.app.job.JobService;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
+import android.database.ContentObserver;
import android.database.sqlite.SqliteWrapper;
import android.net.Uri;
+import android.telephony.Rlog;
+import android.telephony.ServiceState;
import android.telephony.SmsMessage;
import android.telephony.SubscriptionManager;
import android.text.TextUtils;
-import android.telephony.Rlog;
import android.util.Patterns;
import com.android.internal.telephony.PhoneConstants;
@@ -44,7 +47,7 @@
/**
* The Telephony provider contains data related to phone operation, specifically SMS and MMS
- * messages and access to the APN list, including the MMSC to use.
+ * messages, access to the APN list, including the MMSC to use, and the service state.
*
* <p class="note"><strong>Note:</strong> These APIs are not available on all Android-powered
* devices. If your app depends on telephony features such as for managing SMS messages, include
@@ -2972,4 +2975,272 @@
CMAS_CERTAINTY
};
}
+
+ /**
+ * Constants for interfacing with the ServiceStateProvider and the different fields of the
+ * {@link ServiceState} class accessible through the provider.
+ */
+ public static final class ServiceStateTable {
+
+ /**
+ * Not instantiable.
+ * @hide
+ */
+ private ServiceStateTable() {}
+
+ /**
+ * The authority string for the ServiceStateProvider
+ */
+ public static final String AUTHORITY = "service-state";
+
+ /**
+ * The {@code content://} style URL for the ServiceStateProvider
+ */
+ public static final Uri CONTENT_URI = Uri.parse("content://service-state/");
+
+ /**
+ * Generates a content {@link Uri} used to receive updates on a specific field in the
+ * ServiceState provider.
+ * <p>
+ * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+ * {@link ServiceState} while your app is running. You can also use a {@link JobService} to
+ * ensure your app is notified of changes to the {@link Uri} even when it is not running.
+ * Note, however, that using a {@link JobService} does not guarantee timely delivery of
+ * updates to the {@link Uri}.
+ *
+ * @param subId the subId to receive updates on
+ * @param field the ServiceState field to receive updates on
+ * @return the Uri used to observe {@link ServiceState} changes
+ */
+ public static Uri getUriForSubIdAndField(int subId, String field) {
+ return CONTENT_URI.buildUpon().appendEncodedPath(String.valueOf(subId))
+ .appendEncodedPath(field).build();
+ }
+
+ /**
+ * Generates a content {@link Uri} used to receive updates on every field in the
+ * ServiceState provider.
+ * <p>
+ * Use this {@link Uri} with a {@link ContentObserver} to be notified of changes to the
+ * {@link ServiceState} while your app is running. You can also use a {@link JobService} to
+ * ensure your app is notified of changes to the {@link Uri} even when it is not running.
+ * Note, however, that using a {@link JobService} does not guarantee timely delivery of
+ * updates to the {@link Uri}.
+ *
+ * @param subId the subId to receive updates on
+ * @return the Uri used to observe {@link ServiceState} changes
+ */
+ public static Uri getUriForSubId(int subId) {
+ return CONTENT_URI.buildUpon().appendEncodedPath(String.valueOf(subId)).build();
+ }
+
+ /**
+ * Used to insert a ServiceState into the ServiceStateProvider as a ContentValues instance.
+ *
+ * @param state the ServiceState to convert into ContentValues
+ * @return the convertedContentValues instance
+ * @hide
+ */
+ public static ContentValues getContentValuesForServiceState(ServiceState state) {
+ ContentValues values = new ContentValues();
+ values.put(VOICE_REG_STATE, state.getVoiceRegState());
+ values.put(DATA_REG_STATE, state.getDataRegState());
+ values.put(VOICE_ROAMING_TYPE, state.getVoiceRoamingType());
+ values.put(DATA_ROAMING_TYPE, state.getDataRoamingType());
+ values.put(VOICE_OPERATOR_ALPHA_LONG, state.getVoiceOperatorAlphaLong());
+ values.put(VOICE_OPERATOR_ALPHA_SHORT, state.getVoiceOperatorAlphaShort());
+ values.put(VOICE_OPERATOR_NUMERIC, state.getVoiceOperatorNumeric());
+ values.put(DATA_OPERATOR_ALPHA_LONG, state.getDataOperatorAlphaLong());
+ values.put(DATA_OPERATOR_ALPHA_SHORT, state.getDataOperatorAlphaShort());
+ values.put(DATA_OPERATOR_NUMERIC, state.getDataOperatorNumeric());
+ values.put(IS_MANUAL_NETWORK_SELECTION, state.getIsManualSelection());
+ values.put(RIL_VOICE_RADIO_TECHNOLOGY, state.getRilVoiceRadioTechnology());
+ values.put(RIL_DATA_RADIO_TECHNOLOGY, state.getRilDataRadioTechnology());
+ values.put(CSS_INDICATOR, state.getCssIndicator());
+ values.put(NETWORK_ID, state.getNetworkId());
+ values.put(SYSTEM_ID, state.getSystemId());
+ values.put(CDMA_ROAMING_INDICATOR, state.getCdmaRoamingIndicator());
+ values.put(CDMA_DEFAULT_ROAMING_INDICATOR, state.getCdmaDefaultRoamingIndicator());
+ values.put(CDMA_ERI_ICON_INDEX, state.getCdmaEriIconIndex());
+ values.put(CDMA_ERI_ICON_MODE, state.getCdmaEriIconMode());
+ values.put(IS_EMERGENCY_ONLY, state.isEmergencyOnly());
+ values.put(IS_DATA_ROAMING_FROM_REGISTRATION, state.getDataRoamingFromRegistration());
+ values.put(IS_USING_CARRIER_AGGREGATION, state.isUsingCarrierAggregation());
+ return values;
+ }
+
+ /**
+ * An integer value indicating the current voice service state.
+ * <p>
+ * Valid values: {@link ServiceState#STATE_IN_SERVICE},
+ * {@link ServiceState#STATE_OUT_OF_SERVICE}, {@link ServiceState#STATE_EMERGENCY_ONLY},
+ * {@link ServiceState#STATE_POWER_OFF}.
+ * <p>
+ * This is the same as {@link ServiceState#getState()}.
+ */
+ public static final String VOICE_REG_STATE = "voice_reg_state";
+
+ /**
+ * An integer value indicating the current data service state.
+ * <p>
+ * Valid values: {@link ServiceState#STATE_IN_SERVICE},
+ * {@link ServiceState#STATE_OUT_OF_SERVICE}, {@link ServiceState#STATE_EMERGENCY_ONLY},
+ * {@link ServiceState#STATE_POWER_OFF}.
+ * <p>
+ * This is the same as {@link ServiceState#getDataRegState()}.
+ * @hide
+ */
+ public static final String DATA_REG_STATE = "data_reg_state";
+
+ /**
+ * An integer value indicating the current voice roaming type.
+ * <p>
+ * This is the same as {@link ServiceState#getVoiceRoamingType()}.
+ * @hide
+ */
+ public static final String VOICE_ROAMING_TYPE = "voice_roaming_type";
+
+ /**
+ * An integer value indicating the current data roaming type.
+ * <p>
+ * This is the same as {@link ServiceState#getDataRoamingType()}.
+ * @hide
+ */
+ public static final String DATA_ROAMING_TYPE = "data_roaming_type";
+
+ /**
+ * The current registered voice network operator name in long alphanumeric format.
+ * <p>
+ * This is the same as {@link ServiceState#getVoiceOperatorAlphaLong()}.
+ * @hide
+ */
+ public static final String VOICE_OPERATOR_ALPHA_LONG = "voice_operator_alpha_long";
+
+ /**
+ * The current registered operator name in short alphanumeric format.
+ * <p>
+ * In GSM/UMTS, short format can be up to 8 characters long. The current registered voice
+ * network operator name in long alphanumeric format.
+ * <p>
+ * This is the same as {@link ServiceState#getVoiceOperatorAlphaShort()}.
+ * @hide
+ */
+ public static final String VOICE_OPERATOR_ALPHA_SHORT = "voice_operator_alpha_short";
+
+
+ /**
+ * The current registered operator numeric id.
+ * <p>
+ * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit
+ * network code.
+ * <p>
+ * This is the same as {@link ServiceState#getOperatorNumeric()}.
+ */
+ public static final String VOICE_OPERATOR_NUMERIC = "voice_operator_numeric";
+
+ /**
+ * The current registered data network operator name in long alphanumeric format.
+ * <p>
+ * This is the same as {@link ServiceState#getDataOperatorAlphaLong()}.
+ * @hide
+ */
+ public static final String DATA_OPERATOR_ALPHA_LONG = "data_operator_alpha_long";
+
+ /**
+ * The current registered data network operator name in short alphanumeric format.
+ * <p>
+ * This is the same as {@link ServiceState#getDataOperatorAlphaShort()}.
+ * @hide
+ */
+ public static final String DATA_OPERATOR_ALPHA_SHORT = "data_operator_alpha_short";
+
+ /**
+ * The current registered data network operator numeric id.
+ * <p>
+ * This is the same as {@link ServiceState#getDataOperatorNumeric()}.
+ * @hide
+ */
+ public static final String DATA_OPERATOR_NUMERIC = "data_operator_numeric";
+
+ /**
+ * The current network selection mode.
+ * <p>
+ * This is the same as {@link ServiceState#getIsManualSelection()}.
+ */
+ public static final String IS_MANUAL_NETWORK_SELECTION = "is_manual_network_selection";
+
+ /**
+ * This is the same as {@link ServiceState#getRilVoiceRadioTechnology()}.
+ * @hide
+ */
+ public static final String RIL_VOICE_RADIO_TECHNOLOGY = "ril_voice_radio_technology";
+
+ /**
+ * This is the same as {@link ServiceState#getRilDataRadioTechnology()}.
+ * @hide
+ */
+ public static final String RIL_DATA_RADIO_TECHNOLOGY = "ril_data_radio_technology";
+
+ /**
+ * This is the same as {@link ServiceState#getCssIndicator()}.
+ * @hide
+ */
+ public static final String CSS_INDICATOR = "css_indicator";
+
+ /**
+ * This is the same as {@link ServiceState#getNetworkId()}.
+ * @hide
+ */
+ public static final String NETWORK_ID = "network_id";
+
+ /**
+ * This is the same as {@link ServiceState#getSystemId()}.
+ * @hide
+ */
+ public static final String SYSTEM_ID = "system_id";
+
+ /**
+ * This is the same as {@link ServiceState#getCdmaRoamingIndicator()}.
+ * @hide
+ */
+ public static final String CDMA_ROAMING_INDICATOR = "cdma_roaming_indicator";
+
+ /**
+ * This is the same as {@link ServiceState#getCdmaDefaultRoamingIndicator()}.
+ * @hide
+ */
+ public static final String CDMA_DEFAULT_ROAMING_INDICATOR =
+ "cdma_default_roaming_indicator";
+
+ /**
+ * This is the same as {@link ServiceState#getCdmaEriIconIndex()}.
+ * @hide
+ */
+ public static final String CDMA_ERI_ICON_INDEX = "cdma_eri_icon_index";
+
+ /**
+ * This is the same as {@link ServiceState#getCdmaEriIconMode()}.
+ * @hide
+ */
+ public static final String CDMA_ERI_ICON_MODE = "cdma_eri_icon_mode";
+
+ /**
+ * This is the same as {@link ServiceState#isEmergencyOnly()}.
+ * @hide
+ */
+ public static final String IS_EMERGENCY_ONLY = "is_emergency_only";
+
+ /**
+ * This is the same as {@link ServiceState#getDataRoamingFromRegistration()}.
+ * @hide
+ */
+ public static final String IS_DATA_ROAMING_FROM_REGISTRATION =
+ "is_data_roaming_from_registration";
+
+ /**
+ * This is the same as {@link ServiceState#isUsingCarrierAggregation()}.
+ * @hide
+ */
+ public static final String IS_USING_CARRIER_AGGREGATION = "is_using_carrier_aggregation";
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 397aa00..8ee6454 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -911,7 +911,7 @@
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
public String getDeviceSoftwareVersion() {
- return getDeviceSoftwareVersion(getDefaultSim());
+ return getDeviceSoftwareVersion(getSlotIndex());
}
/**
@@ -997,7 +997,7 @@
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
public String getImei() {
- return getImei(getDefaultSim());
+ return getImei(getSlotIndex());
}
/**
@@ -1029,7 +1029,7 @@
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
*/
public String getMeid() {
- return getMeid(getDefaultSim());
+ return getMeid(getSlotIndex());
}
/**
@@ -1059,7 +1059,7 @@
*/
/** {@hide}*/
public String getNai() {
- return getNai(getDefaultSim());
+ return getNai(getSlotIndex());
}
/**
@@ -1303,7 +1303,7 @@
}
private int getPhoneTypeFromProperty() {
- return getPhoneTypeFromProperty(getDefaultPhone());
+ return getPhoneTypeFromProperty(getPhoneId());
}
/** {@hide} */
@@ -1317,7 +1317,7 @@
}
private int getPhoneTypeFromNetworkType() {
- return getPhoneTypeFromNetworkType(getDefaultPhone());
+ return getPhoneTypeFromNetworkType(getPhoneId());
}
/** {@hide} */
@@ -1500,7 +1500,7 @@
* on a CDMA network).
*/
public String getNetworkOperator() {
- return getNetworkOperatorForPhone(getDefaultPhone());
+ return getNetworkOperatorForPhone(getPhoneId());
}
/**
@@ -1546,7 +1546,7 @@
* @see #createForPhoneAccountHandle(PhoneAccountHandle)
*/
public String getNetworkSpecifier() {
- return String.valueOf(mSubId);
+ return String.valueOf(getSubId());
}
/**
@@ -1565,7 +1565,7 @@
public PersistableBundle getCarrierConfig() {
CarrierConfigManager carrierConfigManager = mContext
.getSystemService(CarrierConfigManager.class);
- return carrierConfigManager.getConfigForSubId(mSubId);
+ return carrierConfigManager.getConfigForSubId(getSubId());
}
/**
@@ -1602,7 +1602,7 @@
* on a CDMA network).
*/
public String getNetworkCountryIso() {
- return getNetworkCountryIsoForPhone(getDefaultPhone());
+ return getNetworkCountryIsoForPhone(getPhoneId());
}
/**
@@ -1748,6 +1748,9 @@
* Returns a constant indicating the radio technology (network type)
* currently in use on the device for data transmission.
*
+ * If this object has been created with {@link #createForSubscriptionId}, applies to the given
+ * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
* <p>
* Requires Permission:
* {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
@@ -1772,7 +1775,7 @@
* @see #NETWORK_TYPE_HSPAP
*/
public int getDataNetworkType() {
- return getDataNetworkType(getSubId());
+ return getDataNetworkType(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
}
/**
@@ -1996,7 +1999,7 @@
* @return true if a ICC card is present
*/
public boolean hasIccCard() {
- return hasIccCard(getDefaultSim());
+ return hasIccCard(getSlotIndex());
}
/**
@@ -2037,7 +2040,7 @@
* @see #SIM_STATE_CARD_RESTRICTED
*/
public int getSimState() {
- int slotIndex = getDefaultSim();
+ int slotIndex = getSlotIndex();
// slotIndex may be invalid due to sim being absent. In that case query all slots to get
// sim state
if (slotIndex < 0) {
@@ -2166,7 +2169,7 @@
* @see #getSimState
*/
public String getSimOperatorName() {
- return getSimOperatorNameForPhone(getDefaultPhone());
+ return getSimOperatorNameForPhone(getPhoneId());
}
/**
@@ -2198,7 +2201,7 @@
* Returns the ISO country code equivalent for the SIM provider's country code.
*/
public String getSimCountryIso() {
- return getSimCountryIsoForPhone(getDefaultPhone());
+ return getSimCountryIsoForPhone(getPhoneId());
}
/**
@@ -2754,7 +2757,7 @@
ITelephony telephony = getITelephony();
if (telephony != null) {
return telephony
- .getVisualVoicemailPackageName(mContext.getOpPackageName(), mSubId);
+ .getVisualVoicemailPackageName(mContext.getOpPackageName(), getSubId());
}
} catch (RemoteException ex) {
} catch (NullPointerException ex) {
@@ -4057,35 +4060,67 @@
* subId is returned. Otherwise, the default subId will be returned.
*/
private int getSubId() {
- if (mSubId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID) {
- return getDefaultSubscription();
+ if (SubscriptionManager.isUsableSubIdValue(mSubId)) {
+ return mSubId;
}
- return mSubId;
+ return SubscriptionManager.getDefaultSubscriptionId();
}
/**
- * Returns Default subscription.
+ * Return an appropriate subscription ID for any situation.
+ *
+ * If this object has been created with {@link #createForSubscriptionId}, then the provided
+ * subId is returned. Otherwise, the preferred subId which is based on caller's context is
+ * returned.
+ * {@see SubscriptionManager#getDefaultDataSubscriptionId()}
+ * {@see SubscriptionManager#getDefaultVoiceSubscriptionId()}
+ * {@see SubscriptionManager#getDefaultSmsSubscriptionId()}
*/
- private static int getDefaultSubscription() {
- return SubscriptionManager.getDefaultSubscriptionId();
+ private int getSubId(int preferredSubId) {
+ if (SubscriptionManager.isUsableSubIdValue(mSubId)) {
+ return mSubId;
+ }
+ return preferredSubId;
}
/**
- * Returns Default phone.
+ * Return an appropriate phone ID for any situation.
+ *
+ * If this object has been created with {@link #createForSubscriptionId}, then the phoneId
+ * associated with the provided subId is returned. Otherwise, the default phoneId associated
+ * with the default subId will be returned.
*/
- private static int getDefaultPhone() {
- return SubscriptionManager.getPhoneId(SubscriptionManager.getDefaultSubscriptionId());
+ private int getPhoneId() {
+ return SubscriptionManager.getPhoneId(getSubId());
}
/**
- * @return default SIM's slot index. If SIM is not inserted, return default SIM slot index.
+ * Return an appropriate phone ID for any situation.
+ *
+ * If this object has been created with {@link #createForSubscriptionId}, then the phoneId
+ * associated with the provided subId is returned. Otherwise, return the phoneId associated
+ * with the preferred subId based on caller's context.
+ * {@see SubscriptionManager#getDefaultDataSubscriptionId()}
+ * {@see SubscriptionManager#getDefaultVoiceSubscriptionId()}
+ * {@see SubscriptionManager#getDefaultSmsSubscriptionId()}
+ */
+ private int getPhoneId(int preferredSubId) {
+ return SubscriptionManager.getPhoneId(getSubId(preferredSubId));
+ }
+
+ /**
+ * Return an appropriate slot index for any situation.
+ *
+ * if this object has been created with {@link #createForSubscriptionId}, then the slot index
+ * associated with the provided subId is returned. Otherwise, return the slot index associated
+ * with the default subId.
+ * If SIM is not inserted, return default SIM slot index.
*
* {@hide}
*/
@VisibleForTesting
- public int getDefaultSim() {
- int slotIndex = SubscriptionManager.getSlotIndex(
- SubscriptionManager.getDefaultSubscriptionId());
+ public int getSlotIndex() {
+ int slotIndex = SubscriptionManager.getSlotIndex(getSubId());
if (slotIndex == SubscriptionManager.SIM_NOT_INSERTED) {
slotIndex = SubscriptionManager.DEFAULT_SIM_SLOT_INDEX;
}
@@ -4393,7 +4428,7 @@
* Returns null if the query fails.
*
*
- * <p>Requires that the caller has READ_PRIVILEGED_PHONE_STATE
+ * <p>Requires that the caller has READ_PHONE_STATE
*
* @return an array of forbidden PLMNs or null if not available
*/
@@ -4406,7 +4441,7 @@
* Returns null if the query fails.
*
*
- * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
+ * <p>Requires that the calling app has READ_PHONE_STATE
*
* @param subId subscription ID used for authentication
* @param appType the icc application type, like {@link #APPTYPE_USIM}
@@ -4902,7 +4937,7 @@
/** @hide */
@SystemApi
public List<String> getCarrierPackageNamesForIntent(Intent intent) {
- return getCarrierPackageNamesForIntentAndPhone(intent, getDefaultPhone());
+ return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId());
}
/** @hide */
@@ -5164,7 +5199,7 @@
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- telephony.handleUssdRequest(mSubId, ussdRequest, wrappedCallback);
+ telephony.handleUssdRequest(getSubId(), ussdRequest, wrappedCallback);
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#sendUSSDCode", e);
@@ -5184,7 +5219,8 @@
public boolean isConcurrentVoiceAndDataSupported() {
try {
ITelephony telephony = getITelephony();
- return (telephony == null ? false : telephony.isConcurrentVoiceAndDataAllowed(mSubId));
+ return (telephony == null ? false : telephony.isConcurrentVoiceAndDataAllowed(
+ getSubId()));
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#isConcurrentVoiceAndDataAllowed", e);
}
@@ -5321,6 +5357,8 @@
/**
* Turns mobile data on or off.
+ * If this object has been created with {@link #createForSubscriptionId}, applies to the given
+ * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
*
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
@@ -5331,7 +5369,7 @@
* @see #hasCarrierPrivileges
*/
public void setDataEnabled(boolean enable) {
- setDataEnabled(getSubId(), enable);
+ setDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enable);
}
/** @hide */
@@ -5361,6 +5399,9 @@
/**
* Returns whether mobile data is enabled or not.
*
+ * If this object has been created with {@link #createForSubscriptionId}, applies to the given
+ * subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+ *
* <p>Requires one of the following permissions:
* {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
@@ -5376,7 +5417,7 @@
*/
@SuppressWarnings("deprecation")
public boolean isDataEnabled() {
- return getDataEnabled(getSubId());
+ return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
}
/**
@@ -5626,7 +5667,7 @@
* @hide
*/
public void setSimOperatorNumeric(String numeric) {
- int phoneId = getDefaultPhone();
+ int phoneId = getPhoneId();
setSimOperatorNumericForPhone(phoneId, numeric);
}
@@ -5646,7 +5687,7 @@
* @hide
*/
public void setSimOperatorName(String name) {
- int phoneId = getDefaultPhone();
+ int phoneId = getPhoneId();
setSimOperatorNameForPhone(phoneId, name);
}
@@ -5666,7 +5707,7 @@
* @hide
*/
public void setSimCountryIso(String iso) {
- int phoneId = getDefaultPhone();
+ int phoneId = getPhoneId();
setSimCountryIsoForPhone(phoneId, iso);
}
@@ -5686,7 +5727,7 @@
* @hide
*/
public void setSimState(String state) {
- int phoneId = getDefaultPhone();
+ int phoneId = getPhoneId();
setSimStateForPhone(phoneId, state);
}
@@ -5711,7 +5752,7 @@
* @hide
**/
public void setSimPowerState(boolean powerUp) {
- setSimPowerStateForSlot(getDefaultSim(), powerUp);
+ setSimPowerStateForSlot(getSlotIndex(), powerUp);
}
/**
@@ -5745,7 +5786,7 @@
* @hide
*/
public void setBasebandVersion(String version) {
- int phoneId = getDefaultPhone();
+ int phoneId = getPhoneId();
setBasebandVersionForPhone(phoneId, version);
}
@@ -5772,7 +5813,7 @@
* @hide
*/
public void setPhoneType(int type) {
- int phoneId = getDefaultPhone();
+ int phoneId = getPhoneId();
setPhoneType(phoneId, type);
}
@@ -5800,7 +5841,7 @@
* @hide
*/
public String getOtaSpNumberSchema(String defaultValue) {
- int phoneId = getDefaultPhone();
+ int phoneId = getPhoneId();
return getOtaSpNumberSchemaForPhone(phoneId, defaultValue);
}
@@ -5831,7 +5872,7 @@
* @hide
*/
public boolean getSmsReceiveCapable(boolean defaultValue) {
- int phoneId = getDefaultPhone();
+ int phoneId = getPhoneId();
return getSmsReceiveCapableForPhone(phoneId, defaultValue);
}
@@ -5862,7 +5903,7 @@
* @hide
*/
public boolean getSmsSendCapable(boolean defaultValue) {
- int phoneId = getDefaultPhone();
+ int phoneId = getPhoneId();
return getSmsSendCapableForPhone(phoneId, defaultValue);
}
@@ -5890,7 +5931,7 @@
* @hide
*/
public void setNetworkOperatorName(String name) {
- int phoneId = getDefaultPhone();
+ int phoneId = getPhoneId();
setNetworkOperatorNameForPhone(phoneId, name);
}
@@ -5912,7 +5953,7 @@
* @hide
*/
public void setNetworkOperatorNumeric(String numeric) {
- int phoneId = getDefaultPhone();
+ int phoneId = getPhoneId();
setNetworkOperatorNumericForPhone(phoneId, numeric);
}
@@ -5932,7 +5973,7 @@
* @hide
*/
public void setNetworkRoaming(boolean isRoaming) {
- int phoneId = getDefaultPhone();
+ int phoneId = getPhoneId();
setNetworkRoamingForPhone(phoneId, isRoaming);
}
@@ -5956,7 +5997,7 @@
* @hide
*/
public void setNetworkCountryIso(String iso) {
- int phoneId = getDefaultPhone();
+ int phoneId = getPhoneId();
setNetworkCountryIsoForPhone(phoneId, iso);
}
@@ -5976,11 +6017,15 @@
/**
* Set the network type currently in use on the device for data transmission.
+ *
+ * If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * phoneId associated with the given subId. Otherwise, applies to the phoneId associated with
+ * {@link SubscriptionManager#getDefaultDataSubscriptionId()}
* @param type the network type currently in use on the device for data transmission
* @hide
*/
public void setDataNetworkType(int type) {
- int phoneId = getDefaultPhone();
+ int phoneId = getPhoneId(SubscriptionManager.getDefaultDataSubscriptionId());
setDataNetworkTypeForPhone(phoneId, type);
}
@@ -6200,7 +6245,7 @@
* @hide
*/
public String getAidForAppType(int appType) {
- return getAidForAppType(getDefaultSubscription(), appType);
+ return getAidForAppType(getSubId(), appType);
}
/**
@@ -6234,7 +6279,7 @@
* @hide
*/
public String getEsn() {
- return getEsn(getDefaultSubscription());
+ return getEsn(getSubId());
}
/**
@@ -6267,7 +6312,7 @@
* @hide
*/
public String getCdmaPrlVersion() {
- return getCdmaPrlVersion(getDefaultSubscription());
+ return getCdmaPrlVersion(getSubId());
}
/**
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index a8eb986..b4e3a47 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -530,13 +530,6 @@
throw new UnsupportedOperationException();
}
- /** STOPSHIP remove when trial API is turned down */
- @Override
- public ComponentName startServiceInForeground(Intent service,
- int id, Notification notification) {
- throw new UnsupportedOperationException();
- }
-
@Override
public boolean stopService(Intent service) {
throw new UnsupportedOperationException();
@@ -554,13 +547,6 @@
throw new UnsupportedOperationException();
}
- /** @hide STOPSHIP removed when trial API is turned down */
- @Override
- public ComponentName startServiceInForegroundAsUser(Intent service,
- int id, Notification notification, UserHandle user) {
- throw new UnsupportedOperationException();
- }
-
/** @hide */
@Override
public boolean stopServiceAsUser(Intent service, UserHandle user) {
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 960a2d9..20392e7 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -1126,4 +1126,12 @@
public ComponentName getInstantAppResolverSettingsComponent() {
throw new UnsupportedOperationException();
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public ComponentName getInstantAppInstallerComponent() {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
new file mode 100644
index 0000000..e3b06c8
--- /dev/null
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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.net;
+
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
+import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+
+import android.net.NetworkCapabilities;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NetworkCapabilitiesTest {
+ @Test
+ public void testMaybeMarkCapabilitiesRestricted() {
+ // verify EIMS is restricted
+ assertEquals((1 << NET_CAPABILITY_EIMS) & RESTRICTED_CAPABILITIES,
+ (1 << NET_CAPABILITY_EIMS));
+
+ // verify CBS is also restricted
+ assertEquals((1 << NET_CAPABILITY_CBS) & RESTRICTED_CAPABILITIES,
+ (1 << NET_CAPABILITY_CBS));
+
+ // verify default is not restricted
+ assertEquals((1 << NET_CAPABILITY_INTERNET) & RESTRICTED_CAPABILITIES, 0);
+
+ // just to see
+ assertEquals(RESTRICTED_CAPABILITIES & UNRESTRICTED_CAPABILITIES, 0);
+
+ // check that internet does not get restricted
+ NetworkCapabilities netCap = new NetworkCapabilities();
+ netCap.addCapability(NET_CAPABILITY_INTERNET);
+ netCap.maybeMarkCapabilitiesRestricted();
+ assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+
+ // metered-ness shouldn't matter
+ netCap = new NetworkCapabilities();
+ netCap.addCapability(NET_CAPABILITY_INTERNET);
+ netCap.addCapability(NET_CAPABILITY_NOT_METERED);
+ netCap.maybeMarkCapabilitiesRestricted();
+ assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+ netCap = new NetworkCapabilities();
+ netCap.addCapability(NET_CAPABILITY_INTERNET);
+ netCap.removeCapability(NET_CAPABILITY_NOT_METERED);
+ netCap.maybeMarkCapabilitiesRestricted();
+ assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+
+ // add EIMS - bundled with unrestricted means it's unrestricted
+ netCap = new NetworkCapabilities();
+ netCap.addCapability(NET_CAPABILITY_INTERNET);
+ netCap.addCapability(NET_CAPABILITY_EIMS);
+ netCap.addCapability(NET_CAPABILITY_NOT_METERED);
+ netCap.maybeMarkCapabilitiesRestricted();
+ assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+ netCap = new NetworkCapabilities();
+ netCap.addCapability(NET_CAPABILITY_INTERNET);
+ netCap.addCapability(NET_CAPABILITY_EIMS);
+ netCap.removeCapability(NET_CAPABILITY_NOT_METERED);
+ netCap.maybeMarkCapabilitiesRestricted();
+ assertTrue(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+
+ // just a restricted cap should be restricted regardless of meteredness
+ netCap = new NetworkCapabilities();
+ netCap.addCapability(NET_CAPABILITY_EIMS);
+ netCap.addCapability(NET_CAPABILITY_NOT_METERED);
+ netCap.maybeMarkCapabilitiesRestricted();
+ assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+ netCap = new NetworkCapabilities();
+ netCap.addCapability(NET_CAPABILITY_EIMS);
+ netCap.removeCapability(NET_CAPABILITY_NOT_METERED);
+ netCap.maybeMarkCapabilitiesRestricted();
+ assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+
+ // try 2 restricted caps
+ netCap = new NetworkCapabilities();
+ netCap.addCapability(NET_CAPABILITY_CBS);
+ netCap.addCapability(NET_CAPABILITY_EIMS);
+ netCap.addCapability(NET_CAPABILITY_NOT_METERED);
+ netCap.maybeMarkCapabilitiesRestricted();
+ assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+ netCap = new NetworkCapabilities();
+ netCap.addCapability(NET_CAPABILITY_CBS);
+ netCap.addCapability(NET_CAPABILITY_EIMS);
+ netCap.removeCapability(NET_CAPABILITY_NOT_METERED);
+ netCap.maybeMarkCapabilitiesRestricted();
+ assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
+ }
+
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index c562cb9..6140a896 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -23,7 +23,12 @@
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.NetworkCapabilities.*;
+import static org.mockito.Mockito.anyBoolean;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -33,6 +38,7 @@
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.ConnectivityManager.PacketKeepalive;
@@ -42,6 +48,7 @@
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.MatchAllNetworkSpecifier;
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
@@ -51,7 +58,9 @@
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkMisc;
import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
import android.net.RouteInfo;
+import android.net.StringNetworkSpecifier;
import android.net.metrics.IpConnectivityLog;
import android.net.util.MultinetworkPolicyTracker;
import android.os.ConditionVariable;
@@ -64,24 +73,32 @@
import android.os.MessageQueue;
import android.os.Messenger;
import android.os.MessageQueue.IdleHandler;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.os.Process;
import android.os.SystemClock;
import android.provider.Settings;
import android.test.AndroidTestCase;
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
import android.util.Log;
import android.util.LogPrinter;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
import com.android.server.connectivity.NetworkMonitor.CaptivePortalProbeResult;
import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
@@ -134,8 +151,19 @@
private class MockContext extends BroadcastInterceptingContext {
private final MockContentResolver mContentResolver;
+ @Spy private Resources mResources;
+
MockContext(Context base) {
super(base);
+
+ mResources = spy(base.getResources());
+ when(mResources.getStringArray(com.android.internal.R.array.networkAttributes)).
+ thenReturn(new String[] {
+ "wifi,1,1,1,-1,true",
+ "mobile,0,0,0,-1,true",
+ "mobile_mms,2,0,2,60000,true",
+ });
+
mContentResolver = new MockContentResolver();
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
}
@@ -151,6 +179,11 @@
public ContentResolver getContentResolver() {
return mContentResolver;
}
+
+ @Override
+ public Resources getResources() {
+ return mResources;
+ }
}
/**
@@ -319,6 +352,11 @@
mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
}
+ public void setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
+ mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
public void connectWithoutInternet() {
mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@@ -621,6 +659,7 @@
private class WrappedConnectivityService extends ConnectivityService {
public WrappedMultinetworkPolicyTracker wrappedMultinetworkPolicyTracker;
private WrappedNetworkMonitor mLastCreatedNetworkMonitor;
+ private MockableSystemProperties mSystemProperties;
public WrappedConnectivityService(Context context, INetworkManagementService netManager,
INetworkStatsService statsService, INetworkPolicyManager policyManager,
@@ -630,9 +669,13 @@
}
@Override
- protected int getDefaultTcpRwnd() {
- // Prevent wrapped ConnectivityService from trying to write to SystemProperties.
- return 0;
+ protected MockableSystemProperties getSystemProperties() {
+ // Minimal approach to overriding system properties: let most calls fall through to real
+ // device values, and only override ones values that are important to this test.
+ mSystemProperties = spy(new MockableSystemProperties());
+ when(mSystemProperties.getInt("net.tcp.default_init_rwnd", 0)).thenReturn(0);
+ when(mSystemProperties.getBoolean("ro.radio.noril", false)).thenReturn(false);
+ return mSystemProperties;
}
@Override
@@ -805,6 +848,14 @@
return cv;
}
+ public void testNetworkTypes() {
+ // Ensure that our mocks for the networkAttributes config variable work as expected. If they
+ // don't, then tests that depend on CONNECTIVITY_ACTION broadcasts for these network types
+ // will fail. Failing here is much easier to debug.
+ assertTrue(mCm.isNetworkSupported(TYPE_WIFI));
+ assertTrue(mCm.isNetworkSupported(TYPE_MOBILE));
+ }
+
@SmallTest
public void testLingering() throws Exception {
verifyNoNetwork();
@@ -1819,34 +1870,130 @@
captivePortalCallback.assertNoCallback();
}
- @SmallTest
- public void testInvalidNetworkSpecifier() {
- boolean execptionCalled = true;
+ private NetworkRequest.Builder newWifiRequestBuilder() {
+ return new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI);
+ }
- try {
- NetworkRequest.Builder builder = new NetworkRequest.Builder();
- builder.setNetworkSpecifier(MATCH_ALL_REQUESTS_NETWORK_SPECIFIER);
- execptionCalled = false;
- } catch (IllegalArgumentException e) {
- // do nothing - should get here
+ @SmallTest
+ public void testNetworkSpecifier() {
+ NetworkRequest rEmpty1 = newWifiRequestBuilder().build();
+ NetworkRequest rEmpty2 = newWifiRequestBuilder().setNetworkSpecifier((String) null).build();
+ NetworkRequest rEmpty3 = newWifiRequestBuilder().setNetworkSpecifier("").build();
+ NetworkRequest rEmpty4 = newWifiRequestBuilder().setNetworkSpecifier(
+ (NetworkSpecifier) null).build();
+ NetworkRequest rFoo = newWifiRequestBuilder().setNetworkSpecifier("foo").build();
+ NetworkRequest rBar = newWifiRequestBuilder().setNetworkSpecifier(
+ new StringNetworkSpecifier("bar")).build();
+
+ TestNetworkCallback cEmpty1 = new TestNetworkCallback();
+ TestNetworkCallback cEmpty2 = new TestNetworkCallback();
+ TestNetworkCallback cEmpty3 = new TestNetworkCallback();
+ TestNetworkCallback cEmpty4 = new TestNetworkCallback();
+ TestNetworkCallback cFoo = new TestNetworkCallback();
+ TestNetworkCallback cBar = new TestNetworkCallback();
+ TestNetworkCallback[] emptyCallbacks = new TestNetworkCallback[] {
+ cEmpty1, cEmpty2, cEmpty3 };
+
+ mCm.registerNetworkCallback(rEmpty1, cEmpty1);
+ mCm.registerNetworkCallback(rEmpty2, cEmpty2);
+ mCm.registerNetworkCallback(rEmpty3, cEmpty3);
+ mCm.registerNetworkCallback(rEmpty4, cEmpty4);
+ mCm.registerNetworkCallback(rFoo, cFoo);
+ mCm.registerNetworkCallback(rBar, cBar);
+
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(false);
+ cEmpty1.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cEmpty2.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cEmpty3.expectAvailableCallbacks(mWiFiNetworkAgent);
+ cEmpty4.expectAvailableCallbacks(mWiFiNetworkAgent);
+ assertNoCallbacks(cFoo, cBar);
+
+ mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("foo"));
+ cFoo.expectAvailableCallbacks(mWiFiNetworkAgent);
+ for (TestNetworkCallback c: emptyCallbacks) {
+ c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
+ }
+ cFoo.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
+ cFoo.assertNoCallback();
+
+ mWiFiNetworkAgent.setNetworkSpecifier(new StringNetworkSpecifier("bar"));
+ cFoo.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ cBar.expectAvailableCallbacks(mWiFiNetworkAgent);
+ for (TestNetworkCallback c: emptyCallbacks) {
+ c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
+ }
+ cBar.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
+ cBar.assertNoCallback();
+
+ mWiFiNetworkAgent.setNetworkSpecifier(null);
+ cBar.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ for (TestNetworkCallback c: emptyCallbacks) {
+ c.expectCallback(CallbackState.NETWORK_CAPABILITIES, mWiFiNetworkAgent);
}
- assertTrue("NetworkRequest builder with MATCH_ALL_REQUESTS_NETWORK_SPECIFIER",
- execptionCalled);
+ assertNoCallbacks(cEmpty1, cEmpty2, cEmpty3, cFoo, cBar);
+ }
+
+ @SmallTest
+ public void testInvalidNetworkSpecifier() {
+ try {
+ NetworkRequest.Builder builder = new NetworkRequest.Builder();
+ builder.setNetworkSpecifier(new MatchAllNetworkSpecifier());
+ fail("NetworkRequest builder with MatchAllNetworkSpecifier");
+ } catch (IllegalArgumentException expected) {
+ // expected
+ }
try {
NetworkCapabilities networkCapabilities = new NetworkCapabilities();
networkCapabilities.addTransportType(TRANSPORT_WIFI)
- .setNetworkSpecifier(NetworkCapabilities.MATCH_ALL_REQUESTS_NETWORK_SPECIFIER);
+ .setNetworkSpecifier(new MatchAllNetworkSpecifier());
mService.requestNetwork(networkCapabilities, null, 0, null,
ConnectivityManager.TYPE_WIFI);
- execptionCalled = false;
- } catch (IllegalArgumentException e) {
- // do nothing - should get here
+ fail("ConnectivityService requestNetwork with MatchAllNetworkSpecifier");
+ } catch (IllegalArgumentException expected) {
+ // expected
}
- assertTrue("ConnectivityService requestNetwork with MATCH_ALL_REQUESTS_NETWORK_SPECIFIER",
- execptionCalled);
+ class NonParcelableSpecifier extends NetworkSpecifier {
+ public boolean satisfiedBy(NetworkSpecifier other) { return false; }
+ };
+ class ParcelableSpecifier extends NonParcelableSpecifier implements Parcelable {
+ @Override public int describeContents() { return 0; }
+ @Override public void writeToParcel(Parcel p, int flags) {}
+ }
+ NetworkRequest.Builder builder;
+
+ builder = new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET);
+ try {
+ builder.setNetworkSpecifier(new NonParcelableSpecifier());
+ Parcel parcelW = Parcel.obtain();
+ builder.build().writeToParcel(parcelW, 0);
+ fail("Parceling a non-parcelable specifier did not throw an exception");
+ } catch (Exception e) {
+ // expected
+ }
+
+ builder = new NetworkRequest.Builder().addTransportType(TRANSPORT_ETHERNET);
+ builder.setNetworkSpecifier(new ParcelableSpecifier());
+ NetworkRequest nr = builder.build();
+ assertNotNull(nr);
+
+ try {
+ Parcel parcelW = Parcel.obtain();
+ nr.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ NetworkRequest rereadNr = NetworkRequest.CREATOR.createFromParcel(parcelR);
+ fail("Unparceling a non-framework NetworkSpecifier did not throw an exception");
+ } catch (Exception e) {
+ // expected
+ }
}
@SmallTest
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 2bf5206..bd2b2a36 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -782,7 +782,79 @@
}
}
-status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
+static sp<ResourceTable::ConfigList> findEntry(const String16& packageStr, const String16& typeStr,
+ const String16& nameStr, ResourceTable* table) {
+ sp<ResourceTable::Package> pkg = table->getPackage(packageStr);
+ if (pkg != NULL) {
+ sp<ResourceTable::Type> type = pkg->getTypes().valueFor(typeStr);
+ if (type != NULL) {
+ return type->getConfigs().valueFor(nameStr);
+ }
+ }
+ return NULL;
+}
+
+static uint16_t getMaxSdkVersion(const sp<ResourceTable::ConfigList>& configList) {
+ const DefaultKeyedVector<ConfigDescription, sp<ResourceTable::Entry>>& entries =
+ configList->getEntries();
+ uint16_t maxSdkVersion = 0u;
+ for (size_t i = 0; i < entries.size(); i++) {
+ maxSdkVersion = std::max(maxSdkVersion, entries.keyAt(i).sdkVersion);
+ }
+ return maxSdkVersion;
+}
+
+static void massageRoundIconSupport(const String16& iconRef, const String16& roundIconRef,
+ ResourceTable* table) {
+ bool publicOnly = false;
+ const char* err;
+
+ String16 iconPackage, iconType, iconName;
+ if (!ResTable::expandResourceRef(iconRef.string(), iconRef.size(), &iconPackage, &iconType,
+ &iconName, NULL, &table->getAssetsPackage(), &err,
+ &publicOnly)) {
+ // Errors will be raised in later XML compilation.
+ return;
+ }
+
+ sp<ResourceTable::ConfigList> iconEntry = findEntry(iconPackage, iconType, iconName, table);
+ if (iconEntry == NULL || getMaxSdkVersion(iconEntry) < SDK_O) {
+ // The icon is not adaptive, so nothing to massage.
+ return;
+ }
+
+ String16 roundIconPackage, roundIconType, roundIconName;
+ if (!ResTable::expandResourceRef(roundIconRef.string(), roundIconRef.size(), &roundIconPackage,
+ &roundIconType, &roundIconName, NULL, &table->getAssetsPackage(),
+ &err, &publicOnly)) {
+ // Errors will be raised in later XML compilation.
+ return;
+ }
+
+ sp<ResourceTable::ConfigList> roundIconEntry = findEntry(roundIconPackage, roundIconType,
+ roundIconName, table);
+ if (roundIconEntry == NULL || getMaxSdkVersion(roundIconEntry) >= SDK_O) {
+ // The developer explicitly used a v26 compatible drawable as the roundIcon, meaning we should
+ // not generate an alias to the icon drawable.
+ return;
+ }
+
+ String16 aliasValue = String16(String8::format("@%s:%s/%s", String8(iconPackage).string(),
+ String8(iconType).string(),
+ String8(iconName).string()));
+
+ // Add an equivalent v26 entry to the roundIcon for each v26 variant of the regular icon.
+ const DefaultKeyedVector<ConfigDescription, sp<ResourceTable::Entry>>& configList =
+ iconEntry->getEntries();
+ for (size_t i = 0; i < configList.size(); i++) {
+ if (configList.keyAt(i).sdkVersion >= SDK_O) {
+ table->addEntry(SourcePos(), roundIconPackage, roundIconType, roundIconName, aliasValue,
+ NULL, &configList.keyAt(i));
+ }
+ }
+}
+
+status_t massageManifest(Bundle* bundle, ResourceTable* table, sp<XMLNode> root)
{
root = root->searchElement(String16(), String16("manifest"));
if (root == NULL) {
@@ -916,7 +988,7 @@
String8 tag(child->getElementName());
if (tag == "instrumentation") {
XMLNode::attribute_entry* attr = child->editAttribute(
- String16("http://schemas.android.com/apk/res/android"), String16("targetPackage"));
+ String16(RESOURCES_ANDROID_NAMESPACE), String16("targetPackage"));
if (attr != NULL) {
attr->string.setTo(String16(instrumentationPackageNameOverride));
}
@@ -924,6 +996,19 @@
}
}
+ sp<XMLNode> application = root->getChildElement(String16(), String16("application"));
+ if (application != NULL) {
+ XMLNode::attribute_entry* icon_attr = application->editAttribute(
+ String16(RESOURCES_ANDROID_NAMESPACE), String16("icon"));
+ if (icon_attr != NULL) {
+ XMLNode::attribute_entry* round_icon_attr = application->editAttribute(
+ String16(RESOURCES_ANDROID_NAMESPACE), String16("roundIcon"));
+ if (round_icon_attr != NULL) {
+ massageRoundIconSupport(icon_attr->string, round_icon_attr->string, table);
+ }
+ }
+ }
+
// Generate split name if feature is present.
const XMLNode::attribute_entry* attr = root->getAttribute(String16(), String16("featureName"));
if (attr != NULL) {
@@ -1638,7 +1723,7 @@
if (manifestTree == NULL) {
return UNKNOWN_ERROR;
}
- err = massageManifest(bundle, manifestTree);
+ err = massageManifest(bundle, &table, manifestTree);
if (err < NO_ERROR) {
return err;
}
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index aff22d4..b340fc5 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -590,9 +590,10 @@
const String16& comment,
bool appendComment);
+ sp<Package> getPackage(const String16& package);
+
private:
void writePublicDefinitions(const String16& package, FILE* fp, bool pub);
- sp<Package> getPackage(const String16& package);
sp<Type> getType(const String16& package,
const String16& type,
const SourcePos& pos,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 328fc0a..1e77ac1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1865,13 +1865,6 @@
}
@Override
- public ComponentName startServiceInForeground(Intent service,
- int id, Notification notification) {
- // pass
- return null;
- }
-
- @Override
public boolean stopService(Intent arg0) {
// pass
return false;
@@ -1884,13 +1877,6 @@
}
@Override
- public ComponentName startServiceInForegroundAsUser(Intent service,
- int id, Notification notification, UserHandle user) {
- // pass
- return null;
- }
-
- @Override
public boolean stopServiceAsUser(Intent arg0, UserHandle arg1) {
// pass
return false;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
index 5a239e1..ad26bc8 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePackageManager.java
@@ -923,4 +923,9 @@
public ComponentName getInstantAppResolverSettingsComponent() {
return null;
}
+
+ @Override
+ public ComponentName getInstantAppInstallerComponent() {
+ return null;
+ }
}
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java
index 82b3792..bf5c42b8 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.net.NetworkSpecifier;
import android.net.wifi.RttManager;
import android.util.Log;
@@ -250,8 +251,8 @@
}
/**
- * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
- * unencrypted WiFi Aware connection (link) to the specified peer. The
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
+ * an unencrypted WiFi Aware connection (link) to the specified peer. The
* {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
* {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
* <p>
@@ -276,13 +277,13 @@
* request from only that peer. A RESPONDER may specify a {@code null} -
* indicating that it will accept connection requests from any device.
*
- * @return A string to be used to construct
- * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+ * @return A {@link NetworkSpecifier} to be used to construct
+ * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
* {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
* android.net.ConnectivityManager.NetworkCallback)}
* [or other varieties of that API].
*/
- public String createNetworkSpecifierOpen(@Nullable PeerHandle peerHandle) {
+ public NetworkSpecifier createNetworkSpecifierOpen(@Nullable PeerHandle peerHandle) {
if (mTerminated) {
Log.w(TAG, "createNetworkSpecifierOpen: called on terminated session");
return null;
@@ -302,8 +303,8 @@
}
/**
- * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
- * encrypted WiFi Aware connection (link) to the specified peer. The
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
+ * an encrypted WiFi Aware connection (link) to the specified peer. The
* {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
* {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
* <p>
@@ -329,14 +330,14 @@
* {@link #createNetworkSpecifierOpen(PeerHandle)} API to
* specify an open (unencrypted) link.
*
- * @return A string to be used to construct
- * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+ * @return A {@link NetworkSpecifier} to be used to construct
+ * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
* {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
* android.net.ConnectivityManager.NetworkCallback)}
* [or other varieties of that API].
*/
- public String createNetworkSpecifierPassphrase(@Nullable PeerHandle peerHandle,
- @NonNull String passphrase) {
+ 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");
}
@@ -361,8 +362,8 @@
}
/**
- * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
- * encrypted WiFi Aware connection (link) to the specified peer. The
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
+ * an encrypted WiFi Aware connection (link) to the specified peer. The
* {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
* {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
* <p>
@@ -389,8 +390,8 @@
* Passphrase or {@link #createNetworkSpecifierOpen(PeerHandle)} to specify an
* open (unencrypted) link.
*
- * @return A string to be used to construct
- * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+ * @return A {@link NetworkSpecifier} to be used to construct
+ * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
* {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
* android.net.ConnectivityManager.NetworkCallback)}
* [or other varieties of that API].
@@ -398,7 +399,7 @@
* @hide
*/
@SystemApi
- public String createNetworkSpecifierPmk(@Nullable PeerHandle peerHandle,
+ 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");
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 4d3957a..3fcbd4b 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
import android.net.wifi.RttManager;
import android.os.Binder;
import android.os.Bundle;
@@ -31,7 +32,6 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
-import android.util.Base64;
import android.util.Log;
import android.util.SparseArray;
@@ -39,9 +39,6 @@
import libcore.util.HexEncoding;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
@@ -129,65 +126,6 @@
private static final boolean VDBG = false; // STOPSHIP if true
/**
- * Keys used to generate a Network Specifier for the Aware network request. The network
- * specifier is formatted as a JSON string.
- */
-
- /**
- * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk/passphrase optional
- * @hide
- */
- public static final int NETWORK_SPECIFIER_TYPE_IB = 0;
-
- /**
- * TYPE: in band, any peer: role, client_id, session_id, pmk/passphrase optional
- * [only permitted for RESPONDER]
- * @hide
- */
- public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1;
-
- /**
- * TYPE: out-of-band: role, client_id, peer_mac, pmk/passphrase optional
- * @hide
- */
- public static final int NETWORK_SPECIFIER_TYPE_OOB = 2;
-
- /**
- * TYPE: out-of-band, any peer: role, client_id, pmk/passphrase optional
- * [only permitted for RESPONDER]
- * @hide
- */
- public static final int NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER = 3;
-
-
- /** @hide */
- public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER;
-
- /** @hide */
- public static final String NETWORK_SPECIFIER_KEY_TYPE = "type";
-
- /** @hide */
- public static final String NETWORK_SPECIFIER_KEY_ROLE = "role";
-
- /** @hide */
- public static final String NETWORK_SPECIFIER_KEY_CLIENT_ID = "client_id";
-
- /** @hide */
- public static final String NETWORK_SPECIFIER_KEY_SESSION_ID = "session_id";
-
- /** @hide */
- public static final String NETWORK_SPECIFIER_KEY_PEER_ID = "peer_id";
-
- /** @hide */
- public static final String NETWORK_SPECIFIER_KEY_PEER_MAC = "peer_mac";
-
- /** @hide */
- public static final String NETWORK_SPECIFIER_KEY_PMK = "pmk";
-
- /** @hide */
- public static final String NETWORK_SPECIFIER_KEY_PASSPHRASE = "passphrase";
-
- /**
* Broadcast intent action to indicate that the state of Wi-Fi Aware availability has changed.
* Use the {@link #isAvailable()} to query the current status.
* This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering
@@ -483,7 +421,7 @@
}
/** @hide */
- public String createNetworkSpecifier(int clientId, int role, int sessionId,
+ public NetworkSpecifier createNetworkSpecifier(int clientId, int role, int sessionId,
PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) {
if (VDBG) {
Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId
@@ -492,9 +430,6 @@
+ ", passphrase=" + ((passphrase == null) ? "null" : "non-null"));
}
- int type = (peerHandle == null) ? NETWORK_SPECIFIER_TYPE_IB_ANY_PEER
- : NETWORK_SPECIFIER_TYPE_IB;
-
if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
&& role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
throw new IllegalArgumentException(
@@ -509,35 +444,20 @@
}
}
- JSONObject json;
- try {
- json = new JSONObject();
- json.put(NETWORK_SPECIFIER_KEY_TYPE, type);
- json.put(NETWORK_SPECIFIER_KEY_ROLE, role);
- json.put(NETWORK_SPECIFIER_KEY_CLIENT_ID, clientId);
- json.put(NETWORK_SPECIFIER_KEY_SESSION_ID, sessionId);
- if (peerHandle != null) {
- json.put(NETWORK_SPECIFIER_KEY_PEER_ID, peerHandle.peerId);
- }
- if (pmk == null) {
- pmk = new byte[0];
- }
- json.put(NETWORK_SPECIFIER_KEY_PMK,
- Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT));
- if (passphrase == null) {
- passphrase = new String();
- }
- json.put(NETWORK_SPECIFIER_KEY_PASSPHRASE, passphrase);
-
- } catch (JSONException e) {
- return "";
- }
-
- return json.toString();
+ return new WifiAwareNetworkSpecifier(
+ (peerHandle == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB_ANY_PEER
+ : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB,
+ role,
+ clientId,
+ sessionId,
+ peerHandle != null ? peerHandle.peerId : 0, // 0 is an invalid peer ID
+ null, // peerMac (not used in this method)
+ pmk,
+ passphrase);
}
/** @hide */
- public String createNetworkSpecifier(int clientId, @DataPathRole int role,
+ public NetworkSpecifier createNetworkSpecifier(int clientId, @DataPathRole int role,
@Nullable byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase) {
if (VDBG) {
Log.v(TAG, "createNetworkSpecifier: role=" + role
@@ -545,9 +465,6 @@
+ ", passphrase=" + ((passphrase == null) ? "null" : "non-null"));
}
- int type = (peer == null) ?
- NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER : NETWORK_SPECIFIER_TYPE_OOB;
-
if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
&& role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
throw new IllegalArgumentException(
@@ -564,29 +481,16 @@
throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC address");
}
- JSONObject json;
- try {
- json = new JSONObject();
- json.put(NETWORK_SPECIFIER_KEY_TYPE, type);
- json.put(NETWORK_SPECIFIER_KEY_ROLE, role);
- json.put(NETWORK_SPECIFIER_KEY_CLIENT_ID, clientId);
- if (peer != null) {
- json.put(NETWORK_SPECIFIER_KEY_PEER_MAC, new String(HexEncoding.encode(peer)));
- }
- if (pmk == null) {
- pmk = new byte[0];
- }
- json.put(NETWORK_SPECIFIER_KEY_PMK,
- Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT));
- if (passphrase == null) {
- passphrase = new String();
- }
- json.put(NETWORK_SPECIFIER_KEY_PASSPHRASE, passphrase);
- } catch (JSONException e) {
- return "";
- }
-
- return json.toString();
+ return new WifiAwareNetworkSpecifier(
+ (peer == null) ? WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER
+ : WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_OOB,
+ role,
+ clientId,
+ 0, // 0 is an invalid session ID
+ 0, // 0 is an invalid peer ID
+ peer,
+ pmk,
+ passphrase);
}
private static class WifiAwareEventCallbackProxy extends IWifiAwareEventCallback.Stub {
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
new file mode 100644
index 0000000..5993480
--- /dev/null
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
@@ -0,0 +1,234 @@
+/*
+ * 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.net.wifi.aware;
+
+import android.net.NetworkSpecifier;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Network specifier object used to request a Wi-Fi Aware network. Apps do not create these objects
+ * directly but obtain them using
+ * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])} or
+ * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)} or their secure (Passphrase)
+ * versions.
+ *
+ * @hide
+ */
+public final class WifiAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+ /**
+ * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk/passphrase optional
+ * @hide
+ */
+ public static final int NETWORK_SPECIFIER_TYPE_IB = 0;
+
+ /**
+ * TYPE: in band, any peer: role, client_id, session_id, pmk/passphrase optional
+ * [only permitted for RESPONDER]
+ * @hide
+ */
+ public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1;
+
+ /**
+ * TYPE: out-of-band: role, client_id, peer_mac, pmk/passphrase optional
+ * @hide
+ */
+ public static final int NETWORK_SPECIFIER_TYPE_OOB = 2;
+
+ /**
+ * TYPE: out-of-band, any peer: role, client_id, pmk/passphrase optional
+ * [only permitted for RESPONDER]
+ * @hide
+ */
+ public static final int NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER = 3;
+
+ /** @hide */
+ public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER;
+
+ /**
+ * One of the NETWORK_SPECIFIER_TYPE_* constants. The type of the network specifier object.
+ * @hide
+ */
+ public final int type;
+
+ /**
+ * The role of the device: WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR or
+ * WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER.
+ * @hide
+ */
+ public final int role;
+
+ /**
+ * The client ID of the device.
+ * @hide
+ */
+ public final int clientId;
+
+ /**
+ * The session ID in which context to request a data-path. Only relevant for IB requests.
+ * @hide
+ */
+ public final int sessionId;
+
+ /**
+ * The peer ID of the device which the data-path should be connected to. Only relevant for
+ * IB requests (i.e. not IB_ANY_PEER or OOB*).
+ * @hide
+ */
+ public final int peerId;
+
+ /**
+ * The peer MAC address of the device which the data-path should be connected to. Only relevant
+ * for OB requests (i.e. not OOB_ANY_PEER or IB*).
+ * @hide
+ */
+ public final byte[] peerMac;
+
+ /**
+ * The PMK of the requested data-path. Can be null. Only one or none of pmk or passphrase should
+ * be specified.
+ * @hide
+ */
+ public final byte[] pmk;
+
+ /**
+ * The Passphrase of the requested data-path. Can be null. Only one or none of the pmk or
+ * passphrase should be specified.
+ * @hide
+ */
+ public final String passphrase;
+
+ /** @hide */
+ public WifiAwareNetworkSpecifier(int type, int role, int clientId, int sessionId, int peerId,
+ byte[] peerMac, byte[] pmk, String passphrase) {
+ this.type = type;
+ this.role = role;
+ this.clientId = clientId;
+ this.sessionId = sessionId;
+ this.peerId = peerId;
+ this.peerMac = peerMac;
+ this.pmk = pmk;
+ this.passphrase = passphrase;
+ }
+
+ public static final Creator<WifiAwareNetworkSpecifier> CREATOR =
+ new Creator<WifiAwareNetworkSpecifier>() {
+ @Override
+ public WifiAwareNetworkSpecifier createFromParcel(Parcel in) {
+ return new WifiAwareNetworkSpecifier(
+ in.readInt(), // type
+ in.readInt(), // role
+ in.readInt(), // clientId
+ in.readInt(), // sessionId
+ in.readInt(), // peerId
+ in.createByteArray(), // peerMac
+ in.createByteArray(), // pmk
+ in.readString()); // passphrase
+ }
+
+ @Override
+ public WifiAwareNetworkSpecifier[] newArray(int size) {
+ return new WifiAwareNetworkSpecifier[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(type);
+ dest.writeInt(role);
+ dest.writeInt(clientId);
+ dest.writeInt(sessionId);
+ dest.writeInt(peerId);
+ dest.writeByteArray(peerMac);
+ dest.writeByteArray(pmk);
+ dest.writeString(passphrase);
+ }
+
+ /** @hide */
+ @Override
+ public boolean satisfiedBy(NetworkSpecifier other) {
+ // MatchAllNetworkSpecifier is taken care in NetworkCapabilities#satisfiedBySpecifier.
+ return equals(other);
+ }
+
+ /** @hide */
+ @Override
+ public int hashCode() {
+ int result = 17;
+
+ result = 31 * result + type;
+ result = 31 * result + role;
+ result = 31 * result + clientId;
+ result = 31 * result + sessionId;
+ result = 31 * result + peerId;
+ result = 31 * result + Arrays.hashCode(peerMac);
+ result = 31 * result + Arrays.hashCode(pmk);
+ result = 31 * result + Objects.hashCode(passphrase);
+
+ return result;
+ }
+
+ /** @hide */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof WifiAwareNetworkSpecifier)) {
+ return false;
+ }
+
+ WifiAwareNetworkSpecifier lhs = (WifiAwareNetworkSpecifier) obj;
+
+ return type == lhs.type
+ && role == lhs.role
+ && clientId == lhs.clientId
+ && sessionId == lhs.sessionId
+ && peerId == lhs.peerId
+ && Arrays.equals(peerMac, lhs.peerMac)
+ && Arrays.equals(pmk, lhs.pmk)
+ && Objects.equals(passphrase, lhs.passphrase);
+ }
+
+ /** @hide */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("WifiAwareNetworkSpecifier [");
+ sb.append("type=").append(type)
+ .append(", role=").append(role)
+ .append(", clientId=").append(clientId)
+ .append(", sessionId=").append(sessionId)
+ .append(", peerId=").append(peerId)
+ // masking potential PII (although low impact information)
+ .append(", peerMac=").append((peerMac == null) ? "<null>" : "<non-null>")
+ // masking PII
+ .append(", pmk=").append((pmk == null) ? "<null>" : "<non-null>")
+ // masking PII
+ .append(", passphrase=").append((passphrase == null) ? "<null>" : "<non-null>")
+ .append("]");
+ return sb.toString();
+ }
+}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
index 895defb..ac3a6bb 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.net.NetworkSpecifier;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
@@ -184,8 +185,8 @@
}
/**
- * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
- * unencrypted WiFi Aware connection (link) to the specified peer. The
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
+ * an unencrypted WiFi Aware connection (link) to the specified peer. The
* {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
* {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
* <p>
@@ -205,29 +206,29 @@
* peer. A RESPONDER may specify a {@code null} - indicating that it will accept
* connection requests from any device.
*
- * @return A string to be used to construct
- * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+ * @return A {@link NetworkSpecifier} to be used to construct
+ * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
* {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
* android.net.ConnectivityManager.NetworkCallback)}
* [or other varieties of that API].
*/
- public String createNetworkSpecifierOpen(@WifiAwareManager.DataPathRole int role,
- @Nullable byte[] peer) {
+ public NetworkSpecifier createNetworkSpecifierOpen(
+ @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer) {
WifiAwareManager mgr = mMgr.get();
if (mgr == null) {
Log.e(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager");
- return "";
+ return null;
}
if (mTerminated) {
Log.e(TAG, "createNetworkSpecifierOpen: called after termination");
- return "";
+ return null;
}
return mgr.createNetworkSpecifier(mClientId, role, peer, null, null);
}
/**
- * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
- * encrypted WiFi Aware connection (link) to the specified peer. The
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
+ * an encrypted WiFi Aware connection (link) to the specified peer. The
* {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
* {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
* <p>
@@ -247,22 +248,23 @@
* the passphrase. Use {@link #createNetworkSpecifierOpen(int, byte[])} to
* specify an open (unencrypted) link.
*
- * @return A string to be used to construct
- * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+ * @return A {@link NetworkSpecifier} to be used to construct
+ * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
* {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
* android.net.ConnectivityManager.NetworkCallback)}
* [or other varieties of that API].
*/
- public String createNetworkSpecifierPassphrase(@WifiAwareManager.DataPathRole int role,
- @Nullable byte[] peer, @NonNull String passphrase) {
+ public NetworkSpecifier createNetworkSpecifierPassphrase(
+ @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer,
+ @NonNull String passphrase) {
WifiAwareManager mgr = mMgr.get();
if (mgr == null) {
Log.e(TAG, "createNetworkSpecifierPassphrase: called post GC on WifiAwareManager");
- return "";
+ return null;
}
if (mTerminated) {
Log.e(TAG, "createNetworkSpecifierPassphrase: called after termination");
- return "";
+ return null;
}
if (passphrase == null || passphrase.length() == 0) {
throw new IllegalArgumentException("Passphrase must not be null or empty");
@@ -271,8 +273,8 @@
}
/**
- * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
- * encrypted WiFi Aware connection (link) to the specified peer. The
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} for
+ * an encrypted WiFi Aware connection (link) to the specified peer. The
* {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
* {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
* <p>
@@ -294,8 +296,8 @@
* Passphrase or {@link #createNetworkSpecifierOpen(int, byte[])} to specify an
* open (unencrypted) link.
*
- * @return A string to be used to construct
- * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+ * @return A {@link NetworkSpecifier} to be used to construct
+ * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(NetworkSpecifier)} to pass to
* {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
* android.net.ConnectivityManager.NetworkCallback)}
* [or other varieties of that API].
@@ -303,16 +305,16 @@
* @hide
*/
@SystemApi
- public String createNetworkSpecifierPmk(@WifiAwareManager.DataPathRole int role,
- @Nullable byte[] peer, @NonNull byte[] pmk) {
+ public NetworkSpecifier createNetworkSpecifierPmk(
+ @WifiAwareManager.DataPathRole int role, @Nullable byte[] peer, @NonNull byte[] pmk) {
WifiAwareManager mgr = mMgr.get();
if (mgr == null) {
Log.e(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager");
- return "";
+ return null;
}
if (mTerminated) {
Log.e(TAG, "createNetworkSpecifierPmk: called after termination");
- return "";
+ return null;
}
if (pmk == null || pmk.length == 0) {
throw new IllegalArgumentException("PMK must not be null or empty");
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index 830db22..72a6a7a 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -34,11 +34,9 @@
import android.os.Parcel;
import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
-import android.util.Base64;
import libcore.util.HexEncoding;
-import org.json.JSONObject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -959,8 +957,6 @@
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
final PublishConfig publishConfig = new PublishConfig.Builder().build();
- String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT);
-
ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
WifiAwareSession.class);
ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
@@ -991,51 +987,37 @@
inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
// (3) request an open (unencrypted) network specifier from the session
- String networkSpecifier = publishSession.getValue().createNetworkSpecifierOpen(peerHandle);
+ WifiAwareNetworkSpecifier ns =
+ (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierOpen(
+ peerHandle);
// validate format
- JSONObject jsonObject = new JSONObject(networkSpecifier);
- collector.checkThat("role", role,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
- collector.checkThat("client_id", clientId,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
- collector.checkThat("session_id", sessionId,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
- collector.checkThat("peer_id", peerHandle.peerId,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
+ collector.checkThat("role", role, equalTo(ns.role));
+ collector.checkThat("client_id", clientId, equalTo(ns.clientId));
+ collector.checkThat("session_id", sessionId, equalTo(ns.sessionId));
+ collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
// (4) request an encrypted (PMK) network specifier from the session
- networkSpecifier = publishSession.getValue().createNetworkSpecifierPmk(peerHandle, pmk);
+ ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPmk(
+ peerHandle, pmk);
// validate format
- jsonObject = new JSONObject(networkSpecifier);
- collector.checkThat("role", role,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
- collector.checkThat("client_id", clientId,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
- collector.checkThat("session_id", sessionId,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
- collector.checkThat("peer_id", peerHandle.peerId,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
- collector.checkThat("pmk", pmkB64 ,
- equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK)));
+ collector.checkThat("role", role, equalTo(ns.role));
+ collector.checkThat("client_id", clientId, equalTo(ns.clientId));
+ collector.checkThat("session_id", sessionId, equalTo(ns.sessionId));
+ collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
+ collector.checkThat("pmk", pmk , equalTo(ns.pmk));
// (5) request an encrypted (Passphrase) network specifier from the session
- networkSpecifier = publishSession.getValue().createNetworkSpecifierPassphrase(peerHandle,
- passphrase);
+ ns = (WifiAwareNetworkSpecifier) publishSession.getValue().createNetworkSpecifierPassphrase(
+ peerHandle, passphrase);
// validate format
- jsonObject = new JSONObject(networkSpecifier);
- collector.checkThat("role", role,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
- collector.checkThat("client_id", clientId,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
- collector.checkThat("session_id", sessionId,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
- collector.checkThat("peer_id", peerHandle.peerId,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
- collector.checkThat("passphrase", passphrase,
- equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PASSPHRASE)));
+ collector.checkThat("role", role, equalTo(ns.role));
+ collector.checkThat("client_id", clientId, equalTo(ns.clientId));
+ collector.checkThat("session_id", sessionId, equalTo(ns.sessionId));
+ collector.checkThat("peer_id", peerHandle.peerId, equalTo(ns.peerId));
+ collector.checkThat("passphrase", passphrase, equalTo(ns.passphrase));
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
mockPublishSession, mockRttListener);
@@ -1054,8 +1036,6 @@
final byte[] pmk = "Some arbitrary pmk data".getBytes();
final String passphrase = "A really bad password";
- String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT);
-
ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
WifiAwareSession.class);
ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
@@ -1074,47 +1054,32 @@
WifiAwareSession session = sessionCaptor.getValue();
// (2) request an open (unencrypted) direct network specifier
- String networkSpecifier = session.createNetworkSpecifierOpen(role, someMac);
+ WifiAwareNetworkSpecifier ns =
+ (WifiAwareNetworkSpecifier) session.createNetworkSpecifierOpen(role, someMac);
// validate format
- JSONObject jsonObject = new JSONObject(networkSpecifier);
- collector.checkThat("role", role,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
- collector.checkThat("client_id", clientId,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
- collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
- jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
- false)));
+ collector.checkThat("role", role, equalTo(ns.role));
+ collector.checkThat("client_id", clientId, equalTo(ns.clientId));
+ collector.checkThat("peer_mac", someMac, equalTo(ns.peerMac));
// (3) request an encrypted (PMK) direct network specifier
- networkSpecifier = session.createNetworkSpecifierPmk(role, someMac, pmk);
+ ns = (WifiAwareNetworkSpecifier) session.createNetworkSpecifierPmk(role, someMac, pmk);
// validate format
- jsonObject = new JSONObject(networkSpecifier);
- collector.checkThat("role", role,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
- collector.checkThat("client_id", clientId,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
- collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
- jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
- false)));
- collector.checkThat("pmk", pmkB64,
- equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK)));
+ collector.checkThat("role", role, equalTo(ns.role));
+ collector.checkThat("client_id", clientId, equalTo(ns.clientId));
+ collector.checkThat("peer_mac", someMac, equalTo(ns.peerMac));
+ collector.checkThat("pmk", pmk, equalTo(ns.pmk));
// (4) request an encrypted (Passphrase) direct network specifier
- networkSpecifier = session.createNetworkSpecifierPassphrase(role, someMac, passphrase);
+ ns = (WifiAwareNetworkSpecifier) session.createNetworkSpecifierPassphrase(role, someMac,
+ passphrase);
// validate format
- jsonObject = new JSONObject(networkSpecifier);
- collector.checkThat("role", role,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
- collector.checkThat("client_id", clientId,
- equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
- collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
- jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
- false)));
- collector.checkThat("passphrase", passphrase,
- equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PASSPHRASE)));
+ collector.checkThat("role", role, equalTo(ns.role));
+ collector.checkThat("client_id", clientId, equalTo(ns.clientId));
+ collector.checkThat("peer_mac", someMac, equalTo(ns.peerMac));
+ collector.checkThat("passphrase", passphrase, equalTo(ns.passphrase));
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
mockPublishSession, mockRttListener);