diff options
69 files changed, 1456 insertions, 925 deletions
diff --git a/api/current.txt b/api/current.txt index 06927c3ea569..b400b34170de 100644 --- a/api/current.txt +++ b/api/current.txt @@ -4297,6 +4297,14 @@ package android.app { field public java.lang.String serviceDetails; } + public final class AuthenticationRequiredException extends java.lang.SecurityException implements android.os.Parcelable { + ctor public AuthenticationRequiredException(java.lang.Throwable, android.app.PendingIntent); + method public int describeContents(); + method public android.app.PendingIntent getUserAction(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.AuthenticationRequiredException> CREATOR; + } + public final class AutomaticZenRule implements android.os.Parcelable { ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean); ctor public AutomaticZenRule(android.os.Parcel); @@ -5708,17 +5716,6 @@ package android.app { field public static final int STYLE_SPINNER = 0; // 0x0 } - public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { - ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, android.app.RemoteAction); - method public int describeContents(); - method public android.app.RemoteAction getUserAction(); - method public java.lang.CharSequence getUserMessage(); - method public void showAsDialog(android.app.Activity); - method public void showAsNotification(android.content.Context, java.lang.String); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR; - } - public final class RemoteAction implements android.os.Parcelable { ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); method public android.app.RemoteAction clone(); @@ -24621,6 +24618,7 @@ package android.media.tv { field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; + field public static final java.lang.String COLUMN_LOCKED = "locked"; field public static final java.lang.String COLUMN_NETWORK_AFFILIATION = "network_affiliation"; field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id"; field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; @@ -26280,16 +26278,6 @@ package android.net.sip { package android.net.wifi { - public final class IconInfo implements android.os.Parcelable { - ctor public IconInfo(java.lang.String, byte[]); - ctor public IconInfo(android.net.wifi.IconInfo); - method public int describeContents(); - method public byte[] getData(); - method public java.lang.String getFilename(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.net.wifi.IconInfo> CREATOR; - } - public class ScanResult implements android.os.Parcelable { method public int describeContents(); method public boolean is80211mcResponder(); @@ -26538,7 +26526,8 @@ package android.net.wifi { field public static final java.lang.String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG"; field public static final java.lang.String EXTRA_DELAY = "android.net.wifi.extra.DELAY"; field public static final java.lang.String EXTRA_ESS = "android.net.wifi.extra.ESS"; - field public static final java.lang.String EXTRA_ICON_INFO = "android.net.wifi.extra.ICON_INFO"; + field public static final java.lang.String EXTRA_FILENAME = "android.net.wifi.extra.FILENAME"; + field public static final java.lang.String EXTRA_ICON = "android.net.wifi.extra.ICON"; field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo"; field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi"; field public static final java.lang.String EXTRA_NEW_STATE = "newState"; @@ -31197,7 +31186,6 @@ package android.os { method public final android.util.SizeF readSizeF(); method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader); method public final android.util.SparseBooleanArray readSparseBooleanArray(); - method public final android.util.SparseIntArray readSparseIntArray(); method public final java.lang.String readString(); method public final void readStringArray(java.lang.String[]); method public final void readStringList(java.util.List<java.lang.String>); @@ -31242,7 +31230,6 @@ package android.os { method public final void writeSizeF(android.util.SizeF); method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>); method public final void writeSparseBooleanArray(android.util.SparseBooleanArray); - method public final void writeSparseIntArray(android.util.SparseIntArray); method public final void writeString(java.lang.String); method public final void writeStringArray(java.lang.String[]); method public final void writeStringList(java.util.List<java.lang.String>); @@ -34262,6 +34249,7 @@ package android.provider { method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException; method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException; method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException; + method public static void ejectRoot(android.content.ContentResolver, android.net.Uri); method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException; method public static java.lang.String getDocumentId(android.net.Uri); method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException; @@ -34330,6 +34318,7 @@ package android.provider { field public static final java.lang.String COLUMN_TITLE = "title"; field public static final int FLAG_LOCAL_ONLY = 2; // 0x2 field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1 + field public static final int FLAG_SUPPORTS_EJECT = 32; // 0x20 field public static final int FLAG_SUPPORTS_IS_CHILD = 16; // 0x10 field public static final int FLAG_SUPPORTS_RECENTS = 4; // 0x4 field public static final int FLAG_SUPPORTS_SEARCH = 8; // 0x8 @@ -34343,6 +34332,7 @@ package android.provider { method public android.content.IntentSender createWebLinkIntent(java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException; method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]); method public void deleteDocument(java.lang.String) throws java.io.FileNotFoundException; + method public void ejectRoot(java.lang.String); method public android.provider.DocumentsContract.Path findDocumentPath(java.lang.String, java.lang.String) throws java.io.FileNotFoundException; method public java.lang.String[] getDocumentStreamTypes(java.lang.String, java.lang.String); method public java.lang.String getDocumentType(java.lang.String) throws java.io.FileNotFoundException; @@ -39946,13 +39936,17 @@ package android.telephony { method public int getDataActivity(); method public int getDataNetworkType(); method public int getDataState(); - method public java.lang.String getDeviceId(); - method public java.lang.String getDeviceId(int); + method public deprecated java.lang.String getDeviceId(); + method public deprecated java.lang.String getDeviceId(int); method public java.lang.String getDeviceSoftwareVersion(); method public java.lang.String[] getForbiddenPlmns(); method public java.lang.String getGroupIdLevel1(); method public java.lang.String getIccAuthentication(int, int, java.lang.String); + method public java.lang.String getImei(); + method public java.lang.String getImei(int); method public java.lang.String getLine1Number(); + method public java.lang.String getMeid(); + method public java.lang.String getMeid(int); method public java.lang.String getMmsUAProfUrl(); method public java.lang.String getMmsUserAgent(); method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo(); @@ -43241,7 +43235,6 @@ package android.util { method public E get(long, E); method public int indexOfKey(long); method public int indexOfValue(E); - method public int indexOfValueByValue(E); method public long keyAt(int); method public void put(long, E); method public void remove(long); @@ -43444,7 +43437,6 @@ package android.util { method public E get(int, E); method public int indexOfKey(int); method public int indexOfValue(E); - method public int indexOfValueByValue(E); method public int keyAt(int); method public void put(int, E); method public void remove(int); diff --git a/api/system-current.txt b/api/system-current.txt index c99ca68e58ec..844b897e4507 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4441,6 +4441,14 @@ package android.app { field public java.lang.String serviceDetails; } + public final class AuthenticationRequiredException extends java.lang.SecurityException implements android.os.Parcelable { + ctor public AuthenticationRequiredException(java.lang.Throwable, android.app.PendingIntent); + method public int describeContents(); + method public android.app.PendingIntent getUserAction(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.AuthenticationRequiredException> CREATOR; + } + public final class AutomaticZenRule implements android.os.Parcelable { ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean); ctor public AutomaticZenRule(android.os.Parcel); @@ -5900,17 +5908,6 @@ package android.app { field public static final int STYLE_SPINNER = 0; // 0x0 } - public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { - ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, android.app.RemoteAction); - method public int describeContents(); - method public android.app.RemoteAction getUserAction(); - method public java.lang.CharSequence getUserMessage(); - method public void showAsDialog(android.app.Activity); - method public void showAsNotification(android.content.Context, java.lang.String); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR; - } - public final class RemoteAction implements android.os.Parcelable { ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); method public android.app.RemoteAction clone(); @@ -28564,16 +28561,6 @@ package android.net.wifi { field public boolean truncated; } - public final class IconInfo implements android.os.Parcelable { - ctor public IconInfo(java.lang.String, byte[]); - ctor public IconInfo(android.net.wifi.IconInfo); - method public int describeContents(); - method public byte[] getData(); - method public java.lang.String getFilename(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.net.wifi.IconInfo> CREATOR; - } - public class RttManager { method public void disableResponder(android.net.wifi.RttManager.ResponderCallback); method public void enableResponder(android.net.wifi.RttManager.ResponderCallback); @@ -29069,7 +29056,8 @@ package android.net.wifi { field public static final java.lang.String EXTRA_CHANGE_REASON = "changeReason"; field public static final java.lang.String EXTRA_DELAY = "android.net.wifi.extra.DELAY"; field public static final java.lang.String EXTRA_ESS = "android.net.wifi.extra.ESS"; - field public static final java.lang.String EXTRA_ICON_INFO = "android.net.wifi.extra.ICON_INFO"; + field public static final java.lang.String EXTRA_FILENAME = "android.net.wifi.extra.FILENAME"; + field public static final java.lang.String EXTRA_ICON = "android.net.wifi.extra.ICON"; field public static final java.lang.String EXTRA_MULTIPLE_NETWORKS_CHANGED = "multipleChanges"; field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo"; field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi"; @@ -33925,7 +33913,6 @@ package android.os { method public final android.util.SizeF readSizeF(); method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader); method public final android.util.SparseBooleanArray readSparseBooleanArray(); - method public final android.util.SparseIntArray readSparseIntArray(); method public final java.lang.String readString(); method public final void readStringArray(java.lang.String[]); method public final void readStringList(java.util.List<java.lang.String>); @@ -33970,7 +33957,6 @@ package android.os { method public final void writeSizeF(android.util.SizeF); method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>); method public final void writeSparseBooleanArray(android.util.SparseBooleanArray); - method public final void writeSparseIntArray(android.util.SparseIntArray); method public final void writeString(java.lang.String); method public final void writeStringArray(java.lang.String[]); method public final void writeStringList(java.util.List<java.lang.String>); @@ -37163,6 +37149,7 @@ package android.provider { method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException; method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException; method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException; + method public static void ejectRoot(android.content.ContentResolver, android.net.Uri); method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException; method public static java.lang.String getDocumentId(android.net.Uri); method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException; @@ -37231,6 +37218,7 @@ package android.provider { field public static final java.lang.String COLUMN_TITLE = "title"; field public static final int FLAG_LOCAL_ONLY = 2; // 0x2 field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1 + field public static final int FLAG_SUPPORTS_EJECT = 32; // 0x20 field public static final int FLAG_SUPPORTS_IS_CHILD = 16; // 0x10 field public static final int FLAG_SUPPORTS_RECENTS = 4; // 0x4 field public static final int FLAG_SUPPORTS_SEARCH = 8; // 0x8 @@ -37244,6 +37232,7 @@ package android.provider { method public android.content.IntentSender createWebLinkIntent(java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException; method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]); method public void deleteDocument(java.lang.String) throws java.io.FileNotFoundException; + method public void ejectRoot(java.lang.String); method public android.provider.DocumentsContract.Path findDocumentPath(java.lang.String, java.lang.String) throws java.io.FileNotFoundException; method public java.lang.String[] getDocumentStreamTypes(java.lang.String, java.lang.String); method public java.lang.String getDocumentType(java.lang.String) throws java.io.FileNotFoundException; @@ -43343,8 +43332,8 @@ package android.telephony { method public deprecated boolean getDataEnabled(int); method public int getDataNetworkType(); method public int getDataState(); - method public java.lang.String getDeviceId(); - method public java.lang.String getDeviceId(int); + method public deprecated java.lang.String getDeviceId(); + method public deprecated java.lang.String getDeviceId(int); method public java.lang.String getDeviceSoftwareVersion(); method public java.lang.String[] getForbiddenPlmns(); method public java.lang.String getGroupIdLevel1(); @@ -43352,6 +43341,8 @@ package android.telephony { method public java.lang.String getImei(); method public java.lang.String getImei(int); method public java.lang.String getLine1Number(); + method public java.lang.String getMeid(); + method public java.lang.String getMeid(int); method public java.lang.String getMmsUAProfUrl(); method public java.lang.String getMmsUserAgent(); method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo(); @@ -46699,7 +46690,6 @@ package android.util { method public E get(long, E); method public int indexOfKey(long); method public int indexOfValue(E); - method public int indexOfValueByValue(E); method public long keyAt(int); method public void put(long, E); method public void remove(long); @@ -46902,7 +46892,6 @@ package android.util { method public E get(int, E); method public int indexOfKey(int); method public int indexOfValue(E); - method public int indexOfValueByValue(E); method public int keyAt(int); method public void put(int, E); method public void remove(int); diff --git a/api/test-current.txt b/api/test-current.txt index d1088ddf4dc0..a73ac18ac0c7 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -4307,6 +4307,14 @@ package android.app { field public java.lang.String serviceDetails; } + public final class AuthenticationRequiredException extends java.lang.SecurityException implements android.os.Parcelable { + ctor public AuthenticationRequiredException(java.lang.Throwable, android.app.PendingIntent); + method public int describeContents(); + method public android.app.PendingIntent getUserAction(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.AuthenticationRequiredException> CREATOR; + } + public final class AutomaticZenRule implements android.os.Parcelable { ctor public AutomaticZenRule(java.lang.String, android.content.ComponentName, android.net.Uri, int, boolean); ctor public AutomaticZenRule(android.os.Parcel); @@ -5719,17 +5727,6 @@ package android.app { field public static final int STYLE_SPINNER = 0; // 0x0 } - public final class RecoverableSecurityException extends java.lang.SecurityException implements android.os.Parcelable { - ctor public RecoverableSecurityException(java.lang.Throwable, java.lang.CharSequence, android.app.RemoteAction); - method public int describeContents(); - method public android.app.RemoteAction getUserAction(); - method public java.lang.CharSequence getUserMessage(); - method public void showAsDialog(android.app.Activity); - method public void showAsNotification(android.content.Context, java.lang.String); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.app.RecoverableSecurityException> CREATOR; - } - public final class RemoteAction implements android.os.Parcelable { ctor public RemoteAction(android.graphics.drawable.Icon, java.lang.CharSequence, java.lang.CharSequence, android.app.PendingIntent); method public android.app.RemoteAction clone(); @@ -11751,6 +11748,15 @@ package android.database { field protected final java.util.ArrayList<T> mObservers; } + public final class PageViewCursor extends android.database.CursorWrapper implements android.database.CrossProcessCursor { + ctor public PageViewCursor(android.database.Cursor, int, int); + method public void fillWindow(int, android.database.CursorWindow); + method public android.database.CursorWindow getWindow(); + method public boolean onMove(int, int); + method public static android.database.Cursor wrap(android.database.Cursor, android.os.Bundle); + field public static final java.lang.String EXTRA_AUTO_PAGED = "android.content.extra.AUTO_PAGED"; + } + public class SQLException extends java.lang.RuntimeException { ctor public SQLException(); ctor public SQLException(java.lang.String); @@ -24723,6 +24729,7 @@ package android.media.tv { field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG3 = "internal_provider_flag3"; field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_FLAG4 = "internal_provider_flag4"; field public static final java.lang.String COLUMN_INTERNAL_PROVIDER_ID = "internal_provider_id"; + field public static final java.lang.String COLUMN_LOCKED = "locked"; field public static final java.lang.String COLUMN_NETWORK_AFFILIATION = "network_affiliation"; field public static final java.lang.String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id"; field public static final java.lang.String COLUMN_SEARCHABLE = "searchable"; @@ -26382,16 +26389,6 @@ package android.net.sip { package android.net.wifi { - public final class IconInfo implements android.os.Parcelable { - ctor public IconInfo(java.lang.String, byte[]); - ctor public IconInfo(android.net.wifi.IconInfo); - method public int describeContents(); - method public byte[] getData(); - method public java.lang.String getFilename(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.net.wifi.IconInfo> CREATOR; - } - public class ScanResult implements android.os.Parcelable { method public int describeContents(); method public boolean is80211mcResponder(); @@ -26640,7 +26637,8 @@ package android.net.wifi { field public static final java.lang.String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG"; field public static final java.lang.String EXTRA_DELAY = "android.net.wifi.extra.DELAY"; field public static final java.lang.String EXTRA_ESS = "android.net.wifi.extra.ESS"; - field public static final java.lang.String EXTRA_ICON_INFO = "android.net.wifi.extra.ICON_INFO"; + field public static final java.lang.String EXTRA_FILENAME = "android.net.wifi.extra.FILENAME"; + field public static final java.lang.String EXTRA_ICON = "android.net.wifi.extra.ICON"; field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo"; field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi"; field public static final java.lang.String EXTRA_NEW_STATE = "newState"; @@ -31320,7 +31318,6 @@ package android.os { method public final android.util.SizeF readSizeF(); method public final android.util.SparseArray readSparseArray(java.lang.ClassLoader); method public final android.util.SparseBooleanArray readSparseBooleanArray(); - method public final android.util.SparseIntArray readSparseIntArray(); method public final java.lang.String readString(); method public final void readStringArray(java.lang.String[]); method public final void readStringList(java.util.List<java.lang.String>); @@ -31365,7 +31362,6 @@ package android.os { method public final void writeSizeF(android.util.SizeF); method public final void writeSparseArray(android.util.SparseArray<java.lang.Object>); method public final void writeSparseBooleanArray(android.util.SparseBooleanArray); - method public final void writeSparseIntArray(android.util.SparseIntArray); method public final void writeString(java.lang.String); method public final void writeStringArray(java.lang.String[]); method public final void writeStringList(java.util.List<java.lang.String>); @@ -34391,6 +34387,7 @@ package android.provider { method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException; method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException; method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException; + method public static void ejectRoot(android.content.ContentResolver, android.net.Uri); method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException; method public static java.lang.String getDocumentId(android.net.Uri); method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException; @@ -34459,6 +34456,7 @@ package android.provider { field public static final java.lang.String COLUMN_TITLE = "title"; field public static final int FLAG_LOCAL_ONLY = 2; // 0x2 field public static final int FLAG_SUPPORTS_CREATE = 1; // 0x1 + field public static final int FLAG_SUPPORTS_EJECT = 32; // 0x20 field public static final int FLAG_SUPPORTS_IS_CHILD = 16; // 0x10 field public static final int FLAG_SUPPORTS_RECENTS = 4; // 0x4 field public static final int FLAG_SUPPORTS_SEARCH = 8; // 0x8 @@ -34472,6 +34470,7 @@ package android.provider { method public android.content.IntentSender createWebLinkIntent(java.lang.String, android.os.Bundle) throws java.io.FileNotFoundException; method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]); method public void deleteDocument(java.lang.String) throws java.io.FileNotFoundException; + method public void ejectRoot(java.lang.String); method public android.provider.DocumentsContract.Path findDocumentPath(java.lang.String, java.lang.String) throws java.io.FileNotFoundException; method public java.lang.String[] getDocumentStreamTypes(java.lang.String, java.lang.String); method public java.lang.String getDocumentType(java.lang.String) throws java.io.FileNotFoundException; @@ -40138,13 +40137,17 @@ package android.telephony { method public int getDataActivity(); method public int getDataNetworkType(); method public int getDataState(); - method public java.lang.String getDeviceId(); - method public java.lang.String getDeviceId(int); + method public deprecated java.lang.String getDeviceId(); + method public deprecated java.lang.String getDeviceId(int); method public java.lang.String getDeviceSoftwareVersion(); method public java.lang.String[] getForbiddenPlmns(); method public java.lang.String getGroupIdLevel1(); method public java.lang.String getIccAuthentication(int, int, java.lang.String); + method public java.lang.String getImei(); + method public java.lang.String getImei(int); method public java.lang.String getLine1Number(); + method public java.lang.String getMeid(); + method public java.lang.String getMeid(int); method public java.lang.String getMmsUAProfUrl(); method public java.lang.String getMmsUserAgent(); method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo(); @@ -43438,7 +43441,6 @@ package android.util { method public E get(long, E); method public int indexOfKey(long); method public int indexOfValue(E); - method public int indexOfValueByValue(E); method public long keyAt(int); method public void put(long, E); method public void remove(long); @@ -43641,7 +43643,6 @@ package android.util { method public E get(int, E); method public int indexOfKey(int); method public int indexOfValue(E); - method public int indexOfValueByValue(E); method public int keyAt(int); method public void put(int, E); method public void remove(int); diff --git a/core/java/android/app/AuthenticationRequiredException.java b/core/java/android/app/AuthenticationRequiredException.java new file mode 100644 index 000000000000..89609794a615 --- /dev/null +++ b/core/java/android/app/AuthenticationRequiredException.java @@ -0,0 +1,99 @@ +/* + * 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.content.ContentProvider; +import android.content.ContentResolver; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.Preconditions; + +/** + * Specialization of {@link SecurityException} that is thrown when authentication is needed from the + * end user before viewing the content. + * <p> + * This exception is only appropriate where there is a concrete action the user can take to + * authorize and make forward progress, such as confirming or entering authentication credentials, + * or granting access via other means. + * <p class="note"> + * Note: legacy code that receives this exception may treat it as a general + * {@link SecurityException}, and thus there is no guarantee that the action contained will be + * invoked by the user. + * </p> + */ +public final class AuthenticationRequiredException extends SecurityException implements Parcelable { + private static final String TAG = "AuthenticationRequiredException"; + + private final PendingIntent mUserAction; + + /** {@hide} */ + public AuthenticationRequiredException(Parcel in) { + this(new SecurityException(in.readString()), PendingIntent.CREATOR.createFromParcel(in)); + } + + /** + * Create an instance ready to be thrown. + * + * @param cause original cause with details designed for engineering + * audiences. + * @param userAction primary action that will initiate the recovery. This + * must launch an activity that is expected to set + * {@link Activity#setResult(int)} before finishing to + * communicate the final status of the recovery. For example, + * apps that observe {@link Activity#RESULT_OK} may choose to + * immediately retry their operation. If this exception was + * thrown from a {@link ContentProvider}, you should also send + * any relevant {@link ContentResolver#notifyChange} events to + * trigger reloading of data. + */ + public AuthenticationRequiredException(Throwable cause, PendingIntent userAction) { + super(cause.getMessage()); + mUserAction = Preconditions.checkNotNull(userAction); + } + + /** + * Return primary action that will initiate the authorization. + */ + public PendingIntent getUserAction() { + return mUserAction; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(getMessage()); + mUserAction.writeToParcel(dest, flags); + } + + public static final Creator<AuthenticationRequiredException> CREATOR = + new Creator<AuthenticationRequiredException>() { + @Override + public AuthenticationRequiredException createFromParcel(Parcel source) { + return new AuthenticationRequiredException(source); + } + + @Override + public AuthenticationRequiredException[] newArray(int size) { + return new AuthenticationRequiredException[size]; + } + }; +} diff --git a/core/java/android/app/RecoverableSecurityException.java b/core/java/android/app/RecoverableSecurityException.java index 8612f186ade4..a503a46a29dc 100644 --- a/core/java/android/app/RecoverableSecurityException.java +++ b/core/java/android/app/RecoverableSecurityException.java @@ -45,7 +45,8 @@ import com.android.internal.util.Preconditions; * Note: legacy code that receives this exception may treat it as a general * {@link SecurityException}, and thus there is no guarantee that the messages * contained will be shown to the end user. - * </p> + * + * @hide */ public final class RecoverableSecurityException extends SecurityException implements Parcelable { private static final String TAG = "RecoverableSecurityException"; diff --git a/core/java/android/database/PageViewCursor.java b/core/java/android/database/PageViewCursor.java index 5f42f30be5ba..44727a0d300e 100644 --- a/core/java/android/database/PageViewCursor.java +++ b/core/java/android/database/PageViewCursor.java @@ -18,13 +18,13 @@ package android.database; import static com.android.internal.util.Preconditions.checkArgument; import android.annotation.Nullable; +import android.annotation.TestApi; import android.content.ContentResolver; import android.os.Build; import android.os.Bundle; import android.util.Log; import android.util.MathUtils; -import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; /** @@ -34,11 +34,10 @@ import com.android.internal.util.ArrayUtils; * * @hide */ +@TestApi public final class PageViewCursor extends CursorWrapper implements CrossProcessCursor { - /** - * An in internal extra added to results that are auto-paged using the wrapper. - */ + /** An extra added to results that are auto-paged using the wrapper. */ public static final String EXTRA_AUTO_PAGED = "android.content.extra.AUTO_PAGED"; private static final String TAG = "PageViewCursor"; @@ -56,7 +55,6 @@ public final class PageViewCursor extends CursorWrapper implements CrossProcessC /** * @see PageViewCursor#wrap(Cursor, Bundle) */ - @VisibleForTesting public PageViewCursor(Cursor cursor, int offset, int limit) { super(cursor); diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 7a39d239f84b..2e35a51afe46 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -898,6 +898,9 @@ public final class Parcel { } } + /** + * @hide + */ public final void writeSparseIntArray(SparseIntArray val) { if (val == null) { writeInt(-1); @@ -2323,6 +2326,7 @@ public final class Parcel { /** * Read and return a new SparseIntArray object from the parcel at the current * dataPosition(). Returns null if the previously written array object was null. + * @hide */ public final SparseIntArray readSparseIntArray() { int N = readInt(); diff --git a/core/java/android/os/PowerSaveState.java b/core/java/android/os/PowerSaveState.java index 9269e762ef4c..7058a1dca34d 100644 --- a/core/java/android/os/PowerSaveState.java +++ b/core/java/android/os/PowerSaveState.java @@ -24,7 +24,17 @@ package android.os; * @hide */ public class PowerSaveState implements Parcelable { + /** + * Whether we should enable battery saver for this service. + * + * @see com.android.server.power.BatterySaverPolicy.ServiceType + */ public final boolean batterySaverEnabled; + /** + * Whether the battery saver is enabled globally, which means the data we get from + * {@link PowerManager#isPowerSaveMode()} + */ + public final boolean globalBatterySaverEnabled; public final int gpsMode; public final float brightnessFactor; @@ -32,10 +42,12 @@ public class PowerSaveState implements Parcelable { batterySaverEnabled = builder.mBatterySaverEnabled; gpsMode = builder.mGpsMode; brightnessFactor = builder.mBrightnessFactor; + globalBatterySaverEnabled = builder.mGlobalBatterySaverEnabled; } public PowerSaveState(Parcel in) { batterySaverEnabled = in.readByte() != 0; + globalBatterySaverEnabled = in.readByte() != 0; gpsMode = in.readInt(); brightnessFactor = in.readFloat(); } @@ -48,12 +60,14 @@ public class PowerSaveState implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { dest.writeByte((byte) (batterySaverEnabled ? 1 : 0)); + dest.writeByte((byte) (globalBatterySaverEnabled ? 1 : 0)); dest.writeInt(gpsMode); dest.writeFloat(brightnessFactor); } public static final class Builder { private boolean mBatterySaverEnabled = false; + private boolean mGlobalBatterySaverEnabled = false; private int mGpsMode = 0; private float mBrightnessFactor = 0.5f; @@ -64,6 +78,11 @@ public class PowerSaveState implements Parcelable { return this; } + public Builder setGlobalBatterySaverEnabled(boolean enabled) { + mGlobalBatterySaverEnabled = enabled; + return this; + } + public Builder setGpsMode(int mode) { mGpsMode = mode; return this; diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index f69c996c5368..31376587e144 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -92,6 +92,12 @@ public class Process { public static final int VPN_UID = 1016; /** + * Defines the UID/GID for keystore. + * @hide + */ + public static final int KEYSTORE_UID = 1017; + + /** * Defines the UID/GID for the NFC service process. * @hide */ diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index 9e56e01c24c4..fa9038411010 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -594,6 +594,15 @@ public final class DocumentsContract { public static final int FLAG_SUPPORTS_IS_CHILD = 1 << 4; /** + * Flag indicating that this root can be ejected. + * + * @see #COLUMN_FLAGS + * @see DocumentsContract#ejectRoot(ContentResolver, Uri) + * @see DocumentsProvider#ejectRoot(String) + */ + public static final int FLAG_SUPPORTS_EJECT = 1 << 5; + + /** * Flag indicating that this root is currently empty. This may be used * to hide the root when opening documents, but the root will still be * shown when creating documents and {@link #FLAG_SUPPORTS_CREATE} is @@ -641,9 +650,6 @@ public final class DocumentsContract { * @hide */ public static final int FLAG_REMOVABLE_USB = 1 << 20; - - /** {@hide} */ - public static final int FLAG_SUPPORTS_EJECT = 1 << 21; } /** @@ -1345,35 +1351,30 @@ public final class DocumentsContract { client.call(METHOD_REMOVE_DOCUMENT, null, in); } - /** {@hide} */ - public static boolean ejectRoot(ContentResolver resolver, Uri rootUri) { + /** + * Ejects the given root. It throws {@link IllegalStateException} when ejection failed. + * + * @param rootUri root with {@link Root#FLAG_SUPPORTS_EJECT} to be ejected + */ + public static void ejectRoot(ContentResolver resolver, Uri rootUri) { final ContentProviderClient client = resolver.acquireUnstableContentProviderClient( rootUri.getAuthority()); try { - return ejectRoot(client, rootUri); - } catch (Exception e) { - Log.w(TAG, "Failed to eject root", e); - return false; + ejectRoot(client, rootUri); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); } finally { ContentProviderClient.releaseQuietly(client); } } /** {@hide} */ - public static boolean ejectRoot(ContentProviderClient client, Uri rootUri) + public static void ejectRoot(ContentProviderClient client, Uri rootUri) throws RemoteException { final Bundle in = new Bundle(); in.putParcelable(DocumentsContract.EXTRA_URI, rootUri); - final Bundle out = client.call(METHOD_EJECT_ROOT, null, in); - - if (out == null) { - throw new RemoteException("Failed to get a reponse from ejectRoot."); - } - if (!out.containsKey(DocumentsContract.EXTRA_RESULT)) { - throw new RemoteException("Response did not include result field.."); - } - return out.getBoolean(DocumentsContract.EXTRA_RESULT); + client.call(METHOD_EJECT_ROOT, null, in); } /** diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java index 620d33a5e915..2a83c4b6a6ba 100644 --- a/core/java/android/provider/DocumentsProvider.java +++ b/core/java/android/provider/DocumentsProvider.java @@ -38,7 +38,7 @@ import static android.provider.DocumentsContract.isTreeUri; import android.Manifest; import android.annotation.CallSuper; import android.annotation.Nullable; -import android.app.RecoverableSecurityException; +import android.app.AuthenticationRequiredException; import android.content.ClipDescription; import android.content.ContentProvider; import android.content.ContentResolver; @@ -235,10 +235,6 @@ public abstract class DocumentsProvider extends ContentProvider { * {@link Document#COLUMN_DOCUMENT_ID}. You must allocate a new * {@link Document#COLUMN_DOCUMENT_ID} to represent the document, which must * not change once returned. - * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), but it is not guaranteed that - * the client will handle this properly. * * @param parentDocumentId the parent directory to create the new document * under. @@ -247,6 +243,10 @@ public abstract class DocumentsProvider extends ContentProvider { * @param displayName the display name of the new document. The provider may * alter this name to meet any internal constraints, such as * avoiding conflicting names. + + * @throws AuthenticationRequiredException If authentication is required from the user (such as + * login credentials), but it is not guaranteed that the client will handle this + * properly. */ @SuppressWarnings("unused") public String createDocument(String parentDocumentId, String mimeType, String displayName) @@ -262,15 +262,14 @@ public abstract class DocumentsProvider extends ContentProvider { * URI permission grants will be updated to point at the new document. If * the original {@link Document#COLUMN_DOCUMENT_ID} is still valid after the * rename, return {@code null}. - * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), but it is not guaranteed that - * the client will handle this properly. * * @param documentId the document to rename. * @param displayName the updated display name of the document. The provider * may alter this name to meet any internal constraints, such as * avoiding conflicting names. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. */ @SuppressWarnings("unused") public String renameDocument(String documentId, String displayName) @@ -286,12 +285,11 @@ public abstract class DocumentsProvider extends ContentProvider { * call (such as documents inside a directory) the implementor is * responsible for revoking those permissions using * {@link #revokeDocumentPermission(String)}. - * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), but it is not guaranteed that - * the client will handle this properly. * * @param documentId the document to delete. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. */ @SuppressWarnings("unused") public void deleteDocument(String documentId) throws FileNotFoundException { @@ -305,13 +303,12 @@ public abstract class DocumentsProvider extends ContentProvider { * the same document provider. Upon completion returns the document id of * the copied document at the target destination. {@code null} must never * be returned. - * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), but it is not guaranteed that - * the client will handle this properly. * * @param sourceDocumentId the document to copy. * @param targetParentDocumentId the target document to be copied into as a child. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. */ @SuppressWarnings("unused") public String copyDocument(String sourceDocumentId, String targetParentDocumentId) @@ -329,15 +326,14 @@ public abstract class DocumentsProvider extends ContentProvider { * * <p>It's the responsibility of the provider to revoke grants if the document * is no longer accessible using <code>sourceDocumentId</code>. - * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), but it is not guaranteed that - * the client will handle this properly. * * @param sourceDocumentId the document to move. * @param sourceParentDocumentId the parent of the document to move. * @param targetParentDocumentId the target document to be a new parent of the * source document. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. */ @SuppressWarnings("unused") public String moveDocument(String sourceDocumentId, String sourceParentDocumentId, @@ -355,11 +351,11 @@ public abstract class DocumentsProvider extends ContentProvider { * <p>It's the responsibility of the provider to revoke grants if the document is * removed from the last parent, and effectively the document is deleted. * - * <p>{@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), but it is not guaranteed that - * the client will handle this properly. * @param documentId the document to remove. * @param parentDocumentId the parent of the document to move. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. */ @SuppressWarnings("unused") public void removeDocument(String documentId, String parentDocumentId) @@ -377,9 +373,6 @@ public abstract class DocumentsProvider extends ContentProvider { * <p>This API assumes that document ID has enough info to infer the root. * Different roots should use different document ID to refer to the same * document. - * <p>{@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), but it is not guaranteed that - * the client will handle this properly.perly. * * * @param parentDocumentId the document from which the path starts if not null, @@ -388,6 +381,9 @@ public abstract class DocumentsProvider extends ContentProvider { * @return the path of the requested document. If parentDocumentId is null * returned root ID must not be null. If parentDocumentId is not null * returned root ID must be null. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. */ public Path findDocumentPath(@Nullable String parentDocumentId, String childDocumentId) throws FileNotFoundException { @@ -397,7 +393,7 @@ public abstract class DocumentsProvider extends ContentProvider { /** * Creates an intent sender for a web link, if the document is web linkable. * <p> - * {@link RecoverableSecurityException} can be thrown if user does not have + * {@link AuthenticationRequiredException} can be thrown if user does not have * sufficient permission for the linked document. Before any new permissions * are granted for the linked document, a visible UI must be shown, so the * user can explicitly confirm whether the permission grants are expected. @@ -414,6 +410,9 @@ public abstract class DocumentsProvider extends ContentProvider { * * @param documentId the document to create a web link intent for. * @param options additional information, such as list of recipients. Optional. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. * * @see DocumentsContract.Document#FLAG_WEB_LINKABLE * @see android.app.PendingIntent#getIntentSender @@ -436,9 +435,6 @@ public abstract class DocumentsProvider extends ContentProvider { * android.database.ContentObserver, boolean)} with * {@link DocumentsContract#buildRootsUri(String)} to notify the system. * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), or returned as part of - * Cursor's bundle. It is not guaranteed that the client will handle this properly. * * @param projection list of {@link Root} columns to put into the cursor. If * {@code null} all supported columns should be included. @@ -452,10 +448,6 @@ public abstract class DocumentsProvider extends ContentProvider { * sorted by {@link Document#COLUMN_LAST_MODIFIED} in descending order, and * limited to only return the 64 most recently modified documents. * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), or returned as part of - * Cursor's bundle. It is not guaranteed that the client will handle this properly. - * <p> * Recent documents do not support change notifications. * * @param projection list of {@link Document} columns to put into the @@ -472,16 +464,14 @@ public abstract class DocumentsProvider extends ContentProvider { /** * Return metadata for the single requested document. You should avoid * making network requests to keep this request fast. - * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), or returned as part of - * Cursor's bundle. It is not guaranteed that the client will handle this properly. - * * * @param documentId the document to return. * @param projection list of {@link Document} columns to put into the * cursor. If {@code null} all supported columns should be * included. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. */ public abstract Cursor queryDocument(String documentId, String[] projection) throws FileNotFoundException; @@ -509,11 +499,6 @@ public abstract class DocumentsProvider extends ContentProvider { * you can call {@link ContentResolver#notifyChange(Uri, * android.database.ContentObserver, boolean)} with that Uri to send change * notifications. - * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), or returned as part of - * Cursor's bundle. It is not guaranteed that the client will handle this properly. - * * * @param parentDocumentId the directory to return children for. * @param projection list of {@link Document} columns to put into the @@ -525,6 +510,9 @@ public abstract class DocumentsProvider extends ContentProvider { * may be unordered. This ordering is a hint that can be used to * prioritize how data is fetched from the network, but UI may * always enforce a specific ordering. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. * @see DocumentsContract#EXTRA_LOADING * @see DocumentsContract#EXTRA_INFO * @see DocumentsContract#EXTRA_ERROR @@ -552,10 +540,6 @@ public abstract class DocumentsProvider extends ContentProvider { * you can call {@link ContentResolver#notifyChange(Uri, * android.database.ContentObserver, boolean)} with that Uri to send change * notifications. - * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), or returned as part of - * Cursor's bundle. It is not guaranteed that the client will handle this properly. * * @param parentDocumentId the directory to return children for. * @param projection list of {@link Document} columns to put into the @@ -567,6 +551,9 @@ public abstract class DocumentsProvider extends ContentProvider { * will be used, which may be unordered. See * {@link ContentResolver#QUERY_ARG_SORT_COLUMNS} for * details. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. * * @see DocumentsContract#EXTRA_LOADING * @see DocumentsContract#EXTRA_INFO @@ -609,16 +596,16 @@ public abstract class DocumentsProvider extends ContentProvider { * String, String)}. Then you can call {@link ContentResolver#notifyChange(Uri, * android.database.ContentObserver, boolean)} with that Uri to send change * notifications. - * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), or returned as part of - * Cursor's bundle. It is not guaranteed that the client will handle this properly. * * @param rootId the root to search under. * @param query string to match documents against. * @param projection list of {@link Document} columns to put into the * cursor. If {@code null} all supported columns should be * included. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. + * * @see DocumentsContract#EXTRA_LOADING * @see DocumentsContract#EXTRA_INFO * @see DocumentsContract#EXTRA_ERROR @@ -629,9 +616,14 @@ public abstract class DocumentsProvider extends ContentProvider { throw new UnsupportedOperationException("Search not supported"); } - /** {@hide} */ + /** + * Ejects the root. Throws {@link IllegalStateException} if ejection failed. + * + * @param rootId the root to be ejected. + * @see Root#FLAG_SUPPORTS_EJECT + */ @SuppressWarnings("unused") - public boolean ejectRoot(String rootId) { + public void ejectRoot(String rootId) { throw new UnsupportedOperationException("Eject not supported"); } @@ -641,9 +633,9 @@ public abstract class DocumentsProvider extends ContentProvider { * implementation queries {@link #queryDocument(String, String[])}, so * providers may choose to override this as an optimization. * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), but it is not guaranteed that - * the client will handle this properly. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. */ public String getDocumentType(String documentId) throws FileNotFoundException { final Cursor cursor = queryDocument(documentId, null); @@ -669,15 +661,14 @@ public abstract class DocumentsProvider extends ContentProvider { * <p> * If you block while downloading content, you should periodically check * {@link CancellationSignal#isCanceled()} to abort abandoned open requests. - * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), but it is not guaranteed that - * the client will handle this properly. * * @param documentId the document to return. * @param mode the mode to open with, such as 'r', 'w', or 'rw'. * @param signal used by the caller to signal if the request should be * cancelled. May be null. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. * @see ParcelFileDescriptor#open(java.io.File, int, android.os.Handler, * OnCloseListener) * @see ParcelFileDescriptor#createReliablePipe() @@ -697,15 +688,14 @@ public abstract class DocumentsProvider extends ContentProvider { * If you perform expensive operations to download or generate a thumbnail, * you should periodically check {@link CancellationSignal#isCanceled()} to * abort abandoned thumbnail requests. - * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), but it is not guaranteed that - * the client will handle this properly. * * @param documentId the document to return. * @param sizeHint hint of the optimal thumbnail dimensions. * @param signal used by the caller to signal if the request should be * cancelled. May be null. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. * @see Document#FLAG_SUPPORTS_THUMBNAIL */ @SuppressWarnings("unused") @@ -723,10 +713,6 @@ public abstract class DocumentsProvider extends ContentProvider { * matching the specified MIME type filter. * <p> * Virtual documents must have at least one streamable format. - * <p> - * {@link RecoverableSecurityException} can be thrown if more input is required - * from the user (such as insufficient permission), but it is not guaranteed that - * the client will handle this properly. * * @param documentId the document to return. * @param mimeTypeFilter the MIME type filter for the requested format. May @@ -735,6 +721,9 @@ public abstract class DocumentsProvider extends ContentProvider { * provider. * @param signal used by the caller to signal if the request should be * cancelled. May be null. + * @throws AuthenticationRequiredException If authentication is required from + * the user (such as login credentials), but it is not guaranteed + * that the client will handle this properly. * @see #getDocumentStreamTypes(String, String) */ @SuppressWarnings("unused") @@ -963,14 +952,12 @@ public abstract class DocumentsProvider extends ContentProvider { if (METHOD_EJECT_ROOT.equals(method)) { // Given that certain system apps can hold MOUNT_UNMOUNT permission, but only apps // signed with platform signature can hold MANAGE_DOCUMENTS, we are going to check for - // MANAGE_DOCUMENTS here instead - getContext().enforceCallingPermission( - android.Manifest.permission.MANAGE_DOCUMENTS, null); + // MANAGE_DOCUMENTS or associated URI permission here instead final Uri rootUri = extras.getParcelable(DocumentsContract.EXTRA_URI); - final String rootId = DocumentsContract.getRootId(rootUri); - final boolean ejected = ejectRoot(rootId); + enforceWritePermissionInner(rootUri, getCallingPackage(), null); - out.putBoolean(DocumentsContract.EXTRA_RESULT, ejected); + final String rootId = DocumentsContract.getRootId(rootUri); + ejectRoot(rootId); return out; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 146d2d3caebc..83528741dd82 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9741,6 +9741,16 @@ public final class Settings { public static final String DATABASE_DOWNGRADE_REASON = "database_downgrade_reason"; /** + * The build id of when the settings database was first created (or re-created due it + * being missing). + * + * Type: string + * + * @hide + */ + public static final String DATABASE_CREATION_BUILDID = "database_creation_buildid"; + + /** * Flag to toggle journal mode WAL on or off for the contacts database. WAL is enabled by * default. Set to 0 to disable. * diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java index 58ad2fd0c586..9ba0f5da135e 100644 --- a/core/java/android/util/LongSparseArray.java +++ b/core/java/android/util/LongSparseArray.java @@ -315,6 +315,7 @@ public class LongSparseArray<E> implements Cloneable { * and that multiple keys can map to the same value and this will * find only one of them. * <p>Note also that this method uses {@code equals} unlike {@code indexOfValue}. + * @hide */ public int indexOfValueByValue(E value) { if (mGarbage) { diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java index c76666069d18..b3400ef538b8 100644 --- a/core/java/android/util/SparseArray.java +++ b/core/java/android/util/SparseArray.java @@ -363,6 +363,7 @@ public class SparseArray<E> implements Cloneable { * and that multiple keys can map to the same value and this will * find only one of them. * <p>Note also that this method uses {@code equals} unlike {@code indexOfValue}. + * @hide */ public int indexOfValueByValue(E value) { if (mGarbage) { diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java index 7b99d07d2457..78d18fdbca5f 100644 --- a/core/java/android/widget/ListPopupWindow.java +++ b/core/java/android/widget/ListPopupWindow.java @@ -75,6 +75,7 @@ public class ListPopupWindow implements ShowableListMenu { private int mDropDownWindowLayoutType = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL; private boolean mDropDownVerticalOffsetSet; private boolean mIsAnimatedFromAnchor = true; + private boolean mOverlapAnchor; private int mDropDownGravity = Gravity.NO_GRAVITY; @@ -672,6 +673,7 @@ public class ListPopupWindow implements ShowableListMenu { mPopup.setOutsideTouchable(!mForceIgnoreOutsideTouch && !mDropDownAlwaysVisible); mPopup.setTouchInterceptor(mTouchInterceptor); mPopup.setEpicenterBounds(mEpicenterBounds); + mPopup.setOverlapAnchor(mOverlapAnchor); mPopup.showAsDropDown(getAnchorView(), mDropDownHorizontalOffset, mDropDownVerticalOffset, mDropDownGravity); mDropDownList.setSelection(ListView.INVALID_POSITION); @@ -1245,6 +1247,13 @@ public class ListPopupWindow implements ShowableListMenu { return listContent + otherHeights; } + /** + * @hide + */ + public void setOverlapAnchor(boolean overlap) { + mOverlapAnchor = overlap; + } + private class PopupDataSetObserver extends DataSetObserver { @Override public void onChanged() { diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 26b3ae2c42fc..9f10531841e3 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -55,6 +55,7 @@ import android.view.ViewTreeObserver.OnScrollChangedListener; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.WindowManager.LayoutParams.SoftInputModeFlags; +import android.view.WindowManagerGlobal; import com.android.internal.R; @@ -137,6 +138,7 @@ public class PopupWindow { private final int[] mTmpDrawingLocation = new int[2]; private final int[] mTmpScreenLocation = new int[2]; + private final int[] mTmpAppLocation = new int[2]; private final Rect mTempRect = new Rect(); private Context mContext; @@ -242,6 +244,9 @@ public class PopupWindow { private final OnScrollChangedListener mOnScrollChangedListener = this::alignToAnchor; + private final View.OnLayoutChangeListener mOnLayoutChangeListener = + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> alignToAnchor(); + private int mAnchorXoff; private int mAnchorYoff; private int mAnchoredGravity; @@ -1238,7 +1243,8 @@ public class PopupWindow { mIsShowing = true; mIsDropdown = true; - final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken()); + final WindowManager.LayoutParams p = + createPopupLayoutParams(anchor.getApplicationWindowToken()); preparePopup(p); final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff, @@ -1547,13 +1553,21 @@ public class PopupWindow { } // Initially, align to the bottom-left corner of the anchor plus offsets. + final int[] appScreenLocation = mTmpAppLocation; + final View appRootView = getAppRootView(anchor); + appRootView.getLocationOnScreen(appScreenLocation); + + final int[] screenLocation = mTmpScreenLocation; + anchor.getLocationOnScreen(screenLocation); + final int[] drawingLocation = mTmpDrawingLocation; - anchor.getLocationInWindow(drawingLocation); + drawingLocation[0] = screenLocation[0] - appScreenLocation[0]; + drawingLocation[1] = screenLocation[1] - appScreenLocation[1]; outParams.x = drawingLocation[0] + xOffset; outParams.y = drawingLocation[1] + anchorHeight + yOffset; final Rect displayFrame = new Rect(); - anchor.getWindowVisibleDisplayFrame(displayFrame); + appRootView.getWindowVisibleDisplayFrame(displayFrame); if (width == MATCH_PARENT) { width = displayFrame.right - displayFrame.left; } @@ -1574,9 +1588,6 @@ public class PopupWindow { outParams.x -= width - anchorWidth; } - final int[] screenLocation = mTmpScreenLocation; - anchor.getLocationOnScreen(screenLocation); - // First, attempt to fit the popup vertically without resizing. final boolean fitsVertical = tryFitVertical(outParams, yOffset, height, anchorHeight, drawingLocation[1], screenLocation[1], displayFrame.top, @@ -1595,7 +1606,9 @@ public class PopupWindow { scrollY + height + anchorHeight + yOffset); if (allowScroll && anchor.requestRectangleOnScreen(r, true)) { // Reset for the new anchor position. - anchor.getLocationInWindow(drawingLocation); + anchor.getLocationOnScreen(screenLocation); + drawingLocation[0] = screenLocation[0] - appScreenLocation[0]; + drawingLocation[1] = screenLocation[1] - appScreenLocation[1]; outParams.x = drawingLocation[0] + xOffset; outParams.y = drawingLocation[1] + anchorHeight + yOffset; @@ -1793,7 +1806,8 @@ public class PopupWindow { Rect displayFrame = null; final Rect visibleDisplayFrame = new Rect(); - anchor.getWindowVisibleDisplayFrame(visibleDisplayFrame); + final View appView = getAppRootView(anchor); + appView.getWindowVisibleDisplayFrame(visibleDisplayFrame); if (ignoreBottomDecorations) { // In the ignore bottom decorations case we want to // still respect all other decorations so we use the inset visible @@ -2240,6 +2254,7 @@ public class PopupWindow { final View anchorRoot = mAnchorRoot != null ? mAnchorRoot.get() : null; if (anchorRoot != null) { anchorRoot.removeOnAttachStateChangeListener(mOnAnchorRootDetachedListener); + anchorRoot.removeOnLayoutChangeListener(mOnLayoutChangeListener); } mAnchor = null; @@ -2258,6 +2273,7 @@ public class PopupWindow { final View anchorRoot = anchor.getRootView(); anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener); + anchorRoot.addOnLayoutChangeListener(mOnLayoutChangeListener); mAnchor = new WeakReference<>(anchor); mAnchorRoot = new WeakReference<>(anchorRoot); @@ -2281,6 +2297,15 @@ public class PopupWindow { } } + private View getAppRootView(View anchor) { + final View appWindowView = WindowManagerGlobal.getInstance().getWindowView( + anchor.getApplicationWindowToken()); + if (appWindowView != null) { + return appWindowView; + } + return anchor.getRootView(); + } + private class PopupDecorView extends FrameLayout { private TransitionListenerAdapter mPendingExitListener; diff --git a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java index ebcec5cb5a42..6dff8b41eeb4 100644 --- a/core/java/com/android/internal/view/menu/CascadingMenuPopup.java +++ b/core/java/com/android/internal/view/menu/CascadingMenuPopup.java @@ -381,6 +381,7 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey if (parentView != null) { // This menu is a cascading submenu anchored to a parent view. + popupWindow.setAnchorView(parentView); popupWindow.setTouchModal(false); popupWindow.setEnterTransition(null); @@ -388,42 +389,30 @@ final class CascadingMenuPopup extends MenuPopup implements MenuPresenter, OnKey final boolean showOnRight = nextMenuPosition == HORIZ_POSITION_RIGHT; mLastPosition = nextMenuPosition; - // A popup anchored to mAnchorView with (0,0) offset would be shown at this position. - final int[] offsetOrigin = new int[2]; - mAnchorView.getLocationOnScreen(offsetOrigin); - offsetOrigin[1] += mAnchorView.getHeight(); - - final int[] parentViewScreenLocation = new int[2]; - parentView.getLocationOnScreen(parentViewScreenLocation); - - // Translate the parent view location into the offset coordinate space. - // If used as horizontal/vertical offsets, these values would position the submenu - // at the exact same position as the parent item. - final int parentOffsetLeft = parentViewScreenLocation[0] - offsetOrigin[0]; - final int parentOffsetTop = parentViewScreenLocation[1] - offsetOrigin[1]; - - // Adjust the horizontal offset to display the submenu to the right or to the left + // Compute the horizontal offset to display the submenu to the right or to the left // of the parent item. // By now, mDropDownGravity is the resolved absolute gravity, so // this should work in both LTR and RTL. final int x; if ((mDropDownGravity & Gravity.RIGHT) == Gravity.RIGHT) { if (showOnRight) { - x = parentOffsetLeft + menuWidth; + x = menuWidth; } else { - x = parentOffsetLeft - parentView.getWidth(); + x = -parentView.getWidth(); } } else { if (showOnRight) { - x = parentOffsetLeft + parentView.getWidth(); + x = parentView.getWidth(); } else { - x = parentOffsetLeft - menuWidth; + x = -menuWidth; } } popupWindow.setHorizontalOffset(x); - // Use the same vertical offset as the parent item. - popupWindow.setVerticalOffset(parentOffsetTop); + // Align with the top edge of the parent view (or the bottom edge when the submenu is + // flipped vertically). + popupWindow.setOverlapAnchor(true); + popupWindow.setVerticalOffset(0); } else { if (mHasXOffset) { popupWindow.setHorizontalOffset(mXOffset); diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index a8d683028c13..de5e505af3da 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -621,7 +621,7 @@ static void ToColor_SA8(SkColor dst[], const void* src, int width, SkColorTable* const uint8_t* s = (const uint8_t*)src; do { uint8_t c = *s++; - *dst++ = SkColorSetARGB(c, c, c, c); + *dst++ = SkColorSetARGB(c, 0, 0, 0); } while (--width != 0); } @@ -727,6 +727,50 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, return createBitmap(env, nativeBitmap.release(), getPremulBitmapCreateFlags(isMutable)); } +static bool bitmapCopyTo(SkBitmap* dst, SkColorType dstCT, const SkBitmap& src, + SkBitmap::Allocator* alloc) { + // Skia does not support copying from kAlpha8 to types that are not alpha only. + // We will handle this case here. + if (kAlpha_8_SkColorType == src.colorType() && kAlpha_8_SkColorType != dstCT) { + SkAutoPixmapUnlock srcUnlocker; + if (!src.requestLock(&srcUnlocker)) { + return false; + } + SkPixmap srcPixmap = srcUnlocker.pixmap(); + + SkImageInfo dstInfo = src.info().makeColorType(dstCT); + if (!dst->setInfo(dstInfo)) { + return false; + } + if (!dst->tryAllocPixels(alloc, nullptr)) { + return false; + } + + switch (dstCT) { + case kRGBA_8888_SkColorType: + case kBGRA_8888_SkColorType: { + for (int y = 0; y < src.height(); y++) { + const uint8_t* srcRow = srcPixmap.addr8(0, y); + uint32_t* dstRow = dst->getAddr32(0, y); + ToColor_SA8(dstRow, srcRow, src.width(), nullptr); + } + return true; + } + case kRGB_565_SkColorType: { + for (int y = 0; y < src.height(); y++) { + uint16_t* dstRow = dst->getAddr16(0, y); + memset(dstRow, 0, sizeof(uint16_t) * src.width()); + } + return true; + } + default: + return false; + } + } + + return src.copyTo(dst, dstCT, alloc); +} + static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle, jint dstConfigHandle, jboolean isMutable) { SkBitmap src; @@ -743,7 +787,7 @@ static jobject Bitmap_copy(JNIEnv* env, jobject, jlong srcHandle, SkBitmap result; HeapAllocator allocator; - if (!src.copyTo(&result, dstCT, &allocator)) { + if (!bitmapCopyTo(&result, dstCT, src, &allocator)) { return NULL; } auto bitmap = allocator.getStorageObjAndReset(); @@ -754,7 +798,7 @@ static Bitmap* Bitmap_copyAshmemImpl(JNIEnv* env, SkBitmap& src, SkColorType& ds SkBitmap result; AshmemPixelAllocator allocator(env); - if (!src.copyTo(&result, dstCT, &allocator)) { + if (!bitmapCopyTo(&result, dstCT, src, &allocator)) { return NULL; } auto bitmap = allocator.getStorageObjAndReset(); diff --git a/core/res/res/values-ldrtl-television/config.xml b/core/res/res/values-ldrtl-television/config.xml index 503b9022aa3c..2a5a0c9a1f76 100644 --- a/core/res/res/values-ldrtl-television/config.xml +++ b/core/res/res/values-ldrtl-television/config.xml @@ -22,7 +22,7 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <!-- The default gravity for the picture-in-picture window. - Currently, this maps to Gravity.TOP | Gravity.LEFT --> - <integer name="config_defaultPictureInPictureGravity">0x33</integer> + Currently, this maps to Gravity.BOTTOM | Gravity.LEFT --> + <integer name="config_defaultPictureInPictureGravity">0x53</integer> </resources> diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml index 1987ac454d86..78eeee99150a 100644 --- a/core/res/res/values-television/config.xml +++ b/core/res/res/values-television/config.xml @@ -34,6 +34,6 @@ <string translatable="false" name="config_defaultPictureInPictureScreenEdgeInsets">56x27</string> <!-- The default gravity for the picture-in-picture window. - Currently, this maps to Gravity.TOP | Gravity.RIGHT --> - <integer name="config_defaultPictureInPictureGravity">0x35</integer> + Currently, this maps to Gravity.BOTTOM | Gravity.RIGHT --> + <integer name="config_defaultPictureInPictureGravity">0x55</integer> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index d1c590069c94..f959df9b85f9 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4255,7 +4255,7 @@ <string name="date_picker_day_typeface">sans-serif-medium</string> <!-- Notify use that they are in Lock-to-app --> - <string name="lock_to_app_toast">To unpin this screen, touch & hold Back and Overview.</string> + <string name="lock_to_app_toast">To unpin this screen, touch & hold Back and Overview buttons.</string> <!-- Notify user that they are locked in lock-to-app mode --> <string name="lock_to_app_toast_locked">App is pinned: Unpinning isn\'t allowed on this device.</string> <!-- Starting lock-to-app indication. --> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 0cfdaf5d3cab..3e33dd8e8409 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -141,6 +141,7 @@ public class SettingsBackupTest { Settings.Global.CONTACTS_DATABASE_WAL_ENABLED, Settings.Global.DATA_ACTIVITY_TIMEOUT_MOBILE, Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI, + Settings.Global.DATABASE_CREATION_BUILDID, Settings.Global.DATABASE_DOWNGRADE_REASON, Settings.Global.DATA_ROAMING, Settings.Global.DATA_STALL_ALARM_AGGRESSIVE_DELAY_IN_MS, diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index a84cee8dbce2..44494a644178 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -141,6 +141,11 @@ public class MediaRouter { mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name; mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO; mDefaultAudioVideo.updatePresentationDisplay(); + if (((AudioManager) appContext.getSystemService(Context.AUDIO_SERVICE)) + .isVolumeFixed()) { + mDefaultAudioVideo.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED; + } + addRouteStatic(mDefaultAudioVideo); // This will select the active wifi display route if there is one. diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java index 7122eaf3563b..5bf205e12799 100644 --- a/media/java/android/media/browse/MediaBrowser.java +++ b/media/java/android/media/browse/MediaBrowser.java @@ -87,10 +87,11 @@ public final class MediaBrowser { */ public static final String EXTRA_PAGE_SIZE = "android.media.browse.extra.PAGE_SIZE"; - private static final int CONNECT_STATE_DISCONNECTED = 0; - private static final int CONNECT_STATE_CONNECTING = 1; - private static final int CONNECT_STATE_CONNECTED = 2; - private static final int CONNECT_STATE_SUSPENDED = 3; + private static final int CONNECT_STATE_DISCONNECTING = 0; + private static final int CONNECT_STATE_DISCONNECTED = 1; + private static final int CONNECT_STATE_CONNECTING = 2; + private static final int CONNECT_STATE_CONNECTED = 3; + private static final int CONNECT_STATE_SUSPENDED = 4; private final Context mContext; private final ComponentName mServiceComponent; @@ -213,6 +214,7 @@ public final class MediaBrowser { // It's ok to call this any state, because allowing this lets apps not have // to check isConnected() unnecessarily. They won't appreciate the extra // assertions for this. We do everything we can here to go back to a sane state. + mState = CONNECT_STATE_DISCONNECTING; mHandler.post(new Runnable() { @Override public void run() { @@ -535,7 +537,7 @@ public final class MediaBrowser { // If we are connected, tell the service that we are watching. If we aren't connected, // the service will be told when we connect. - if (mState == CONNECT_STATE_CONNECTED) { + if (isConnected()) { try { if (options == null) { mServiceBinder.addSubscriptionDeprecated(parentId, mServiceCallbacks); @@ -563,7 +565,7 @@ public final class MediaBrowser { // Tell the service if necessary. try { if (callback == null) { - if (mState == CONNECT_STATE_CONNECTED) { + if (isConnected()) { mServiceBinder.removeSubscriptionDeprecated(parentId, mServiceCallbacks); mServiceBinder.removeSubscription(parentId, null, mServiceCallbacks); } @@ -572,7 +574,7 @@ public final class MediaBrowser { final List<Bundle> optionsList = sub.getOptionsList(); for (int i = callbacks.size() - 1; i >= 0; --i) { if (callbacks.get(i) == callback) { - if (mState == CONNECT_STATE_CONNECTED) { + if (isConnected()) { mServiceBinder.removeSubscription( parentId, callback.mToken, mServiceCallbacks); } @@ -597,6 +599,8 @@ public final class MediaBrowser { */ private static String getStateLabel(int state) { switch (state) { + case CONNECT_STATE_DISCONNECTING: + return "CONNECT_STATE_DISCONNECTING"; case CONNECT_STATE_DISCONNECTED: return "CONNECT_STATE_DISCONNECTED"; case CONNECT_STATE_CONNECTING: diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java index c28aa5ec8711..6808b57c2642 100644 --- a/media/java/android/media/tv/TvContract.java +++ b/media/java/android/media/tv/TvContract.java @@ -1971,10 +1971,11 @@ public final class TvContract { * channel is not locked thus the user is not prompted to enter passcode If not specified, * this value is set to 0 (not locked) by default. * + * <p>This column can only be set by applications having proper system permission to + * modify parental control settings. For other applications, this is a read-only column. + * <p>Type: INTEGER (boolean) - * @hide */ - @SystemApi public static final String COLUMN_LOCKED = "locked"; /** diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 8802010c06d0..b60e2fefd6e7 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -480,21 +480,18 @@ public class ExternalStorageProvider extends FileSystemProvider { } @Override - public boolean ejectRoot(String rootId) { + public void ejectRoot(String rootId) { final long token = Binder.clearCallingIdentity(); - boolean ejected = false; RootInfo root = mRoots.get(rootId); if (root != null) { try { mStorageManager.unmount(root.volumeId); - ejected = true; } catch (RuntimeException e) { - Log.w(TAG, "Root '" + root.title + "' could not be ejected"); + throw new IllegalStateException(e); } finally { Binder.restoreCallingIdentity(token); } } - return ejected; } @Override diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 1f1c18976edf..1a752f924425 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2037,6 +2037,8 @@ public class SettingsProvider extends ContentProvider { private final BackupManager mBackupManager; + private String mSettingsCreationBuildId; + public SettingsRegistry() { mHandler = new MyHandler(getContext().getMainLooper()); mGenerationRegistry = new GenerationRegistry(mLock); @@ -2502,6 +2504,8 @@ public class SettingsProvider extends ContentProvider { return; } + mSettingsCreationBuildId = Build.ID; + final long identity = Binder.clearCallingIdentity(); try { List<UserInfo> users = mUserManager.getUsers(true); @@ -2570,6 +2574,12 @@ public class SettingsProvider extends ContentProvider { ensureSettingsStateLocked(globalKey); SettingsState globalSettings = mSettingsStates.get(globalKey); migrateLegacySettingsLocked(globalSettings, database, TABLE_GLOBAL); + // If this was just created + if (mSettingsCreationBuildId != null) { + globalSettings.insertSettingLocked(Settings.Global.DATABASE_CREATION_BUILDID, + mSettingsCreationBuildId, null, true, + SettingsState.SYSTEM_PACKAGE_NAME); + } globalSettings.persistSyncLocked(); } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialog.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialog.java new file mode 100644 index 000000000000..9e5db73cf885 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialog.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.plugins; + +import com.android.systemui.plugins.VolumeDialog.Callback; +import com.android.systemui.plugins.annotations.DependsOn; +import com.android.systemui.plugins.annotations.ProvidesInterface; + +/** + * This interface is really just a stub for initialization/teardown, actual handling of + * when to show will be done through {@link VolumeDialogController} + */ +@ProvidesInterface(action = VolumeDialog.ACTION, version = VolumeDialog.VERSION) +@DependsOn(target = Callback.class) +public interface VolumeDialog extends Plugin { + String ACTION = "com.android.systemui.action.PLUGIN_VOLUME"; + int VERSION = 1; + + void init(int windowType, Callback callback); + void destroy(); + + @ProvidesInterface(version = VERSION) + public interface Callback { + int VERSION = 1; + + void onZenSettingsClicked(); + void onZenPrioritySettingsClicked(); + } +} diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java new file mode 100644 index 000000000000..903ff7276101 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/VolumeDialogController.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package com.android.systemui.plugins; + +import android.annotation.IntegerRes; +import android.content.ComponentName; +import android.media.AudioManager; +import android.media.AudioSystem; +import android.os.Handler; +import android.util.SparseArray; + +import com.android.systemui.plugins.VolumeDialogController.Callbacks; +import com.android.systemui.plugins.VolumeDialogController.State; +import com.android.systemui.plugins.VolumeDialogController.StreamState; +import com.android.systemui.plugins.annotations.DependsOn; +import com.android.systemui.plugins.annotations.ProvidesInterface; + +/** + * Manages the VolumeDialog. + * + * Accessible through {@link PluginDependency} + */ +@ProvidesInterface(version = VolumeDialogController.VERSION) +@DependsOn(target = StreamState.class) +@DependsOn(target = State.class) +@DependsOn(target = Callbacks.class) +public interface VolumeDialogController { + int VERSION = 1; + + void setActiveStream(int stream); + void setStreamVolume(int stream, int userLevel); + void setRingerMode(int ringerModeNormal, boolean external); + + boolean hasVibrator(); + void vibrate(); + + AudioManager getAudioManager(); + + void notifyVisible(boolean visible); + + void addCallback(Callbacks callbacks, Handler handler); + void removeCallback(Callbacks callbacks); + + void userActivity(); + void getState(); + + @ProvidesInterface(version = StreamState.VERSION) + public static final class StreamState { + public static final int VERSION = 1; + + public boolean dynamic; + public int level; + public int levelMin; + public int levelMax; + public boolean muted; + public boolean muteSupported; + public @IntegerRes int name; + public String remoteLabel; + public boolean routedToBluetooth; + + public StreamState copy() { + final StreamState rt = new StreamState(); + rt.dynamic = dynamic; + rt.level = level; + rt.levelMin = levelMin; + rt.levelMax = levelMax; + rt.muted = muted; + rt.muteSupported = muteSupported; + rt.name = name; + rt.remoteLabel = remoteLabel; + rt.routedToBluetooth = routedToBluetooth; + return rt; + } + } + + @ProvidesInterface(version = State.VERSION) + public static final class State { + public static final int VERSION = 1; + + public static int NO_ACTIVE_STREAM = -1; + + public final SparseArray<StreamState> states = new SparseArray<>(); + + public int ringerModeInternal; + public int ringerModeExternal; + public int zenMode; + public ComponentName effectsSuppressor; + public String effectsSuppressorName; + public int activeStream = NO_ACTIVE_STREAM; + + public State copy() { + final State rt = new State(); + for (int i = 0; i < states.size(); i++) { + rt.states.put(states.keyAt(i), states.valueAt(i).copy()); + } + rt.ringerModeExternal = ringerModeExternal; + rt.ringerModeInternal = ringerModeInternal; + rt.zenMode = zenMode; + if (effectsSuppressor != null) { + rt.effectsSuppressor = effectsSuppressor.clone(); + } + rt.effectsSuppressorName = effectsSuppressorName; + rt.activeStream = activeStream; + return rt; + } + + @Override + public String toString() { + return toString(0); + } + + public String toString(int indent) { + final StringBuilder sb = new StringBuilder("{"); + if (indent > 0) sep(sb, indent); + for (int i = 0; i < states.size(); i++) { + if (i > 0) { + sep(sb, indent); + } + final int stream = states.keyAt(i); + final StreamState ss = states.valueAt(i); + sb.append(AudioSystem.streamToString(stream)).append(":").append(ss.level) + .append('[').append(ss.levelMin).append("..").append(ss.levelMax) + .append(']'); + if (ss.muted) sb.append(" [MUTED]"); + if (ss.dynamic) sb.append(" [DYNAMIC]"); + } + sep(sb, indent); sb.append("ringerModeExternal:").append(ringerModeExternal); + sep(sb, indent); sb.append("ringerModeInternal:").append(ringerModeInternal); + sep(sb, indent); sb.append("zenMode:").append(zenMode); + sep(sb, indent); sb.append("effectsSuppressor:").append(effectsSuppressor); + sep(sb, indent); sb.append("effectsSuppressorName:").append(effectsSuppressorName); + sep(sb, indent); sb.append("activeStream:").append(activeStream); + if (indent > 0) sep(sb, indent); + return sb.append('}').toString(); + } + + private static void sep(StringBuilder sb, int indent) { + if (indent > 0) { + sb.append('\n'); + for (int i = 0; i < indent; i++) { + sb.append(' '); + } + } else { + sb.append(','); + } + } + } + + @ProvidesInterface(version = Callbacks.VERSION) + public interface Callbacks { + int VERSION = 1; + + void onShowRequested(int reason); + void onDismissRequested(int reason); + void onStateChanged(State state); + void onLayoutDirectionChanged(int layoutDirection); + void onConfigurationChanged(); + void onShowVibrateHint(); + void onShowSilentHint(); + void onScreenOff(); + void onShowSafetyWarning(int flags); + void onAccessibilityModeChanged(Boolean showA11yStream); + } +} diff --git a/packages/SystemUI/res/drawable/ic_add_circle_qs.xml b/packages/SystemUI/res/drawable/ic_add_circle_qs.xml index 6415ecb44c2e..8e933445be0b 100644 --- a/packages/SystemUI/res/drawable/ic_add_circle_qs.xml +++ b/packages/SystemUI/res/drawable/ic_add_circle_qs.xml @@ -18,7 +18,7 @@ Copyright (C) 2014 The Android Open Source Project android:height="48.0dp" android:viewportWidth="24.0" android:viewportHeight="24.0" - android:tint="?android:attr/colorControlNormal"> + android:tint="?android:attr/colorForeground"> <group android:scaleX="1.2" android:scaleY="1.2" diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml index fc1271cf74b6..e3063c5915a9 100644 --- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml +++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml @@ -85,6 +85,7 @@ android:layout_height="@dimen/keyguard_affordance_height" android:layout_gravity="bottom|center_horizontal" android:src="@drawable/ic_lock_24dp" + android:contentDescription="@string/accessibility_unlock_button" android:scaleType="center" /> </com.android.systemui.statusbar.phone.KeyguardBottomAreaView> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 8dc694cc6e47..fec3702145aa 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -221,8 +221,8 @@ <string name="accessibility_voice_assist_button">Voice Assist</string> <!-- Content description of the unlock button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_unlock_button">Unlock</string> - <!-- Content description of the unlock button when fingerpint is on (not shown on the screen). [CHAR LIMIT=NONE] --> - <string name="accessibility_unlock_button_fingerprint">Unlock button, waiting for fingerprint</string> + <!-- Content description hint of the unlock button when fingerprint is on (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_waiting_for_fingerprint">Waiting for fingerprint</string> <!-- Accessibility action of the unlock button when fingerpint is on (not shown on the screen). [CHAR LIMIT=NONE] --> <string name="accessibility_unlock_without_fingerprint">Unlock without using your fingerprint</string> <!-- Click action label for accessibility for the unlock button. [CHAR LIMIT=NONE] --> @@ -1382,11 +1382,15 @@ of notifications. Replaces the channel name and only appears when there is more than one channel. --> <string name="notification_num_channels"> <xliff:g id="number">%d</xliff:g> notification categories</string> + <!-- Notification: Control panel: Label that shows when an app has not upgraded to use channels. + Hints that the user's only option is to block all of the app's notifications. --> + <string name="notification_default_channel_desc">This app doesn\'t have notification categories</string> + <!-- Notification: Control panel: Label that shows how many channels this application has - defined, describing the current notification channel as "1 out of n categories from this app". --> + defined, describing the current notification channel as "1 out of n notification categories from this app". --> <plurals name="notification_num_channels_desc"> - <item quantity="one">1 out of <xliff:g id="number">%d</xliff:g> category from this app</item> - <item quantity="other">1 out of <xliff:g id="number">%d</xliff:g> categories from this app</item> + <item quantity="one">1 out of <xliff:g id="number">%d</xliff:g> notification category from this app</item> + <item quantity="other">1 out of <xliff:g id="number">%d</xliff:g> notification categories from this app</item> </plurals> <!-- Notification: Control panel: For bundles of notifications, this label that lists the diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 374086d696e6..d058e7837423 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -32,6 +32,7 @@ import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.PluginManager; import com.android.systemui.plugins.PluginManagerImpl; +import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; import com.android.systemui.statusbar.phone.DarkIconDispatcherImpl; import com.android.systemui.statusbar.phone.ManagedProfileController; @@ -79,6 +80,7 @@ import com.android.systemui.tuner.TunerServiceImpl; import com.android.systemui.util.leak.GarbageMonitor; import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.util.leak.LeakReporter; +import com.android.systemui.volume.VolumeDialogControllerImpl; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -252,6 +254,9 @@ public class Dependency extends SystemUI { mProviders.put(LocalBluetoothManager.class, () -> LocalBluetoothManager.getInstance(mContext, null)); + mProviders.put(VolumeDialogController.class, () -> + new VolumeDialogControllerImpl(mContext)); + // Put all dependencies above here so the factory can override them if it wants. SystemUIFactory.getInstance().injectDependencies(mProviders, mContext); } diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index af7e9b4e45a8..523724499d96 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -16,7 +16,6 @@ package com.android.systemui; -import android.content.ComponentName; import android.content.Context; import android.util.ArrayMap; import android.util.Log; @@ -39,7 +38,7 @@ import com.android.systemui.qs.QSTileHost; import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.volume.VolumeDialogController; +import com.android.systemui.volume.VolumeDialogControllerImpl; /** * Class factory to provide customizable SystemUI components. @@ -89,11 +88,6 @@ public class SystemUIFactory { return new ScrimController(lightBarController, scrimBehind, scrimInFront, headsUpScrim); } - public VolumeDialogController createVolumeDialogController(Context context, - ComponentName name) { - return new VolumeDialogController(context, name); - } - public NotificationIconAreaController createNotificationIconAreaController(Context context, StatusBar statusBar) { return new NotificationIconAreaController(context, statusBar); diff --git a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java index 67aa4dc1630a..5a19e7dc60ed 100644 --- a/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/car/CarSystemUIFactory.java @@ -15,11 +15,12 @@ */ package com.android.systemui.car; -import android.content.ComponentName; import android.content.Context; +import android.util.ArrayMap; +import com.android.systemui.Dependency.DependencyProvider; import com.android.systemui.SystemUIFactory; -import com.android.systemui.volume.VolumeDialogController; +import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.volume.car.CarVolumeDialogController; /** @@ -27,8 +28,9 @@ import com.android.systemui.volume.car.CarVolumeDialogController; */ public class CarSystemUIFactory extends SystemUIFactory { @Override - public VolumeDialogController createVolumeDialogController(Context context, - ComponentName name) { - return new CarVolumeDialogController(context, name); + public void injectDependencies(ArrayMap<Object, DependencyProvider> providers, + Context context) { + super.injectDependencies(providers, context); + providers.put(VolumeDialogController.class, () -> new CarVolumeDialogController(context)); } } diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java index 1fb6c8704587..ec5f9e7eb6f5 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java @@ -59,7 +59,6 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage private static PluginManager sInstance; - private final HandlerThread mBackgroundThread; private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap = new ArrayMap<>(); private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>(); @@ -71,6 +70,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage private ClassLoaderFilter mParentClassLoader; private boolean mListening; private boolean mHasOneShot; + private Looper mLooper; public PluginManagerImpl(Context context) { this(context, new PluginInstanceManagerFactory(), @@ -82,8 +82,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage UncaughtExceptionHandler defaultHandler) { mContext = context; mFactory = factory; - mBackgroundThread = new HandlerThread("Plugins"); - mBackgroundThread.start(); + mLooper = Dependency.get(Dependency.BG_LOOPER); isDebuggable = debuggable; mPluginPrefs = new PluginPrefs(mContext); @@ -91,7 +90,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage defaultHandler); Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler); if (isDebuggable) { - new Handler(mBackgroundThread.getLooper()).post(() -> { + new Handler(mLooper).post(() -> { // Plugin dependencies that don't have another good home can go here, but // dependencies that have better places to init can happen elsewhere. Dependency.get(PluginDependencyProvider.class) @@ -120,7 +119,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage throw new RuntimeException("Must be called from UI thread"); } PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, null, - false, mBackgroundThread.getLooper(), cls, this); + false, mLooper, cls, this); mPluginPrefs.addAction(action); PluginInfo<T> info = p.getPlugin(); if (info != null) { @@ -154,7 +153,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage } mPluginPrefs.addAction(action); PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener, - allowMultiple, mBackgroundThread.getLooper(), cls, this); + allowMultiple, mLooper, cls, this); p.loadAll(); mPluginMap.put(listener, p); startListening(); 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 25eea951c585..9b75f0103cc9 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -32,7 +32,6 @@ import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.IActivityManager; import android.app.KeyguardManager; -import android.app.UiModeManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; @@ -42,7 +41,6 @@ import android.content.pm.ApplicationInfo; 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.graphics.Bitmap; import android.graphics.BitmapFactory; @@ -298,15 +296,9 @@ public class SystemServicesProxy { mDummyIcon.eraseColor(0xFF999999); } - UiModeManager uiModeManager = (UiModeManager) context. - getSystemService(Context.UI_MODE_SERVICE); - if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) { - Collections.addAll(sRecentsBlacklist, - res.getStringArray(R.array.recents_tv_blacklist_array)); - } else { - Collections.addAll(sRecentsBlacklist, - res.getStringArray(R.array.recents_blacklist_array)); - } + Collections.addAll(sRecentsBlacklist, + res.getStringArray(R.array.recents_blacklist_array)); + mLauncherIcons = new LauncherIcons(context); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java index a9043e4c83bb..54921a7108c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java @@ -99,11 +99,14 @@ public class NotificationInfo extends LinearLayout implements GutsContent { mINotificationManager = iNotificationManager; mPkg = pkg; mNotificationChannels = notificationChannels; + boolean isSingleDefaultChannel = false; if (mNotificationChannels.isEmpty()) { throw new IllegalArgumentException("bindNotification requires at least one channel"); } else if (mNotificationChannels.size() == 1) { mSingleNotificationChannel = mNotificationChannels.get(0); mStartingUserImportance = mSingleNotificationChannel.getImportance(); + isSingleDefaultChannel = mSingleNotificationChannel.getId() + .equals(NotificationChannel.DEFAULT_CHANNEL_ID); } else { mSingleNotificationChannel = null; } @@ -135,24 +138,30 @@ public class NotificationInfo extends LinearLayout implements GutsContent { String channelsDescText; mNumChannelsView = (TextView) (findViewById(R.id.num_channels_desc)); - switch (mNotificationChannels.size()) { - case 1: - channelsDescText = String.format(mContext.getResources().getQuantityString( - R.plurals.notification_num_channels_desc, numChannels), numChannels); - break; - case 2: - channelsDescText = mContext.getString(R.string.notification_channels_list_desc_2, - mNotificationChannels.get(0).getName(), - mNotificationChannels.get(1).getName()); - break; - default: - final int numOthers = mNotificationChannels.size() - 2; - channelsDescText = String.format( - mContext.getResources().getQuantityString( - R.plurals.notification_channels_list_desc_2_and_others, numOthers), - mNotificationChannels.get(0).getName(), - mNotificationChannels.get(1).getName(), - numOthers); + if (isSingleDefaultChannel) { + channelsDescText = mContext.getString(R.string.notification_default_channel_desc); + } else { + switch (mNotificationChannels.size()) { + case 1: + channelsDescText = String.format(mContext.getResources().getQuantityString( + R.plurals.notification_num_channels_desc, numChannels), numChannels); + break; + case 2: + channelsDescText = mContext.getString( + R.string.notification_channels_list_desc_2, + mNotificationChannels.get(0).getName(), + mNotificationChannels.get(1).getName()); + break; + default: + final int numOthers = mNotificationChannels.size() - 2; + channelsDescText = String.format( + mContext.getResources().getQuantityString( + R.plurals.notification_channels_list_desc_2_and_others, + numOthers), + mNotificationChannels.get(0).getName(), + mNotificationChannels.get(1).getName(), + numOthers); + } } mNumChannelsView.setText(channelsDescText); @@ -160,9 +169,8 @@ public class NotificationInfo extends LinearLayout implements GutsContent { // Multiple channels don't use a channel name for the title. channelNameText = mContext.getString(R.string.notification_num_channels, mNotificationChannels.size()); - } else if (mSingleNotificationChannel.getId() - .equals(NotificationChannel.DEFAULT_CHANNEL_ID)) { - // If this is the placeholder channel, don't use our channel-specific text. + } else if (isSingleDefaultChannel) { + // If this is the default channel, don't use our channel-specific text. channelNameText = mContext.getString(R.string.notification_header_default_channel); } else { channelNameText = mSingleNotificationChannel.getName(); @@ -282,15 +290,9 @@ public class NotificationInfo extends LinearLayout implements GutsContent { } private void updateSecondaryText() { - final boolean defaultChannel = mSingleNotificationChannel != null && - mSingleNotificationChannel.getId().equals(NotificationChannel.DEFAULT_CHANNEL_ID); final boolean disabled = mSingleNotificationChannel != null && getSelectedImportance() == NotificationManager.IMPORTANCE_NONE; - if (defaultChannel) { - // Don't show any secondary text if this is from the default channel. - mChannelDisabledView.setVisibility(View.GONE); - mNumChannelsView.setVisibility(View.GONE); - } else if (disabled) { + if (disabled) { mChannelDisabledView.setVisibility(View.VISIBLE); mNumChannelsView.setVisibility(View.GONE); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java index ef42b2f43287..bccc5d5d5fa1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java @@ -179,10 +179,6 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange setRestingAlpha( anyFingerprintIcon ? 1f : KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT); setImageDrawable(icon); - String contentDescription = getResources().getString(anyFingerprintIcon - ? R.string.accessibility_unlock_button_fingerprint - : R.string.accessibility_unlock_button); - setContentDescription(contentDescription); mHasFingerPrintIcon = anyFingerprintIcon; if (animation != null && isAnim) { animation.forceAnimationOnUI(); @@ -225,13 +221,13 @@ public class LockIcon extends KeyguardAffordanceView implements OnUserInfoChange public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); if (mHasFingerPrintIcon) { - // Avoid that the button description is also spoken - info.setClassName(LockIcon.class.getName()); AccessibilityNodeInfo.AccessibilityAction unlock = new AccessibilityNodeInfo.AccessibilityAction( AccessibilityNodeInfo.ACTION_CLICK, getContext().getString(R.string.accessibility_unlock_without_fingerprint)); info.addAction(unlock); + info.setHintText(getContext().getString( + R.string.accessibility_waiting_for_fingerprint)); } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java index ca53bc4f7928..8ed4fcac63b0 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/Events.java +++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java @@ -24,7 +24,7 @@ import android.util.Log; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.systemui.volume.VolumeDialogController.State; +import com.android.systemui.plugins.VolumeDialogController.State; import java.util.Arrays; diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java index 9d0ecec43b30..2f9bcff3f8b3 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogComponent.java @@ -24,13 +24,19 @@ import android.media.VolumePolicy; import android.os.Bundle; import android.os.Handler; import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.Dependency; import com.android.systemui.SystemUI; -import com.android.systemui.SystemUIFactory; import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.plugins.PluginDependency; +import com.android.systemui.plugins.PluginDependencyProvider; +import com.android.systemui.plugins.PluginManager; +import com.android.systemui.plugins.VolumeDialog; +import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.qs.tiles.DndTile; +import com.android.systemui.statusbar.policy.ExtensionController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; @@ -41,7 +47,7 @@ import java.io.PrintWriter; * Implementation of VolumeComponent backed by the new volume dialog. */ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tunable, - VolumeDialogController.UserActivityListener{ + VolumeDialogControllerImpl.UserActivityListener{ public static final String VOLUME_DOWN_SILENT = "sysui_volume_down_silent"; public static final String VOLUME_UP_SILENT = "sysui_volume_up_silent"; @@ -53,9 +59,8 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna private final SystemUI mSysui; private final Context mContext; - private final VolumeDialogController mController; - private final ZenModeController mZenModeController; - private final VolumeDialog mDialog; + private final VolumeDialogControllerImpl mController; + private VolumeDialog mDialog; private VolumePolicy mVolumePolicy = new VolumePolicy( DEFAULT_VOLUME_DOWN_TO_ENTER_SILENT, // volumeDownToEnterSilent DEFAULT_VOLUME_UP_TO_EXIT_SILENT, // volumeUpToExitSilent @@ -66,16 +71,35 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna public VolumeDialogComponent(SystemUI sysui, Context context, Handler handler) { mSysui = sysui; mContext = context; - mController = SystemUIFactory.getInstance().createVolumeDialogController(context, null); + mController = (VolumeDialogControllerImpl) Dependency.get(VolumeDialogController.class); mController.setUserActivityListener(this); - mZenModeController = Dependency.get(ZenModeController.class); - mDialog = new VolumeDialog(context, WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY, - mController, mZenModeController, mVolumeDialogCallback); + // Allow plugins to reference the VolumeDialogController. + Dependency.get(PluginDependencyProvider.class) + .allowPluginDependency(VolumeDialogController.class); + Dependency.get(ExtensionController.class).newExtension(VolumeDialog.class) + .withPlugin(VolumeDialog.class) + .withDefault(this::createDefault) + .withCallback(dialog -> { + if (mDialog != null) { + mDialog.destroy(); + } + mDialog = dialog; + mDialog.init(LayoutParams.TYPE_VOLUME_OVERLAY, mVolumeDialogCallback); + }).build(); applyConfiguration(); Dependency.get(TunerService.class).addTunable(this, VOLUME_DOWN_SILENT, VOLUME_UP_SILENT, VOLUME_SILENT_DO_NOT_DISTURB); } + private VolumeDialog createDefault() { + VolumeDialogImpl impl = new VolumeDialogImpl(mContext); + impl.setStreamImportant(AudioManager.STREAM_ALARM, true); + impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false); + impl.setAutomute(true); + impl.setSilentMode(false); + return impl; + } + @Override public void onTuningChanged(String key, String newValue) { if (VOLUME_DOWN_SILENT.equals(key)) { @@ -118,10 +142,6 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna } private void applyConfiguration() { - mDialog.setStreamImportant(AudioManager.STREAM_ALARM, true); - mDialog.setStreamImportant(AudioManager.STREAM_SYSTEM, false); - mDialog.setAutomute(true); - mDialog.setSilentMode(false); mController.setVolumePolicy(mVolumePolicy); mController.showDndTile(true); } @@ -149,8 +169,6 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - mController.dump(fd, pw, args); - mDialog.dump(pw); } private void startSettings(Intent intent) { @@ -158,7 +176,7 @@ public class VolumeDialogComponent implements VolumeComponent, TunerService.Tuna true /* onlyProvisioned */, true /* dismissShade */); } - private final VolumeDialog.Callback mVolumeDialogCallback = new VolumeDialog.Callback() { + private final VolumeDialogImpl.Callback mVolumeDialogCallback = new VolumeDialogImpl.Callback() { @Override public void onZenSettingsClicked() { startSettings(ZenModePanel.ZEN_SETTINGS); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index 276b7c376867..5d51a33c6307 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -16,7 +16,6 @@ package com.android.systemui.volume; -import android.annotation.IntegerRes; import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -44,10 +43,11 @@ import android.provider.Settings; import android.service.notification.Condition; import android.util.ArrayMap; import android.util.Log; -import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.systemui.Dumpable; import com.android.systemui.R; +import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.qs.tiles.DndTile; import java.io.FileDescriptor; @@ -63,8 +63,8 @@ import java.util.Objects; * * Methods ending in "W" must be called on the worker thread. */ -public class VolumeDialogController { - private static final String TAG = Util.logTag(VolumeDialogController.class); +public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpable { + private static final String TAG = Util.logTag(VolumeDialogControllerImpl.class); private static final int DYNAMIC_STREAM_START_INDEX = 100; private static final int VIBRATE_HINT_DURATION = 50; @@ -89,7 +89,6 @@ public class VolumeDialogController { private final Context mContext; private AudioManager mAudio; private final NotificationManager mNoMan; - private final ComponentName mComponent; private final SettingObserver mObserver; private final Receiver mReceiver = new Receiver(); private final MediaSessions mMediaSessions; @@ -108,11 +107,10 @@ public class VolumeDialogController { protected final VC mVolumeController = new VC(); - public VolumeDialogController(Context context, ComponentName component) { + public VolumeDialogControllerImpl(Context context) { mContext = context.getApplicationContext(); Events.writeEvent(mContext, Events.EVENT_COLLECTION_STARTED); - mComponent = component; - mWorkerThread = new HandlerThread(VolumeDialogController.class.getSimpleName()); + mWorkerThread = new HandlerThread(VolumeDialogControllerImpl.class.getSimpleName()); mWorkerThread.start(); mWorker = new W(mWorkerThread.getLooper()); mMediaSessions = createMediaSessions(mContext, mWorkerThread.getLooper(), @@ -197,7 +195,7 @@ public class VolumeDialogController { } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(VolumeDialogController.class.getSimpleName() + " state:"); + pw.println(VolumeDialogControllerImpl.class.getSimpleName() + " state:"); pw.print(" mDestroyed: "); pw.println(mDestroyed); pw.print(" mVolumePolicy: "); pw.println(mVolumePolicy); pw.print(" mState: "); pw.println(mState.toString(4)); @@ -530,7 +528,7 @@ public class VolumeDialogController { } private final class VC extends IVolumeController.Stub { - private final String TAG = VolumeDialogController.TAG + ".VC"; + private final String TAG = VolumeDialogControllerImpl.TAG + ".VC"; @Override public void displaySafeVolumeWarning(int flags) throws RemoteException { @@ -958,113 +956,6 @@ public class VolumeDialogController { } } - public static final class StreamState { - public boolean dynamic; - public int level; - public int levelMin; - public int levelMax; - public boolean muted; - public boolean muteSupported; - public @IntegerRes int name; - public String remoteLabel; - public boolean routedToBluetooth; - - public StreamState copy() { - final StreamState rt = new StreamState(); - rt.dynamic = dynamic; - rt.level = level; - rt.levelMin = levelMin; - rt.levelMax = levelMax; - rt.muted = muted; - rt.muteSupported = muteSupported; - rt.name = name; - rt.remoteLabel = remoteLabel; - rt.routedToBluetooth = routedToBluetooth; - return rt; - } - } - - public static final class State { - public static int NO_ACTIVE_STREAM = -1; - - public final SparseArray<StreamState> states = new SparseArray<StreamState>(); - - public int ringerModeInternal; - public int ringerModeExternal; - public int zenMode; - public ComponentName effectsSuppressor; - public String effectsSuppressorName; - public int activeStream = NO_ACTIVE_STREAM; - - public State copy() { - final State rt = new State(); - for (int i = 0; i < states.size(); i++) { - rt.states.put(states.keyAt(i), states.valueAt(i).copy()); - } - rt.ringerModeExternal = ringerModeExternal; - rt.ringerModeInternal = ringerModeInternal; - rt.zenMode = zenMode; - if (effectsSuppressor != null) rt.effectsSuppressor = effectsSuppressor.clone(); - rt.effectsSuppressorName = effectsSuppressorName; - rt.activeStream = activeStream; - return rt; - } - - @Override - public String toString() { - return toString(0); - } - - public String toString(int indent) { - final StringBuilder sb = new StringBuilder("{"); - if (indent > 0) sep(sb, indent); - for (int i = 0; i < states.size(); i++) { - if (i > 0) { - sep(sb, indent); - } - final int stream = states.keyAt(i); - final StreamState ss = states.valueAt(i); - sb.append(AudioSystem.streamToString(stream)).append(":").append(ss.level) - .append('[').append(ss.levelMin).append("..").append(ss.levelMax) - .append(']'); - if (ss.muted) sb.append(" [MUTED]"); - if (ss.dynamic) sb.append(" [DYNAMIC]"); - } - sep(sb, indent); sb.append("ringerModeExternal:").append(ringerModeExternal); - sep(sb, indent); sb.append("ringerModeInternal:").append(ringerModeInternal); - sep(sb, indent); sb.append("zenMode:").append(zenMode); - sep(sb, indent); sb.append("effectsSuppressor:").append(effectsSuppressor); - sep(sb, indent); sb.append("effectsSuppressorName:").append(effectsSuppressorName); - sep(sb, indent); sb.append("activeStream:").append(activeStream); - if (indent > 0) sep(sb, indent); - return sb.append('}').toString(); - } - - private static void sep(StringBuilder sb, int indent) { - if (indent > 0) { - sb.append('\n'); - for (int i = 0; i < indent; i++) { - sb.append(' '); - } - } else { - sb.append(','); - } - } - } - - public interface Callbacks { - void onShowRequested(int reason); - void onDismissRequested(int reason); - void onStateChanged(State state); - void onLayoutDirectionChanged(int layoutDirection); - void onConfigurationChanged(); - void onShowVibrateHint(); - void onShowSilentHint(); - void onScreenOff(); - void onShowSafetyWarning(int flags); - void onAccessibilityModeChanged(Boolean showA11yStream); - } - public interface UserActivityListener { void onUserActivity(); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 1933349abb00..697cac99f244 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -16,6 +16,9 @@ package com.android.systemui.volume; +import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; +import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC; + import android.accessibilityservice.AccessibilityServiceInfo; import android.animation.ObjectAnimator; import android.annotation.NonNull; @@ -72,28 +75,27 @@ import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.plugins.VolumeDialogController; +import com.android.systemui.plugins.VolumeDialogController.State; +import com.android.systemui.plugins.VolumeDialogController.StreamState; +import com.android.systemui.plugins.VolumeDialog; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerZenModePanel; -import com.android.systemui.volume.VolumeDialogController.State; -import com.android.systemui.volume.VolumeDialogController.StreamState; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; -import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; -import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC; - /** * Visual presentation of the volume dialog. * - * A client of VolumeDialogController and its state model. + * A client of VolumeDialogControllerImpl and its state model. * * Methods ending in "H" must be called on the (ui) handler. */ -public class VolumeDialog implements TunerService.Tunable { - private static final String TAG = Util.logTag(VolumeDialog.class); +public class VolumeDialogImpl implements VolumeDialog, TunerService.Tunable { + private static final String TAG = Util.logTag(VolumeDialogImpl.class); public static final String SHOW_FULL_ZEN = "sysui_show_full_zen"; @@ -102,7 +104,7 @@ public class VolumeDialog implements TunerService.Tunable { private final Context mContext; private final H mHandler = new H(); - private final VolumeDialogController mController; + private VolumeDialogController mController; private Window mWindow; private CustomDialog mDialog; @@ -123,7 +125,7 @@ public class VolumeDialog implements TunerService.Tunable { private final ColorStateList mActiveSliderTint; private final ColorStateList mInactiveSliderTint; private VolumeDialogMotion mMotion; - private final int mWindowType; + private int mWindowType; private final ZenModeController mZenModeController; private boolean mShowing; @@ -146,32 +148,39 @@ public class VolumeDialog implements TunerService.Tunable { private boolean mShowFullZen; private TunerZenModePanel mZenPanel; - public VolumeDialog(Context context, int windowType, VolumeDialogController controller, - ZenModeController zenModeController, Callback callback) { + public VolumeDialogImpl(Context context) { mContext = context; - mController = controller; - mCallback = callback; - mWindowType = windowType; - mZenModeController = zenModeController; - mKeyguard = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); - mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); + mZenModeController = Dependency.get(ZenModeController.class); + mController = Dependency.get(VolumeDialogController.class); + mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); mAccessibilityMgr = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext)); mInactiveSliderTint = loadColorStateList(R.color.volume_slider_inactive); + } + + public void init(int windowType, Callback callback) { + mCallback = callback; + mWindowType = windowType; initDialog(); mAccessibility.init(); - controller.addCallback(mControllerCallbackH, mHandler); - controller.getState(); + mController.addCallback(mControllerCallbackH, mHandler); + mController.getState(); Dependency.get(TunerService.class).addTunable(this, SHOW_FULL_ZEN); final Configuration currentConfig = mContext.getResources().getConfiguration(); mDensity = currentConfig.densityDpi; } + @Override + public void destroy() { + mController.removeCallback(mControllerCallbackH); + } + private void initDialog() { mDialog = new CustomDialog(mContext); @@ -193,7 +202,7 @@ public class VolumeDialog implements TunerService.Tunable { final WindowManager.LayoutParams lp = mWindow.getAttributes(); lp.type = mWindowType; lp.format = PixelFormat.TRANSLUCENT; - lp.setTitle(VolumeDialog.class.getSimpleName()); + lp.setTitle(VolumeDialogImpl.class.getSimpleName()); lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; lp.y = res.getDimensionPixelSize(R.dimen.volume_offset_top); lp.gravity = Gravity.TOP; @@ -361,7 +370,7 @@ public class VolumeDialog implements TunerService.Tunable { } public void dump(PrintWriter writer) { - writer.println(VolumeDialog.class.getSimpleName() + " state:"); + writer.println(VolumeDialogImpl.class.getSimpleName() + " state:"); writer.print(" mShowing: "); writer.println(mShowing); writer.print(" mExpanded: "); writer.println(mExpanded); writer.print(" mExpandButtonAnimationRunning: "); @@ -459,10 +468,6 @@ public class VolumeDialog implements TunerService.Tunable { } } - public void destroy() { - mController.removeCallback(mControllerCallbackH); - } - public void show(int reason) { mHandler.obtainMessage(H.SHOW, reason, 0).sendToTarget(); } @@ -1289,9 +1294,4 @@ public class VolumeDialog implements TunerService.Tunable { private int animTargetProgress; private int lastAudibleLevel = 1; } - - public interface Callback { - void onZenSettingsClicked(); - void onZenPrioritySettingsClicked(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/car/CarVolumeDialogController.java b/packages/SystemUI/src/com/android/systemui/volume/car/CarVolumeDialogController.java index a2c32b7e83ad..d28e42e61f29 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/car/CarVolumeDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/volume/car/CarVolumeDialogController.java @@ -25,7 +25,7 @@ import android.content.ServiceConnection; import android.os.IBinder; import android.util.Log; -import com.android.systemui.volume.VolumeDialogController; +import com.android.systemui.volume.VolumeDialogControllerImpl; /** * A volume dialog controller for the automotive use case. @@ -33,7 +33,7 @@ import com.android.systemui.volume.VolumeDialogController; * {@link android.car.media.CarAudioManager} is the source of truth to get the stream volumes. * And volume changes should be sent to the car's audio module instead of the android's audio mixer. */ -public class CarVolumeDialogController extends VolumeDialogController { +public class CarVolumeDialogController extends VolumeDialogControllerImpl { private static final String TAG = "CarVolumeDialogController"; private final Car mCar; @@ -57,8 +57,8 @@ public class CarVolumeDialogController extends VolumeDialogController { } }; - public CarVolumeDialogController(Context context, ComponentName component) { - super(context, component); + public CarVolumeDialogController(Context context) { + super(context); mCar = Car.createCar(context, mConnection); mCar.connect(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java index a3d5d5fc3871..b8e9fcd29096 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java @@ -29,13 +29,18 @@ import android.net.Uri; import android.support.test.annotation.UiThreadTest; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableLooper.RunWithLooper; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; +import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.annotations.ProvidesInterface; import com.android.systemui.plugins.PluginInstanceManager.PluginInfo; import com.android.systemui.plugins.PluginManagerImpl.PluginInstanceManagerFactory; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -45,7 +50,8 @@ import org.mockito.Mockito; import java.lang.Thread.UncaughtExceptionHandler; @SmallTest -@RunWith(AndroidJUnit4.class) +@RunWith(AndroidTestingRunner.class) +@RunWithLooper public class PluginManagerTest extends SysuiTestCase { private PluginInstanceManagerFactory mMockFactory; @@ -59,6 +65,8 @@ public class PluginManagerTest extends SysuiTestCase { @Before public void setup() throws Exception { + mDependency.injectTestDependency(Dependency.BG_LOOPER, + TestableLooper.get(this).getLooper()); mRealExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); mMockExceptionHandler = mock(UncaughtExceptionHandler.class); mMockFactory = mock(PluginInstanceManagerFactory.class); @@ -72,7 +80,7 @@ public class PluginManagerTest extends SysuiTestCase { mMockListener = mock(PluginListener.class); } - @UiThreadTest + @RunWithLooper(setAsMainLooper = true) @Test public void testOneShot() { Plugin mockPlugin = mock(Plugin.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java index 8aca546b2730..5632b719df23 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java @@ -260,12 +260,14 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test - public void testBindNotification_NumChannelsTextHiddenWhenDefaultChannel() throws Exception { + public void testBindNotification_NumChannelsTextUniqueWhenDefaultChannel() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), null, null, null); final TextView numChannelsView = (TextView) mNotificationInfo.findViewById(R.id.num_channels_desc); - assertTrue(numChannelsView.getVisibility() != View.VISIBLE); + assertEquals(View.VISIBLE, numChannelsView.getVisibility()); + assertEquals(mContext.getString(R.string.notification_default_channel_desc), + numChannelsView.getText()); } @Test @@ -390,13 +392,14 @@ public class NotificationInfoTest extends SysuiTestCase { @Test @UiThreadTest - public void testBindNotification_ChannelDisabledTextHiddenWhenDefaultChannel() + public void testBindNotification_ChannelDisabledTextShowsForDefaultChannel() throws Exception { + mDefaultNotificationChannel.setImportance(NotificationManager.IMPORTANCE_NONE); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, Arrays.asList(mDefaultNotificationChannel), null, null, null); final TextView channelDisabledView = (TextView) mNotificationInfo.findViewById(R.id.channel_disabled); - assertTrue(channelDisabledView.getVisibility() != View.VISIBLE); + assertEquals(View.VISIBLE, channelDisabledView.getVisibility()); } @Test diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index fb714b9b191a..57d25819654f 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -3780,6 +3780,9 @@ message MetricsEvent { // meta-event: a reader has checkpointed the log here. METRICS_CHECKPOINT = 920; + // OPEN: Settings -> Display -> When in VR Mode + VR_DISPLAY_PREFERENCE = 921; + // ---- End O Constants, all O constants go above this line ---- // Add new aosp constants above this line. 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 776fa1e58db8..375a72607aa5 100644 --- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java @@ -18,6 +18,8 @@ package com.android.server.autofill.ui; import static android.view.autofill.AutofillManager.AutofillCallback.EVENT_INPUT_HIDDEN; import static android.view.autofill.AutofillManager.AutofillCallback.EVENT_INPUT_SHOWN; +import static com.android.server.autofill.ui.Helper.DEBUG; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -160,7 +162,11 @@ public final class AutoFillUI { */ public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response, @NonNull Rect anchorBounds, @Nullable String filterText, @NonNull String packageName) { - LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_FILL_UI)) + if (DEBUG) { + Slog.d(TAG, "showFillUi(): id=" + focusedId + ", bounds=" + anchorBounds + " filter=" + + filterText); + } + final LogMaker log = (new LogMaker(MetricsProto.MetricsEvent.AUTOFILL_FILL_UI)) .setPackageName(packageName) .addTaggedData(MetricsProto.MetricsEvent.FIELD_AUTOFILL_FILTERTEXT_LEN, filterText == null ? 0 : filterText.length()) diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 99014a5432b6..98a02f2aa0ca 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -326,6 +326,16 @@ final class FillUi { } public void show(int desiredWidth, int desiredHeight, Rect anchorBounds) { + try { + // TODO: temporary workaround to avoud system_server crashes. + unsafelyShow(desiredWidth, desiredHeight, anchorBounds); + } catch (RuntimeException e) { + Slog.w(TAG, "Error showing Anchored window: w=" + desiredWidth + ", h=" + + desiredHeight + ", b=" + anchorBounds, e); + } + } + + private void unsafelyShow(int desiredWidth, int desiredHeight, Rect anchorBounds) { final WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.setTitle("FillUi"); diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 8e6310fdacbb..891a13bc6878 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -97,6 +97,7 @@ import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IMediaContainerService; import com.android.internal.os.AppFuseMount; +import com.android.internal.os.FuseAppLoop; import com.android.internal.os.SomeArgs; import com.android.internal.os.Zygote; import com.android.internal.util.ArrayUtils; @@ -350,7 +351,7 @@ class StorageManagerService extends IStorageManager.Stub private int mNextAppFuseName = 0; @GuardedBy("mAppFuseLock") - private final SparseArray<Integer> mAppFusePids = new SparseArray<>(); + private AppFuseBridge mAppFuseBridge = null; private VolumeInfo findVolumeByIdOrThrow(String id) { synchronized (mLock) { @@ -2991,124 +2992,79 @@ class StorageManagerService extends IStorageManager.Stub } } - class CloseableHolder<T extends AutoCloseable> implements AutoCloseable { - @Nullable T mCloseable; - - CloseableHolder(T closeable) { - mCloseable = closeable; - } - - @Nullable T get() { - return mCloseable; - } - - @Nullable T release() { - final T result = mCloseable; - mCloseable = null; - return result; - } - - @Override - public void close() { - if (mCloseable != null) { - IoUtils.closeQuietly(mCloseable); - } + private ParcelFileDescriptor mountAppFuse(int uid, int mountId) + throws NativeDaemonConnectorException { + final NativeDaemonEvent event = StorageManagerService.this.mConnector.execute( + "appfuse", "mount", uid, Process.myPid(), mountId); + if (event.getFileDescriptors() == null || + event.getFileDescriptors().length == 0) { + throw new NativeDaemonConnectorException("Cannot obtain device FD"); } + return new ParcelFileDescriptor(event.getFileDescriptors()[0]); } - class AppFuseMountScope implements AppFuseBridge.IMountScope { - final int mUid; - final int mName; - final ParcelFileDescriptor mDeviceFd; - - AppFuseMountScope(int uid, int pid, int name) throws NativeDaemonConnectorException { - final NativeDaemonEvent event = mConnector.execute( - "appfuse", "mount", uid, Process.myPid(), name); - mUid = uid; - mName = name; - synchronized (mLock) { - mAppFusePids.put(name, pid); - } - if (event.getFileDescriptors() != null && - event.getFileDescriptors().length > 0) { - mDeviceFd = new ParcelFileDescriptor(event.getFileDescriptors()[0]); - } else { - mDeviceFd = null; - } - } - - @Override - public void close() throws NativeDaemonConnectorException { - try { - IoUtils.closeQuietly(mDeviceFd); - mConnector.execute( - "appfuse", "unmount", mUid, Process.myPid(), mName); - } finally { - synchronized (mLock) { - mAppFusePids.delete(mName); - } - } + class AppFuseMountScope extends AppFuseBridge.MountScope { + public AppFuseMountScope(int uid, int pid, int mountId) + throws NativeDaemonConnectorException { + super(uid, pid, mountId, mountAppFuse(uid, mountId)); } @Override - public ParcelFileDescriptor getDeviceFileDescriptor() { - return mDeviceFd; + public void close() throws Exception { + super.close(); + mConnector.execute("appfuse", "unmount", uid, Process.myPid(), mountId); } } @Override public AppFuseMount mountProxyFileDescriptorBridge() throws RemoteException { + Slog.v(TAG, "mountProxyFileDescriptorBridge"); final int uid = Binder.getCallingUid(); final int pid = Binder.getCallingPid(); - final int name; - synchronized (mAppFuseLock) { - name = mNextAppFuseName++; - } - try (CloseableHolder<AppFuseMountScope> mountScope = - new CloseableHolder<>(new AppFuseMountScope(uid, pid, name))) { - if (mountScope.get().getDeviceFileDescriptor() == null) { - throw new RemoteException("Failed to obtain device FD"); - } - // Create communication channel. - final ArrayBlockingQueue<Boolean> channel = new ArrayBlockingQueue<>(1); - final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createSocketPair(); - try (CloseableHolder<ParcelFileDescriptor> remote = new CloseableHolder<>(fds[0])) { - new Thread( - new AppFuseBridge(mountScope.release(), fds[1], channel), - AppFuseBridge.TAG).start(); - if (!channel.take()) { - throw new RemoteException("Failed to init AppFuse mount point"); + while (true) { + synchronized (mAppFuseLock) { + boolean newlyCreated = false; + if (mAppFuseBridge == null) { + mAppFuseBridge = new AppFuseBridge(); + new Thread(mAppFuseBridge, AppFuseBridge.TAG).start(); + newlyCreated = true; + } + try { + final int name = mNextAppFuseName++; + try { + return new AppFuseMount( + name, + mAppFuseBridge.addBridge(new AppFuseMountScope(uid, pid, name))); + } catch (AppFuseBridge.BridgeException e) { + if (newlyCreated) { + // If newly created bridge fails, it's a real error. + throw new RemoteException(e.getMessage()); + } + // It seems the thread of mAppFuseBridge has already been terminated. + mAppFuseBridge = null; + } + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); } - - return new AppFuseMount(name, remote.release()); } - } catch (NativeDaemonConnectorException e){ - throw e.rethrowAsParcelableException(); - } catch (IOException | InterruptedException error) { - throw new RemoteException(error.getMessage()); } } @Override - public ParcelFileDescriptor openProxyFileDescriptor(int mountId, int fileId, int mode) { - final int uid = Binder.getCallingUid(); + public ParcelFileDescriptor openProxyFileDescriptor(int mountId, int fileId, int mode) + throws RemoteException { + Slog.v(TAG, "mountProxyFileDescriptorBridge"); final int pid = Binder.getCallingPid(); try { synchronized (mAppFuseLock) { - final int expectedPid = mAppFusePids.get(mountId, -1); - if (expectedPid == -1) { - Slog.i(TAG, "The mount point has already been unmounted"); - return null; - } - if (expectedPid != pid) { - throw new SecurityException("Mount point was not created by this process."); + if (mAppFuseBridge == null) { + throw new RemoteException("Cannot find mount point"); } + return mAppFuseBridge.openFile(pid, mountId, fileId, mode); } - return AppFuseBridge.openFile(uid, mountId, fileId, mode); - } catch (FileNotFoundException error) { - Slog.e(TAG, "Failed to openProxyFileDescriptor", error); - return null; + } catch (FileNotFoundException | SecurityException | InterruptedException error) { + throw new RemoteException(error.getMessage()); } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 8a4f3f76c0ab..fc45344b2f4d 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -322,6 +322,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private INotificationManager mNotifManager; private PowerManagerInternal mPowerManagerInternal; private IDeviceIdleController mDeviceIdleController; + @GuardedBy("mUidRulesFirstLock") + private PowerSaveState mRestrictBackgroundPowerState; + + // Store the status of restrict background before turning on battery saver. + // Used to restore mRestrictBackground when battery saver is turned off. + private boolean mRestrictBackgroundBeforeBsm; // See main javadoc for instructions on how to use these locks. final Object mUidRulesFirstLock = new Object(); @@ -332,6 +338,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackground; @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictPower; @GuardedBy("mUidRulesFirstLock") volatile boolean mDeviceIdleMode; + // Store whether user flipped restrict background in battery saver mode + @GuardedBy("mUidRulesFirstLock") volatile boolean mRestrictBackgroundChangedInBsm; private final boolean mSuppressDefaultPolicy; @@ -617,8 +625,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public void onLowPowerModeChanged(PowerSaveState result) { final boolean enabled = result.batterySaverEnabled; - if (LOGD) Slog.d(TAG, - "onLowPowerModeChanged(" + enabled + ")"); + if (LOGD) { + Slog.d(TAG, "onLowPowerModeChanged(" + enabled + ")"); + } synchronized (mUidRulesFirstLock) { if (mRestrictPower != enabled) { mRestrictPower = enabled; @@ -626,7 +635,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } } - }); + }); mRestrictPower = mPowerManagerInternal.getLowPowerState( ServiceType.NETWORK_FIREWALL).batterySaverEnabled; @@ -635,6 +644,32 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // read policy from disk readPolicyAL(); + // Update the restrictBackground if battery saver is turned on + mRestrictBackgroundBeforeBsm = mRestrictBackground; + mRestrictBackgroundPowerState = mPowerManagerInternal + .getLowPowerState(ServiceType.DATA_SAVER); + final boolean localRestrictBackground = + mRestrictBackgroundPowerState.batterySaverEnabled; + if (localRestrictBackground && localRestrictBackground != mRestrictBackground) { + mRestrictBackground = localRestrictBackground; + mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_CHANGED, + mRestrictBackground ? 1 : 0, 0).sendToTarget(); + } + mPowerManagerInternal.registerLowPowerModeObserver( + new PowerManagerInternal.LowPowerModeListener() { + @Override + public int getServiceType() { + return ServiceType.DATA_SAVER; + } + + @Override + public void onLowPowerModeChanged(PowerSaveState result) { + synchronized (mUidRulesFirstLock) { + updateRestrictBackgroundByLowPowerModeUL(result); + } + } + }); + if (addDefaultRestrictBackgroundWhitelistUidsUL()) { writePolicyAL(); } @@ -2159,6 +2194,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } catch (RemoteException e) { // ignored; service lives in system_server } + + if (mRestrictBackgroundPowerState.globalBatterySaverEnabled) { + mRestrictBackgroundChangedInBsm = true; + } synchronized (mNetworkPoliciesSecondLock) { updateNotificationsNL(); writePolicyAL(); @@ -3645,6 +3684,35 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mHandler.getLooper().getQueue().addIdleHandler(handler); } + @VisibleForTesting + public void updateRestrictBackgroundByLowPowerModeUL(final PowerSaveState result) { + mRestrictBackgroundPowerState = result; + + boolean restrictBackground = result.batterySaverEnabled; + boolean shouldInvokeRestrictBackground; + // store the temporary mRestrictBackgroundChangedInBsm and update it at last + boolean localRestrictBgChangedInBsm = mRestrictBackgroundChangedInBsm; + + if (result.globalBatterySaverEnabled) { + // Try to turn on restrictBackground if (1) it is off and (2) batter saver need to + // turn it on. + shouldInvokeRestrictBackground = !mRestrictBackground && result.batterySaverEnabled; + mRestrictBackgroundBeforeBsm = mRestrictBackground; + localRestrictBgChangedInBsm = false; + } else { + // Try to restore the restrictBackground if it doesn't change in bsm + shouldInvokeRestrictBackground = !mRestrictBackgroundChangedInBsm; + restrictBackground = mRestrictBackgroundBeforeBsm; + } + + if (shouldInvokeRestrictBackground) { + setRestrictBackground(restrictBackground); + } + + // Change it at last so setRestrictBackground() won't affect this variable + mRestrictBackgroundChangedInBsm = localRestrictBgChangedInBsm; + } + private static void collectKeys(SparseIntArray source, SparseBooleanArray target) { final int size = source.size(); for (int i = 0; i < size; i++) { diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index 11cc52df180d..0774779a4cf2 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -583,7 +583,7 @@ abstract public class ManagedServices { ServiceInfo info = mPm.getServiceInfo(component, PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userIds[i]); - if (!mConfig.bindPermission.equals(info.permission)) { + if (info == null || !mConfig.bindPermission.equals(info.permission)) { Slog.w(TAG, "Skipping " + getCaption() + " service " + component + ": it does not require the permission " + mConfig.bindPermission); continue; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 3d18160ca7b7..ede5a5e8e337 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -179,6 +179,7 @@ import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -3116,8 +3117,19 @@ public class NotificationManagerService extends SystemService { + " - notification=" + notification); return; } - throw new IllegalArgumentException("No Channel found for channelId=" + channelId - + ", notification=" + notification); + final String noChannelStr = "No Channel found for " + + "pkg=" + pkg + + ", channelId=" + channelId + + ", opPkg=" + opPkg + + ", callingUid=" + callingUid + + ", userId=" + userId + + ", incomingUserId=" + incomingUserId + + ", notificationUid=" + notificationUid + + ", notification=" + notification; + // STOPSHIP TODO: should throw instead of logging. + // throw new IllegalArgumentException(noChannelStr); + Log.e(TAG, noChannelStr); + return; } final StatusBarNotification n = new StatusBarNotification( pkg, opPkg, id, tag, notificationUid, callingPid, notification, @@ -3640,6 +3652,10 @@ public class NotificationManagerService extends SystemService { mVibrator.vibrate(record.sbn.getUid(), record.sbn.getOpPkg(), effect, record.getAudioAttributes()); return true; + } catch (IllegalArgumentException e) { + Slog.e(TAG, "Error creating vibration waveform with pattern: " + + Arrays.toString(vibration)); + return false; } finally{ Binder.restoreCallingIdentity(identity); } diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java index db23a6ac8305..342ec4b79fed 100644 --- a/services/core/java/com/android/server/policy/GlobalActions.java +++ b/services/core/java/com/android/server/policy/GlobalActions.java @@ -29,9 +29,10 @@ class GlobalActions implements GlobalActionsListener { private static final boolean DEBUG = false; private final Context mContext; - private final LegacyGlobalActions mLegacyGlobalActions; private final StatusBarManagerInternal mStatusBarInternal; private final Handler mHandler; + private final WindowManagerFuncs mWindowManagerFuncs; + private LegacyGlobalActions mLegacyGlobalActions; private boolean mKeyguardShowing; private boolean mDeviceProvisioned; private boolean mStatusBarConnected; @@ -40,8 +41,7 @@ class GlobalActions implements GlobalActionsListener { public GlobalActions(Context context, WindowManagerFuncs windowManagerFuncs) { mContext = context; mHandler = new Handler(); - mLegacyGlobalActions = new LegacyGlobalActions(context, windowManagerFuncs, - this::onGlobalActionsDismissed); + mWindowManagerFuncs = windowManagerFuncs; mStatusBarInternal = LocalServices.getService(StatusBarManagerInternal.class); // Some form factors do not have a status bar. @@ -50,6 +50,12 @@ class GlobalActions implements GlobalActionsListener { } } + private void ensureLegacyCreated() { + if (mLegacyGlobalActions != null) return; + mLegacyGlobalActions = new LegacyGlobalActions(mContext, mWindowManagerFuncs, + this::onGlobalActionsDismissed); + } + public void showDialog(boolean keyguardShowing, boolean deviceProvisioned) { if (DEBUG) Slog.d(TAG, "showDialog " + keyguardShowing + " " + deviceProvisioned); mKeyguardShowing = keyguardShowing; @@ -60,6 +66,7 @@ class GlobalActions implements GlobalActionsListener { mHandler.postDelayed(mShowTimeout, 5000); } else { // SysUI isn't alive, show legacy menu. + ensureLegacyCreated(); mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned); } } @@ -83,6 +90,7 @@ class GlobalActions implements GlobalActionsListener { mStatusBarConnected = connected; if (mShowing && !mStatusBarConnected) { // Status bar died but we need to be showing global actions still, show the legacy. + ensureLegacyCreated(); mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned); } } @@ -92,6 +100,7 @@ class GlobalActions implements GlobalActionsListener { public void run() { if (DEBUG) Slog.d(TAG, "Global actions timeout"); // We haven't heard from sysui, show the legacy dialog. + ensureLegacyCreated(); mLegacyGlobalActions.showDialog(mKeyguardShowing, mDeviceProvisioned); } }; diff --git a/services/core/java/com/android/server/power/BatterySaverPolicy.java b/services/core/java/com/android/server/power/BatterySaverPolicy.java index 8d20531a6a17..1781d8c657c3 100644 --- a/services/core/java/com/android/server/power/BatterySaverPolicy.java +++ b/services/core/java/com/android/server/power/BatterySaverPolicy.java @@ -43,7 +43,8 @@ public class BatterySaverPolicy extends ContentObserver { ServiceType.NETWORK_FIREWALL, ServiceType.SCREEN_BRIGHTNESS, ServiceType.SOUND, - ServiceType.BATTERY_STATS}) + ServiceType.BATTERY_STATS, + ServiceType.DATA_SAVER}) public @interface ServiceType { int NULL = 0; int GPS = 1; @@ -55,6 +56,7 @@ public class BatterySaverPolicy extends ContentObserver { int SCREEN_BRIGHTNESS = 7; int SOUND = 8; int BATTERY_STATS = 9; + int DATA_SAVER = 10; } private static final String TAG = "BatterySaverPolicy"; @@ -73,6 +75,7 @@ public class BatterySaverPolicy extends ContentObserver { private static final String KEY_SOUNDTRIGGER_DISABLED = "soundtrigger_disabled"; private static final String KEY_FIREWALL_DISABLED = "firewall_disabled"; private static final String KEY_ADJUST_BRIGHTNESS_DISABLED = "adjust_brightness_disabled"; + private static final String KEY_DATASAVER_DISABLED = "datasaver_disabled"; private static final String KEY_ADJUST_BRIGHTNESS_FACTOR = "adjust_brightness_factor"; private static final String KEY_FULLBACKUP_DEFERRED = "fullbackup_deferred"; private static final String KEY_KEYVALUE_DEFERRED = "keyvaluebackup_deferred"; @@ -137,6 +140,14 @@ public class BatterySaverPolicy extends ContentObserver { private boolean mAdjustBrightnessDisabled; /** + * {@code true} if data saver is disabled in battery saver mode. + * + * @see Settings.Global#BATTERY_SAVER_CONSTANTS + * @see #KEY_DATASAVER_DISABLED + */ + private boolean mDataSaverDisabled; + + /** * This is the flag to decide the gps mode in battery saver mode. * * @see Settings.Global#BATTERY_SAVER_CONSTANTS @@ -191,6 +202,7 @@ public class BatterySaverPolicy extends ContentObserver { mFireWallDisabled = mParser.getBoolean(KEY_FIREWALL_DISABLED, false); mAdjustBrightnessDisabled = mParser.getBoolean(KEY_ADJUST_BRIGHTNESS_DISABLED, false); mAdjustBrightnessFactor = mParser.getFloat(KEY_ADJUST_BRIGHTNESS_FACTOR, 0.5f); + mDataSaverDisabled = mParser.getBoolean(KEY_DATASAVER_DISABLED, true); // Get default value from Settings.Secure final int defaultGpsMode = Settings.Secure.getInt(mContentResolver, SECURE_KEY_GPS_MODE, @@ -210,7 +222,8 @@ public class BatterySaverPolicy extends ContentObserver { */ public PowerSaveState getBatterySaverPolicy(@ServiceType int type, boolean realMode) { synchronized (BatterySaverPolicy.this) { - final PowerSaveState.Builder builder = new PowerSaveState.Builder(); + final PowerSaveState.Builder builder = new PowerSaveState.Builder() + .setGlobalBatterySaverEnabled(realMode); if (!realMode) { return builder.setBatterySaverEnabled(realMode) .build(); @@ -236,6 +249,9 @@ public class BatterySaverPolicy extends ContentObserver { return builder.setBatterySaverEnabled(!mAdjustBrightnessDisabled) .setBrightnessFactor(mAdjustBrightnessFactor) .build(); + case ServiceType.DATA_SAVER: + return builder.setBatterySaverEnabled(!mDataSaverDisabled) + .build(); case ServiceType.SOUND: return builder.setBatterySaverEnabled(mSoundTriggerDisabled) .build(); @@ -262,6 +278,7 @@ public class BatterySaverPolicy extends ContentObserver { pw.println(" " + KEY_FULLBACKUP_DEFERRED + "=" + mFullBackupDeferred); pw.println(" " + KEY_KEYVALUE_DEFERRED + "=" + mKeyValueBackupDeferred); pw.println(" " + KEY_FIREWALL_DISABLED + "=" + mFireWallDisabled); + pw.println(" " + KEY_DATASAVER_DISABLED + "=" + mDataSaverDisabled); pw.println(" " + KEY_ADJUST_BRIGHTNESS_DISABLED + "=" + mAdjustBrightnessDisabled); pw.println(" " + KEY_ADJUST_BRIGHTNESS_FACTOR + "=" + mAdjustBrightnessFactor); pw.println(" " + KEY_GPS_MODE + "=" + mGpsMode); diff --git a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java index 0b80d819fd80..ab9ab6713eea 100644 --- a/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java +++ b/services/core/java/com/android/server/security/KeyAttestationApplicationIdProviderService.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Binder; import android.os.RemoteException; import android.os.UserHandle; import android.security.keymaster.KeyAttestationPackageInfo; @@ -45,14 +46,19 @@ public class KeyAttestationApplicationIdProviderService public KeyAttestationApplicationId getKeyAttestationApplicationId(int uid) throws RemoteException { - String[] packageNames = mPackageManager.getPackagesForUid(uid); - if (packageNames == null) { - throw new RemoteException("No packages for uid"); + if (Binder.getCallingUid() != android.os.Process.KEYSTORE_UID) { + throw new SecurityException("This service can only be used by Keystore"); } - int userId = UserHandle.getUserId(uid); - KeyAttestationPackageInfo[] keyAttestationPackageInfos = - new KeyAttestationPackageInfo[packageNames.length]; + KeyAttestationPackageInfo[] keyAttestationPackageInfos = null; + final long token = Binder.clearCallingIdentity(); try { + String[] packageNames = mPackageManager.getPackagesForUid(uid); + if (packageNames == null) { + throw new RemoteException("No packages for uid"); + } + int userId = UserHandle.getUserId(uid); + keyAttestationPackageInfos = new KeyAttestationPackageInfo[packageNames.length]; + for (int i = 0; i < packageNames.length; ++i) { PackageInfo packageInfo = mPackageManager.getPackageInfoAsUser(packageNames[i], PackageManager.GET_SIGNATURES, userId); @@ -61,6 +67,8 @@ public class KeyAttestationApplicationIdProviderService } } catch (NameNotFoundException nnfe) { throw new RemoteException(nnfe.getMessage()); + } finally { + Binder.restoreCallingIdentity(token); } return new KeyAttestationApplicationId(keyAttestationPackageInfos); } diff --git a/services/core/java/com/android/server/security/KeyChainSystemService.java b/services/core/java/com/android/server/security/KeyChainSystemService.java index bfeaee6af7c4..2f681a3f568e 100644 --- a/services/core/java/com/android/server/security/KeyChainSystemService.java +++ b/services/core/java/com/android/server/security/KeyChainSystemService.java @@ -22,10 +22,13 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; +import android.os.Process; import android.os.UserHandle; import android.security.IKeyChainService; import android.util.Slog; +import com.android.server.DeviceIdleController; +import com.android.server.LocalServices; import com.android.server.SystemService; /** @@ -45,6 +48,11 @@ public class KeyChainSystemService extends SystemService { private static final String TAG = "KeyChainSystemService"; + /** + * Maximum time limit for the KeyChain app to deal with packages being removed. + */ + private static final int KEYCHAIN_IDLE_WHITELIST_DURATION_MS = 30 * 1000; + public KeyChainSystemService(final Context context) { super(context); } @@ -77,10 +85,25 @@ public class KeyChainSystemService extends SystemService { } intent.setComponent(service); intent.setAction(broadcastIntent.getAction()); - getContext().startServiceAsUser(intent, UserHandle.of(getSendingUserId())); + startServiceInBackgroundAsUser(intent, UserHandle.of(getSendingUserId())); } catch (RuntimeException e) { Slog.e(TAG, "Unable to forward package removed broadcast to KeyChain", e); } } }; + + + private void startServiceInBackgroundAsUser(final Intent intent, final UserHandle user) { + if (intent.getComponent() == null) { + return; + } + + final String packageName = intent.getComponent().getPackageName(); + final DeviceIdleController.LocalService idleController = + LocalServices.getService(DeviceIdleController.LocalService.class); + idleController.addPowerSaveTempWhitelistApp(Process.myUid(), packageName, + KEYCHAIN_IDLE_WHITELIST_DURATION_MS, user.getIdentifier(), false, "keychain"); + + getContext().startServiceAsUser(intent, user); + } } diff --git a/services/core/java/com/android/server/storage/AppFuseBridge.java b/services/core/java/com/android/server/storage/AppFuseBridge.java index 5a1f473b5ea2..904d9159e578 100644 --- a/services/core/java/com/android/server/storage/AppFuseBridge.java +++ b/services/core/java/com/android/server/storage/AppFuseBridge.java @@ -19,18 +19,20 @@ package com.android.server.storage; import android.os.ParcelFileDescriptor; import android.system.ErrnoException; import android.system.Os; +import android.util.SparseArray; +import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import libcore.io.IoUtils; import java.io.File; import java.io.FileNotFoundException; -import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; /** * Runnable that delegates FUSE command from the kernel to application. * run() blocks until all opened files on the FUSE mount point are closed. So this should be run in * a separated thread. */ -public class AppFuseBridge implements Runnable, AutoCloseable { +public class AppFuseBridge implements Runnable { public static final String TAG = "AppFuseBridge"; /** @@ -41,71 +43,138 @@ public class AppFuseBridge implements Runnable, AutoCloseable { */ private static final String APPFUSE_MOUNT_NAME_TEMPLATE = "/mnt/appfuse/%d_%d"; - private final IMountScope mMountScope; - private final ParcelFileDescriptor mProxyFd; - private final BlockingQueue<Boolean> mChannel; + @GuardedBy("this") + private final SparseArray<MountScope> mScopes = new SparseArray<>(); - /** - * @param mountScope Listener to unmount mount point. - * @param proxyFd FD of socket pair. Ownership of FD is taken by AppFuseBridge. - * @param channel Channel that the runnable send mount result to. - */ - public AppFuseBridge( - IMountScope mountScope, ParcelFileDescriptor proxyFd, BlockingQueue<Boolean> channel) { - Preconditions.checkNotNull(mountScope); - Preconditions.checkNotNull(proxyFd); - Preconditions.checkNotNull(channel); - mMountScope = mountScope; - mProxyFd = proxyFd; - mChannel = channel; + @GuardedBy("this") + private long mNativeLoop; + + public AppFuseBridge() { + mNativeLoop = native_new(); } - @Override - public void run() { + public ParcelFileDescriptor addBridge(MountScope mountScope) + throws BridgeException { try { - // deviceFd and proxyFd must be closed in native_start_loop. - native_start_loop( - mMountScope.getDeviceFileDescriptor().detachFd(), - mProxyFd.detachFd()); + synchronized (this) { + Preconditions.checkArgument(mScopes.indexOfKey(mountScope.mountId) < 0); + if (mNativeLoop == 0) { + throw new BridgeException("The thread has already been terminated"); + } + final int fd = native_add_bridge( + mNativeLoop, mountScope.mountId, mountScope.deviceFd.detachFd()); + if (fd == -1) { + throw new BridgeException("Failed to invoke native_add_bridge"); + } + final ParcelFileDescriptor result = ParcelFileDescriptor.adoptFd(fd); + mScopes.put(mountScope.mountId, mountScope); + mountScope = null; + return result; + } } finally { - close(); + IoUtils.closeQuietly(mountScope); + } + } + + @Override + public void run() { + native_start_loop(mNativeLoop); + synchronized (this) { + native_delete(mNativeLoop); + mNativeLoop = 0; } } - public static ParcelFileDescriptor openFile(int uid, int mountId, int fileId, int mode) - throws FileNotFoundException { - final File mountPoint = getMountPoint(uid, mountId); + public ParcelFileDescriptor openFile(int pid, int mountId, int fileId, int mode) + throws FileNotFoundException, SecurityException, InterruptedException { + final MountScope scope; + synchronized (this) { + scope = mScopes.get(mountId); + if (scope == null) { + throw new FileNotFoundException("Cannot find mount point"); + } + } + if (scope.pid != pid) { + throw new SecurityException("PID does not match"); + } + final boolean result = scope.waitForMount(); + if (result == false) { + throw new FileNotFoundException("Mount failed"); + } try { - if (Os.stat(mountPoint.getPath()).st_ino != 1) { + if (Os.stat(scope.mountPoint.getPath()).st_ino != 1) { throw new FileNotFoundException("Could not find bridge mount point."); } } catch (ErrnoException e) { throw new FileNotFoundException( - "Failed to stat mount point: " + mountPoint.getParent()); + "Failed to stat mount point: " + scope.mountPoint.getParent()); } - return ParcelFileDescriptor.open(new File(mountPoint, String.valueOf(fileId)), mode); + return ParcelFileDescriptor.open(new File(scope.mountPoint, String.valueOf(fileId)), mode); } - private static File getMountPoint(int uid, int mountId) { - return new File(String.format(APPFUSE_MOUNT_NAME_TEMPLATE, uid, mountId)); + // Used by com_android_server_storage_AppFuse.cpp. + synchronized private void onMount(int mountId) { + final MountScope scope = mScopes.get(mountId); + if (scope != null) { + scope.setMountResultLocked(true); + } } - @Override - public void close() { - IoUtils.closeQuietly(mMountScope); - IoUtils.closeQuietly(mProxyFd); - // Invoke countDown here in case where close is invoked before mount. - mChannel.offer(false); + // Used by com_android_server_storage_AppFuse.cpp. + synchronized private void onClosed(int mountId) { + final MountScope scope = mScopes.get(mountId); + if (scope != null) { + scope.setMountResultLocked(false); + IoUtils.closeQuietly(scope); + mScopes.remove(mountId); + } } - // Used by com_android_server_storage_AppFuse.cpp. - private void onMount() { - mChannel.offer(true); + public static class MountScope implements AutoCloseable { + public final int uid; + public final int pid; + public final int mountId; + public final ParcelFileDescriptor deviceFd; + public final File mountPoint; + private final CountDownLatch mMounted = new CountDownLatch(1); + private boolean mMountResult = false; + + public MountScope(int uid, int pid, int mountId, ParcelFileDescriptor deviceFd) { + this.uid = uid; + this.pid = pid; + this.mountId = mountId; + this.deviceFd = deviceFd; + this.mountPoint = new File(String.format(APPFUSE_MOUNT_NAME_TEMPLATE, uid, mountId)); + } + + @GuardedBy("AppFuseBridge.this") + void setMountResultLocked(boolean result) { + if (mMounted.getCount() == 0) { + return; + } + mMountResult = result; + mMounted.countDown(); + } + + boolean waitForMount() throws InterruptedException { + mMounted.await(); + return mMountResult; + } + + @Override + public void close() throws Exception { + deviceFd.close(); + } } - public static interface IMountScope extends AutoCloseable { - ParcelFileDescriptor getDeviceFileDescriptor(); + public static class BridgeException extends Exception { + public BridgeException(String message) { + super(message); + } } - private native boolean native_start_loop(int deviceFd, int proxyFd); + private native long native_new(); + private native void native_delete(long loop); + private native void native_start_loop(long loop); + private native int native_add_bridge(long loop, int mountId, int deviceId); } diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index fc377973221b..85eae0212373 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -540,12 +540,14 @@ public class DockedStackDividerController implements DimLayerUser { checkMinimizeChanged(true /* animate */); // We were minimized, and now we are still minimized, but somebody is trying to launch an - // app in docked stack, better show recent apps so we actually get unminimized! This catches + // app in docked stack, better show recent apps so we actually get unminimized! However do + // not do this if keyguard is dismissed such as when the device is unlocking. This catches // any case that was missed in ActivityStarter.postStartActivityUncheckedProcessing because // we couldn't retrace the launch of the app in the docked stack to the launch from // homescreen. if (wasMinimized && mMinimizedDock && containsAppInDockedStack(openingApps) - && appTransition != TRANSIT_NONE) { + && appTransition != TRANSIT_NONE && + !AppTransition.isKeyguardGoingAwayTransit(appTransition)) { mService.showRecentApps(true /* fromHome */); } } @@ -579,6 +581,12 @@ public class DockedStackDividerController implements DimLayerUser { if (homeTask == null || !isWithinDisplay(homeTask)) { return; } + + // Do not minimize when dock is already minimized while keyguard is showing and not + // occluded such as unlocking the screen + if (mMinimizedDock && mService.mPolicy.isKeyguardShowingAndNotOccluded()) { + return; + } final TaskStack fullscreenStack = mDisplayContent.getStackById(FULLSCREEN_WORKSPACE_STACK_ID); final boolean homeVisible = homeTask.getTopVisibleAppToken() != null; diff --git a/services/core/jni/com_android_server_storage_AppFuseBridge.cpp b/services/core/jni/com_android_server_storage_AppFuseBridge.cpp index 2f20ecd1954d..c8f842dde7ae 100644 --- a/services/core/jni/com_android_server_storage_AppFuseBridge.cpp +++ b/services/core/jni/com_android_server_storage_AppFuseBridge.cpp @@ -19,16 +19,19 @@ #include <android_runtime/Log.h> #include <android-base/logging.h> +#include <android-base/unique_fd.h> #include <core_jni_helpers.h> #include <libappfuse/FuseBridgeLoop.h> +#include <libappfuse/FuseBuffer.h> #include <nativehelper/JNIHelp.h> namespace android { namespace { constexpr const char* CLASS_NAME = "com/android/server/storage/AppFuseBridge"; -static jclass appFuseClass; -static jmethodID appFuseOnMount; +static jclass gAppFuseClass; +static jmethodID gAppFuseOnMount; +static jmethodID gAppFuseOnClosed; class Callback : public fuse::FuseBridgeLoopCallback { JNIEnv* mEnv; @@ -36,8 +39,16 @@ class Callback : public fuse::FuseBridgeLoopCallback { public: Callback(JNIEnv* env, jobject self) : mEnv(env), mSelf(self) {} - void OnMount() override { - mEnv->CallVoidMethod(mSelf, appFuseOnMount); + void OnMount(int mount_id) override { + mEnv->CallVoidMethod(mSelf, gAppFuseOnMount, mount_id); + if (mEnv->ExceptionCheck()) { + LOGE_EX(mEnv, nullptr); + mEnv->ExceptionClear(); + } + } + + void OnClosed(int mount_id) override { + mEnv->CallVoidMethod(mSelf, gAppFuseOnClosed, mount_id); if (mEnv->ExceptionCheck()) { LOGE_EX(mEnv, nullptr); mEnv->ExceptionClear(); @@ -45,17 +56,93 @@ public: } }; -jboolean com_android_server_storage_AppFuseBridge_start_loop( - JNIEnv* env, jobject self, jint devJavaFd, jint proxyJavaFd) { +class MonitorScope final { +public: + MonitorScope(JNIEnv* env, jobject obj) : mEnv(env), mObj(obj), mLocked(false) { + if (mEnv->MonitorEnter(obj) == JNI_OK) { + mLocked = true; + } else { + LOG(ERROR) << "Failed to enter monitor."; + } + } + + ~MonitorScope() { + if (mLocked) { + if (mEnv->MonitorExit(mObj) != JNI_OK) { + LOG(ERROR) << "Failed to exit monitor."; + } + } + } + + operator bool() { + return mLocked; + } + +private: + // Lifetime of |MonitorScope| must be shorter than the reference of mObj. + JNIEnv* mEnv; + jobject mObj; + bool mLocked; + + DISALLOW_COPY_AND_ASSIGN(MonitorScope); +}; + +jlong com_android_server_storage_AppFuseBridge_new(JNIEnv* env, jobject self) { + return reinterpret_cast<jlong>(new fuse::FuseBridgeLoop()); +} + +void com_android_server_storage_AppFuseBridge_delete(JNIEnv* env, jobject self, jlong java_loop) { + fuse::FuseBridgeLoop* const loop = reinterpret_cast<fuse::FuseBridgeLoop*>(java_loop); + CHECK(loop); + delete loop; +} + +void com_android_server_storage_AppFuseBridge_start_loop( + JNIEnv* env, jobject self, jlong java_loop) { + fuse::FuseBridgeLoop* const loop = reinterpret_cast<fuse::FuseBridgeLoop*>(java_loop); + CHECK(loop); Callback callback(env, self); - return fuse::StartFuseBridgeLoop(devJavaFd, proxyJavaFd, &callback); + loop->Start(&callback); +} + +jint com_android_server_storage_AppFuseBridge_add_bridge( + JNIEnv* env, jobject self, jlong java_loop, jint mountId, jint javaDevFd) { + base::unique_fd devFd(javaDevFd); + fuse::FuseBridgeLoop* const loop = reinterpret_cast<fuse::FuseBridgeLoop*>(java_loop); + CHECK(loop); + + base::unique_fd proxyFd[2]; + if (!fuse::SetupMessageSockets(&proxyFd)) { + return -1; + } + + if (!loop->AddBridge(mountId, std::move(devFd), std::move(proxyFd[0]))) { + return -1; + } + + return proxyFd[1].release(); } const JNINativeMethod methods[] = { { + "native_new", + "()J", + reinterpret_cast<void*>(com_android_server_storage_AppFuseBridge_new) + }, + { + "native_delete", + "(J)V", + reinterpret_cast<void*>(com_android_server_storage_AppFuseBridge_delete) + }, + { "native_start_loop", - "(II)Z", - (void *) com_android_server_storage_AppFuseBridge_start_loop + "(J)V", + reinterpret_cast<void*>(com_android_server_storage_AppFuseBridge_start_loop) + }, + { + "native_add_bridge", + "(JII)I", + reinterpret_cast<void*>(com_android_server_storage_AppFuseBridge_add_bridge) } }; @@ -64,8 +151,9 @@ const JNINativeMethod methods[] = { void register_android_server_storage_AppFuse(JNIEnv* env) { CHECK(env != nullptr); - appFuseClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, CLASS_NAME)); - appFuseOnMount = GetMethodIDOrDie(env, appFuseClass, "onMount", "()V"); + gAppFuseClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, CLASS_NAME)); + gAppFuseOnMount = GetMethodIDOrDie(env, gAppFuseClass, "onMount", "(I)V"); + gAppFuseOnClosed = GetMethodIDOrDie(env, gAppFuseClass, "onClosed", "(I)V"); RegisterMethodsOrDie(env, CLASS_NAME, methods, NELEM(methods)); } } // namespace android 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 ab83b9d84747..4c23d79cf7ff 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -52,6 +52,7 @@ import java.util.Arrays; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import com.android.server.lights.Light; @@ -259,6 +260,7 @@ public class NotificationManagerServiceTest { @Test @UiThreadTest + @Ignore("Flaky") public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception { mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", 0, generateNotificationRecord(null).getNotification(), new int[1], 0); diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index f8d105e5ac31..29c6f89e5fa0 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -37,6 +37,7 @@ import static com.android.server.net.NetworkPolicyManagerService.ProcStateSeqHis import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT; import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED; import static com.android.server.net.NetworkPolicyManagerService.TYPE_WARNING; +import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -51,10 +52,15 @@ import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Matchers.isA; import static org.mockito.Matchers.isNull; +import static org.mockito.Mockito.atLeast; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -89,6 +95,7 @@ import android.net.NetworkTemplate; import android.os.Binder; import android.os.INetworkManagementService; import android.os.PowerManagerInternal; +import android.os.PowerSaveState; import android.os.UserHandle; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; @@ -198,6 +205,7 @@ public class NetworkPolicyManagerServiceTest { private IUidObserver mUidObserver; private INetworkManagementEventObserver mNetworkObserver; + private PowerManagerInternal mPowerManagerInternal; private NetworkPolicyListenerAnswer mPolicyListener; private NetworkPolicyManagerService mService; @@ -227,12 +235,16 @@ public class NetworkPolicyManagerServiceTest { @BeforeClass public static void registerLocalServices() { - addLocalServiceMock(PowerManagerInternal.class); addLocalServiceMock(DeviceIdleController.LocalService.class); final UsageStatsManagerInternal usageStats = addLocalServiceMock(UsageStatsManagerInternal.class); when(usageStats.getIdleUidsForUser(anyInt())).thenReturn(new int[]{}); mActivityManagerInternal = addLocalServiceMock(ActivityManagerInternal.class); + + final PowerSaveState state = new PowerSaveState.Builder() + .setBatterySaverEnabled(false).build(); + final PowerManagerInternal pmInternal = addLocalServiceMock(PowerManagerInternal.class); + when(pmInternal.getLowPowerState(anyInt())).thenReturn(state); } @Before @@ -401,6 +413,85 @@ public class NetworkPolicyManagerServiceTest { removeRestrictBackgroundWhitelist(false); } + @Test + public void testLowPowerModeObserver_ListenersRegistered() + throws Exception { + PowerManagerInternal pmInternal = LocalServices.getService(PowerManagerInternal.class); + + verify(pmInternal, atLeast(2)).registerLowPowerModeObserver(any()); + } + + @Test + public void updateRestrictBackgroundByLowPowerMode_RestrictOnBeforeBsm_RestrictOnAfterBsm() + throws Exception { + setRestrictBackground(true); + PowerSaveState stateOn = new PowerSaveState.Builder() + .setGlobalBatterySaverEnabled(true) + .setBatterySaverEnabled(false) + .build(); + mService.updateRestrictBackgroundByLowPowerModeUL(stateOn); + + // RestrictBackground should be on even though battery saver want to turn it off + assertThat(mService.getRestrictBackground()).isTrue(); + + PowerSaveState stateOff = new PowerSaveState.Builder() + .setGlobalBatterySaverEnabled(false) + .setBatterySaverEnabled(false) + .build(); + mService.updateRestrictBackgroundByLowPowerModeUL(stateOff); + + // RestrictBackground should be on, following its previous state + assertThat(mService.getRestrictBackground()).isTrue(); + } + + @Test + public void updateRestrictBackgroundByLowPowerMode_RestrictOffBeforeBsm_RestrictOffAfterBsm() + throws Exception { + setRestrictBackground(false); + PowerSaveState stateOn = new PowerSaveState.Builder() + .setGlobalBatterySaverEnabled(true) + .setBatterySaverEnabled(true) + .build(); + + doReturn(true).when(mNetworkManager).setDataSaverModeEnabled(true); + mService.updateRestrictBackgroundByLowPowerModeUL(stateOn); + + // RestrictBackground should be turned on because of battery saver + assertThat(mService.getRestrictBackground()).isTrue(); + + PowerSaveState stateOff = new PowerSaveState.Builder() + .setGlobalBatterySaverEnabled(false) + .setBatterySaverEnabled(false) + .build(); + mService.updateRestrictBackgroundByLowPowerModeUL(stateOff); + + // RestrictBackground should be off, following its previous state + assertThat(mService.getRestrictBackground()).isFalse(); + } + + @Test + public void updateRestrictBackgroundByLowPowerMode_StatusChangedInBsm_DoNotRestore() + throws Exception { + setRestrictBackground(true); + PowerSaveState stateOn = new PowerSaveState.Builder() + .setGlobalBatterySaverEnabled(true) + .setBatterySaverEnabled(true) + .build(); + mService.updateRestrictBackgroundByLowPowerModeUL(stateOn); + + // RestrictBackground should still be on + assertThat(mService.getRestrictBackground()).isTrue(); + + // User turns off RestrictBackground manually + setRestrictBackground(false); + PowerSaveState stateOff = new PowerSaveState.Builder().setBatterySaverEnabled( + false).build(); + mService.updateRestrictBackgroundByLowPowerModeUL(stateOff); + + // RestrictBackground should be off because user changes it manually + assertThat(mService.getRestrictBackground()).isFalse(); + } + private void removeRestrictBackgroundWhitelist(boolean expectIntent) throws Exception { // Sanity checks. assertWhitelistUids(UID_A); @@ -1231,7 +1322,7 @@ public class NetworkPolicyManagerServiceTest { private void setRestrictBackground(boolean flag) throws Exception { // Must set expectation, otherwise NMPS will reset value to previous one. - when(mNetworkManager.setDataSaverModeEnabled(flag)).thenReturn(true); + doReturn(true).when(mNetworkManager).setDataSaverModeEnabled(flag); mService.setRestrictBackground(flag); // Sanity check. assertEquals("restrictBackground not set", flag, mService.getRestrictBackground()); diff --git a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java index 7282b3ea9d85..69589e7d1f66 100644 --- a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java +++ b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java @@ -19,7 +19,9 @@ import android.os.PowerSaveState; import android.os.Handler; import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; + import com.android.server.power.BatterySaverPolicy.ServiceType; + import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -40,6 +42,7 @@ public class BatterySaverPolicyTest extends AndroidTestCase { + "animation_disabled=false," + "soundtrigger_disabled=true," + "firewall_disabled=false," + + "datasaver_disabled=false," + "adjust_brightness_disabled=true," + "adjust_brightness_factor=0.7," + "fullbackup_deferred=true," @@ -99,6 +102,18 @@ public class BatterySaverPolicyTest extends AndroidTestCase { } @SmallTest + public void testGetBatterySaverPolicy_PolicyDataSaver_DefaultValueCorrect() { + mBatterySaverPolicy.updateConstants(""); + final PowerSaveState batterySaverStateOn = + mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.DATA_SAVER, BATTERY_SAVER_ON); + assertThat(batterySaverStateOn.batterySaverEnabled).isFalse(); + + final PowerSaveState batterySaverStateOff = mBatterySaverPolicy.getBatterySaverPolicy( + ServiceType.DATA_SAVER, BATTERY_SAVER_OFF); + assertThat(batterySaverStateOff.batterySaverEnabled).isFalse(); + } + + @SmallTest public void testGetBatterySaverPolicy_PolicyScreenBrightness_DefaultValueCorrect() { testServiceDefaultValue(ServiceType.SCREEN_BRIGHTNESS); @@ -137,7 +152,8 @@ public class BatterySaverPolicyTest extends AndroidTestCase { assertThat(networkState.batterySaverEnabled).isTrue(); final PowerSaveState screenState = - mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS, BATTERY_SAVER_ON); + mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.SCREEN_BRIGHTNESS, + BATTERY_SAVER_ON); assertThat(screenState.batterySaverEnabled).isFalse(); assertThat(screenState.brightnessFactor).isWithin(PRECISION).of(BRIGHTNESS_FACTOR); @@ -149,6 +165,10 @@ public class BatterySaverPolicyTest extends AndroidTestCase { ServiceType.KEYVALUE_BACKUP, BATTERY_SAVER_ON); assertThat(keyValueBackupState.batterySaverEnabled).isFalse(); + final PowerSaveState dataSaverState = mBatterySaverPolicy.getBatterySaverPolicy( + ServiceType.DATA_SAVER, BATTERY_SAVER_ON); + assertThat(dataSaverState.batterySaverEnabled).isTrue(); + final PowerSaveState gpsState = mBatterySaverPolicy.getBatterySaverPolicy(ServiceType.GPS, BATTERY_SAVER_ON); assertThat(gpsState.batterySaverEnabled).isTrue(); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index dd23850c2fca..26c9430408e8 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -23,8 +23,8 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; -import android.annotation.WorkerThread; import android.annotation.SystemApi; +import android.annotation.WorkerThread; import android.app.ActivityThread; import android.app.PendingIntent; import android.content.ContentResolver; @@ -50,6 +50,7 @@ import android.util.Log; import com.android.ims.internal.IImsServiceController; import com.android.ims.internal.IImsServiceFeatureListener; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telecom.ITelecomService; import com.android.internal.telephony.CellNetworkScanResult; import com.android.internal.telephony.IPhoneSubInfo; @@ -940,7 +941,11 @@ public class TelephonyManager { * * <p>Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * + * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns + * MEID for CDMA. */ + @Deprecated public String getDeviceId() { try { ITelephony telephony = getITelephony(); @@ -962,7 +967,11 @@ public class TelephonyManager { * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * * @param slotIndex of which deviceID is returned + * + * @deprecated Use (@link getImei} which returns IMEI for GSM or (@link getMeid} which returns + * MEID for CDMA. */ + @Deprecated public String getDeviceId(int slotIndex) { // FIXME this assumes phoneId == slotIndex try { @@ -978,29 +987,25 @@ public class TelephonyManager { } /** - * Returns the IMEI. Return null if IMEI is not available. + * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not + * available. * * <p>Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} - * - * @hide */ - @SystemApi public String getImei() { return getImei(getDefaultSim()); } /** - * Returns the IMEI. Return null if IMEI is not available. + * Returns the IMEI (International Mobile Equipment Identity). Return null if IMEI is not + * available. * * <p>Requires Permission: * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} * - * @param slotIndex of which deviceID is returned - * - * @hide + * @param slotIndex of which IMEI is returned */ - @SystemApi public String getImei(int slotIndex) { ITelephony telephony = getITelephony(); if (telephony == null) return null; @@ -1015,6 +1020,37 @@ public class TelephonyManager { } /** + * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + */ + public String getMeid() { + return getMeid(getDefaultSim()); + } + + /** + * Returns the MEID (Mobile Equipment Identifier). Return null if MEID is not available. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * + * @param slotIndex of which MEID is returned + */ + public String getMeid(int slotIndex) { + ITelephony telephony = getITelephony(); + if (telephony == null) return null; + + try { + return telephony.getMeidForSlot(slotIndex, getOpPackageName()); + } catch (RemoteException ex) { + return null; + } catch (NullPointerException ex) { + return null; + } + } + + /** * Returns the NAI. Return null if NAI is not available. * */ @@ -4056,9 +4092,19 @@ public class TelephonyManager { return SubscriptionManager.getPhoneId(SubscriptionManager.getDefaultSubscriptionId()); } - /** {@hide} */ + /** + * @return default SIM's slot index. If SIM is not inserted, return default SIM slot index. + * + * {@hide} + */ + @VisibleForTesting public int getDefaultSim() { - return SubscriptionManager.getSlotIndex(SubscriptionManager.getDefaultSubscriptionId()); + int slotIndex = SubscriptionManager.getSlotIndex( + SubscriptionManager.getDefaultSubscriptionId()); + if (slotIndex == SubscriptionManager.SIM_NOT_INSERTED) { + slotIndex = SubscriptionManager.DEFAULT_SIM_SLOT_INDEX; + } + return slotIndex; } /** diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 76138373f457..cd15c444d8bd 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -1096,6 +1096,16 @@ interface ITelephony { String getImeiForSlot(int slotIndex, String callingPackage); /** + * Returns the MEID for the given slot. + * + * @param slotIndex - device slot. + * @param callingPackage The package making the call. + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + */ + String getMeidForSlot(int slotIndex, String callingPackage); + + /** * Returns the device software version. * * @param slotIndex - device slot. diff --git a/wifi/java/android/net/wifi/IconInfo.aidl b/wifi/java/android/net/wifi/IconInfo.aidl deleted file mode 100644 index a7bb2ef8bc3f..000000000000 --- a/wifi/java/android/net/wifi/IconInfo.aidl +++ /dev/null @@ -1,19 +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.net.wifi; - -parcelable IconInfo; diff --git a/wifi/java/android/net/wifi/IconInfo.java b/wifi/java/android/net/wifi/IconInfo.java deleted file mode 100644 index 0eae363861f8..000000000000 --- a/wifi/java/android/net/wifi/IconInfo.java +++ /dev/null @@ -1,111 +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.net.wifi; - -import android.os.Parcelable; -import android.text.TextUtils; -import android.os.Parcel; - -import java.util.Arrays; -import java.util.Objects; - -/** - * A class representing icon information. - */ -public final class IconInfo implements Parcelable { - /** - * Name of the icon file. - */ - private final String mFilename; - - /** - * Raw binary data of the icon. - */ - private final byte[] mData; - - public IconInfo(String filename, byte[] data) { - mFilename = filename; - mData = data; - } - - public IconInfo(IconInfo source) { - if (source == null) { - mFilename = null; - mData = null; - return; - } - - mFilename = source.mFilename; - if (source.mData != null) { - mData = Arrays.copyOf(source.mData, source.mData.length); - } else { - mData = null; - } - } - - public String getFilename() { - return mFilename; - } - - public byte[] getData() { - return mData; - } - - @Override - public boolean equals(Object thatObject) { - if (this == thatObject) { - return true; - } - if (!(thatObject instanceof IconInfo)) { - return false; - } - IconInfo that = (IconInfo) thatObject; - return TextUtils.equals(mFilename, that.mFilename) - && Arrays.equals(mData, that.mData); - } - - @Override - public int hashCode() { - return Objects.hash(mFilename, mData); - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(mFilename); - dest.writeByteArray(mData); - } - - public static final Creator<IconInfo> CREATOR = - new Creator<IconInfo>() { - @Override - public IconInfo createFromParcel(Parcel in) { - String filename = in.readString(); - byte[] data = in.createByteArray(); - return new IconInfo(filename, data); - } - - @Override - public IconInfo[] newArray(int size) { - return new IconInfo[size]; - } - }; -} diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 4f2881b87616..824c436ac9fd 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -120,7 +120,8 @@ public class WifiManager { * * Included extras: * {@link #EXTRA_BSSID_LONG} - * {@link #EXTRA_ICON_INFO} + * {@link #EXTRA_FILENAME} + * {@link #EXTRA_ICON} * * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE * @@ -136,12 +137,18 @@ public class WifiManager { */ public static final String EXTRA_BSSID_LONG = "android.net.wifi.extra.BSSID_LONG"; /** - * Icon information. + * Icon data. * * Retrieve with {@link android.content.Intent#getParcelableExtra(String)} and cast into - * {@link IconInfo}. + * {@link android.graphics.drawable.Icon}. */ - public static final String EXTRA_ICON_INFO = "android.net.wifi.extra.ICON_INFO"; + public static final String EXTRA_ICON = "android.net.wifi.extra.ICON"; + /** + * Name of a file. + * + * Retrieve with {@link android.content.Intent#getStringExtra(String)}. + */ + public static final String EXTRA_FILENAME = "android.net.wifi.extra.FILENAME"; /** * Broadcast intent action indicating a Passpoint OSU Providers List element has been received. @@ -984,9 +991,9 @@ public class WifiManager { /** * Query for a Hotspot 2.0 release 2 OSU icon file. An {@link #ACTION_PASSPOINT_ICON} intent - * will be broadcasted once the request is completed. The return value of - * {@link IconInfo#getData} from the intent extra will indicate the result of the request. - * A value of {@code null} will indicate a failure. + * will be broadcasted once the request is completed. The presence of the intent extra + * {@link #EXTRA_ICON} will indicate the result of the request. + * A missing intent extra {@link #EXTRA_ICON} will indicate a failure. * * @param bssid The BSSID of the AP * @param fileName Name of the icon file (remote file) to query from the AP diff --git a/wifi/tests/src/android/net/wifi/IconInfoTest.java b/wifi/tests/src/android/net/wifi/IconInfoTest.java deleted file mode 100644 index 2fdb484fce19..000000000000 --- a/wifi/tests/src/android/net/wifi/IconInfoTest.java +++ /dev/null @@ -1,122 +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.net.wifi; - -import static org.junit.Assert.assertEquals; - -import android.net.wifi.IconInfo; -import android.os.Parcel; -import android.test.suitebuilder.annotation.SmallTest; - -import org.junit.Test; - -/** - * Unit tests for {@link android.net.wifi.IconInfo}. - */ -@SmallTest -public class IconInfoTest { - private static final String TEST_FILENAME = "testIcon"; - private static final byte[] TEST_DATA = new byte[] {0x12, 0x23, 0x34, 0x45, 0x56, 0x67}; - - /** - * Verify parcel write and read consistency for the given {@link IconInfo} - * - * @param writeIcon the {@link IconInfo} to write and verify - * @throws Exception - */ - private static void verifyParcel(IconInfo writeIcon) throws Exception { - Parcel parcel = Parcel.obtain(); - writeIcon.writeToParcel(parcel, 0); - - parcel.setDataPosition(0); // Rewind data position back to the beginning for read. - IconInfo readIcon = IconInfo.CREATOR.createFromParcel(parcel); - assertEquals(writeIcon, readIcon); - } - - /** - * Verify parcel serialization for a {@link IconInfo} with null data. - * - * @throws Exception - */ - @Test - public void verifyParcelWithNullData() throws Exception { - verifyParcel(new IconInfo(TEST_FILENAME, (byte[]) null)); - } - - /** - * Verify parcel serialization for a {@link IconInfo} with zero length data. - * - * @throws Exception - */ - @Test - public void verifyParcelWithZeroLengthData() throws Exception { - verifyParcel(new IconInfo(TEST_FILENAME, new byte[0])); - } - - /** - * Verify parcel serialization for a {@link IconInfo} with non-zero length data. - * - * @throws Exception - */ - @Test - public void verifyParcelWithNonZeroLengthData() throws Exception { - verifyParcel(new IconInfo(TEST_FILENAME, TEST_DATA)); - } - - /** - * Verify parcel serialization for a {@link IconInfo} with a null filename. - * - * @throws Exception - */ - @Test - public void verifyParcelWithNullFilename() throws Exception { - verifyParcel(new IconInfo(null, TEST_DATA)); - } - - /** - * Verify the copy constructor with non-null filename and data. - * - * @throws Exception - */ - @Test - public void verifyCopyConstructor() throws Exception { - IconInfo source = new IconInfo(TEST_FILENAME, TEST_DATA); - assertEquals(source, new IconInfo(source)); - } - - /** - * Verify the copy constructor with null data. - * - * @throws Exception - */ - @Test - public void verifyCopyConstructorWithNullData() throws Exception { - IconInfo source = new IconInfo(TEST_FILENAME, (byte[]) null); - assertEquals(source, new IconInfo(source)); - } - - /** - * Verify the copy constructor with null file name. - * - * @throws Exception - */ - @Test - public void verifyCopyConstructorWithNullFilename() throws Exception { - IconInfo source = new IconInfo(null, TEST_DATA); - assertEquals(source, new IconInfo(source)); - } -} |