diff options
82 files changed, 2231 insertions, 1316 deletions
diff --git a/api/current.txt b/api/current.txt index 3b57f18d360a..a400fa6b70d2 100644 --- a/api/current.txt +++ b/api/current.txt @@ -20,8 +20,8 @@ package android { field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE"; field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; - field public static final java.lang.String BIND_CARRIER_CONFIG_SERVICE = "android.permission.BIND_CARRIER_CONFIG_SERVICE"; - field public static final java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE"; + field public static final deprecated java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE"; + field public static final java.lang.String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES"; field public static final java.lang.String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE"; field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN"; field public static final java.lang.String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE"; @@ -15730,7 +15730,7 @@ package android.media { method public void restoreKeys(byte[], byte[]); method public void setOnEventListener(android.media.MediaDrm.OnEventListener); method public void setOnExpirationUpdateListener(android.media.MediaDrm.OnExpirationUpdateListener, android.os.Handler); - method public void setOnKeysChangeListener(android.media.MediaDrm.OnKeysChangeListener, android.os.Handler); + method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler); method public void setPropertyByteArray(java.lang.String, byte[]); method public void setPropertyString(java.lang.String, java.lang.String); field public static final int EVENT_KEY_EXPIRED = 3; // 0x3 @@ -15738,11 +15738,6 @@ package android.media { field public static final deprecated int EVENT_PROVISION_REQUIRED = 1; // 0x1 field public static final int EVENT_SESSION_RECLAIMED = 5; // 0x5 field public static final int EVENT_VENDOR_DEFINED = 4; // 0x4 - field public static final int KEY_STATUS_EXPIRED = 1; // 0x1 - field public static final int KEY_STATUS_INTERNAL_ERROR = 4; // 0x4 - field public static final int KEY_STATUS_OUTPUT_NOT_ALLOWED = 2; // 0x2 - field public static final int KEY_STATUS_PENDING = 3; // 0x3 - field public static final int KEY_STATUS_USABLE = 0; // 0x0 field public static final int KEY_TYPE_OFFLINE = 2; // 0x2 field public static final int KEY_TYPE_RELEASE = 3; // 0x3 field public static final int KEY_TYPE_STREAMING = 1; // 0x1 @@ -15751,9 +15746,6 @@ package android.media { field public static final java.lang.String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId"; field public static final java.lang.String PROPERTY_VENDOR = "vendor"; field public static final java.lang.String PROPERTY_VERSION = "version"; - field public static final int REQUEST_TYPE_INITIAL = 0; // 0x0 - field public static final int REQUEST_TYPE_RELEASE = 2; // 0x2 - field public static final int REQUEST_TYPE_RENEWAL = 1; // 0x1 } public final class MediaDrm.CryptoSession { @@ -15767,11 +15759,19 @@ package android.media { method public byte[] getData(); method public java.lang.String getDefaultUrl(); method public int getRequestType(); + field public static final int REQUEST_TYPE_INITIAL = 0; // 0x0 + field public static final int REQUEST_TYPE_RELEASE = 2; // 0x2 + field public static final int REQUEST_TYPE_RENEWAL = 1; // 0x1 } public static final class MediaDrm.KeyStatus { method public byte[] getKeyId(); method public int getStatusCode(); + field public static final int STATUS_EXPIRED = 1; // 0x1 + field public static final int STATUS_INTERNAL_ERROR = 4; // 0x4 + field public static final int STATUS_OUTPUT_NOT_ALLOWED = 2; // 0x2 + field public static final int STATUS_PENDING = 3; // 0x3 + field public static final int STATUS_USABLE = 0; // 0x0 } public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException { @@ -15786,8 +15786,8 @@ package android.media { method public abstract void onExpirationUpdate(android.media.MediaDrm, byte[], long); } - public static abstract interface MediaDrm.OnKeysChangeListener { - method public abstract void onKeysChange(android.media.MediaDrm, byte[], java.util.List<android.media.MediaDrm.KeyStatus>, boolean); + public static abstract interface MediaDrm.OnKeyStatusChangeListener { + method public abstract void onKeyStatusChange(android.media.MediaDrm, byte[], java.util.List<android.media.MediaDrm.KeyStatus>, boolean); } public static final class MediaDrm.ProvisionRequest { diff --git a/api/system-current.txt b/api/system-current.txt index dbe0d4f7a8be..351ec8e703fd 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -28,8 +28,8 @@ package android { field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS"; field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE"; field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET"; - field public static final java.lang.String BIND_CARRIER_CONFIG_SERVICE = "android.permission.BIND_CARRIER_CONFIG_SERVICE"; - field public static final java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE"; + field public static final deprecated java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE"; + field public static final java.lang.String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES"; field public static final java.lang.String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE"; field public static final java.lang.String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE"; field public static final deprecated java.lang.String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE"; @@ -8367,6 +8367,7 @@ package android.content { field public static final java.lang.String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS"; field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE"; field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE"; + field public static final java.lang.String ACTION_MANAGE_PERMISSIONS = "android.intent.action.MANAGE_PERMISSIONS"; field public static final java.lang.String ACTION_MANAGE_PERMISSION_APPS = "android.intent.action.MANAGE_PERMISSION_APPS"; field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL"; field public static final java.lang.String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON"; @@ -16967,7 +16968,7 @@ package android.media { method public void restoreKeys(byte[], byte[]); method public void setOnEventListener(android.media.MediaDrm.OnEventListener); method public void setOnExpirationUpdateListener(android.media.MediaDrm.OnExpirationUpdateListener, android.os.Handler); - method public void setOnKeysChangeListener(android.media.MediaDrm.OnKeysChangeListener, android.os.Handler); + method public void setOnKeyStatusChangeListener(android.media.MediaDrm.OnKeyStatusChangeListener, android.os.Handler); method public void setPropertyByteArray(java.lang.String, byte[]); method public void setPropertyString(java.lang.String, java.lang.String); method public void unprovisionDevice(); @@ -16976,11 +16977,6 @@ package android.media { field public static final deprecated int EVENT_PROVISION_REQUIRED = 1; // 0x1 field public static final int EVENT_SESSION_RECLAIMED = 5; // 0x5 field public static final int EVENT_VENDOR_DEFINED = 4; // 0x4 - field public static final int KEY_STATUS_EXPIRED = 1; // 0x1 - field public static final int KEY_STATUS_INTERNAL_ERROR = 4; // 0x4 - field public static final int KEY_STATUS_OUTPUT_NOT_ALLOWED = 2; // 0x2 - field public static final int KEY_STATUS_PENDING = 3; // 0x3 - field public static final int KEY_STATUS_USABLE = 0; // 0x0 field public static final int KEY_TYPE_OFFLINE = 2; // 0x2 field public static final int KEY_TYPE_RELEASE = 3; // 0x3 field public static final int KEY_TYPE_STREAMING = 1; // 0x1 @@ -16989,9 +16985,6 @@ package android.media { field public static final java.lang.String PROPERTY_DEVICE_UNIQUE_ID = "deviceUniqueId"; field public static final java.lang.String PROPERTY_VENDOR = "vendor"; field public static final java.lang.String PROPERTY_VERSION = "version"; - field public static final int REQUEST_TYPE_INITIAL = 0; // 0x0 - field public static final int REQUEST_TYPE_RELEASE = 2; // 0x2 - field public static final int REQUEST_TYPE_RENEWAL = 1; // 0x1 } public final class MediaDrm.CryptoSession { @@ -17005,11 +16998,19 @@ package android.media { method public byte[] getData(); method public java.lang.String getDefaultUrl(); method public int getRequestType(); + field public static final int REQUEST_TYPE_INITIAL = 0; // 0x0 + field public static final int REQUEST_TYPE_RELEASE = 2; // 0x2 + field public static final int REQUEST_TYPE_RENEWAL = 1; // 0x1 } public static final class MediaDrm.KeyStatus { method public byte[] getKeyId(); method public int getStatusCode(); + field public static final int STATUS_EXPIRED = 1; // 0x1 + field public static final int STATUS_INTERNAL_ERROR = 4; // 0x4 + field public static final int STATUS_OUTPUT_NOT_ALLOWED = 2; // 0x2 + field public static final int STATUS_PENDING = 3; // 0x3 + field public static final int STATUS_USABLE = 0; // 0x0 } public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException { @@ -17024,8 +17025,8 @@ package android.media { method public abstract void onExpirationUpdate(android.media.MediaDrm, byte[], long); } - public static abstract interface MediaDrm.OnKeysChangeListener { - method public abstract void onKeysChange(android.media.MediaDrm, byte[], java.util.List<android.media.MediaDrm.KeyStatus>, boolean); + public static abstract interface MediaDrm.OnKeyStatusChangeListener { + method public abstract void onKeyStatusChange(android.media.MediaDrm, byte[], java.util.List<android.media.MediaDrm.KeyStatus>, boolean); } public static final class MediaDrm.ProvisionRequest { diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java index 4a8cf0818edb..0dad4dcec892 100644 --- a/cmds/sm/src/com/android/commands/sm/Sm.java +++ b/cmds/sm/src/com/android/commands/sm/Sm.java @@ -71,6 +71,8 @@ public final class Sm { runHasAdoptable(); } else if ("get-primary-storage-uuid".equals(op)) { runGetPrimaryStorageUuid(); + } else if ("set-force-adoptable".equals(op)) { + runSetForceAdoptable(); } else if ("partition".equals(op)) { runPartition(); } else if ("mount".equals(op)) { @@ -116,14 +118,19 @@ public final class Sm { } public void runHasAdoptable() { - System.out.println(SystemProperties.getBoolean(StorageManager.PROP_HAS_ADOPTABLE, false) - || SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)); + System.out.println(SystemProperties.getBoolean(StorageManager.PROP_HAS_ADOPTABLE, false)); } public void runGetPrimaryStorageUuid() throws RemoteException { System.out.println(mSm.getPrimaryStorageUuid()); } + public void runSetForceAdoptable() throws RemoteException { + final boolean forceAdoptable = Boolean.parseBoolean(nextArg()); + mSm.setDebugFlags(forceAdoptable ? StorageManager.DEBUG_FORCE_ADOPTABLE : 0, + StorageManager.DEBUG_FORCE_ADOPTABLE); + } + public void runPartition() throws RemoteException { final String diskId = nextArg(); final String type = nextArg(); @@ -177,6 +184,7 @@ public final class Sm { System.err.println(" sm list-volumes [public|private|emulated|all]"); System.err.println(" sm has-adoptable"); System.err.println(" sm get-primary-storage-uuid"); + System.err.println(" sm set-force-adoptable [true|false]"); System.err.println(""); System.err.println(" sm partition DISK [public|private|mixed] [ratio]"); System.err.println(" sm mount VOLUME"); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 7d767602541a..2db623b342da 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1535,6 +1535,22 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.MANAGE_APP_PERMISSIONS"; /** + * Activity action: Launch UI to manage permissions. + * <p> + * Input: Nothing. + * </p> + * <p> + * Output: Nothing. + * </p> + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_MANAGE_PERMISSIONS = + "android.intent.action.MANAGE_PERMISSIONS"; + + /** * Intent extra: An app package name. * <p> * Type: String diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 4dfe0de831e2..5f515ebb10e6 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -425,6 +425,24 @@ public abstract class BatteryStats implements Parcelable { public abstract long getMobileRadioActiveTime(int which); public abstract int getMobileRadioActiveCount(int which); + /** + * Get the total cpu time (in microseconds) this UID had processes executing in userspace. + */ + public abstract long getUserCpuTimeUs(int which); + + /** + * Get the total cpu time (in microseconds) this UID had processes executing kernel syscalls. + */ + public abstract long getSystemCpuTimeUs(int which); + + /** + * Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed. + * @param speedStep the index of the CPU speed. This is not the actual speed of the CPU. + * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT. + * @see BatteryStats#getCpuSpeedSteps() + */ + public abstract long getTimeAtCpuSpeed(int step, int which); + public static abstract class Sensor { /* * FIXME: it's not correct to use this magic value because it @@ -506,15 +524,6 @@ public abstract class BatteryStats implements Parcelable { */ public abstract long getForegroundTime(int which); - /** - * Returns the approximate cpu time (in milliseconds) spent at a certain CPU speed. - * @param speedStep the index of the CPU speed. This is not the actual speed of the - * CPU. - * @param which one of STATS_SINCE_CHARGED, STATS_SINCE_UNPLUGGED, or STATS_CURRENT. - * @see BatteryStats#getCpuSpeedSteps() - */ - public abstract long getTimeAtCpuSpeedStep(int speedStep, int which); - public abstract int countExcessivePowers(); public abstract ExcessivePower getExcessivePower(int i); @@ -3873,6 +3882,16 @@ public abstract class BatteryStats implements Parcelable { } } + final long userCpuTimeUs = u.getUserCpuTimeUs(which); + final long systemCpuTimeUs = u.getSystemCpuTimeUs(which); + if (userCpuTimeUs > 0 || systemCpuTimeUs > 0) { + sb.setLength(0); + sb.append(prefix); + sb.append(" Total cpu time: "); + formatTimeMs(sb, (userCpuTimeUs + systemCpuTimeUs) / 1000); + pw.println(sb.toString()); + } + final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); for (int ipr=processStats.size()-1; ipr>=0; ipr--) { diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index 2b058a884f75..e55ae9928042 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -1005,6 +1005,22 @@ public interface IMountService extends IInterface { } @Override + public long benchmark(String volId) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeString(volId); + mRemote.transact(Stub.TRANSACTION_benchmark, _data, _reply, 0); + _reply.readException(); + return _reply.readLong(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } + + @Override public void partitionPublic(String diskId) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); @@ -1113,6 +1129,22 @@ public interface IMountService extends IInterface { } @Override + public void setDebugFlags(int _flags, int _mask) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeInt(_flags); + _data.writeInt(_mask); + mRemote.transact(Stub.TRANSACTION_setDebugFlags, _data, _reply, 0); + _reply.readException(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } + + @Override public String getPrimaryStorageUuid() throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); @@ -1257,6 +1289,9 @@ public interface IMountService extends IInterface { static final int TRANSACTION_getPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 57; static final int TRANSACTION_setPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 58; + static final int TRANSACTION_benchmark = IBinder.FIRST_CALL_TRANSACTION + 59; + static final int TRANSACTION_setDebugFlags = IBinder.FIRST_CALL_TRANSACTION + 60; + /** * Cast an IBinder object into an IMountService interface, generating a * proxy if needed. @@ -1726,6 +1761,14 @@ public interface IMountService extends IInterface { reply.writeNoException(); return true; } + case TRANSACTION_benchmark: { + data.enforceInterface(DESCRIPTOR); + String volId = data.readString(); + long res = benchmark(volId); + reply.writeNoException(); + reply.writeLong(res); + return true; + } case TRANSACTION_partitionPublic: { data.enforceInterface(DESCRIPTOR); String diskId = data.readString(); @@ -1778,6 +1821,14 @@ public interface IMountService extends IInterface { reply.writeNoException(); return true; } + case TRANSACTION_setDebugFlags: { + data.enforceInterface(DESCRIPTOR); + int _flags = data.readInt(); + int _mask = data.readInt(); + setDebugFlags(_flags, _mask); + reply.writeNoException(); + return true; + } case TRANSACTION_getPrimaryStorageUuid: { data.enforceInterface(DESCRIPTOR); String volumeUuid = getPrimaryStorageUuid(); @@ -2088,6 +2139,7 @@ public interface IMountService extends IInterface { public void mount(String volId) throws RemoteException; public void unmount(String volId) throws RemoteException; public void format(String volId) throws RemoteException; + public long benchmark(String volId) throws RemoteException; public void partitionPublic(String diskId) throws RemoteException; public void partitionPrivate(String diskId) throws RemoteException; @@ -2097,6 +2149,7 @@ public interface IMountService extends IInterface { public void setVolumeUserFlags(String fsUuid, int flags, int mask) throws RemoteException; public void forgetVolume(String fsUuid) throws RemoteException; public void forgetAllVolumes() throws RemoteException; + public void setDebugFlags(int flags, int mask) throws RemoteException; public String getPrimaryStorageUuid() throws RemoteException; public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 8c0bbbf11a76..8ff56f8cc139 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -82,6 +82,9 @@ public class StorageManager { /** {@hide} */ public static final String UUID_PRIMARY_PHYSICAL = "primary_physical"; + /** {@hide} */ + public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0; + private final Context mContext; private final ContentResolver mResolver; @@ -641,6 +644,15 @@ public class StorageManager { } /** {@hide} */ + public long benchmark(String volId) { + try { + return mMountService.benchmark(volId); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + + /** {@hide} */ public void partitionPublic(String diskId) { try { mMountService.partitionPublic(diskId); diff --git a/core/java/android/service/carrier/CarrierConfigService.java b/core/java/android/service/carrier/CarrierConfigService.java index bc42b6baa97e..bf33ad529563 100644 --- a/core/java/android/service/carrier/CarrierConfigService.java +++ b/core/java/android/service/carrier/CarrierConfigService.java @@ -23,14 +23,14 @@ import android.os.PersistableBundle; * A service that sets carrier configuration for telephony services. * <p> * To extend this class, you must declare the service in your manifest file to require the - * {@link android.Manifest.permission#BIND_CARRIER_CONFIG_SERVICE} permission and include an intent + * {@link android.Manifest.permission#BIND_CARRIER_SERVICES} permission and include an intent * filter with the {@link #SERVICE_INTERFACE} action. For example: * </p> * * <pre>{@code * <service android:name=".MyCarrierConfigService" * android:label="@string/service_name" - * android:permission="android.permission.BIND_CARRIER_CONFIG_SERVICE"> + * android:permission="android.permission.BIND_CARRIER_SERVICES"> * <intent-filter> * <action android:name="android.service.carrier.CarrierConfigService" /> * </intent-filter> diff --git a/core/java/android/service/carrier/CarrierMessagingService.java b/core/java/android/service/carrier/CarrierMessagingService.java index d7bf10c2c7d0..f5396a347cf9 100644 --- a/core/java/android/service/carrier/CarrierMessagingService.java +++ b/core/java/android/service/carrier/CarrierMessagingService.java @@ -31,12 +31,12 @@ import java.util.List; * A service that receives calls from the system when new SMS and MMS are * sent or received. * <p>To extend this class, you must declare the service in your manifest file with - * the {@link android.Manifest.permission#BIND_CARRIER_MESSAGING_SERVICE} permission + * the {@link android.Manifest.permission#BIND_CARRIER_SERVICES} permission * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p> * <pre> * <service android:name=".MyMessagingService" * android:label="@string/service_name" - * android:permission="android.permission.BIND_CARRIER_MESSAGING_SERVICE"> + * android:permission="android.permission.BIND_CARRIER_SERVICES"> * <intent-filter> * <action android:name="android.service.carrier.CarrierMessagingService" /> * </intent-filter> diff --git a/core/java/android/text/BidiFormatter.java b/core/java/android/text/BidiFormatter.java index 2a2589ae7ee3..7ea9da1715c4 100644 --- a/core/java/android/text/BidiFormatter.java +++ b/core/java/android/text/BidiFormatter.java @@ -25,7 +25,7 @@ import java.util.Locale; /** * Utility class for formatting text for display in a potentially opposite-directionality context * without garbling. The directionality of the context is set at formatter creation and the - * directionality of the text can be either estimated or passed in when known. + * directionality of the text can be either estimated or passed in when known. * * <p>To support versions lower than {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, * you can use the support library's {@link android.support.v4.text.BidiFormatter} class. @@ -377,9 +377,11 @@ public final class BidiFormatter { * See {@link TextDirectionHeuristics} for pre-defined heuristics. * @param isolate Whether to directionally isolate the string to prevent it from garbling the * content around it - * @return Input string after applying the above processing. + * @return Input string after applying the above processing. {@code null} if {@code str} is + * {@code null}. */ public String unicodeWrap(String str, TextDirectionHeuristic heuristic, boolean isolate) { + if (str == null) return null; final boolean isRtl = heuristic.isRtl(str, 0, str.length()); StringBuilder result = new StringBuilder(); if (getStereoReset() && isolate) { diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index 9782d7202b73..b7d529e0c884 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -40,7 +40,10 @@ import com.android.server.LocalServices; import dalvik.system.VMRuntime; import java.io.File; +import java.io.IOException; import java.util.Arrays; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; /** * Top level factory, used creating all the main WebView implementation classes. @@ -323,15 +326,30 @@ public final class WebViewFactory { long newVmSize = 0L; for (String path : nativeLibs) { + if (path == null || TextUtils.isEmpty(path)) continue; if (DEBUG) Log.d(LOGTAG, "Checking file size of " + path); - if (path == null) continue; File f = new File(path); if (f.exists()) { - long length = f.length(); - if (length > newVmSize) { - newVmSize = length; + newVmSize = Math.max(newVmSize, f.length()); + continue; + } + if (path.contains("!")) { + String[] split = TextUtils.split(path, "!"); + if (split.length == 2) { + try { + ZipFile z = new ZipFile(split[0]); + ZipEntry e = z.getEntry(split[1]); + if (e != null && e.getMethod() == ZipEntry.STORED) { + newVmSize = Math.max(newVmSize, e.getSize()); + continue; + } + } + catch (IOException e) { + Log.e(LOGTAG, "error reading APK file " + split[0] + ", ", e); + } } } + Log.e(LOGTAG, "error sizing load for " + path); } if (DEBUG) { @@ -355,6 +373,27 @@ public final class WebViewFactory { } // throws MissingWebViewPackageException + private static String getLoadFromApkPath(String apkPath, + String[] abiList, + String nativeLibFileName) { + // Search the APK for a native library conforming to a listed ABI. + try { + ZipFile z = new ZipFile(apkPath); + for (String abi : abiList) { + final String entry = "lib/" + abi + "/" + nativeLibFileName; + ZipEntry e = z.getEntry(entry); + if (e != null && e.getMethod() == ZipEntry.STORED) { + // Return a path formatted for dlopen() load from APK. + return apkPath + "!" + entry; + } + } + } catch (IOException e) { + throw new MissingWebViewPackageException(e); + } + return ""; + } + + // throws MissingWebViewPackageException private static String[] getWebViewNativeLibraryPaths() { ApplicationInfo ai = getWebViewApplicationInfo(); final String NATIVE_LIB_FILE_NAME = getWebViewLibrary(ai); @@ -382,8 +421,29 @@ public final class WebViewFactory { path32 = ai.nativeLibraryDir; path64 = ""; } - if (!TextUtils.isEmpty(path32)) path32 += "/" + NATIVE_LIB_FILE_NAME; - if (!TextUtils.isEmpty(path64)) path64 += "/" + NATIVE_LIB_FILE_NAME; + + // Form the full paths to the extracted native libraries. + // If libraries were not extracted, try load from APK paths instead. + if (!TextUtils.isEmpty(path32)) { + path32 += "/" + NATIVE_LIB_FILE_NAME; + File f = new File(path32); + if (!f.exists()) { + path32 = getLoadFromApkPath(ai.sourceDir, + Build.SUPPORTED_32_BIT_ABIS, + NATIVE_LIB_FILE_NAME); + } + } + if (!TextUtils.isEmpty(path64)) { + path64 += "/" + NATIVE_LIB_FILE_NAME; + File f = new File(path64); + if (!f.exists()) { + path64 = getLoadFromApkPath(ai.sourceDir, + Build.SUPPORTED_64_BIT_ABIS, + NATIVE_LIB_FILE_NAME); + } + } + + if (DEBUG) Log.v(LOGTAG, "Native 32-bit lib: " + path32 + ", 64-bit lib: " + path64); return new String[] { path32, path64 }; } diff --git a/core/java/android/widget/DayPickerPagerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java index d271af28b037..8fe8252014fa 100644 --- a/core/java/android/widget/DayPickerPagerAdapter.java +++ b/core/java/android/widget/DayPickerPagerAdapter.java @@ -186,11 +186,12 @@ class DayPickerPagerAdapter extends PagerAdapter { } private int getMonthForPosition(int position) { - return position % MONTHS_IN_YEAR + mMinDate.get(Calendar.MONTH); + return (position + mMinDate.get(Calendar.MONTH)) % MONTHS_IN_YEAR; } private int getYearForPosition(int position) { - return position / MONTHS_IN_YEAR + mMinDate.get(Calendar.YEAR); + final int yearOffset = (position + mMinDate.get(Calendar.MONTH)) / MONTHS_IN_YEAR; + return yearOffset + mMinDate.get(Calendar.YEAR); } private int getPositionForDay(@Nullable Calendar day) { @@ -198,8 +199,8 @@ class DayPickerPagerAdapter extends PagerAdapter { return -1; } - final int yearOffset = (day.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR)); - final int monthOffset = (day.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH)); + final int yearOffset = day.get(Calendar.YEAR) - mMinDate.get(Calendar.YEAR); + final int monthOffset = day.get(Calendar.MONTH) - mMinDate.get(Calendar.MONTH); final int position = yearOffset * MONTHS_IN_YEAR + monthOffset; return position; } diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java index a434317f68ca..dc772fbf63dc 100644 --- a/core/java/android/widget/DayPickerView.java +++ b/core/java/android/widget/DayPickerView.java @@ -176,8 +176,6 @@ class DayPickerView extends ViewGroup { } } }); - - updateButtonVisibility(mViewPager.getCurrentItem()); } private void updateButtonVisibility(int position) { @@ -346,6 +344,8 @@ class DayPickerView extends ViewGroup { // Changing the min/max date changes the selection position since we // don't really have stable IDs. Jumps immediately to the new position. setDate(mSelectedDay.getTimeInMillis(), false, false); + + updateButtonVisibility(mViewPager.getCurrentItem()); } /** diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 1d6f6dceae08..c829783e685d 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -3426,9 +3426,13 @@ public class Editor { protected void updateDrawable() { final int offset = getCurrentCursorOffset(); final boolean isRtlCharAtOffset = mTextView.getLayout().isRtlCharAt(offset); + final Drawable oldDrawable = mDrawable; mDrawable = isRtlCharAtOffset ? mDrawableRtl : mDrawableLtr; mHotspotX = getHotspotX(mDrawable, isRtlCharAtOffset); mHorizontalGravity = getHorizontalGravity(isRtlCharAtOffset); + if (oldDrawable != mDrawable) { + postInvalidate(); + } } protected abstract int getHotspotX(Drawable drawable, boolean isRtlRun); diff --git a/core/java/com/android/internal/app/PlatLogoActivity.java b/core/java/com/android/internal/app/PlatLogoActivity.java index 80bc5feac4c4..49230c107c3e 100644 --- a/core/java/com/android/internal/app/PlatLogoActivity.java +++ b/core/java/com/android/internal/app/PlatLogoActivity.java @@ -45,33 +45,11 @@ import android.widget.FrameLayout; import android.widget.ImageView; public class PlatLogoActivity extends Activity { - final static int[] FLAVORS = { - 0xFF9C27B0, 0xFFBA68C8, // grape - 0xFFFF9800, 0xFFFFB74D, // orange - 0xFFF06292, 0xFFF8BBD0, // bubblegum - 0xFFAFB42B, 0xFFCDDC39, // lime - 0xFFFFEB3B, 0xFFFFF176, // lemon - 0xFF795548, 0xFFA1887F, // mystery flavor - }; FrameLayout mLayout; int mTapCount; int mKeyCount; PathInterpolator mInterpolator = new PathInterpolator(0f, 0f, 0.5f, 1f); - static int newColorIndex() { - return 2*((int) (Math.random()*FLAVORS.length/2)); - } - - Drawable makeRipple() { - final int idx = newColorIndex(); - final ShapeDrawable popbg = new ShapeDrawable(new OvalShape()); - popbg.getPaint().setColor(FLAVORS[idx]); - final RippleDrawable ripple = new RippleDrawable( - ColorStateList.valueOf(FLAVORS[idx+1]), - popbg, null); - return ripple; - } - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -87,120 +65,62 @@ public class PlatLogoActivity extends Activity { final int size = (int) (Math.min(Math.min(dm.widthPixels, dm.heightPixels), 600*dp) - 100*dp); - final View stick = new View(this) { - Paint mPaint = new Paint(); - Path mShadow = new Path(); - - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - setWillNotDraw(false); - setOutlineProvider(new ViewOutlineProvider() { - @Override - public void getOutline(View view, Outline outline) { - outline.setRect(0, getHeight() / 2, getWidth(), getHeight()); - } - }); - } + final View im = new View(this); + im.setTranslationZ(20); + im.setScaleX(0.5f); + im.setScaleY(0.5f); + im.setAlpha(0f); + im.setOutlineProvider(new ViewOutlineProvider() { @Override - public void onDraw(Canvas c) { - final int w = c.getWidth(); - final int h = c.getHeight() / 2; - c.translate(0, h); - final GradientDrawable g = new GradientDrawable(); - g.setOrientation(GradientDrawable.Orientation.LEFT_RIGHT); - g.setGradientCenter(w * 0.75f, 0); - g.setColors(new int[] { 0xFFFFFFFF, 0xFFAAAAAA }); - g.setBounds(0, 0, w, h); - g.draw(c); - mPaint.setColor(0xFFAAAAAA); - mShadow.reset(); - mShadow.moveTo(0,0); - mShadow.lineTo(w, 0); - mShadow.lineTo(w, size/2 + 1.5f*w); - mShadow.lineTo(0, size/2); - mShadow.close(); - c.drawPath(mShadow, mPaint); + public void getOutline(View view, Outline outline) { + final int pad = (int) (8*dp); + outline.setOval(pad, pad, view.getWidth()-pad, view.getHeight()-pad); } - }; - mLayout.addView(stick, new FrameLayout.LayoutParams((int) (32 * dp), - ViewGroup.LayoutParams.MATCH_PARENT, Gravity.CENTER_HORIZONTAL)); - stick.setAlpha(0f); - - final ImageView im = new ImageView(this); - im.setTranslationZ(20); - im.setScaleX(0); - im.setScaleY(0); + }); final Drawable platlogo = getDrawable(com.android.internal.R.drawable.platlogo); - platlogo.setAlpha(0); - im.setImageDrawable(platlogo); - im.setBackground(makeRipple()); + im.setBackground(new RippleDrawable( + ColorStateList.valueOf(0xFFFFFFFF), + platlogo, + null)); im.setClickable(true); - final ShapeDrawable highlight = new ShapeDrawable(new OvalShape()); - highlight.getPaint().setColor(0x10FFFFFF); - highlight.setBounds((int)(size*.15f), (int)(size*.15f), - (int)(size*.6f), (int)(size*.6f)); - im.getOverlay().add(highlight); im.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if (mTapCount == 0) { - im.animate() - .translationZ(40) - .scaleX(1) - .scaleY(1) - .setInterpolator(mInterpolator) - .setDuration(700) - .setStartDelay(500) - .start(); - - final ObjectAnimator a = ObjectAnimator.ofInt(platlogo, "alpha", 0, 255); - a.setInterpolator(mInterpolator); - a.setStartDelay(1000); - a.start(); - - stick.animate() - .translationZ(20) - .alpha(1) - .setInterpolator(mInterpolator) - .setDuration(700) - .setStartDelay(750) - .start(); - - im.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - if (mTapCount < 5) return false; - - final ContentResolver cr = getContentResolver(); - if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0) - == 0) { - // For posterity: the moment this user unlocked the easter egg + im.setOnLongClickListener(new View.OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (mTapCount < 5) return false; + + final ContentResolver cr = getContentResolver(); + if (Settings.System.getLong(cr, Settings.System.EGG_MODE, 0) + == 0) { + // For posterity: the moment this user unlocked the easter egg + try { Settings.System.putLong(cr, Settings.System.EGG_MODE, System.currentTimeMillis()); + } catch (RuntimeException e) { + Log.e("PlatLogoActivity", "Can't write settings", e); } - im.post(new Runnable() { - @Override - public void run() { - try { - startActivity(new Intent(Intent.ACTION_MAIN) - .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_CLEAR_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) - .addCategory("com.android.internal.category.PLATLOGO")); - } catch (ActivityNotFoundException ex) { - Log.e("PlatLogoActivity", "No more eggs."); - } - finish(); - } - }); - return true; } - }); - } else { - im.setBackground(makeRipple()); - } + im.post(new Runnable() { + @Override + public void run() { + try { + startActivity(new Intent(Intent.ACTION_MAIN) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) + .addCategory("com.android.internal.category.PLATLOGO")); + } catch (ActivityNotFoundException ex) { + Log.e("PlatLogoActivity", "No more eggs."); + } + finish(); + } + }); + return true; + } + }); mTapCount++; } }); @@ -229,7 +149,7 @@ public class PlatLogoActivity extends Activity { mLayout.addView(im, new FrameLayout.LayoutParams(size, size, Gravity.CENTER)); - im.animate().scaleX(0.3f).scaleY(0.3f) + im.animate().scaleX(1f).scaleY(1f).alpha(1f) .setInterpolator(mInterpolator) .setDuration(500) .setStartDelay(800) diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index a53d46c8cead..fbe87c5148be 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -615,6 +615,7 @@ public final class BatteryStatsHelper { mStatsType); if (bs.sumPower() > 0) { aggregateSippers(bs, mBluetoothSippers, "Bluetooth"); + mUsageList.add(bs); } } @@ -742,7 +743,6 @@ public final class BatteryStatsHelper { parcel.setDataPosition(0); BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR .createFromParcel(parcel); - stats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED); return stats; } catch (IOException e) { Log.w(TAG, "Unable to read statistics stream", e); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index c2212f7fed0d..eaca43bd7d01 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -105,7 +105,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 125 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 126 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -132,6 +132,9 @@ public final class BatteryStatsImpl extends BatteryStats { private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader(); private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats(); + private final KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader(); + private final KernelCpuSpeedReader mKernelCpuSpeedReader = new KernelCpuSpeedReader(); + public interface BatteryCallback { public void batteryNeedsCpuUpdate(); public void batteryPowerChanged(boolean onBattery); @@ -794,20 +797,6 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public static class SamplingCounter extends Counter { - SamplingCounter(TimeBase timeBase, Parcel in) { - super(timeBase, in); - } - - SamplingCounter(TimeBase timeBase) { - super(timeBase); - } - - public void addCountAtomic(long count) { - mCount.addAndGet((int)count); - } - } - public static class LongSamplingCounter extends LongCounter implements TimeBaseObs { final TimeBase mTimeBase; long mCount; @@ -2918,8 +2907,7 @@ public final class BatteryStatsImpl extends BatteryStats { public void finishAddingCpuLocked(int perc, int remainUTime, int remainSTtime, int totalUTime, int totalSTime, int statUserTime, int statSystemTime, - int statIOWaitTime, int statIrqTime, int statSoftIrqTime, int statIdleTime, - long[] cpuSpeedTimes) { + int statIOWaitTime, int statIrqTime, int statSoftIrqTime, int statIdleTime) { if (DEBUG) Slog.d(TAG, "Adding cpu: tuser=" + totalUTime + " tsys=" + totalSTime + " user=" + statUserTime + " sys=" + statSystemTime + " io=" + statIOWaitTime + " irq=" + statIrqTime @@ -2959,7 +2947,7 @@ public final class BatteryStatsImpl extends BatteryStats { remainSTtime -= mySTime; num--; Uid.Proc proc = uid.getProcessStatsLocked("*wakelock*"); - proc.addCpuTimeLocked(myUTime, mySTime, cpuSpeedTimes); + proc.addCpuTimeLocked(myUTime, mySTime); } } } @@ -2970,7 +2958,7 @@ public final class BatteryStatsImpl extends BatteryStats { Uid uid = getUidStatsLocked(Process.SYSTEM_UID); if (uid != null) { Uid.Proc proc = uid.getProcessStatsLocked("*lost*"); - proc.addCpuTimeLocked(remainUTime, remainSTtime, cpuSpeedTimes); + proc.addCpuTimeLocked(remainUTime, remainSTtime); } } } @@ -4398,6 +4386,10 @@ public final class BatteryStatsImpl extends BatteryStats { long mCurStepUserTime; long mCurStepSystemTime; + LongSamplingCounter mUserCpuTime = new LongSamplingCounter(mOnBatteryTimeBase); + LongSamplingCounter mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase); + LongSamplingCounter[] mSpeedBins; + /** * The statistics we have collected for this uid's wake locks. */ @@ -4455,6 +4447,7 @@ public final class BatteryStatsImpl extends BatteryStats { mWifiMulticastTimer = new StopwatchTimer(Uid.this, WIFI_MULTICAST_ENABLED, mWifiMulticastTimers, mOnBatteryTimeBase); mProcessStateTimer = new StopwatchTimer[NUM_PROCESS_STATE]; + mSpeedBins = new LongSamplingCounter[getCpuSpeedSteps()]; } @Override @@ -4925,6 +4918,26 @@ public final class BatteryStatsImpl extends BatteryStats { } @Override + public long getUserCpuTimeUs(int which) { + return mUserCpuTime.getCountLocked(which); + } + + @Override + public long getSystemCpuTimeUs(int which) { + return mSystemCpuTime.getCountLocked(which); + } + + @Override + public long getTimeAtCpuSpeed(int step, int which) { + if (step >= 0 && step < mSpeedBins.length) { + if (mSpeedBins[step] != null) { + return mSpeedBins[step].getCountLocked(which); + } + } + return 0; + } + + @Override public long getWifiControllerActivity(int type, int which) { if (type >= 0 && type < NUM_CONTROLLER_ACTIVITY_TYPES && mWifiControllerTime[type] != null) { @@ -5026,6 +5039,15 @@ public final class BatteryStatsImpl extends BatteryStats { } } + mUserCpuTime.reset(false); + mSystemCpuTime.reset(false); + for (int i = 0; i < mSpeedBins.length; i++) { + LongSamplingCounter c = mSpeedBins[i]; + if (c != null) { + c.reset(false); + } + } + final ArrayMap<String, Wakelock> wakeStats = mWakelockStats.getMap(); for (int iw=wakeStats.size()-1; iw>=0; iw--) { Wakelock wl = wakeStats.valueAt(iw); @@ -5159,6 +5181,15 @@ public final class BatteryStatsImpl extends BatteryStats { } } mPids.clear(); + + mUserCpuTime.detach(); + mSystemCpuTime.detach(); + for (int i = 0; i < mSpeedBins.length; i++) { + LongSamplingCounter c = mSpeedBins[i]; + if (c != null) { + c.detach(); + } + } } return !active; @@ -5317,6 +5348,20 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(0); } } + + mUserCpuTime.writeToParcel(out); + mSystemCpuTime.writeToParcel(out); + + out.writeInt(mSpeedBins.length); + for (int i = 0; i < mSpeedBins.length; i++) { + LongSamplingCounter c = mSpeedBins[i]; + if (c != null) { + out.writeInt(1); + c.writeToParcel(out); + } else { + out.writeInt(0); + } + } } void readFromParcelLocked(TimeBase timeBase, TimeBase screenOffTimeBase, Parcel in) { @@ -5482,6 +5527,18 @@ public final class BatteryStatsImpl extends BatteryStats { mBluetoothControllerTime[i] = null; } } + + mUserCpuTime = new LongSamplingCounter(mOnBatteryTimeBase, in); + mSystemCpuTime = new LongSamplingCounter(mOnBatteryTimeBase, in); + + int bins = in.readInt(); + int steps = getCpuSpeedSteps(); + mSpeedBins = new LongSamplingCounter[bins >= steps ? bins : steps]; + for (int i = 0; i < bins; i++) { + if (in.readInt() != 0) { + mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase, in); + } + } } /** @@ -5762,14 +5819,11 @@ public final class BatteryStatsImpl extends BatteryStats { */ int mProcessState = PROCESS_STATE_NONE; - SamplingCounter[] mSpeedBins; - ArrayList<ExcessivePower> mExcessivePower; Proc(String name) { mName = name; mOnBatteryTimeBase.add(this); - mSpeedBins = new SamplingCounter[getCpuSpeedSteps()]; } public void onTimeStarted(long elapsedRealtime, long baseUptime, long baseRealtime) { @@ -5791,25 +5845,12 @@ public final class BatteryStatsImpl extends BatteryStats { mLoadedStarts = mLoadedNumCrashes = mLoadedNumAnrs = 0; mUnpluggedUserTime = mUnpluggedSystemTime = mUnpluggedForegroundTime = 0; mUnpluggedStarts = mUnpluggedNumCrashes = mUnpluggedNumAnrs = 0; - for (int i = 0; i < mSpeedBins.length; i++) { - SamplingCounter c = mSpeedBins[i]; - if (c != null) { - c.reset(false); - } - } mExcessivePower = null; } void detach() { mActive = false; mOnBatteryTimeBase.remove(this); - for (int i = 0; i < mSpeedBins.length; i++) { - SamplingCounter c = mSpeedBins[i]; - if (c != null) { - mOnBatteryTimeBase.remove(c); - mSpeedBins[i] = null; - } - } } public int countExcessivePowers() { @@ -5903,18 +5944,6 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(mUnpluggedStarts); out.writeInt(mUnpluggedNumCrashes); out.writeInt(mUnpluggedNumAnrs); - - out.writeInt(mSpeedBins.length); - for (int i = 0; i < mSpeedBins.length; i++) { - SamplingCounter c = mSpeedBins[i]; - if (c != null) { - out.writeInt(1); - c.writeToParcel(out); - } else { - out.writeInt(0); - } - } - writeExcessivePowerToParcelLocked(out); } @@ -5937,39 +5966,12 @@ public final class BatteryStatsImpl extends BatteryStats { mUnpluggedStarts = in.readInt(); mUnpluggedNumCrashes = in.readInt(); mUnpluggedNumAnrs = in.readInt(); - - int bins = in.readInt(); - int steps = getCpuSpeedSteps(); - mSpeedBins = new SamplingCounter[bins >= steps ? bins : steps]; - for (int i = 0; i < bins; i++) { - if (in.readInt() != 0) { - mSpeedBins[i] = new SamplingCounter(mOnBatteryTimeBase, in); - } - } - readExcessivePowerFromParcelLocked(in); } - public BatteryStatsImpl getBatteryStats() { - return BatteryStatsImpl.this; - } - - public void addCpuTimeLocked(int utime, int stime, long[] speedStepBins) { + public void addCpuTimeLocked(int utime, int stime) { mUserTime += utime; - mCurStepUserTime += utime; mSystemTime += stime; - mCurStepSystemTime += stime; - - for (int i = 0; i < mSpeedBins.length && i < speedStepBins.length; i++) { - long amt = speedStepBins[i]; - if (amt != 0) { - SamplingCounter c = mSpeedBins[i]; - if (c == null) { - mSpeedBins[i] = c = new SamplingCounter(mOnBatteryTimeBase); - } - c.addCountAtomic(speedStepBins[i]); - } - } } public void addForegroundTimeLocked(long ttime) { @@ -6058,16 +6060,6 @@ public final class BatteryStatsImpl extends BatteryStats { } return val; } - - @Override - public long getTimeAtCpuSpeedStep(int speedStep, int which) { - if (speedStep < mSpeedBins.length) { - SamplingCounter c = mSpeedBins[speedStep]; - return c != null ? c.getCountLocked(which) : 0; - } else { - return 0; - } - } } /** @@ -7729,7 +7721,7 @@ public final class BatteryStatsImpl extends BatteryStats { * @param info The energy information from the bluetooth controller. */ public void updateBluetoothStateLocked(@Nullable final BluetoothActivityEnergyInfo info) { - if (info != null && mOnBatteryInternal && false) { + if (info != null && mOnBatteryInternal) { mHasBluetoothEnergyReporting = true; mBluetoothActivityCounters[CONTROLLER_RX_TIME].addCountLocked( info.getControllerRxTimeMillis()); @@ -7785,6 +7777,32 @@ public final class BatteryStatsImpl extends BatteryStats { } } + /** + * Read and distribute CPU usage across apps. + */ + public void updateCpuTimeLocked(boolean firstTime) { + final int cpuSpeedSteps = getCpuSpeedSteps(); + final long[] cpuSpeeds = mKernelCpuSpeedReader.readDelta(); + KernelUidCpuTimeReader.Callback callback = null; + if (mOnBatteryInternal && !firstTime) { + callback = new KernelUidCpuTimeReader.Callback() { + @Override + public void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs) { + final Uid u = getUidStatsLocked(mapUid(uid)); + u.mUserCpuTime.addCountLocked(userTimeUs); + u.mSystemCpuTime.addCountLocked(systemTimeUs); + for (int i = 0; i < cpuSpeedSteps; i++) { + if (u.mSpeedBins[i] == null) { + u.mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase); + } + u.mSpeedBins[i].addCountLocked(cpuSpeeds[i]); + } + } + }; + } + mKernelUidCpuTimeReader.readDelta(callback); + } + boolean setChargingLocked(boolean charging) { if (mCharging != charging) { mCharging = charging; @@ -8407,6 +8425,7 @@ public final class BatteryStatsImpl extends BatteryStats { * Remove the statistics object for a particular uid. */ public void removeUidStatsLocked(int uid) { + mKernelUidCpuTimeReader.removeUid(uid); mUidStats.remove(uid); } @@ -8440,58 +8459,6 @@ public final class BatteryStatsImpl extends BatteryStats { return u.getServiceStatsLocked(pkg, name); } - /** - * Massage data to distribute any reasonable work down to more specific - * owners. Must only be called on a dead BatteryStats object! - */ - public void distributeWorkLocked(int which) { - // Aggregate all CPU time associated with WIFI. - Uid wifiUid = mUidStats.get(Process.WIFI_UID); - if (wifiUid != null) { - long uSecTime = computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which); - for (int ip=wifiUid.mProcessStats.size()-1; ip>=0; ip--) { - Uid.Proc proc = wifiUid.mProcessStats.valueAt(ip); - long totalRunningTime = getGlobalWifiRunningTime(uSecTime, which); - for (int i=0; i<mUidStats.size(); i++) { - Uid uid = mUidStats.valueAt(i); - if (uid.mUid != Process.WIFI_UID) { - long uidRunningTime = uid.getWifiRunningTime(uSecTime, which); - if (uidRunningTime > 0) { - Uid.Proc uidProc = uid.getProcessStatsLocked("*wifi*"); - long time = proc.getUserTime(which); - time = (time*uidRunningTime)/totalRunningTime; - uidProc.mUserTime += time; - proc.mUserTime -= time; - time = proc.getSystemTime(which); - time = (time*uidRunningTime)/totalRunningTime; - uidProc.mSystemTime += time; - proc.mSystemTime -= time; - time = proc.getForegroundTime(which); - time = (time*uidRunningTime)/totalRunningTime; - uidProc.mForegroundTime += time; - proc.mForegroundTime -= time; - for (int sb=0; sb<proc.mSpeedBins.length; sb++) { - SamplingCounter sc = proc.mSpeedBins[sb]; - if (sc != null) { - time = sc.getCountLocked(which); - time = (time*uidRunningTime)/totalRunningTime; - SamplingCounter uidSc = uidProc.mSpeedBins[sb]; - if (uidSc == null) { - uidSc = new SamplingCounter(mOnBatteryTimeBase); - uidProc.mSpeedBins[sb] = uidSc; - } - uidSc.mCount.addAndGet((int)time); - sc.mCount.addAndGet((int)-time); - } - } - totalRunningTime -= uidRunningTime; - } - } - } - } - } - } - public void shutdownLocked() { recordShutdownLocked(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); writeSyncLocked(); @@ -8950,6 +8917,23 @@ public final class BatteryStatsImpl extends BatteryStats { u.mMobileRadioActiveCount.readSummaryFromParcelLocked(in); } + u.mUserCpuTime.readSummaryFromParcelLocked(in); + u.mSystemCpuTime.readSummaryFromParcelLocked(in); + + int NSB = in.readInt(); + if (NSB > 100) { + Slog.w(TAG, "File corrupt: too many speed bins " + NSB); + return; + } + + u.mSpeedBins = new LongSamplingCounter[NSB]; + for (int i=0; i<NSB; i++) { + if (in.readInt() != 0) { + u.mSpeedBins[i] = new LongSamplingCounter(mOnBatteryTimeBase); + u.mSpeedBins[i].readSummaryFromParcelLocked(in); + } + } + int NW = in.readInt(); if (NW > 100) { Slog.w(TAG, "File corrupt: too many wake locks " + NW); @@ -9007,18 +8991,6 @@ public final class BatteryStatsImpl extends BatteryStats { p.mStarts = p.mLoadedStarts = in.readInt(); p.mNumCrashes = p.mLoadedNumCrashes = in.readInt(); p.mNumAnrs = p.mLoadedNumAnrs = in.readInt(); - int NSB = in.readInt(); - if (NSB > 100) { - Slog.w(TAG, "File corrupt: too many speed bins " + NSB); - return; - } - p.mSpeedBins = new SamplingCounter[NSB]; - for (int i=0; i<NSB; i++) { - if (in.readInt() != 0) { - p.mSpeedBins[i] = new SamplingCounter(mOnBatteryTimeBase); - p.mSpeedBins[i].readSummaryFromParcelLocked(in); - } - } if (!p.readExcessivePowerFromParcelLocked(in)) { return; } @@ -9278,6 +9250,20 @@ public final class BatteryStatsImpl extends BatteryStats { u.mMobileRadioActiveCount.writeSummaryFromParcelLocked(out); } + u.mUserCpuTime.writeSummaryFromParcelLocked(out); + u.mSystemCpuTime.writeSummaryFromParcelLocked(out); + + out.writeInt(u.mSpeedBins.length); + for (int i = 0; i < u.mSpeedBins.length; i++) { + LongSamplingCounter speedBin = u.mSpeedBins[i]; + if (speedBin != null) { + out.writeInt(1); + speedBin.writeSummaryFromParcelLocked(out); + } else { + out.writeInt(0); + } + } + final ArrayMap<String, Uid.Wakelock> wakeStats = u.mWakelockStats.getMap(); int NW = wakeStats.size(); out.writeInt(NW); @@ -9344,16 +9330,6 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(ps.mStarts); out.writeInt(ps.mNumCrashes); out.writeInt(ps.mNumAnrs); - final int N = ps.mSpeedBins.length; - out.writeInt(N); - for (int i=0; i<N; i++) { - if (ps.mSpeedBins[i] != null) { - out.writeInt(1); - ps.mSpeedBins[i].writeSummaryFromParcelLocked(out); - } else { - out.writeInt(0); - } - } ps.writeExcessivePowerToParcelLocked(out); } diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index 6c3f958548b1..a3ef612077bf 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -44,55 +44,57 @@ public class CpuPowerCalculator extends PowerCalculator { long rawUptimeUs, int statsType) { final int speedSteps = mSpeedStepTimes.length; + long totalTimeAtSpeeds = 0; + for (int step = 0; step < speedSteps; step++) { + mSpeedStepTimes[step] = u.getTimeAtCpuSpeed(step, statsType); + totalTimeAtSpeeds += mSpeedStepTimes[step]; + } + totalTimeAtSpeeds = Math.max(totalTimeAtSpeeds, 1); + + app.cpuTimeMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; + if (DEBUG && app.cpuTimeMs != 0) { + Log.d(TAG, "UID " + u.getUid() + ": CPU time " + app.cpuTimeMs + " ms"); + } + + double cpuPowerMaMs = 0; + for (int step = 0; step < speedSteps; step++) { + final double ratio = (double) mSpeedStepTimes[step] / totalTimeAtSpeeds; + final double cpuSpeedStepPower = ratio * app.cpuTimeMs * mPowerCpuNormal[step]; + if (DEBUG && ratio != 0) { + Log.d(TAG, "UID " + u.getUid() + ": CPU step #" + + step + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power=" + + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000))); + } + cpuPowerMaMs += cpuSpeedStepPower; + } + + if (DEBUG && cpuPowerMaMs != 0) { + Log.d(TAG, "UID " + u.getUid() + ": cpu total power=" + + BatteryStatsHelper.makemAh(cpuPowerMaMs / (60 * 60 * 1000))); + } + // Keep track of the package with highest drain. double highestDrain = 0; + app.cpuFgTimeMs = 0; final ArrayMap<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); final int processStatsCount = processStats.size(); for (int i = 0; i < processStatsCount; i++) { final BatteryStats.Uid.Proc ps = processStats.valueAt(i); final String processName = processStats.keyAt(i); - app.cpuFgTimeMs += ps.getForegroundTime(statsType); - final long totalCpuTime = ps.getUserTime(statsType) + ps.getSystemTime(statsType); - app.cpuTimeMs += totalCpuTime; - - // Calculate the total CPU time spent at the various speed steps. - long totalTimeAtSpeeds = 0; - for (int step = 0; step < speedSteps; step++) { - mSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, statsType); - totalTimeAtSpeeds += mSpeedStepTimes[step]; - } - totalTimeAtSpeeds = Math.max(totalTimeAtSpeeds, 1); - - // Then compute the ratio of time spent at each speed and figure out - // the total power consumption. - double cpuPower = 0; - for (int step = 0; step < speedSteps; step++) { - final double ratio = (double) mSpeedStepTimes[step] / totalTimeAtSpeeds; - final double cpuSpeedStepPower = ratio * totalCpuTime * mPowerCpuNormal[step]; - if (DEBUG && ratio != 0) { - Log.d(TAG, "UID " + u.getUid() + ": CPU step #" - + step + " ratio=" + BatteryStatsHelper.makemAh(ratio) + " power=" - + BatteryStatsHelper.makemAh(cpuSpeedStepPower / (60 * 60 * 1000))); - } - cpuPower += cpuSpeedStepPower; - } - if (DEBUG && cpuPower != 0) { - Log.d(TAG, String.format("process %s, cpu power=%s", - processName, BatteryStatsHelper.makemAh(cpuPower / (60 * 60 * 1000)))); - } - app.cpuPowerMah += cpuPower; + final long costValue = ps.getUserTime(statsType) + ps.getSystemTime(statsType) + + ps.getForegroundTime(statsType); // Each App can have multiple packages and with multiple running processes. // Keep track of the package who's process has the highest drain. if (app.packageWithHighestDrain == null || app.packageWithHighestDrain.startsWith("*")) { - highestDrain = cpuPower; + highestDrain = costValue; app.packageWithHighestDrain = processName; - } else if (highestDrain < cpuPower && !processName.startsWith("*")) { - highestDrain = cpuPower; + } else if (highestDrain < costValue && !processName.startsWith("*")) { + highestDrain = costValue; app.packageWithHighestDrain = processName; } } @@ -108,6 +110,6 @@ public class CpuPowerCalculator extends PowerCalculator { } // Convert the CPU power to mAh - app.cpuPowerMah /= (60 * 60 * 1000); + app.cpuPowerMah = cpuPowerMaMs / (60 * 60 * 1000); } } diff --git a/core/java/com/android/internal/os/KernelCpuSpeedReader.java b/core/java/com/android/internal/os/KernelCpuSpeedReader.java new file mode 100644 index 000000000000..c30df28c5c1f --- /dev/null +++ b/core/java/com/android/internal/os/KernelCpuSpeedReader.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.os; + +import android.text.TextUtils; +import android.util.Slog; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.Arrays; + +/** + * Reads CPU time spent at various frequencies and provides a delta from the last call to + * {@link #readDelta}. Each line in the proc file has the format: + * + * freq time + * + * where time is measured in 1/100 seconds. + */ +public class KernelCpuSpeedReader { + private static final String TAG = "KernelCpuSpeedReader"; + private static final String sProcFile = + "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state"; + private static final int MAX_SPEEDS = 60; + + private long[] mLastSpeedTimes = new long[MAX_SPEEDS]; + private long[] mDeltaSpeedTimes = new long[MAX_SPEEDS]; + + /** + * The returned array is modified in subsequent calls to {@link #readDelta}. + * @return The time (in milliseconds) spent at different cpu speeds since the last call to + * {@link #readDelta}. + */ + public long[] readDelta() { + try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) { + TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' '); + String line; + int speedIndex = 0; + while ((line = reader.readLine()) != null) { + splitter.setString(line); + Long.parseLong(splitter.next()); + + // The proc file reports time in 1/100 sec, so convert to milliseconds. + long time = Long.parseLong(splitter.next()) * 10; + mDeltaSpeedTimes[speedIndex] = time - mLastSpeedTimes[speedIndex]; + mLastSpeedTimes[speedIndex] = time; + speedIndex++; + } + } catch (IOException e) { + Slog.e(TAG, "Failed to read cpu-freq", e); + Arrays.fill(mDeltaSpeedTimes, 0); + } + return mDeltaSpeedTimes; + } +} diff --git a/core/java/com/android/internal/os/KernelUidCpuTimeReader.java b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java new file mode 100644 index 000000000000..b2363786029c --- /dev/null +++ b/core/java/com/android/internal/os/KernelUidCpuTimeReader.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.os; + +import android.annotation.Nullable; +import android.text.TextUtils; +import android.util.Slog; +import android.util.SparseLongArray; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +/** + * Reads /proc/uid_cputime/show_uid_stat which has the line format: + * + * uid: user_time_micro_seconds system_time_micro_seconds + * + * This provides the time a UID's processes spent executing in user-space and kernel-space. + * The file contains a monotonically increasing count of time for a single boot. This class + * maintains the previous results of a call to {@link #readDelta} in order to provide a proper + * delta. + */ +public class KernelUidCpuTimeReader { + private static final String TAG = "KernelUidCpuTimeReader"; + private static final String sProcFile = "/proc/uid_cputime/show_uid_stat"; + private static final String sRemoveUidProcFile = "/proc/uid_cputime/remove_uid_range"; + + /** + * Callback interface for processing each line of the proc file. + */ + public interface Callback { + void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs); + } + + private SparseLongArray mLastUserTimeUs = new SparseLongArray(); + private SparseLongArray mLastSystemTimeUs = new SparseLongArray(); + + /** + * Reads the proc file, calling into the callback with a delta of time for each UID. + * @param callback The callback to invoke for each line of the proc file. If null, + * the data is consumed and subsequent calls to readDelta will provide + * a fresh delta. + */ + public void readDelta(@Nullable Callback callback) { + try (BufferedReader reader = new BufferedReader(new FileReader(sProcFile))) { + TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(' '); + String line; + while ((line = reader.readLine()) != null) { + splitter.setString(line); + final String uidStr = splitter.next(); + final int uid = Integer.parseInt(uidStr.substring(0, uidStr.length() - 1), 10); + final long userTimeUs = Long.parseLong(splitter.next(), 10); + final long systemTimeUs = Long.parseLong(splitter.next(), 10); + + if (callback != null) { + long userTimeDeltaUs = userTimeUs; + long systemTimeDeltaUs = systemTimeUs; + int index = mLastUserTimeUs.indexOfKey(uid); + if (index >= 0) { + userTimeDeltaUs -= mLastUserTimeUs.valueAt(index); + systemTimeDeltaUs -= mLastSystemTimeUs.valueAt(index); + + if (userTimeDeltaUs < 0 || systemTimeDeltaUs < 0) { + // The UID must have been removed from accounting, then added back. + userTimeDeltaUs = userTimeUs; + systemTimeDeltaUs = systemTimeUs; + } + } + + if (userTimeDeltaUs != 0 || systemTimeDeltaUs != 0) { + callback.onUidCpuTime(uid, userTimeDeltaUs, systemTimeDeltaUs); + } + } + mLastUserTimeUs.put(uid, userTimeUs); + mLastSystemTimeUs.put(uid, systemTimeUs); + } + } catch (IOException e) { + Slog.e(TAG, "Failed to read uid_cputime", e); + } + } + + /** + * Removes the UID from the kernel module and from internal accounting data. + * @param uid The UID to remove. + */ + public void removeUid(int uid) { + int index = mLastUserTimeUs.indexOfKey(uid); + if (index >= 0) { + mLastUserTimeUs.removeAt(index); + mLastSystemTimeUs.removeAt(index); + } + + try (FileWriter writer = new FileWriter(sRemoveUidProcFile)) { + writer.write(Integer.toString(uid) + "-" + Integer.toString(uid)); + writer.flush(); + } catch (IOException e) { + Slog.e(TAG, "failed to remove uid from uid_cputime module", e); + } + } +} diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java index 29830473255f..8393e2af87c1 100644 --- a/core/java/com/android/internal/os/ProcessCpuTracker.java +++ b/core/java/com/android/internal/os/ProcessCpuTracker.java @@ -170,21 +170,6 @@ public class ProcessCpuTracker { private byte[] mBuffer = new byte[4096]; - /** - * The time in microseconds that the CPU has been running at each speed. - */ - private long[] mCpuSpeedTimes; - - /** - * The relative time in microseconds that the CPU has been running at each speed. - */ - private long[] mRelCpuSpeedTimes; - - /** - * The different speeds that the CPU can be running at. - */ - private long[] mCpuSpeeds; - public static class Stats { public final int pid; public final int uid; @@ -590,70 +575,6 @@ public class ProcessCpuTracker { } } - /** - * Returns the delta time (in clock ticks, or 1/100 sec) spent at each CPU - * speed, since the last call to this method. If this is the first call, it - * will return 1 for each value. - */ - public long[] getLastCpuSpeedTimes() { - if (mCpuSpeedTimes == null) { - mCpuSpeedTimes = getCpuSpeedTimes(null); - mRelCpuSpeedTimes = new long[mCpuSpeedTimes.length]; - for (int i = 0; i < mCpuSpeedTimes.length; i++) { - mRelCpuSpeedTimes[i] = 1; // Initialize - } - } else { - getCpuSpeedTimes(mRelCpuSpeedTimes); - for (int i = 0; i < mCpuSpeedTimes.length; i++) { - long temp = mRelCpuSpeedTimes[i]; - mRelCpuSpeedTimes[i] -= mCpuSpeedTimes[i]; - mCpuSpeedTimes[i] = temp; - } - } - return mRelCpuSpeedTimes; - } - - private long[] getCpuSpeedTimes(long[] out) { - long[] tempTimes = out; - long[] tempSpeeds = mCpuSpeeds; - final int MAX_SPEEDS = 60; - if (out == null) { - tempTimes = new long[MAX_SPEEDS]; // Hopefully no more than that - tempSpeeds = new long[MAX_SPEEDS]; - } - int speed = 0; - String file = readFile("/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state", '\0'); - // Note: file may be null on kernels without cpufreq (i.e. the emulator's) - if (file != null) { - StringTokenizer st = new StringTokenizer(file, "\n "); - while (st.hasMoreElements()) { - String token = st.nextToken(); - try { - long val = Long.parseLong(token); - tempSpeeds[speed] = val; - token = st.nextToken(); - val = Long.parseLong(token); - tempTimes[speed] = val; - speed++; - if (speed == MAX_SPEEDS) break; // No more - if (localLOGV && out == null) { - Slog.v(TAG, "First time : Speed/Time = " + tempSpeeds[speed - 1] - + "\t" + tempTimes[speed - 1]); - } - } catch (NumberFormatException nfe) { - Slog.i(TAG, "Unable to parse time_in_state"); - } - } - } - if (out == null) { - out = new long[speed]; - mCpuSpeeds = new long[speed]; - System.arraycopy(tempSpeeds, 0, mCpuSpeeds, 0, speed); - System.arraycopy(tempTimes, 0, out, 0, speed); - } - return out; - } - final public int getLastUserTime() { return mRelUserTime; } diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java index f98fbfc5c77a..fdc3547ae67a 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -79,7 +79,6 @@ public final class FloatingToolbar { private final FloatingToolbarPopup mPopup; private final Rect mContentRect = new Rect(); - private final Point mCoordinates = new Point(); private Menu mMenu; private List<CharSequence> mShowingTitles = new ArrayList<CharSequence>(); @@ -87,7 +86,6 @@ public final class FloatingToolbar { private int mSuggestedWidth; private boolean mWidthChanged = true; - private int mOverflowDirection; /** * Initializes a floating toolbar. @@ -157,11 +155,9 @@ public final class FloatingToolbar { mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth); mShowingTitles = getMenuItemTitles(menuItems); } - refreshCoordinates(); - mPopup.setOverflowDirection(mOverflowDirection); - mPopup.updateCoordinates(mCoordinates.x, mCoordinates.y); + mPopup.updateCoordinates(mContentRect); if (!mPopup.isShowing()) { - mPopup.show(mCoordinates.x, mCoordinates.y); + mPopup.show(mContentRect); } mWidthChanged = false; return this; @@ -209,25 +205,6 @@ public final class FloatingToolbar { } /** - * Refreshes {@link #mCoordinates} with values based on {@link #mContentRect}. - */ - private void refreshCoordinates() { - int x = mContentRect.centerX() - mPopup.getWidth() / 2; - int y; - if (mContentRect.top > mPopup.getHeight()) { - y = mContentRect.top - mPopup.getHeight(); - mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_UP; - } else if (mContentRect.top > mPopup.getToolbarHeightWithVerticalMargin()) { - y = mContentRect.top - mPopup.getToolbarHeightWithVerticalMargin(); - mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN; - } else { - y = mContentRect.bottom; - mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN; - } - mCoordinates.set(x, y); - } - - /** * Returns true if this floating toolbar is currently showing the specified menu items. */ private boolean isCurrentlyShowing(List<MenuItem> menuItems) { @@ -345,6 +322,8 @@ public final class FloatingToolbar { } }; + private final Point mCoords = new Point(); + private final Region mTouchableRegion = new Region(); private final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = new ViewTreeObserver.OnComputeInternalInsetsListener() { @@ -378,6 +357,7 @@ public final class FloatingToolbar { mShowAnimation = createGrowFadeInFromBottom(mContentContainer); mDismissAnimation = createShrinkFadeOutFromBottomAnimation( mContentContainer, + 0, new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -387,6 +367,7 @@ public final class FloatingToolbar { }); mHideAnimation = createShrinkFadeOutFromBottomAnimation( mContentContainer, + 150, new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -404,6 +385,8 @@ public final class FloatingToolbar { */ public void layoutMenuItems(List<MenuItem> menuItems, MenuItem.OnMenuItemClickListener menuItemClickListener, int suggestedWidth) { + Preconditions.checkNotNull(menuItems); + mContentContainer.removeAllViews(); if (mMainPanel == null) { mMainPanel = new FloatingToolbarMainPanel(mParent.getContext(), mOpenOverflow); @@ -426,7 +409,9 @@ public final class FloatingToolbar { * Shows this popup at the specified coordinates. * The specified coordinates may be adjusted to make sure the popup is entirely on-screen. */ - public void show(int x, int y) { + public void show(Rect contentRect) { + Preconditions.checkNotNull(contentRect); + if (isShowing()) { return; } @@ -435,6 +420,7 @@ public final class FloatingToolbar { mDismissed = false; cancelDismissAndHideAnimations(); cancelOverflowAnimations(); + // Make sure a panel is set as the content. if (mContentContainer.getChildCount() == 0) { setMainPanelAsContent(); @@ -442,8 +428,10 @@ public final class FloatingToolbar { // The "show" animation will make this visible. mContentContainer.setAlpha(0); } + updateOverflowHeight(contentRect.top - (mMarginVertical * 2)); + refreshCoordinatesAndOverflowDirection(contentRect); preparePopupContent(); - mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, x, y); + mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, mCoords.x, mCoords.y); setTouchableSurfaceInsetsComputer(); runShowAnimation(); } @@ -496,27 +484,17 @@ public final class FloatingToolbar { * The specified coordinates may be adjusted to make sure the popup is entirely on-screen. * This is a no-op if this popup is not showing. */ - public void updateCoordinates(int x, int y) { + public void updateCoordinates(Rect contentRect) { + Preconditions.checkNotNull(contentRect); + if (!isShowing() || !mPopupWindow.isShowing()) { return; } cancelOverflowAnimations(); + refreshCoordinatesAndOverflowDirection(contentRect); preparePopupContent(); - mPopupWindow.update(x, y, getWidth(), getHeight()); - } - - /** - * Sets the direction in which the overflow will open. i.e. up or down. - * - * @param overflowDirection Either {@link #OVERFLOW_DIRECTION_UP} - * or {@link #OVERFLOW_DIRECTION_DOWN}. - */ - public void setOverflowDirection(int overflowDirection) { - mOverflowDirection = overflowDirection; - if (mOverflowPanel != null) { - mOverflowPanel.setOverflowDirection(mOverflowDirection); - } + mPopupWindow.update(mCoords.x, mCoords.y, getWidth(), getHeight()); } /** @@ -540,7 +518,26 @@ public final class FloatingToolbar { return mContentContainer.getContext(); } - int getToolbarHeightWithVerticalMargin() { + private void refreshCoordinatesAndOverflowDirection(Rect contentRect) { + int x = contentRect.centerX() - getWidth() / 2; + int y; + if (contentRect.top > getHeight()) { + y = contentRect.top - getHeight(); + mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_UP; + } else if (contentRect.top > getToolbarHeightWithVerticalMargin()) { + y = contentRect.top - getToolbarHeightWithVerticalMargin(); + mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN; + } else { + y = contentRect.bottom; + mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN; + } + mCoords.set(x, y); + if (mOverflowPanel != null) { + mOverflowPanel.setOverflowDirection(mOverflowDirection); + } + } + + private int getToolbarHeightWithVerticalMargin() { return getEstimatedToolbarHeight(mParent.getContext()) + mMarginVertical * 2; } @@ -693,16 +690,24 @@ public final class FloatingToolbar { } // Reset position. - if (mMainPanel != null - && mContentContainer.getChildAt(0) == mMainPanel.getView()) { + if (isMainPanelContent()) { positionMainPanel(); } - if (mOverflowPanel != null - && mContentContainer.getChildAt(0) == mOverflowPanel.getView()) { + if (isOverflowPanelContent()) { positionOverflowPanel(); } } + private boolean isMainPanelContent() { + return mMainPanel != null + && mContentContainer.getChildAt(0) == mMainPanel.getView(); + } + + private boolean isOverflowPanelContent() { + return mOverflowPanel != null + && mContentContainer.getChildAt(0) == mOverflowPanel.getView(); + } + /** * Sets the current content to be the main view panel. */ @@ -765,6 +770,25 @@ public final class FloatingToolbar { setContentAreaAsTouchableSurface(); } + private void updateOverflowHeight(int height) { + if (mOverflowPanel != null) { + mOverflowPanel.setSuggestedHeight(height); + + // Re-measure the popup and it's contents. + boolean mainPanelContent = isMainPanelContent(); + boolean overflowPanelContent = isOverflowPanelContent(); + mContentContainer.removeAllViews(); // required to update popup size. + updatePopupSize(); + // Reset the appropriate content. + if (mainPanelContent) { + setMainPanelAsContent(); + } + if (overflowPanelContent) { + setOverflowPanelAsContent(); + } + } + } + private void updatePopupSize() { int width = 0; int height = 0; @@ -864,6 +888,8 @@ public final class FloatingToolbar { * @return The menu items that are not included in this main panel. */ public List<MenuItem> layoutMenuItems(List<MenuItem> menuItems, int suggestedWidth) { + Preconditions.checkNotNull(menuItems); + final int toolbarWidth = getAdjustedToolbarWidth(mContext, suggestedWidth) // Reserve space for the "open overflow" button. - getEstimatedOpenOverflowButtonWidth(mContext); @@ -972,6 +998,7 @@ public final class FloatingToolbar { private MenuItem.OnMenuItemClickListener mOnMenuItemClickListener; private int mOverflowWidth = 0; + private int mSuggestedHeight; /** * Initializes a floating toolbar popup overflow view panel. @@ -981,6 +1008,7 @@ public final class FloatingToolbar { */ public FloatingToolbarOverflowPanel(Context context, Runnable closeOverflow) { mCloseOverflow = Preconditions.checkNotNull(closeOverflow); + mSuggestedHeight = getScreenHeight(context); mContentView = new LinearLayout(context); mContentView.setOrientation(LinearLayout.VERTICAL); @@ -1043,6 +1071,11 @@ public final class FloatingToolbar { mContentView.addView(mBackButtonContainer, index); } + public void setSuggestedHeight(int height) { + mSuggestedHeight = height; + setListViewHeight(); + } + /** * Returns the content view of the overflow. */ @@ -1074,9 +1107,17 @@ public final class FloatingToolbar { int itemHeight = getEstimatedToolbarHeight(mContentView.getContext()); int height = mListView.getAdapter().getCount() * itemHeight; int maxHeight = mContentView.getContext().getResources(). + getDimensionPixelSize(R.dimen.floating_toolbar_maximum_overflow_height); + int minHeight = mContentView.getContext().getResources(). getDimensionPixelSize(R.dimen.floating_toolbar_minimum_overflow_height); + int availableHeight = mSuggestedHeight - (mSuggestedHeight % itemHeight) + - itemHeight; // reserve space for the back button. ViewGroup.LayoutParams params = mListView.getLayoutParams(); - params.height = Math.min(height, maxHeight); + if (availableHeight >= minHeight) { + params.height = Math.min(Math.min(availableHeight, maxHeight), height); + } else { + params.height = Math.min(maxHeight, height); + } mListView.setLayoutParams(params); } @@ -1224,15 +1265,16 @@ public final class FloatingToolbar { * Creates a "shrink and fade out from bottom" animation for the specified view. * * @param view The view to animate + * @param startDelay The start delay of the animation * @param listener The animation listener */ private static AnimatorSet createShrinkFadeOutFromBottomAnimation( - View view, Animator.AnimatorListener listener) { + View view, int startDelay, Animator.AnimatorListener listener) { AnimatorSet shrinkFadeOutFromBottomAnimation = new AnimatorSet(); shrinkFadeOutFromBottomAnimation.playTogether( ObjectAnimator.ofFloat(view, View.SCALE_Y, 1, 0.5f).setDuration(125), ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(75)); - shrinkFadeOutFromBottomAnimation.setStartDelay(150); + shrinkFadeOutFromBottomAnimation.setStartDelay(startDelay); shrinkFadeOutFromBottomAnimation.addListener(listener); return shrinkFadeOutFromBottomAnimation; } @@ -1271,16 +1313,4 @@ public final class FloatingToolbar { private static int getScreenHeight(Context context) { return context.getResources().getDisplayMetrics().heightPixels; } - - /** - * Returns value, restricted to the range min->max (inclusive). - * If maximum is less than minimum, the result is undefined. - * - * @param value The value to clamp. - * @param minimum The minimum value in the range. - * @param maximum The maximum value in the range. Must not be less than minimum. - */ - private static int clamp(int value, int minimum, int maximum) { - return Math.max(minimum, Math.min(value, maximum)); - } } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 3d23986d580b..e5ef60cc991e 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -1097,8 +1097,11 @@ public class LockPatternUtils { Log.w(TAG, "Only device owner may call setCredentialRequiredForDecrypt()"); return; } - Settings.Global.putInt(mContext.getContentResolver(), - Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, required ? 1 : 0); + + if (isDeviceEncryptionEnabled()){ + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.REQUIRE_PASSWORD_TO_DECRYPT, required ? 1 : 0); + } } private boolean isDoNotAskCredentialsOnBootSet() { diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 01e835bc2650..be727f17ce9e 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -661,13 +661,20 @@ public class ResolverDrawerLayout extends ViewGroup { } } + final int oldCollapsibleHeight = mCollapsibleHeight; mCollapsibleHeight = Math.max(0, heightUsed - alwaysShowHeight - getMaxCollapsedHeight()); mUncollapsibleHeight = heightUsed - mCollapsibleHeight; if (isLaidOut()) { final boolean isCollapsedOld = mCollapseOffset != 0; - mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); + if (oldCollapsibleHeight < mCollapsibleHeight + && mCollapseOffset == oldCollapsibleHeight) { + // Stay closed even at the new height. + mCollapseOffset = mCollapsibleHeight; + } else { + mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); + } final boolean isCollapsedNew = mCollapseOffset != 0; if (isCollapsedOld != isCollapsedNew) { notifyViewAccessibilityStateChangedIfNeeded( diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index 348b0ec083b7..e69f64e05bdb 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -102,8 +102,8 @@ public: canvas->translate(bounds.fLeft, bounds.fTop); canvas->scale(scale, scale); - bounds.fRight = SkScalarDiv(bounds.fRight-bounds.fLeft, scale); - bounds.fBottom = SkScalarDiv(bounds.fBottom-bounds.fTop, scale); + bounds.fRight = (bounds.fRight-bounds.fLeft) / scale; + bounds.fBottom = (bounds.fBottom-bounds.fTop) / scale; bounds.fLeft = bounds.fTop = 0; ALOGV("Drawing scaled 9-patch: (%g,%g)-(%g,%g) srcDensity=%d destDensity=%d", diff --git a/core/jni/android/graphics/NinePatchImpl.cpp b/core/jni/android/graphics/NinePatchImpl.cpp index 26ce96728516..323f832df84d 100644 --- a/core/jni/android/graphics/NinePatchImpl.cpp +++ b/core/jni/android/graphics/NinePatchImpl.cpp @@ -94,8 +94,7 @@ SkScalar calculateStretch(SkScalar boundsLimit, SkScalar startingPoint, SkScalar spaceRemaining = boundsLimit - startingPoint; SkScalar stretchySpaceRemaining = spaceRemaining - SkIntToScalar(numFixedPixelsRemaining); - return SkScalarMulDiv(srcSpace, stretchySpaceRemaining, - numStrechyPixelsRemaining); + return srcSpace * stretchySpaceRemaining / numStrechyPixelsRemaining; } void NinePatch_Draw(SkCanvas* canvas, const SkRect& bounds, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 4c034b3169bd..608d718be133 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -678,6 +678,7 @@ <!-- Allows an app to use fingerprint hardware. --> <permission android:name="android.permission.USE_FINGERPRINT" + android:permissionGroup="android.permission-group.SENSORS" android:label="@string/permlab_useFingerprint" android:description="@string/permdesc_useFingerprint" android:protectionLevel="dangerous" /> @@ -2404,8 +2405,7 @@ <permission android:name="android.permission.REMOVE_DRM_CERTIFICATES" android:protectionLevel="signature|system" /> - <!-- Must be required by a {@link android.service.carrier.CarrierMessagingService}. - Any service that filters for this intent must be a carrier privileged app. --> + <!-- @deprecated Use {@link android.Manifest.permission#BIND_CARRIER_SERVICES} instead --> <permission android:name="android.permission.BIND_CARRIER_MESSAGING_SERVICE" android:protectionLevel="signature|system" /> @@ -2427,13 +2427,12 @@ android:permissionGroup="android.permission-group.SYSTEM_TOOLS" android:protectionLevel="signature" /> - <!-- The system process that pulls carrier configuration from carrier apps will - have this permission. Carrier apps that provide - {@link android.service.carrier.CarrierConfigService} should require this - permission for clients binding to their service. --> - <permission android:name="android.permission.BIND_CARRIER_CONFIG_SERVICE" - android:label="@string/permlab_bindCarrierConfigService" - android:description="@string/permdesc_bindCarrierConfigService" + <!-- The system process that is allowed to bind to services in carrier apps will + have this permission. Carrier apps should use this permission to protect + their services that only the system is allowed to bind to. --> + <permission android:name="android.permission.BIND_CARRIER_SERVICES" + android:label="@string/permlab_bindCarrierServices" + android:description="@string/permdesc_bindCarrierServices" android:protectionLevel="signature|system" /> <!-- Allows an application to query whether DO_NOT_ASK_CREDENTIALS_ON_BOOT diff --git a/core/res/res/drawable-nodpi/platlogo.xml b/core/res/res/drawable-nodpi/platlogo.xml index 65cb0464deec..f5d945ada52b 100644 --- a/core/res/res/drawable-nodpi/platlogo.xml +++ b/core/res/res/drawable-nodpi/platlogo.xml @@ -14,35 +14,30 @@ Copyright (C) 2014 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="560.0dp" - android:height="560.0dp" - android:viewportWidth="560.0" - android:viewportHeight="560.0"> + android:width="480dp" + android:height="480dp" + android:viewportWidth="48.0" + android:viewportHeight="48.0"> <path - android:pathData="M264.079987,240.736l0.0,9.82c7.31,-7.15 17.139999,-11.56 28.07,-11.56c22.639999,0.0 40.799999,18.48 40.799999,41.12c0.0,22.48 -18.16,40.880 -40.799999,40.880c-10.93,0.0 -20.280,-4.09 -27.59,-10.93L264.559998,339.0l-11.32,0.0l0.0,-98.269997L264.079987,240.731zM265.809998,264.869995c-0.47,0.79 -1.26,2.04 -1.26,4.79l0.0,21.07c0.0,1.97 0.47,3.07 1.1,4.17c5.19,8.88 14.78,14.94 25.63,14.94c16.43,0.0 29.950,-13.44 29.950,-29.870c0.0,-16.280 -13.52,-29.799999 -29.950,-29.799999C280.51,250.169998 271.0,256.059998 265.809998,264.869995z" - android:fillColor="#FFFFFF"/> - <path - android:pathData="M445.731,240.736l0.0,9.82c7.31,-7.15 17.139999,-11.56 28.07,-11.56c22.639999,0.0 40.799999,18.48 40.799999,41.12c0.0,22.48 -18.16,40.880 -40.799999,40.880c-10.93,0.0 -20.280,-4.09 -27.59,-10.93L446.210052,339.0l-11.32,0.0l0.0,-98.269997L445.731,240.731zM447.459991,264.869995c-0.47,0.79 -1.26,2.04 -1.26,4.79l0.0,21.07c0.0,1.97 0.47,3.07 1.1,4.17c5.19,8.88 14.78,14.94 25.63,14.94c16.43,0.0 29.950,-13.44 29.950,-29.870c0.0,-16.280 -13.52,-29.799999 -29.950,-29.799999C462.160004,250.169998 452.649994,256.059998 447.459991,264.869995z" - android:fillColor="#FFFFFF"/> - <path - android:pathData="M169.490005,279.880005c0.0,22.639999 -18.32,41.12 -40.810,41.12c-22.639999,0.0 -41.040,-18.48 -41.040,-41.12c0.0,-22.48 18.389999,-40.880 41.040,-40.880C151.169998,239.0 169.490005,257.399994 169.490005,279.880005zM158.089996,280.040009c0.0,-16.43 -13.13,-29.870 -29.41,-29.870c-16.51,0.0 -29.4,13.44 -29.4,29.870c0.0,16.280 12.89,29.799999 29.4,29.799999C144.960007,309.8387 158.089996,296.309998 158.089996,280.040009z" - android:fillColor="#FFFFFF"/> + android:pathData="M24.0,2.0C11.8,2.0 2.0,11.8 2.0,24.0c0.0,6.1 2.5,11.6 6.4,15.6L39.6,8.4C35.6,4.5 30.1,2.0 24.0,2.0z" + android:fillColor="#F57C00"/> <path - android:pathData="M423.790009,279.880005c0.0,22.639999 -18.32,41.12 -40.810,41.12c-22.639999,0.0 -41.040,-18.48 -41.040,-41.12c0.0,-22.48 18.389999,-40.880 41.040,-40.880C405.470,239.0 423.790009,257.399994 423.790009,279.880005zM412.395,280.040009c0.0,-16.43 -13.13,-29.870 -29.41,-29.870c-16.51,0.0 -29.4,13.44 -29.4,29.870c0.0,16.280 12.89,29.799999 29.4,29.799999C399.26,309.8387 412.395,296.309998 412.395,280.040009z" - android:fillColor="#FFFFFF"/> + android:pathData="M39.6,8.4L8.4,39.6c4.0,4.0 9.5,6.4 15.6,6.4c12.2,0.0 22.0,-9.8 22.0,-22.0C46.0,17.9 43.5,12.4 39.6,8.4z" + android:fillColor="#FF9800"/> <path - android:pathData="M229.02,221.0l11.17,0.0l0.0,11.48l-11.17,0.0z" - android:fillColor="#FFFFFF"/> + android:pathData="M45.9,25.9L34.0,14.0L14.0,34.0l11.9,11.9C36.5,45.0 45.0,36.5 45.9,25.9z" + android:fillAlpha="0.33" + android:fillColor="#F57C00"/> <path - android:pathData="M229.02,240.65l11.17,0.0l0.0,78.62l-11.17,0.0z" + android:pathData="M24.0,24.0c0.0,0.0 0.0,2.2 0.0,5.0s0.0,5.0 0.0,5.0l10.0,-10.0L34.0,14.0L24.0,24.0z" android:fillColor="#FFFFFF"/> <path - android:pathData="M65.4,221.0l11.17,0.0l0.0,98.27l-11.17,0.0z" - android:fillColor="#FFFFFF"/> + android:pathData="M24.0,24.0L14.0,14.0l0.0,10.0l10.0,10.0c0.0,0.0 0.0,-2.2 0.0,-5.0S24.0,24.0 24.0,24.0z" + android:fillColor="#EEEEEE"/> <path - android:pathData="M180.58,221.0l11.17,0.0l0.0,98.27l-11.17,0.0z" - android:fillColor="#FFFFFF"/> + android:pathData="M14.0,34.0l10.0,0.0 -10.0,-10.0z" + android:fillColor="#DDDDDD"/> <path - android:pathData="M204.8,221.0l11.17,0.0l0.0,98.27l-11.17,0.0z" - android:fillColor="#FFFFFF"/> + android:pathData="M34.0,34.0l0.0,-10.0 -10.0,10.0z" + android:fillColor="#DDDDDD"/> </vector> diff --git a/core/res/res/drawable-nodpi/stat_sys_adb.xml b/core/res/res/drawable-nodpi/stat_sys_adb.xml index d89d1f922a0e..8f5109df4804 100644 --- a/core/res/res/drawable-nodpi/stat_sys_adb.xml +++ b/core/res/res/drawable-nodpi/stat_sys_adb.xml @@ -19,9 +19,6 @@ Copyright (C) 2014 The Android Open Source Project android:viewportWidth="24.0" android:viewportHeight="24.0"> <path - android:pathData="M19.000000,10.000000c0.000000,3.866000 -3.134000,7.000000 -7.000000,7.000000s-7.000000,-3.134000 -7.000000,-7.000000c0.000000,-2.318000 1.131000,-4.367000 2.867000,-5.641000L5.778000,2.270000l0.824000,-0.825000l2.290000,2.290000C9.830000,3.269000 10.882000,3.000000 12.000000,3.000000c1.118000,0.000000 2.170000,0.269000 3.107000,0.734000l2.290000,-2.290000l0.824000,0.825000l-2.089000,2.090000C17.868000,5.633000 19.000000,7.682000 19.000000,10.000000zM10.000000,8.000000c0.000000,-0.552000 -0.447000,-1.000000 -1.000000,-1.000000S8.000000,7.448000 8.000000,8.000000s0.447000,1.000000 1.000000,1.000000S10.000000,8.552000 10.000000,8.000000zM16.000000,8.000000c0.000000,-0.552000 -0.447000,-1.000000 -1.000000,-1.000000s-1.000000,0.448000 -1.000000,1.000000s0.447000,1.000000 1.000000,1.000000S16.000000,8.552000 16.000000,8.000000z" - android:fillColor="#FFFFFF"/> - <path - android:pathData="M11,18l2,0l0,5l-2,0z" - android:fillColor="#FFFFFF"/> + android:fillColor="#FF000000" + android:pathData="M12.0,12.0l-10.0,-10.0 0.0,10.0 0.0,10.0 10.0,0.0 10.0,0.0 0.0,-10.0 0.0,-10.0z"/> </vector> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 84747f13cbb4..813591b5b78b 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -392,7 +392,8 @@ <dimen name="floating_toolbar_text_size">14sp</dimen> <dimen name="floating_toolbar_menu_button_minimum_width">48dp</dimen> <dimen name="floating_toolbar_preferred_width">328dp</dimen> - <dimen name="floating_toolbar_minimum_overflow_height">144dp</dimen> + <dimen name="floating_toolbar_minimum_overflow_height">96dp</dimen> + <dimen name="floating_toolbar_maximum_overflow_height">192dp</dimen> <dimen name="floating_toolbar_horizontal_margin">16dp</dimen> <dimen name="floating_toolbar_vertical_margin">8dp</dimen> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 1c0122d754b8..0e6b2dfef533 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1408,9 +1408,9 @@ <string name="permdesc_bindCarrierMessagingService">Allows the holder to bind to the top-level interface of a carrier messaging service. Should never be needed for normal apps.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permlab_bindCarrierConfigService">bind to a carrier config service</string> + <string name="permlab_bindCarrierServices">bind to carrier services</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_bindCarrierConfigService">Allows the holder to bind to a carrier config service. Should never be needed for normal apps.</string> + <string name="permdesc_bindCarrierServices">Allows the holder to bind to carrier services. Should never be needed for normal apps.</string> <!-- Policy administration --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 0d306c6b2971..64e396468fd5 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2256,6 +2256,7 @@ <java-symbol type="dimen" name="floating_toolbar_menu_button_minimum_width" /> <java-symbol type="dimen" name="floating_toolbar_preferred_width" /> <java-symbol type="dimen" name="floating_toolbar_minimum_overflow_height" /> + <java-symbol type="dimen" name="floating_toolbar_maximum_overflow_height" /> <java-symbol type="dimen" name="floating_toolbar_horizontal_margin" /> <java-symbol type="dimen" name="floating_toolbar_vertical_margin" /> diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java index 0da4275faba7..bd8eae016807 100644 --- a/graphics/java/android/graphics/drawable/DrawableWrapper.java +++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java @@ -432,7 +432,8 @@ public abstract class DrawableWrapper extends Drawable implements Drawable.Callb @Override public int getChangingConfigurations() { - return mChangingConfigurations | mDrawableState.getChangingConfigurations(); + return mChangingConfigurations + | (mDrawableState != null ? mDrawableState.getChangingConfigurations() : 0); } public boolean canConstantState() { diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index ba7bd74fa952..f893fdd51c45 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -242,7 +242,7 @@ public class AudioTrack /** * The audio channel mask used for calling native AudioTrack */ - private int mChannels = AudioFormat.CHANNEL_OUT_MONO; + private int mChannelMask = AudioFormat.CHANNEL_OUT_MONO; /** * The type of the audio stream to play. See @@ -485,7 +485,7 @@ public class AudioTrack session[0] = sessionId; // native initialization int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes, - mSampleRate, mChannels, mChannelIndexMask, mAudioFormat, + mSampleRate, mChannelMask, mChannelIndexMask, mAudioFormat, mNativeBufferSizeInBytes, mDataLoadMode, session); if (initResult != SUCCESS) { loge("Error code "+initResult+" when initializing AudioTrack."); @@ -699,7 +699,7 @@ public class AudioTrack // This is where constructor IllegalArgumentException-s are thrown // postconditions: // mChannelCount is valid - // mChannels is valid + // mChannelMask is valid // mAudioFormat is valid // mSampleRate is valid // mDataLoadMode is valid @@ -722,12 +722,12 @@ public class AudioTrack case AudioFormat.CHANNEL_OUT_MONO: case AudioFormat.CHANNEL_CONFIGURATION_MONO: mChannelCount = 1; - mChannels = AudioFormat.CHANNEL_OUT_MONO; + mChannelMask = AudioFormat.CHANNEL_OUT_MONO; break; case AudioFormat.CHANNEL_OUT_STEREO: case AudioFormat.CHANNEL_CONFIGURATION_STEREO: mChannelCount = 2; - mChannels = AudioFormat.CHANNEL_OUT_STEREO; + mChannelMask = AudioFormat.CHANNEL_OUT_STEREO; break; default: if (channelConfig == AudioFormat.CHANNEL_INVALID && channelIndexMask != 0) { @@ -738,7 +738,7 @@ public class AudioTrack // input channel configuration features unsupported channels throw new IllegalArgumentException("Unsupported channel configuration."); } - mChannels = channelConfig; + mChannelMask = channelConfig; mChannelCount = AudioFormat.channelCountFromOutChannelMask(channelConfig); } // check the channel index configuration (if present) diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java index acff3010ac4d..52ba9ec572a6 100644 --- a/media/java/android/media/MediaDrm.java +++ b/media/java/android/media/MediaDrm.java @@ -110,10 +110,10 @@ public final class MediaDrm { private static final String PERMISSION = android.Manifest.permission.ACCESS_DRM_CERTIFICATES; private EventHandler mEventHandler; - private EventHandler mOnKeysChangeEventHandler; + private EventHandler mOnKeyStatusChangeEventHandler; private EventHandler mOnExpirationUpdateEventHandler; private OnEventListener mOnEventListener; - private OnKeysChangeListener mOnKeysChangeListener; + private OnKeyStatusChangeListener mOnKeyStatusChangeListener; private OnExpirationUpdateListener mOnExpirationUpdateListener; private long mNativeContext; @@ -297,8 +297,8 @@ public final class MediaDrm { * @param handler the handler on which the listener should be invoked, or * null if the listener should be invoked on the calling thread's looper. */ - public void setOnKeysChangeListener( - @Nullable OnKeysChangeListener listener, @Nullable Handler handler) { + public void setOnKeyStatusChangeListener( + @Nullable OnKeyStatusChangeListener listener, @Nullable Handler handler) { if (listener != null) { Looper looper = handler != null ? handler.getLooper() : Looper.myLooper(); if (looper != null) { @@ -307,14 +307,14 @@ public final class MediaDrm { } } } - mOnKeysChangeListener = listener; + mOnKeyStatusChangeListener = listener; } /** * Interface definition for a callback to be invoked when the keys in a drm * session change states. */ - public interface OnKeysChangeListener + public interface OnKeyStatusChangeListener { /** * Called when the keys in a session change status, such as when the license @@ -328,64 +328,64 @@ public final class MediaDrm { * which may trigger an attempt to resume playback on the media stream * if it is currently blocked waiting for a key. */ - void onKeysChange( + void onKeyStatusChange( @NonNull MediaDrm md, @NonNull byte[] sessionId, @NonNull List<KeyStatus> keyInformation, boolean hasNewUsableKey); } /** - * The key is currently usable to decrypt media data - */ - public static final int KEY_STATUS_USABLE = 0; - - /** - * The key is no longer usable to decrypt media data because its - * expiration time has passed. - */ - public static final int KEY_STATUS_EXPIRED = 1; - - /** - * The key is not currently usable to decrypt media data because its - * output requirements cannot currently be met. - */ - public static final int KEY_STATUS_OUTPUT_NOT_ALLOWED = 2; - - /** - * The status of the key is not yet known and is being determined. - * The status will be updated with the actual status when it has - * been determined. - */ - public static final int KEY_STATUS_PENDING = 3; - - /** - * The key is not currently usable to decrypt media data because of an - * internal error in processing unrelated to input parameters. This error - * is not actionable by an app. - */ - public static final int KEY_STATUS_INTERNAL_ERROR = 4; - - /** @hide */ - @IntDef({ - KEY_STATUS_USABLE, - KEY_STATUS_EXPIRED, - KEY_STATUS_OUTPUT_NOT_ALLOWED, - KEY_STATUS_PENDING, - KEY_STATUS_INTERNAL_ERROR, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface KeyStatusCode {} - - /** * Defines the status of a key. * A KeyStatus for each key in a session is provided to the - * {@link OnKeysChangeListener#onKeysChange} + * {@link OnKeyStatusChangeListener#onKeyStatusChange} * listener. */ public static final class KeyStatus { private final byte[] mKeyId; private final int mStatusCode; + /** + * The key is currently usable to decrypt media data + */ + public static final int STATUS_USABLE = 0; + + /** + * The key is no longer usable to decrypt media data because its + * expiration time has passed. + */ + public static final int STATUS_EXPIRED = 1; + + /** + * The key is not currently usable to decrypt media data because its + * output requirements cannot currently be met. + */ + public static final int STATUS_OUTPUT_NOT_ALLOWED = 2; + + /** + * The status of the key is not yet known and is being determined. + * The status will be updated with the actual status when it has + * been determined. + */ + public static final int STATUS_PENDING = 3; + + /** + * The key is not currently usable to decrypt media data because of an + * internal error in processing unrelated to input parameters. This error + * is not actionable by an app. + */ + public static final int STATUS_INTERNAL_ERROR = 4; + + /** @hide */ + @IntDef({ + STATUS_USABLE, + STATUS_EXPIRED, + STATUS_OUTPUT_NOT_ALLOWED, + STATUS_PENDING, + STATUS_INTERNAL_ERROR, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface KeyStatusCode {} + KeyStatus(@NonNull byte[] keyId, @KeyStatusCode int statusCode) { mKeyId = keyId; mStatusCode = statusCode; @@ -393,6 +393,9 @@ public final class MediaDrm { /** * Returns the status code for the key + * @return one of {@link #STATUS_USABLE}, {@link #STATUS_EXPIRED}, + * {@link #STATUS_OUTPUT_NOT_ALLOWED}, {@link #STATUS_PENDING} + * or {@link #STATUS_INTERNAL_ERROR}. */ @KeyStatusCode public int getStatusCode() { return mStatusCode; } @@ -484,7 +487,7 @@ public final class MediaDrm { private static final int DRM_EVENT = 200; private static final int EXPIRATION_UPDATE = 201; - private static final int KEYS_CHANGE = 202; + private static final int KEY_STATUS_CHANGE = 202; private class EventHandler extends Handler { @@ -522,8 +525,8 @@ public final class MediaDrm { } return; - case KEYS_CHANGE: - if (mOnKeysChangeListener != null) { + case KEY_STATUS_CHANGE: + if (mOnKeyStatusChangeListener != null) { if (msg.obj != null && msg.obj instanceof Parcel) { Parcel parcel = (Parcel)msg.obj; byte[] sessionId = parcel.createByteArray(); @@ -531,9 +534,9 @@ public final class MediaDrm { List<KeyStatus> keyStatusList = keyStatusListFromParcel(parcel); boolean hasNewUsableKey = (parcel.readInt() != 0); - Log.i(TAG, "Drm keys change"); - mOnKeysChangeListener.onKeysChange(mMediaDrm, sessionId, keyStatusList, - hasNewUsableKey); + Log.i(TAG, "Drm key status changed"); + mOnKeyStatusChangeListener.onKeyStatusChange(mMediaDrm, sessionId, + keyStatusList, hasNewUsableKey); } } } @@ -641,30 +644,6 @@ public final class MediaDrm { public @interface KeyType {} /** - * Key request type is initial license request - */ - public static final int REQUEST_TYPE_INITIAL = 0; - - /** - * Key request type is license renewal - */ - public static final int REQUEST_TYPE_RENEWAL = 1; - - /** - * Key request type is license release - */ - public static final int REQUEST_TYPE_RELEASE = 2; - - /** @hide */ - @IntDef({ - REQUEST_TYPE_INITIAL, - REQUEST_TYPE_RENEWAL, - REQUEST_TYPE_RELEASE, - }) - @Retention(RetentionPolicy.SOURCE) - public @interface RequestType {} - - /** * Contains the opaque data an app uses to request keys from a license server */ public static final class KeyRequest { @@ -672,6 +651,30 @@ public final class MediaDrm { private String mDefaultUrl; private int mRequestType; + /** + * Key request type is initial license request + */ + public static final int REQUEST_TYPE_INITIAL = 0; + + /** + * Key request type is license renewal + */ + public static final int REQUEST_TYPE_RENEWAL = 1; + + /** + * Key request type is license release + */ + public static final int REQUEST_TYPE_RELEASE = 2; + + /** @hide */ + @IntDef({ + REQUEST_TYPE_INITIAL, + REQUEST_TYPE_RENEWAL, + REQUEST_TYPE_RELEASE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RequestType {} + KeyRequest() {} /** @@ -707,6 +710,8 @@ public final class MediaDrm { /** * Get the type of the request + * @return one of {@link #REQUEST_TYPE_INITIAL}, + * {@link #REQUEST_TYPE_RENEWAL} or {@link #REQUEST_TYPE_RELEASE} */ @RequestType public int getRequestType() { return mRequestType; } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index e3a6a8395421..f14860643c4b 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -40,6 +40,7 @@ import android.provider.Settings; import android.system.ErrnoException; import android.system.OsConstants; import android.util.Log; +import android.util.Pair; import android.view.Surface; import android.view.SurfaceHolder; import android.widget.VideoView; @@ -70,6 +71,8 @@ import java.lang.Runnable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.net.InetSocketAddress; +import java.util.BitSet; +import java.util.HashSet; import java.util.Map; import java.util.Scanner; import java.util.Set; @@ -639,9 +642,7 @@ public class MediaPlayer implements SubtitleController.Listener } mTimeProvider = new TimeProvider(this); - mOutOfBandSubtitleTracks = new Vector<SubtitleTrack>(); mOpenSubtitleSources = new Vector<InputStream>(); - mInbandSubtitleTracks = new SubtitleTrack[0]; IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE); mAppOps = IAppOpsService.Stub.asInterface(b); @@ -1693,8 +1694,6 @@ public class MediaPlayer implements SubtitleController.Listener } mOpenSubtitleSources.clear(); } - mOutOfBandSubtitleTracks.clear(); - mInbandSubtitleTracks = new SubtitleTrack[0]; if (mSubtitleController != null) { mSubtitleController.reset(); } @@ -1709,6 +1708,11 @@ public class MediaPlayer implements SubtitleController.Listener if (mEventHandler != null) { mEventHandler.removeCallbacksAndMessages(null); } + + synchronized (mIndexTrackPairs) { + mIndexTrackPairs.clear(); + mInbandTrackIndices.clear(); + }; } private native void _reset(); @@ -2050,6 +2054,16 @@ public class MediaPlayer implements SubtitleController.Listener }; + // We would like domain specific classes with more informative names than the `first` and `second` + // in generic Pair, but we would also like to avoid creating new/trivial classes. As a compromise + // we document the meanings of `first` and `second` here: + // + // Pair.first - inband track index; non-null iff representing an inband track. + // Pair.second - a SubtitleTrack registered with mSubtitleController; non-null iff representing + // an inband subtitle track or any out-of-band track (subtitle or timedtext). + private Vector<Pair<Integer, SubtitleTrack>> mIndexTrackPairs = new Vector<>(); + private BitSet mInbandTrackIndices = new BitSet(); + /** * Returns an array of track information. * @@ -2061,17 +2075,20 @@ public class MediaPlayer implements SubtitleController.Listener public TrackInfo[] getTrackInfo() throws IllegalStateException { TrackInfo trackInfo[] = getInbandTrackInfo(); // add out-of-band tracks - TrackInfo allTrackInfo[] = new TrackInfo[trackInfo.length + mOutOfBandSubtitleTracks.size()]; - System.arraycopy(trackInfo, 0, allTrackInfo, 0, trackInfo.length); - int i = trackInfo.length; - for (SubtitleTrack track: mOutOfBandSubtitleTracks) { - int type = track.isTimedText() - ? TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT - : TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE; - allTrackInfo[i] = new TrackInfo(type, track.getFormat()); - ++i; + synchronized (mIndexTrackPairs) { + TrackInfo allTrackInfo[] = new TrackInfo[mIndexTrackPairs.size()]; + for (int i = 0; i < allTrackInfo.length; i++) { + Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i); + if (p.first != null) { + // inband track + allTrackInfo[i] = trackInfo[p.first]; + } else { + SubtitleTrack track = p.second; + allTrackInfo[i] = new TrackInfo(track.getTrackType(), track.getFormat()); + } + } + return allTrackInfo; } - return allTrackInfo; } private TrackInfo[] getInbandTrackInfo() throws IllegalStateException { @@ -2167,22 +2184,21 @@ public class MediaPlayer implements SubtitleController.Listener } } - private final Object mInbandSubtitleLock = new Object(); - private SubtitleTrack[] mInbandSubtitleTracks; private int mSelectedSubtitleTrackIndex = -1; - private Vector<SubtitleTrack> mOutOfBandSubtitleTracks; private Vector<InputStream> mOpenSubtitleSources; private OnSubtitleDataListener mSubtitleDataListener = new OnSubtitleDataListener() { @Override public void onSubtitleData(MediaPlayer mp, SubtitleData data) { int index = data.getTrackIndex(); - if (index >= mInbandSubtitleTracks.length) { - return; - } - SubtitleTrack track = mInbandSubtitleTracks[index]; - if (track != null) { - track.onData(data); + synchronized (mIndexTrackPairs) { + for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) { + if (p.first != null && p.first == index && p.second != null) { + // inband subtitle track that owns data + SubtitleTrack track = p.second; + track.onData(data); + } + } } } }; @@ -2201,18 +2217,24 @@ public class MediaPlayer implements SubtitleController.Listener if (track == null) { return; } - for (int i = 0; i < mInbandSubtitleTracks.length; i++) { - if (mInbandSubtitleTracks[i] == track) { - Log.v(TAG, "Selecting subtitle track " + i); - mSelectedSubtitleTrackIndex = i; - try { - selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, true); - } catch (IllegalStateException e) { + + synchronized (mIndexTrackPairs) { + for (Pair<Integer, SubtitleTrack> p : mIndexTrackPairs) { + if (p.first != null && p.second == track) { + // inband subtitle track that is selected + mSelectedSubtitleTrackIndex = p.first; + break; } - setOnSubtitleDataListener(mSubtitleDataListener); - break; } } + + if (mSelectedSubtitleTrackIndex >= 0) { + try { + selectOrDeselectInbandTrack(mSelectedSubtitleTrackIndex, true); + } catch (IllegalStateException e) { + } + setOnSubtitleDataListener(mSubtitleDataListener); + } // no need to select out-of-band tracks } @@ -2252,7 +2274,9 @@ public class MediaPlayer implements SubtitleController.Listener mOpenSubtitleSources.remove(fIs); } scanner.close(); - mOutOfBandSubtitleTracks.add(track); + synchronized (mIndexTrackPairs) { + mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track)); + } track.onData(contents.getBytes(), true /* eos */, ~0 /* runID: keep forever */); return MEDIA_INFO_EXTERNAL_METADATA_UPDATE; } @@ -2270,27 +2294,37 @@ public class MediaPlayer implements SubtitleController.Listener private void scanInternalSubtitleTracks() { if (mSubtitleController == null) { - Log.e(TAG, "Should have subtitle controller already set"); - return; + Log.w(TAG, "setSubtitleAnchor in MediaPlayer"); + setSubtitleAnchor(); } + populateInbandTracks(); + + if (mSubtitleController != null) { + mSubtitleController.selectDefaultTrack(); + } + } + + private void populateInbandTracks() { TrackInfo[] tracks = getInbandTrackInfo(); - synchronized (mInbandSubtitleLock) { - SubtitleTrack[] inbandTracks = new SubtitleTrack[tracks.length]; - for (int i=0; i < tracks.length; i++) { + synchronized (mIndexTrackPairs) { + for (int i = 0; i < tracks.length; i++) { + if (mInbandTrackIndices.get(i)) { + continue; + } else { + mInbandTrackIndices.set(i); + } + + // newly appeared inband track if (tracks[i].getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) { - if (i < mInbandSubtitleTracks.length) { - inbandTracks[i] = mInbandSubtitleTracks[i]; - } else { - SubtitleTrack track = mSubtitleController.addTrack( - tracks[i].getFormat()); - inbandTracks[i] = track; - } + SubtitleTrack track = mSubtitleController.addTrack( + tracks[i].getFormat()); + mIndexTrackPairs.add(Pair.create(i, track)); + } else { + mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(i, null)); } } - mInbandSubtitleTracks = inbandTracks; } - mSubtitleController.selectDefaultTrack(); } /* TODO: Limit the total number of external timed text source to a reasonable number. @@ -2438,7 +2472,9 @@ public class MediaPlayer implements SubtitleController.Listener mSubtitleController.registerRenderer(new SRTRenderer(context, mEventHandler)); } final SubtitleTrack track = mSubtitleController.addTrack(fFormat); - mOutOfBandSubtitleTracks.add(track); + synchronized (mIndexTrackPairs) { + mIndexTrackPairs.add(Pair.<Integer, SubtitleTrack>create(null, track)); + } final FileDescriptor fd3 = fd2; final long offset2 = offset; @@ -2510,12 +2546,18 @@ public class MediaPlayer implements SubtitleController.Listener * @see #deselectTrack(int) */ public int getSelectedTrack(int trackType) throws IllegalStateException { - if (trackType == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE && mSubtitleController != null) { + if (mSubtitleController != null + && (trackType == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE + || trackType == TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT)) { SubtitleTrack subtitleTrack = mSubtitleController.getSelectedTrack(); if (subtitleTrack != null) { - int index = mOutOfBandSubtitleTracks.indexOf(subtitleTrack); - if (index >= 0) { - return mInbandSubtitleTracks.length + index; + synchronized (mIndexTrackPairs) { + for (int i = 0; i < mIndexTrackPairs.size(); i++) { + Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i); + if (p.second == subtitleTrack && subtitleTrack.getTrackType() == trackType) { + return i; + } + } } } } @@ -2527,8 +2569,16 @@ public class MediaPlayer implements SubtitleController.Listener request.writeInt(INVOKE_ID_GET_SELECTED_TRACK); request.writeInt(trackType); invoke(request, reply); - int selectedTrack = reply.readInt(); - return selectedTrack; + int inbandTrackIndex = reply.readInt(); + synchronized (mIndexTrackPairs) { + for (int i = 0; i < mIndexTrackPairs.size(); i++) { + Pair<Integer, SubtitleTrack> p = mIndexTrackPairs.get(i); + if (p.first != null && p.first == inbandTrackIndex) { + return i; + } + } + } + return -1; } finally { request.recycle(); reply.recycle(); @@ -2588,36 +2638,30 @@ public class MediaPlayer implements SubtitleController.Listener private void selectOrDeselectTrack(int index, boolean select) throws IllegalStateException { // handle subtitle track through subtitle controller - SubtitleTrack track = null; - synchronized (mInbandSubtitleLock) { - if (mInbandSubtitleTracks.length == 0) { - TrackInfo[] tracks = getInbandTrackInfo(); - mInbandSubtitleTracks = new SubtitleTrack[tracks.length]; - for (int i=0; i < tracks.length; i++) { - if (tracks[i].getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) { - mInbandSubtitleTracks[i] = mSubtitleController.addTrack(tracks[i].getFormat()); - } - } - } + populateInbandTracks(); + + Pair<Integer,SubtitleTrack> p = null; + try { + p = mIndexTrackPairs.get(index); + } catch (ArrayIndexOutOfBoundsException e) { + // ignore bad index + return; } - if (index < mInbandSubtitleTracks.length) { - track = mInbandSubtitleTracks[index]; - } else if (index < mInbandSubtitleTracks.length + mOutOfBandSubtitleTracks.size()) { - track = mOutOfBandSubtitleTracks.get(index - mInbandSubtitleTracks.length); + SubtitleTrack track = p.second; + if (track == null) { + // inband (de)select + selectOrDeselectInbandTrack(p.first, select); + return; } - if (mSubtitleController != null && track != null) { - if (select) { - if (track.isTimedText()) { - int ttIndex = getSelectedTrack(TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT); - if (ttIndex >= 0 && ttIndex < mInbandSubtitleTracks.length) { - // deselect inband counterpart - selectOrDeselectInbandTrack(ttIndex, false); - } - } - mSubtitleController.selectTrack(track); - } else if (mSubtitleController.getSelectedTrack() == track) { + if (mSubtitleController == null) { + return; + } + + if (!select) { + // out-of-band deselect + if (mSubtitleController.getSelectedTrack() == track) { mSubtitleController.selectTrack(null); } else { Log.w(TAG, "trying to deselect track that was not selected"); @@ -2625,7 +2669,20 @@ public class MediaPlayer implements SubtitleController.Listener return; } - selectOrDeselectInbandTrack(index, select); + // out-of-band select + if (track.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) { + int ttIndex = getSelectedTrack(TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT); + synchronized (mIndexTrackPairs) { + if (ttIndex >= 0 && ttIndex < mIndexTrackPairs.size()) { + Pair<Integer,SubtitleTrack> p2 = mIndexTrackPairs.get(ttIndex); + if (p2.first != null && p2.second == null) { + // deselect inband counterpart + selectOrDeselectInbandTrack(p2.first, false); + } + } + } + } + mSubtitleController.selectTrack(track); } private void selectOrDeselectInbandTrack(int index, boolean select) diff --git a/media/java/android/media/SubtitleController.java b/media/java/android/media/SubtitleController.java index f82dbe0702f2..fd72b39b17ab 100644 --- a/media/java/android/media/SubtitleController.java +++ b/media/java/android/media/SubtitleController.java @@ -20,6 +20,7 @@ import java.util.Locale; import java.util.Vector; import android.content.Context; +import android.media.MediaPlayer.TrackInfo; import android.media.SubtitleTrack.RenderingWidget; import android.os.Handler; import android.os.Looper; @@ -275,7 +276,8 @@ public class SubtitleController { mSelectedTrack.getFormat().getInteger( MediaFormat.KEY_IS_FORCED_SUBTITLE, 0) != 0)) { show(); - } else if (mSelectedTrack != null && !mSelectedTrack.isTimedText()) { + } else if (mSelectedTrack != null + && mSelectedTrack.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE) { hide(); } mVisibilityIsExplicit = false; diff --git a/media/java/android/media/SubtitleTrack.java b/media/java/android/media/SubtitleTrack.java index c760810ef4cd..6c8e3231da3a 100644 --- a/media/java/android/media/SubtitleTrack.java +++ b/media/java/android/media/SubtitleTrack.java @@ -17,6 +17,7 @@ package android.media; import android.graphics.Canvas; +import android.media.MediaPlayer.TrackInfo; import android.os.Handler; import android.util.Log; import android.util.LongSparseArray; @@ -609,8 +610,10 @@ public abstract class SubtitleTrack implements MediaTimeProvider.OnMediaTimeList } /** @hide whether this is a text track who fires events instead getting rendered */ - public boolean isTimedText() { - return getRenderingWidget() == null; + public int getTrackType() { + return getRenderingWidget() == null + ? TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT + : TrackInfo.MEDIA_TRACK_TYPE_SUBTITLE; } diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp index f8146a79b60d..d456dc1080d2 100644 --- a/media/jni/android_media_MediaDrm.cpp +++ b/media/jni/android_media_MediaDrm.cpp @@ -99,7 +99,7 @@ struct EventTypes { struct EventWhat { jint kWhatDrmEvent; jint kWhatExpirationUpdate; - jint kWhatKeysChange; + jint kWhatKeyStatusChange; } gEventWhat; struct KeyTypes { @@ -221,7 +221,7 @@ void JNIDrmListener::notify(DrmPlugin::EventType eventType, int extra, jwhat = gEventWhat.kWhatExpirationUpdate; break; case DrmPlugin::kDrmPluginEventKeysChange: - jwhat = gEventWhat.kWhatKeysChange; + jwhat = gEventWhat.kWhatKeyStatusChange; break; default: ALOGE("Invalid event DrmPlugin::EventType %d, ignored", (int)eventType); @@ -609,8 +609,8 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) { gEventWhat.kWhatDrmEvent = env->GetStaticIntField(clazz, field); GET_STATIC_FIELD_ID(field, clazz, "EXPIRATION_UPDATE", "I"); gEventWhat.kWhatExpirationUpdate = env->GetStaticIntField(clazz, field); - GET_STATIC_FIELD_ID(field, clazz, "KEYS_CHANGE", "I"); - gEventWhat.kWhatKeysChange = env->GetStaticIntField(clazz, field); + GET_STATIC_FIELD_ID(field, clazz, "KEY_STATUS_CHANGE", "I"); + gEventWhat.kWhatKeyStatusChange = env->GetStaticIntField(clazz, field); GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_STREAMING", "I"); gKeyTypes.kKeyTypeStreaming = env->GetStaticIntField(clazz, field); @@ -619,13 +619,6 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) { GET_STATIC_FIELD_ID(field, clazz, "KEY_TYPE_RELEASE", "I"); gKeyTypes.kKeyTypeRelease = env->GetStaticIntField(clazz, field); - GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_INITIAL", "I"); - gKeyRequestTypes.kKeyRequestTypeInitial = env->GetStaticIntField(clazz, field); - GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RENEWAL", "I"); - gKeyRequestTypes.kKeyRequestTypeRenewal = env->GetStaticIntField(clazz, field); - GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RELEASE", "I"); - gKeyRequestTypes.kKeyRequestTypeRelease = env->GetStaticIntField(clazz, field); - GET_STATIC_FIELD_ID(field, clazz, "CERTIFICATE_TYPE_NONE", "I"); gCertificateTypes.kCertificateTypeNone = env->GetStaticIntField(clazz, field); GET_STATIC_FIELD_ID(field, clazz, "CERTIFICATE_TYPE_X509", "I"); @@ -636,6 +629,13 @@ static void android_media_MediaDrm_native_init(JNIEnv *env) { GET_FIELD_ID(gFields.keyRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;"); GET_FIELD_ID(gFields.keyRequest.requestType, clazz, "mRequestType", "I"); + GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_INITIAL", "I"); + gKeyRequestTypes.kKeyRequestTypeInitial = env->GetStaticIntField(clazz, field); + GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RENEWAL", "I"); + gKeyRequestTypes.kKeyRequestTypeRenewal = env->GetStaticIntField(clazz, field); + GET_STATIC_FIELD_ID(field, clazz, "REQUEST_TYPE_RELEASE", "I"); + gKeyRequestTypes.kKeyRequestTypeRelease = env->GetStaticIntField(clazz, field); + FIND_CLASS(clazz, "android/media/MediaDrm$ProvisionRequest"); GET_FIELD_ID(gFields.provisionRequest.data, clazz, "mData", "[B"); GET_FIELD_ID(gFields.provisionRequest.defaultUrl, clazz, "mDefaultUrl", "Ljava/lang/String;"); diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp index 6c1bd972ccc6..8038cdf32263 100644 --- a/media/jni/soundpool/SoundPool.cpp +++ b/media/jni/soundpool/SoundPool.cpp @@ -229,7 +229,7 @@ bool SoundPool::unload(int sampleID) { ALOGV("unload: sampleID=%d", sampleID); Mutex::Autolock lock(&mLock); - return mSamples.removeItem(sampleID); + return mSamples.removeItem(sampleID) >= 0; // removeItem() returns index or BAD_VALUE } int SoundPool::play(int sampleID, float leftVolume, float rightVolume, diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java index cc20d391ab75..883c8c6ed75d 100644 --- a/obex/javax/obex/ClientOperation.java +++ b/obex/javax/obex/ClientOperation.java @@ -784,12 +784,12 @@ public final class ClientOperation implements Operation, BaseStream { mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE; } - while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) { + while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) { if (!sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL)) { break; } } - while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) { + while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE && !mOperationDone) { mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, null, mReplyHeader, mPrivateInput, false); // Regardless of the SRM state, wait for the response. diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index dda9358a8940..fd0ba73c27b2 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -95,6 +95,8 @@ <uses-permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"/> <uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" /> <uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" /> + <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /> + <uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS" /> <application android:label="@string/app_label"> <provider diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index 9d6d937e1883..e47c7a0cb221 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -184,16 +184,17 @@ <activity android:name=".tuner.TunerActivity" android:enabled="false" - android:icon="@*android:drawable/stat_sys_adb" + android:icon="@drawable/tuner" android:theme="@android:style/Theme.Material.Settings" android:label="@string/system_ui_tuner" + android:process=":tuner" android:exported="true"> <intent-filter> <action android:name="com.android.settings.action.EXTRA_SETTINGS" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <meta-data android:name="com.android.settings.category" - android:value="com.android.settings.category.device" /> + android:value="com.android.settings.category.system" /> </activity> <!-- Alternate Recents --> @@ -319,6 +320,19 @@ <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + + <activity android:name=".egg.ShruggyActivity" + android:theme="@android:style/Theme.NoDisplay" + android:exported="true" + android:launchMode="singleInstance" + android:screenOrientation="locked" + android:process=":sweetsweetdesserts" + android:excludeFromRecents="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.DEFAULT" /> <category android:name="com.android.internal.category.PLATLOGO" /> </intent-filter> </activity> diff --git a/packages/SystemUI/docs/demo_mode.md b/packages/SystemUI/docs/demo_mode.md index 18ae4cbd7832..258c76b85ee7 100644 --- a/packages/SystemUI/docs/demo_mode.md +++ b/packages/SystemUI/docs/demo_mode.md @@ -38,6 +38,8 @@ Command | Subcommand | Argument | Description | | ```datatype``` | Values: ```1x```, ```3g```, ```4g```, ```e```, ```g```, ```h```, ```lte```, ```roam```, any other value to hide | | ```level``` | Sets mobile signal strength level (null or 0-4) | ```carriernetworkchange``` | | Sets mobile signal icon to carrier network change UX when disconnected (```show``` to show icon, any other value to hide) + | ```sims``` | | Sets the number of sims (1-8) + | ```nosim``` | | ```show``` to show icon, any other value to hide ```bars``` | | | Control the visual style of the bars (opaque, translucent, etc) | ```mode``` | | Sets the bars visual style (opaque, translucent, semi-transparent) ```status``` | | | Control the system status icons diff --git a/packages/SystemUI/res/drawable-nodpi/icon.xml b/packages/SystemUI/res/drawable-nodpi/icon.xml index dc1e6339119b..7b8975a45427 100644 --- a/packages/SystemUI/res/drawable-nodpi/icon.xml +++ b/packages/SystemUI/res/drawable-nodpi/icon.xml @@ -14,47 +14,30 @@ Copyright (C) 2014 The Android Open Source Project limitations under the License. --> <vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="48dp" - android:height="48dp" - android:viewportWidth="560.0" - android:viewportHeight="560.0"> + android:width="48.0dp" + android:height="48.0dp" + android:viewportWidth="48.0" + android:viewportHeight="48.0"> <path - android:pathData="M280.000000,288.000000m-260.000000,0.000000a260.000000,260.000000 0.000000,1.000000 1.000000,520.000000 0.000000a260.000000,260.000000 0.000000,1.000000 1.000000,-520.000000 0.000000" - android:fillColor="#14000000"/> + android:pathData="M24.0,2.0C11.8,2.0 2.0,11.8 2.0,24.0c0.0,6.1 2.5,11.6 6.4,15.6L39.6,8.4C35.6,4.5 30.1,2.0 24.0,2.0z" + android:fillColor="#F57C00"/> <path - android:pathData="M280.000000,285.000000m-255.000000,0.000000a255.000000,255.000000 0.000000,1.000000 1.000000,510.000000 0.000000a255.000000,255.000000 0.000000,1.000000 1.000000,-510.000000 0.000000" - android:fillColor="#26000000"/> + android:pathData="M39.6,8.4L8.4,39.6c4.0,4.0 9.5,6.4 15.6,6.4c12.2,0.0 22.0,-9.8 22.0,-22.0C46.0,17.9 43.5,12.4 39.6,8.4z" + android:fillColor="#FF9800"/> <path - android:pathData="M280.000000,282.000000m-252.000000,0.000000a252.000000,252.000000 0.000000,1.000000 1.000000,504.000000 0.000000a252.000000,252.000000 0.000000,1.000000 1.000000,-504.000000 0.000000" - android:fillColor="#26000000"/> + android:pathData="M45.9,25.9L34.0,14.0L14.0,34.0l11.9,11.9C36.5,45.0 45.0,36.5 45.9,25.9z" + android:fillAlpha="0.33" + android:fillColor="#F57C00"/> <path - android:pathData="M280.000000,280.000000m-250.000000,0.000000a250.000000,250.000000 0.000000,1.000000 1.000000,500.000000 0.000000a250.000000,250.000000 0.000000,1.000000 1.000000,-500.000000 0.000000" - android:fillColor="#9C27B0"/> - <path - android:pathData="M265.786011,244.938004l0.000000,8.768000c6.527000,-6.384000 15.303000,-10.321000 25.063000,-10.321000c20.214001,0.000000 36.429001,16.500000 36.429001,36.714001c0.000000,20.072001 -16.215000,36.500000 -36.429001,36.500000c-9.759000,0.000000 -18.107000,-3.651000 -24.634001,-9.759000l0.000000,25.839001l-10.107000,0.000000l0.000000,-87.740997L265.786011,244.937988L265.786011,244.938004zM267.330994,266.490997c-0.420000,0.706000 -1.125000,1.821000 -1.125000,4.277000l0.000000,18.813000c0.000000,1.759000 0.420000,2.740000 0.982000,3.723000c4.634000,7.929000 13.197000,13.339000 22.882999,13.339000c14.671000,0.000000 26.742001,-11.999000 26.742001,-26.669001c0.000000,-14.536000 -12.071000,-26.607000 -26.742001,-26.607000C280.454987,253.365997 271.963989,258.625000 267.330994,266.490997z" - android:fillColor="#FFFFFF"/> - <path - android:pathData="M427.973999,244.938004l0.000000,8.768000c6.526000,-6.384000 15.304000,-10.321000 25.062000,-10.321000c20.215000,0.000000 36.429001,16.500000 36.429001,36.714001c0.000000,20.072001 -16.214001,36.500000 -36.429001,36.500000c-9.758000,0.000000 -18.106001,-3.651000 -24.634001,-9.759000l0.000000,25.839001l-10.107000,0.000000l0.000000,-87.740997L427.973999,244.937988L427.973999,244.938004zM429.518005,266.490997c-0.419000,0.706000 -1.125000,1.821000 -1.125000,4.277000l0.000000,18.813000c0.000000,1.759000 0.420000,2.740000 0.982000,3.723000c4.634000,7.929000 13.196000,13.339000 22.884001,13.339000c14.670000,0.000000 26.740999,-11.999000 26.740999,-26.669001c0.000000,-14.536000 -12.071000,-26.607000 -26.740999,-26.607000C442.643005,253.365997 434.152008,258.625000 429.518005,266.490997z" - android:fillColor="#FFFFFF"/> - <path - android:pathData="M181.330994,279.893005c0.000000,20.214001 -16.357000,36.715000 -36.438000,36.715000c-20.214001,0.000000 -36.643002,-16.500999 -36.643002,-36.715000c0.000000,-20.070999 16.419001,-36.500000 36.643002,-36.500000C164.973007,243.393005 181.330994,259.821014 181.330994,279.893005zM171.151993,280.036011c0.000000,-14.669000 -11.723000,-26.669001 -26.259001,-26.669001c-14.741000,0.000000 -26.250000,12.000000 -26.250000,26.669001c0.000000,14.536000 11.509000,26.607000 26.250000,26.607000C159.429001,306.634003 171.151993,294.562012 171.151993,280.036011z" - android:fillColor="#FFFFFF"/> - <path - android:pathData="M408.384003,279.893005c0.000000,20.214001 -16.357000,36.715000 -36.437000,36.715000c-20.215000,0.000000 -36.644001,-16.500999 -36.644001,-36.715000c0.000000,-20.070999 16.420000,-36.500000 36.644001,-36.500000C392.026001,243.393005 408.384003,259.821014 408.384003,279.893005zM398.204987,280.036011c0.000000,-14.669000 -11.723000,-26.669001 -26.257999,-26.669001c-14.742000,0.000000 -26.250999,12.000000 -26.250999,26.669001c0.000000,14.536000 11.509000,26.607000 26.250999,26.607000C386.481995,306.634003 398.204987,294.562012 398.204987,280.036011z" + android:pathData="M24.0,24.0c0.0,0.0 0.0,2.2 0.0,5.0s0.0,5.0 0.0,5.0l10.0,-10.0L34.0,14.0L24.0,24.0z" android:fillColor="#FFFFFF"/> <path - android:pathData="M234.481995,227.320999l9.973000,0.000000l0.000000,10.250000l-9.973000,0.000000z" - android:fillColor="#FFFFFF"/> - <path - android:pathData="M234.481995,244.865997l9.973000,0.000000l0.000000,70.195999l-9.973000,0.000000z" - android:fillColor="#FFFFFF"/> + android:pathData="M24.0,24.0L14.0,14.0l0.0,10.0l10.0,10.0c0.0,0.0 0.0,-2.2 0.0,-5.0S24.0,24.0 24.0,24.0z" + android:fillColor="#EEEEEE"/> <path - android:pathData="M88.392998,227.320999l9.973000,0.000000l0.000000,87.740997l-9.973000,0.000000z" - android:fillColor="#FFFFFF"/> + android:pathData="M14.0,34.0l10.0,0.0 -10.0,-10.0z" + android:fillColor="#DDDDDD"/> <path - android:pathData="M191.231995,227.320999l9.973000,0.000000l0.000000,87.740997l-9.973000,0.000000z" - android:fillColor="#FFFFFF"/> - <path - android:pathData="M212.856995,227.320999l9.974000,0.000000l0.000000,87.740997l-9.974000,0.000000z" - android:fillColor="#FFFFFF"/> + android:pathData="M34.0,34.0l0.0,-10.0 -10.0,10.0z" + android:fillColor="#DDDDDD"/> </vector> diff --git a/packages/SystemUI/res/drawable-nodpi/tuner.xml b/packages/SystemUI/res/drawable-nodpi/tuner.xml new file mode 100644 index 000000000000..e27423f275e1 --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/tuner.xml @@ -0,0 +1,27 @@ +<!-- + Copyright (C) 2015 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="48.0dp" + android:height="48.0dp" + android:viewportWidth="48.0" + android:viewportHeight="48.0"> + <path + android:fillColor="#FF000000" + android:pathData="M29.9,24.8c0.0,-0.3 0.1,-0.5 0.1,-0.8s0.0,-0.5 -0.1,-0.8l1.7,-1.3c0.2,-0.1 0.2,-0.3 0.1,-0.5l-1.6,-2.8c-0.1,-0.2 -0.3,-0.2 -0.5,-0.2l-2.0,0.8c-0.4,-0.3 -0.9,-0.6 -1.4,-0.8L26.0,16.3c0.0,-0.2 -0.2,-0.3 -0.4,-0.3l-3.2,0.0c-0.2,0.0 -0.4,0.1 -0.4,0.3l-0.3,2.1c-0.5,0.2 -0.9,0.5 -1.4,0.8l-2.0,-0.8c-0.2,-0.1 -0.4,0.0 -0.5,0.2l-1.6,2.8c-0.1,0.2 -0.1,0.4 0.1,0.5l1.7,1.3c0.0,0.3 -0.1,0.5 -0.1,0.8s0.0,0.5 0.1,0.8l-1.7,1.3c-0.2,0.1 -0.2,0.3 -0.1,0.5l1.6,2.8c0.1,0.2 0.3,0.2 0.5,0.2l2.0,-0.8c0.4,0.3 0.9,0.6 1.4,0.8l0.3,2.1c0.0,0.2 0.2,0.3 0.4,0.3l3.2,0.0c0.2,0.0 0.4,-0.1 0.4,-0.3l0.3,-2.1c0.5,-0.2 0.9,-0.5 1.4,-0.8l2.0,0.8c0.2,0.1 0.4,0.0 0.5,-0.2l1.6,-2.8c0.1,-0.2 0.1,-0.4 -0.1,-0.5L29.9,24.8zM24.0,26.8c-1.5,0.0 -2.8,-1.3 -2.8,-2.8s1.3,-2.8 2.8,-2.8s2.8,1.3 2.8,2.8S25.5,26.8 24.0,26.8z"/> + <path + android:fillColor="#FF000000" + android:pathData="M18.0,38.0c-0.6,0.0 -1.0,-0.4 -1.0,-1.0s0.4,-1.0 1.0,-1.0s1.0,0.4 1.0,1.0S18.6,38.0 18.0,38.0zM24.0,38.0c-0.6,0.0 -1.0,-0.4 -1.0,-1.0s0.4,-1.0 1.0,-1.0s1.0,0.4 1.0,1.0S24.6,38.0 24.0,38.0zM30.0,38.0c-0.6,0.0 -1.0,-0.4 -1.0,-1.0s0.4,-1.0 1.0,-1.0s1.0,0.4 1.0,1.0S30.6,38.0 30.0,38.0zM42.0,6.0L6.0,6.0c-2.2,0.0 -4.0,1.8 -4.0,4.0l0.0,28.0c0.0,2.2 1.8,4.0 4.0,4.0l36.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L46.0,10.0C46.0,7.8 44.2,6.0 42.0,6.0zM42.0,34.0L6.0,34.0L6.0,14.0l36.0,0.0L42.0,34.0zM9.0,12.0L7.0,12.0l0.0,-2.0l2.0,0.0L9.0,12.0zM13.0,12.0l-2.0,0.0l0.0,-2.0l2.0,0.0L13.0,12.0zM17.0,12.0l-2.0,0.0l0.0,-2.0l2.0,0.0L17.0,12.0z"/> +</vector> diff --git a/packages/SystemUI/res/layout/zen_mode_panel.xml b/packages/SystemUI/res/layout/zen_mode_panel.xml index 2160ca3ee303..731d4c14b518 100644 --- a/packages/SystemUI/res/layout/zen_mode_panel.xml +++ b/packages/SystemUI/res/layout/zen_mode_panel.xml @@ -61,6 +61,7 @@ android:layout_height="wrap_content" android:layout_marginTop="12dp" android:layout_marginStart="24dp" + android:textDirection="locale" android:lineSpacingMultiplier="1.20029" android:layout_toStartOf="@id/zen_introduction_confirm" android:textAppearance="@style/TextAppearance.QS.Introduction" /> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index aac03118f2bf..f1bbb0dc5cfb 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1023,7 +1023,7 @@ <string name="volume_stream_vibrate_dnd" translatable="false">%s vibrate — Priority only</string> <!-- Name of special SystemUI debug settings --> - <string name="system_ui_tuner">SystemUI Tuner</string> + <string name="system_ui_tuner">System UI tuner</string> <!-- Name of quick settings --> <string name="quick_settings">Quick Settings</string> @@ -1033,4 +1033,9 @@ <!-- Name of a quick settings tile controlled by broadcast --> <string name="broadcast_tile">Broadcast Tile</string> + + <!-- For preview release. DO NOT TRANSLATE --> + <string name="regrettable_lack_of_easter_egg"> + ¯\\_(ツ)_/¯ + </string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/egg/ShruggyActivity.java b/packages/SystemUI/src/com/android/systemui/egg/ShruggyActivity.java new file mode 100644 index 000000000000..745995759be9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/egg/ShruggyActivity.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.egg; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.widget.Toast; +import com.android.systemui.R; + +public class ShruggyActivity extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Toast.makeText(this, getString(R.string.regrettable_lack_of_easter_egg), + Toast.LENGTH_SHORT).show(); + Log.v("SystemUI", "Hey, it's just a preview; what did you expect?"); + finish(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java index 3b217dfa9170..72bb136bcc99 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -24,23 +24,21 @@ import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import android.view.View; import android.view.ViewGroup; -import com.android.internal.logging.MetricsLogger; import com.android.systemui.qs.QSTile.State; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.FlashlightController; +import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.KeyguardMonitor; import com.android.systemui.statusbar.policy.Listenable; import com.android.systemui.statusbar.policy.LocationController; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.RotationLockController; -import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.ZenModeController; import java.util.Collection; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index 172133571215..dcf0438b64ad 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -30,9 +30,11 @@ import com.android.systemui.qs.QSTile; import com.android.systemui.qs.QSTileView; import com.android.systemui.qs.SignalTileView; import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkController.IconState; import com.android.systemui.statusbar.policy.NetworkController.MobileDataController; import com.android.systemui.statusbar.policy.NetworkController.MobileDataController.DataUsageInfo; -import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; +import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import com.android.systemui.statusbar.policy.SignalCallbackAdapter; /** Quick settings tile: Cellular **/ public class CellularTile extends QSTile<QSTile.SignalState> { @@ -63,9 +65,9 @@ public class CellularTile extends QSTile<QSTile.SignalState> { @Override public void setListening(boolean listening) { if (listening) { - mController.addNetworkSignalChangedCallback(mCallback); + mController.addSignalCallback(mSignalCallback); } else { - mController.removeNetworkSignalChangedCallback(mCallback); + mController.removeSignalCallback(mSignalCallback); } } @@ -138,7 +140,6 @@ public class CellularTile extends QSTile<QSTile.SignalState> { private static final class CallbackInfo { boolean enabled; boolean wifiEnabled; - boolean wifiConnected; boolean airplaneModeEnabled; int mobileSignalIconId; String signalContentDescription; @@ -151,40 +152,39 @@ public class CellularTile extends QSTile<QSTile.SignalState> { boolean isDataTypeIconWide; } - private final NetworkSignalChangedCallback mCallback = new NetworkSignalChangedCallback() { + private final SignalCallback mSignalCallback = new SignalCallbackAdapter() { private final CallbackInfo mInfo = new CallbackInfo(); - @Override - public void onWifiSignalChanged(boolean enabled, boolean connected, int wifiSignalIconId, - boolean activityIn, boolean activityOut, - String wifiSignalContentDescriptionId, String description) { + public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, + boolean activityIn, boolean activityOut, String description) { mInfo.wifiEnabled = enabled; - mInfo.wifiConnected = connected; refreshState(mInfo); } @Override - public void onMobileDataSignalChanged(boolean enabled, - int mobileSignalIconId, - String mobileSignalContentDescriptionId, int dataTypeIconId, - boolean activityIn, boolean activityOut, - String dataTypeContentDescriptionId, String description, - boolean isDataTypeIconWide) { - mInfo.enabled = enabled; - mInfo.mobileSignalIconId = mobileSignalIconId; - mInfo.signalContentDescription = mobileSignalContentDescriptionId; - mInfo.dataTypeIconId = dataTypeIconId; - mInfo.dataContentDescription = dataTypeContentDescriptionId; + public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, + int darkStatusIcon, int statusType, int qsType, boolean activityIn, + boolean activityOut, String typeContentDescription, String description, + boolean isWide, int subId) { + if (qsIcon == null) { + // Not data sim, don't display. + return; + } + mInfo.enabled = qsIcon.visible; + mInfo.mobileSignalIconId = qsIcon.icon; + mInfo.signalContentDescription = qsIcon.contentDescription; + mInfo.dataTypeIconId = qsType; + mInfo.dataContentDescription = typeContentDescription; mInfo.activityIn = activityIn; mInfo.activityOut = activityOut; mInfo.enabledDesc = description; - mInfo.isDataTypeIconWide = isDataTypeIconWide; + mInfo.isDataTypeIconWide = qsType != 0 && isWide; refreshState(mInfo); } @Override - public void onNoSimVisibleChanged(boolean visible) { - mInfo.noSim = visible; + public void setNoSims(boolean show) { + mInfo.noSim = show; if (mInfo.noSim) { // Make sure signal gets cleared out when no sims. mInfo.mobileSignalIconId = 0; @@ -199,12 +199,13 @@ public class CellularTile extends QSTile<QSTile.SignalState> { } @Override - public void onAirplaneModeChanged(boolean enabled) { - mInfo.airplaneModeEnabled = enabled; + public void setIsAirplaneMode(IconState icon) { + mInfo.airplaneModeEnabled = icon.visible; refreshState(mInfo); } - public void onMobileDataEnabled(boolean enabled) { + @Override + public void setMobileDataEnabled(boolean enabled) { mDetailAdapter.setMobileDataEnabled(enabled); } }; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index c3f9e333e422..9504ea37bb05 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -16,8 +16,6 @@ package com.android.systemui.qs.tiles; -import java.util.List; - import android.content.Context; import android.content.Intent; import android.content.res.Resources; @@ -36,7 +34,11 @@ import com.android.systemui.qs.QSTileView; import com.android.systemui.qs.SignalTileView; import com.android.systemui.statusbar.policy.NetworkController; import com.android.systemui.statusbar.policy.NetworkController.AccessPointController; -import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; +import com.android.systemui.statusbar.policy.NetworkController.IconState; +import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import com.android.systemui.statusbar.policy.SignalCallbackAdapter; + +import java.util.List; /** Quick settings tile: Wifi **/ public class WifiTile extends QSTile<QSTile.SignalState> { @@ -67,9 +69,9 @@ public class WifiTile extends QSTile<QSTile.SignalState> { @Override public void setListening(boolean listening) { if (listening) { - mController.addNetworkSignalChangedCallback(mCallback); + mController.addSignalCallback(mSignalCallback); } else { - mController.removeNetworkSignalChangedCallback(mCallback); + mController.removeSignalCallback(mSignalCallback); } } @@ -211,46 +213,21 @@ public class WifiTile extends QSTile<QSTile.SignalState> { } } - private final NetworkSignalChangedCallback mCallback = new NetworkSignalChangedCallback() { + private final SignalCallback mSignalCallback = new SignalCallbackAdapter() { @Override - public void onWifiSignalChanged(boolean enabled, boolean connected, int wifiSignalIconId, - boolean activityIn, boolean activityOut, - String wifiSignalContentDescriptionId, String description) { + public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, + boolean activityIn, boolean activityOut, String description) { if (DEBUG) Log.d(TAG, "onWifiSignalChanged enabled=" + enabled); final CallbackInfo info = new CallbackInfo(); info.enabled = enabled; - info.connected = connected; - info.wifiSignalIconId = wifiSignalIconId; + info.connected = qsIcon.visible; + info.wifiSignalIconId = qsIcon.icon; info.enabledDesc = description; info.activityIn = activityIn; info.activityOut = activityOut; - info.wifiSignalContentDescription = wifiSignalContentDescriptionId; + info.wifiSignalContentDescription = qsIcon.contentDescription; refreshState(info); } - - @Override - public void onMobileDataSignalChanged(boolean enabled, - int mobileSignalIconId, - String mobileSignalContentDescriptionId, int dataTypeIconId, - boolean activityIn, boolean activityOut, - String dataTypeContentDescriptionId, String description, - boolean isDataTypeIconWide) { - // noop - } - - public void onNoSimVisibleChanged(boolean noSims) { - // noop - } - - @Override - public void onAirplaneModeChanged(boolean enabled) { - // noop - } - - @Override - public void onMobileDataEnabled(boolean enabled) { - // noop - } }; private final class WifiDetailAdapter implements DetailAdapter, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index f6629ddbd325..14e491b2f9ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -33,6 +33,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import com.android.systemui.R; +import com.android.systemui.statusbar.policy.NetworkController.IconState; import com.android.systemui.statusbar.policy.NetworkControllerImpl; import com.android.systemui.statusbar.policy.SecurityController; @@ -42,7 +43,7 @@ import java.util.List; // Intimately tied to the design of res/layout/signal_cluster_view.xml public class SignalClusterView extends LinearLayout - implements NetworkControllerImpl.SignalCluster, + implements NetworkControllerImpl.SignalCallback, SecurityController.SecurityControllerCallback { static final String TAG = "SignalClusterView"; @@ -59,7 +60,7 @@ public class SignalClusterView private int mWifiStrengthId = 0; private boolean mIsAirplaneMode = false; private int mAirplaneIconId = 0; - private int mAirplaneContentDescription; + private String mAirplaneContentDescription; private String mWifiDescription; private String mEthernetDescription; private ArrayList<PhoneState> mPhoneStates = new ArrayList<PhoneState>(); @@ -166,35 +167,36 @@ public class SignalClusterView } @Override - public void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription) { - mWifiVisible = visible; - mWifiStrengthId = strengthIcon; - mWifiDescription = contentDescription; + public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, + boolean activityIn, boolean activityOut, String description) { + mWifiVisible = statusIcon.visible; + mWifiStrengthId = statusIcon.icon; + mWifiDescription = statusIcon.contentDescription; apply(); } @Override - public void setMobileDataIndicators(boolean visible, int strengthIcon, int darkStrengthIcon, - int typeIcon, String contentDescription, String typeContentDescription, - boolean isTypeIconWide, int subId) { + public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int darkStatusIcon, + int statusType, int qsType, boolean activityIn, boolean activityOut, + String typeContentDescription, String description, boolean isWide, int subId) { PhoneState state = getOrInflateState(subId); - state.mMobileVisible = visible; - state.mMobileStrengthId = strengthIcon; - state.mMobileDarkStrengthId = darkStrengthIcon; - state.mMobileTypeId = typeIcon; - state.mMobileDescription = contentDescription; + state.mMobileVisible = statusIcon.visible; + state.mMobileStrengthId = statusIcon.icon; + state.mMobileDarkStrengthId = darkStatusIcon; + state.mMobileTypeId = statusType; + state.mMobileDescription = statusIcon.contentDescription; state.mMobileTypeDescription = typeContentDescription; - state.mIsMobileTypeIconWide = isTypeIconWide; + state.mIsMobileTypeIconWide = statusType != 0 && isWide; apply(); } @Override - public void setEthernetIndicators(boolean visible, int icon, String contentDescription) { - mEthernetVisible = visible; - mEthernetIconId = icon; - mEthernetDescription = contentDescription; + public void setEthernetIndicators(IconState state) { + mEthernetVisible = state.visible; + mEthernetIconId = state.icon; + mEthernetDescription = state.contentDescription; apply(); } @@ -239,15 +241,20 @@ public class SignalClusterView } @Override - public void setIsAirplaneMode(boolean is, int airplaneIconId, int contentDescription) { - mIsAirplaneMode = is; - mAirplaneIconId = airplaneIconId; - mAirplaneContentDescription = contentDescription; + public void setIsAirplaneMode(IconState icon) { + mIsAirplaneMode = icon.visible; + mAirplaneIconId = icon.icon; + mAirplaneContentDescription = icon.contentDescription; apply(); } @Override + public void setMobileDataEnabled(boolean enabled) { + // Don't care. + } + + @Override public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { // Standard group layout onPopulateAccessibilityEvent() implementations // ignore content description, so populate manually @@ -343,8 +350,7 @@ public class SignalClusterView if (mIsAirplaneMode) { mAirplane.setImageResource(mAirplaneIconId); - mAirplane.setContentDescription(mAirplaneContentDescription != 0 ? - mContext.getString(mAirplaneContentDescription) : null); + mAirplane.setContentDescription(mAirplaneContentDescription); mAirplane.setVisibility(View.VISIBLE); } else { mAirplane.setVisibility(View.GONE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 887b8f4fe2d3..2a9df19da600 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -779,9 +779,9 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, (SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster); final SignalClusterView signalClusterQs = (SignalClusterView) mHeader.findViewById(R.id.signal_cluster); - mNetworkController.addSignalCluster(signalCluster); - mNetworkController.addSignalCluster(signalClusterKeyguard); - mNetworkController.addSignalCluster(signalClusterQs); + mNetworkController.addSignalCallback(signalCluster); + mNetworkController.addSignalCallback(signalClusterKeyguard); + mNetworkController.addSignalCallback(signalClusterQs); signalCluster.setSecurityController(mSecurityController); signalCluster.setNetworkController(mNetworkController); signalClusterKeyguard.setSecurityController(mSecurityController); @@ -3087,6 +3087,16 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } mContext.unregisterReceiver(mBroadcastReceiver); mAssistManager.destroy(); + + final SignalClusterView signalCluster = + (SignalClusterView) mStatusBarView.findViewById(R.id.signal_cluster); + final SignalClusterView signalClusterKeyguard = + (SignalClusterView) mKeyguardStatusBar.findViewById(R.id.signal_cluster); + final SignalClusterView signalClusterQs = + (SignalClusterView) mHeader.findViewById(R.id.signal_cluster); + mNetworkController.addSignalCallback(signalCluster); + mNetworkController.addSignalCallback(signalClusterKeyguard); + mNetworkController.addSignalCallback(signalClusterQs); } private boolean mDemoModeAllowed; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java new file mode 100644 index 000000000000..7f5219177e5f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.policy; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.telephony.SubscriptionInfo; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.statusbar.policy.NetworkController.IconState; +import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import com.android.systemui.statusbar.policy.NetworkControllerImpl.EmergencyListener; + +import java.util.ArrayList; +import java.util.List; + + +/** + * Implements network listeners and forwards the calls along onto other listeners but on + * the current or specified Looper. + */ +public class CallbackHandler extends Handler implements EmergencyListener, SignalCallback { + private static final int MSG_EMERGENCE_CHANGED = 0; + private static final int MSG_SUBS_CHANGED = 1; + private static final int MSG_NO_SIM_VISIBLE_CHANGED = 2; + private static final int MSG_ETHERNET_CHANGED = 3; + private static final int MSG_AIRPLANE_MODE_CHANGED = 4; + private static final int MSG_MOBILE_DATA_ENABLED_CHANGED = 5; + private static final int MSG_ADD_REMOVE_EMERGENCY = 6; + private static final int MSG_ADD_REMOVE_SIGNAL = 7; + + // All the callbacks. + private final ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<>(); + private final ArrayList<SignalCallback> mSignalCallbacks = new ArrayList<>(); + + public CallbackHandler() { + super(); + } + + @VisibleForTesting + CallbackHandler(Looper looper) { + super(looper); + } + + @Override + @SuppressWarnings("unchecked") + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_EMERGENCE_CHANGED: + for (EmergencyListener listener : mEmergencyListeners) { + listener.setEmergencyCallsOnly(msg.arg1 != 0); + } + break; + case MSG_SUBS_CHANGED: + for (SignalCallback signalCluster : mSignalCallbacks) { + signalCluster.setSubs((List<SubscriptionInfo>) msg.obj); + } + break; + case MSG_NO_SIM_VISIBLE_CHANGED: + for (SignalCallback signalCluster : mSignalCallbacks) { + signalCluster.setNoSims(msg.arg1 != 0); + } + break; + case MSG_ETHERNET_CHANGED: + for (SignalCallback signalCluster : mSignalCallbacks) { + signalCluster.setEthernetIndicators((IconState) msg.obj); + } + break; + case MSG_AIRPLANE_MODE_CHANGED: + for (SignalCallback signalCluster : mSignalCallbacks) { + signalCluster.setIsAirplaneMode((IconState) msg.obj); + } + break; + case MSG_MOBILE_DATA_ENABLED_CHANGED: + for (SignalCallback signalCluster : mSignalCallbacks) { + signalCluster.setMobileDataEnabled(msg.arg1 != 0); + } + break; + case MSG_ADD_REMOVE_EMERGENCY: + if (msg.arg1 != 0) { + mEmergencyListeners.add((EmergencyListener) msg.obj); + } else { + mEmergencyListeners.remove((EmergencyListener) msg.obj); + } + break; + case MSG_ADD_REMOVE_SIGNAL: + if (msg.arg1 != 0) { + mSignalCallbacks.add((SignalCallback) msg.obj); + } else { + mSignalCallbacks.remove((SignalCallback) msg.obj); + } + break; + } + } + + @Override + public void setWifiIndicators(final boolean enabled, final IconState statusIcon, + final IconState qsIcon, final boolean activityIn, final boolean activityOut, + final String description) { + post(new Runnable() { + @Override + public void run() { + for (SignalCallback callback : mSignalCallbacks) { + callback.setWifiIndicators(enabled, statusIcon, qsIcon, activityIn, activityOut, + description); + } + } + }); + } + + @Override + public void setMobileDataIndicators(final IconState statusIcon, final IconState qsIcon, + final int darkStatusIcon, final int statusType, final int qsType, + final boolean activityIn, final boolean activityOut, + final String typeContentDescription, final String description, final boolean isWide, + final int subId) { + post(new Runnable() { + @Override + public void run() { + for (SignalCallback signalCluster : mSignalCallbacks) { + signalCluster.setMobileDataIndicators(statusIcon, qsIcon, darkStatusIcon, + statusType, qsType, activityIn, activityOut, typeContentDescription, + description, isWide, subId); + } + } + }); + } + + @Override + public void setSubs(List<SubscriptionInfo> subs) { + obtainMessage(MSG_SUBS_CHANGED, subs).sendToTarget(); + } + + @Override + public void setNoSims(boolean show) { + obtainMessage(MSG_NO_SIM_VISIBLE_CHANGED, show ? 1 : 0, 0).sendToTarget(); + } + + @Override + public void setMobileDataEnabled(boolean enabled) { + obtainMessage(MSG_MOBILE_DATA_ENABLED_CHANGED, enabled ? 1 : 0, 0).sendToTarget(); + } + + @Override + public void setEmergencyCallsOnly(boolean emergencyOnly) { + obtainMessage(MSG_EMERGENCE_CHANGED, emergencyOnly ? 1 : 0, 0).sendToTarget(); + } + + @Override + public void setEthernetIndicators(IconState icon) { + obtainMessage(MSG_ETHERNET_CHANGED, icon).sendToTarget();; + } + + @Override + public void setIsAirplaneMode(IconState icon) { + obtainMessage(MSG_AIRPLANE_MODE_CHANGED, icon).sendToTarget();; + } + + public void setListening(EmergencyListener listener, boolean listening) { + obtainMessage(MSG_ADD_REMOVE_EMERGENCY, listening ? 1 : 0, 0, listener).sendToTarget(); + } + + public void setListening(SignalCallback listener, boolean listening) { + obtainMessage(MSG_ADD_REMOVE_SIGNAL, listening ? 1 : 0, 0, listener).sendToTarget(); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java index 9c044c4b87c9..bd36462ca612 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/EthernetSignalController.java @@ -18,22 +18,18 @@ package com.android.systemui.statusbar.policy; import android.content.Context; import android.net.NetworkCapabilities; -import com.android.internal.annotations.VisibleForTesting; -import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; -import com.android.systemui.statusbar.policy.NetworkControllerImpl.SignalCluster; +import com.android.systemui.statusbar.policy.NetworkController.IconState; -import java.util.List; -import java.util.Objects; +import java.util.BitSet; public class EthernetSignalController extends SignalController<SignalController.State, SignalController.IconGroup> { public EthernetSignalController(Context context, - List<NetworkSignalChangedCallback> signalCallbacks, - List<SignalCluster> signalClusters, NetworkControllerImpl networkController) { + CallbackHandler callbackHandler, NetworkControllerImpl networkController) { super("EthernetSignalController", context, NetworkCapabilities.TRANSPORT_ETHERNET, - signalCallbacks, signalClusters, networkController); + callbackHandler, networkController); mCurrentState.iconGroup = mLastState.iconGroup = new IconGroup( "Ethernet Icons", EthernetIcons.ETHERNET_ICONS, @@ -44,25 +40,23 @@ public class EthernetSignalController extends } @Override + public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) { + mCurrentState.connected = connectedTransports.get(mTransportType); + super.updateConnectivity(connectedTransports, validatedTransports); + } + + @Override public void notifyListeners() { boolean ethernetVisible = mCurrentState.connected; String contentDescription = getStringIfExists(getContentDescription()); // TODO: wire up data transfer using WifiSignalPoller. - int signalClustersLength = mSignalClusters.size(); - for (int i = 0; i < signalClustersLength; i++) { - mSignalClusters.get(i).setEthernetIndicators(ethernetVisible, getCurrentIconId(), - contentDescription); - } + mCallbackHandler.setEthernetIndicators(new IconState(ethernetVisible, getCurrentIconId(), + contentDescription)); } @Override public SignalController.State cleanState() { return new SignalController.State(); } - - public void setConnected(boolean connected) { - mCurrentState.connected = connected; - notifyListenersIfNecessary(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 22bf47ce8318..0d5995325231 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.policy; import android.content.Context; import android.content.Intent; import android.net.NetworkCapabilities; +import android.os.Looper; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; @@ -31,12 +32,11 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.cdma.EriInfo; import com.android.systemui.R; -import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; +import com.android.systemui.statusbar.policy.NetworkController.IconState; import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config; -import com.android.systemui.statusbar.policy.NetworkControllerImpl.SignalCluster; import java.io.PrintWriter; -import java.util.List; +import java.util.BitSet; import java.util.Objects; @@ -66,17 +66,17 @@ public class MobileSignalController extends SignalController< // TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't // need listener lists anymore. public MobileSignalController(Context context, Config config, boolean hasMobileData, - TelephonyManager phone, List<NetworkSignalChangedCallback> signalCallbacks, - List<SignalCluster> signalClusters, NetworkControllerImpl networkController, - SubscriptionInfo info) { + TelephonyManager phone, CallbackHandler callbackHandler, + NetworkControllerImpl networkController, SubscriptionInfo info, Looper receiverLooper) { super("MobileSignalController(" + info.getSubscriptionId() + ")", context, - NetworkCapabilities.TRANSPORT_CELLULAR, signalCallbacks, signalClusters, + NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler, networkController); mNetworkToIconLookup = new SparseArray<>(); mConfig = config; mPhone = phone; mSubscriptionInfo = info; - mPhoneStateListener = new MobilePhoneStateListener(info.getSubscriptionId()); + mPhoneStateListener = new MobilePhoneStateListener(info.getSubscriptionId(), + receiverLooper); mNetworkNameSeparator = getStringIfExists(R.string.status_bar_network_name_separator); mNetworkNameDefault = getStringIfExists( com.android.internal.R.string.lockscreen_carrier_default); @@ -106,13 +106,13 @@ public class MobileSignalController extends SignalController< notifyListenersIfNecessary(); } - public void setInetCondition(int inetCondition, int inetConditionForNetwork) { - // For mobile data, use general inet condition for phone signal indexing, - // and network specific for data indexing (I think this might be a bug, but - // keeping for now). - // TODO: Update with explanation of why. - mCurrentState.inetForNetwork = inetConditionForNetwork; - setInetCondition(inetCondition); + @Override + public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) { + boolean isValidated = validatedTransports.get(mTransportType); + mCurrentState.isDefault = connectedTransports.get(mTransportType); + // Only show this as not having connectivity if we are default. + mCurrentState.inetCondition = (isValidated || !mCurrentState.isDefault) ? 1 : 0; + notifyListenersIfNecessary(); } public void setCarrierNetworkChangeMode(boolean carrierNetworkChangeMode) { @@ -196,44 +196,33 @@ public class MobileSignalController extends SignalController< String contentDescription = getStringIfExists(getContentDescription()); String dataContentDescription = getStringIfExists(icons.mDataContentDescription); - boolean showDataIcon = mCurrentState.dataConnected && mCurrentState.inetForNetwork != 0 + // Show icon in QS when we are connected or need to show roaming. + boolean showDataIcon = mCurrentState.dataConnected || mCurrentState.iconGroup == TelephonyIcons.ROAMING; + IconState statusIcon = new IconState(mCurrentState.enabled && !mCurrentState.airplaneMode, + getCurrentIconId(), contentDescription); + int qsTypeIcon = 0; + IconState qsIcon = null; + String description = null; // Only send data sim callbacks to QS. if (mCurrentState.dataSim) { - int qsTypeIcon = showDataIcon ? icons.mQsDataType[mCurrentState.inetForNetwork] : 0; - int length = mSignalsChangedCallbacks.size(); - for (int i = 0; i < length; i++) { - mSignalsChangedCallbacks.get(i).onMobileDataSignalChanged(mCurrentState.enabled - && !mCurrentState.isEmergency, - getQsCurrentIconId(), contentDescription, - qsTypeIcon, - mCurrentState.dataConnected - && !mCurrentState.carrierNetworkChangeMode - && mCurrentState.activityIn, - mCurrentState.dataConnected - && !mCurrentState.carrierNetworkChangeMode - && mCurrentState.activityOut, - dataContentDescription, - mCurrentState.isEmergency ? null : mCurrentState.networkName, - // Only wide if actually showing something. - icons.mIsWide && qsTypeIcon != 0); - } - } + qsTypeIcon = showDataIcon ? icons.mQsDataType : 0; + qsIcon = new IconState(mCurrentState.enabled + && !mCurrentState.isEmergency, getQsCurrentIconId(), contentDescription); + description = mCurrentState.isEmergency ? null : mCurrentState.networkName; + } + boolean activityIn = mCurrentState.dataConnected + && !mCurrentState.carrierNetworkChangeMode + && mCurrentState.activityIn; + boolean activityOut = mCurrentState.dataConnected + && !mCurrentState.carrierNetworkChangeMode + && mCurrentState.activityOut; + showDataIcon &= mCurrentState.isDefault; int typeIcon = showDataIcon ? icons.mDataType : 0; - int signalClustersLength = mSignalClusters.size(); - for (int i = 0; i < signalClustersLength; i++) { - mSignalClusters.get(i).setMobileDataIndicators( - mCurrentState.enabled && !mCurrentState.airplaneMode, - getCurrentIconId(), - getCurrentDarkIconId(), - typeIcon, - contentDescription, - dataContentDescription, - // Only wide if actually showing something. - icons.mIsWide && typeIcon != 0, - mSubscriptionInfo.getSubscriptionId()); - } + mCallbackHandler.setMobileDataIndicators(statusIcon, qsIcon, getCurrentDarkIconId(), + typeIcon, qsTypeIcon, activityIn, activityOut, dataContentDescription, description, + icons.mIsWide, mSubscriptionInfo.getSubscriptionId()); } private int getCurrentDarkIconId() { @@ -425,8 +414,8 @@ public class MobileSignalController extends SignalController< } class MobilePhoneStateListener extends PhoneStateListener { - public MobilePhoneStateListener(int subId) { - super(subId); + public MobilePhoneStateListener(int subId, Looper looper) { + super(subId, looper); } @Override @@ -483,12 +472,12 @@ public class MobileSignalController extends SignalController< final int mDataContentDescription; // mContentDescriptionDataType final int mDataType; final boolean mIsWide; - final int[] mQsDataType; + final int mQsDataType; public MobileIconGroup(String name, int[][] sbIcons, int[][] qsIcons, int[] contentDesc, int sbNullState, int qsNullState, int sbDiscState, int qsDiscState, int discContentDesc, int dataContentDesc, int dataType, boolean isWide, - int[] qsDataType) { + int qsDataType) { this(name, sbIcons, sbIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState, sbDiscState, qsDiscState, discContentDesc, dataContentDesc, dataType, isWide, qsDataType); @@ -497,7 +486,7 @@ public class MobileSignalController extends SignalController< public MobileIconGroup(String name, int[][] sbIcons, int[][] sbDarkIcons, int[][] qsIcons, int[] contentDesc, int sbNullState, int qsNullState, int sbDiscState, int sbDarkDiscState, int qsDiscState, int discContentDesc, int dataContentDesc, - int dataType, boolean isWide, int[] qsDataType) { + int dataType, boolean isWide, int qsDataType) { super(name, sbIcons, sbDarkIcons, qsIcons, contentDesc, sbNullState, qsNullState, sbDiscState, sbDarkDiscState, qsDiscState, discContentDesc); mDataContentDescription = dataContentDesc; @@ -515,7 +504,7 @@ public class MobileSignalController extends SignalController< boolean isEmergency; boolean airplaneMode; boolean carrierNetworkChangeMode; - int inetForNetwork; + boolean isDefault; @Override public void copyFrom(State s) { @@ -525,7 +514,7 @@ public class MobileSignalController extends SignalController< networkName = state.networkName; networkNameData = state.networkNameData; dataConnected = state.dataConnected; - inetForNetwork = state.inetForNetwork; + isDefault = state.isDefault; isEmergency = state.isEmergency; airplaneMode = state.airplaneMode; carrierNetworkChangeMode = state.carrierNetworkChangeMode; @@ -539,7 +528,7 @@ public class MobileSignalController extends SignalController< builder.append("networkName=").append(networkName).append(','); builder.append("networkNameData=").append(networkNameData).append(','); builder.append("dataConnected=").append(dataConnected).append(','); - builder.append("inetForNetwork=").append(inetForNetwork).append(','); + builder.append("isDefault=").append(isDefault).append(','); builder.append("isEmergency=").append(isEmergency).append(','); builder.append("airplaneMode=").append(airplaneMode).append(','); builder.append("carrierNetworkChangeMode=").append(carrierNetworkChangeMode); @@ -555,7 +544,7 @@ public class MobileSignalController extends SignalController< && ((MobileState) o).isEmergency == isEmergency && ((MobileState) o).airplaneMode == airplaneMode && ((MobileState) o).carrierNetworkChangeMode == carrierNetworkChangeMode - && ((MobileState) o).inetForNetwork == inetForNetwork; + && ((MobileState) o).isDefault == isDefault; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index 92128379bb90..070ca637d31e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -16,7 +16,9 @@ package com.android.systemui.statusbar.policy; +import android.content.Context; import android.content.Intent; +import android.telephony.SubscriptionInfo; import com.android.settingslib.wifi.AccessPoint; @@ -25,25 +27,45 @@ import java.util.List; public interface NetworkController { boolean hasMobileDataFeature(); - void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb); - void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb); + void addSignalCallback(SignalCallback cb); + void removeSignalCallback(SignalCallback cb); void setWifiEnabled(boolean enabled); void onUserSwitched(int newUserId); AccessPointController getAccessPointController(); MobileDataController getMobileDataController(); - public interface NetworkSignalChangedCallback { - void onWifiSignalChanged(boolean enabled, boolean connected, int wifiSignalIconId, - boolean activityIn, boolean activityOut, - String wifiSignalContentDescriptionId, String description); - void onMobileDataSignalChanged(boolean enabled, int mobileSignalIconId, - String mobileSignalContentDescriptionId, int dataTypeIconId, - boolean activityIn, boolean activityOut, - String dataTypeContentDescriptionId, String description, - boolean isDataTypeIconWide); - void onNoSimVisibleChanged(boolean visible); - void onAirplaneModeChanged(boolean enabled); - void onMobileDataEnabled(boolean enabled); + public interface SignalCallback { + void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, + boolean activityIn, boolean activityOut, String description); + + void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int darkStatusIcon, + int statusType, int qsType, boolean activityIn, boolean activityOut, + String typeContentDescription, String description, boolean isWide, int subId); + void setSubs(List<SubscriptionInfo> subs); + void setNoSims(boolean show); + + void setEthernetIndicators(IconState icon); + + void setIsAirplaneMode(IconState icon); + + void setMobileDataEnabled(boolean enabled); + } + + public static class IconState { + public final boolean visible; + public final int icon; + public final String contentDescription; + + public IconState(boolean visible, int icon, String contentDescription) { + this.visible = visible; + this.icon = icon; + this.contentDescription = contentDescription; + } + + public IconState(boolean visible, int icon, int contentDescription, + Context context) { + this(visible, icon, context.getString(contentDescription)); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 92e03654dc9c..e8957f972ec6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -28,6 +28,7 @@ import android.net.NetworkCapabilities; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Handler; import android.os.Looper; import android.provider.Settings; import android.telephony.SubscriptionInfo; @@ -36,6 +37,7 @@ import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.Log; +import android.util.MathUtils; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.telephony.PhoneConstants; @@ -61,7 +63,7 @@ public class NetworkControllerImpl extends BroadcastReceiver static final String TAG = "NetworkController"; static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); // additional diagnostics, but not logspew - static final boolean CHATTY = Log.isLoggable(TAG + ".Chat", Log.DEBUG); + static final boolean CHATTY = Log.isLoggable(TAG + "Chat", Log.DEBUG); private final Context mContext; private final TelephonyManager mPhone; @@ -99,20 +101,19 @@ public class NetworkControllerImpl extends BroadcastReceiver private boolean mHasNoSims; private Locale mLocale = null; // This list holds our ordering. - private List<SubscriptionInfo> mCurrentSubscriptions - = new ArrayList<SubscriptionInfo>(); - - // All the callbacks. - private ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<EmergencyListener>(); - private ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>(); - private ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks = - new ArrayList<NetworkSignalChangedCallback>(); + private List<SubscriptionInfo> mCurrentSubscriptions = new ArrayList<>(); + @VisibleForTesting boolean mListening; // The current user ID. private int mCurrentUserId; + // Handler that all broadcasts are received on. + private final Handler mReceiverHandler; + // Handler that all callbacks are made on. + private final CallbackHandler mCallbackHandler; + /** * Construct this controller object and register for updates. */ @@ -120,20 +121,24 @@ public class NetworkControllerImpl extends BroadcastReceiver this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE), (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE), (WifiManager) context.getSystemService(Context.WIFI_SERVICE), - SubscriptionManager.from(context), Config.readConfig(context), + SubscriptionManager.from(context), Config.readConfig(context), bgLooper, + new CallbackHandler(), new AccessPointControllerImpl(context, bgLooper), new MobileDataControllerImpl(context)); - registerListeners(); + mReceiverHandler.post(mRegisterListeners); } @VisibleForTesting NetworkControllerImpl(Context context, ConnectivityManager connectivityManager, TelephonyManager telephonyManager, WifiManager wifiManager, - SubscriptionManager subManager, Config config, + SubscriptionManager subManager, Config config, Looper bgLooper, + CallbackHandler callbackHandler, AccessPointControllerImpl accessPointController, MobileDataControllerImpl mobileDataController) { mContext = context; mConfig = config; + mReceiverHandler = new Handler(bgLooper); + mCallbackHandler = callbackHandler; mSubscriptionManager = subManager; mConnectivityManager = connectivityManager; @@ -141,7 +146,7 @@ public class NetworkControllerImpl extends BroadcastReceiver mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE); // telephony - mPhone = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); + mPhone = telephonyManager; // wifi mWifiManager = wifiManager; @@ -154,14 +159,13 @@ public class NetworkControllerImpl extends BroadcastReceiver mMobileDataController.setCallback(new MobileDataControllerImpl.Callback() { @Override public void onMobileDataEnabled(boolean enabled) { - notifyMobileDataEnabled(enabled); + mCallbackHandler.setMobileDataEnabled(enabled); } }); mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature, - mSignalsChangedCallbacks, mSignalClusters, this); + mCallbackHandler, this); - mEthernetSignalController = new EthernetSignalController(mContext, mSignalsChangedCallbacks, - mSignalClusters, this); + mEthernetSignalController = new EthernetSignalController(mContext, mCallbackHandler, this); // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it updateAirplaneMode(true /* force callback */); @@ -186,7 +190,7 @@ public class NetworkControllerImpl extends BroadcastReceiver filter.addAction(ConnectivityManager.INET_CONDITION_ACTION); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); - mContext.registerReceiver(this, filter); + mContext.registerReceiver(this, filter, null, mReceiverHandler); mListening = true; updateMobileControllers(); @@ -216,15 +220,8 @@ public class NetworkControllerImpl extends BroadcastReceiver } public void addEmergencyListener(EmergencyListener listener) { - mEmergencyListeners.add(listener); - listener.setEmergencyCallsOnly(isEmergencyOnly()); - } - - private void notifyMobileDataEnabled(boolean enabled) { - final int length = mSignalsChangedCallbacks.size(); - for (int i = 0; i < length; i++) { - mSignalsChangedCallbacks.get(i).onMobileDataEnabled(enabled); - } + mCallbackHandler.setListening(listener, true); + mCallbackHandler.setEmergencyCallsOnly(isEmergencyOnly()); } public boolean hasMobileDataFeature() { @@ -276,19 +273,15 @@ public class NetworkControllerImpl extends BroadcastReceiver * so we should recheck and send out the state to listeners. */ void recalculateEmergency() { - final boolean emergencyOnly = isEmergencyOnly(); - final int length = mEmergencyListeners.size(); - for (int i = 0; i < length; i++) { - mEmergencyListeners.get(i).setEmergencyCallsOnly(emergencyOnly); - } + mCallbackHandler.setEmergencyCallsOnly(isEmergencyOnly()); } - public void addSignalCluster(SignalCluster cluster) { - mSignalClusters.add(cluster); - cluster.setSubs(mCurrentSubscriptions); - cluster.setIsAirplaneMode(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON, - R.string.accessibility_airplane_mode); - cluster.setNoSims(mHasNoSims); + public void addSignalCallback(SignalCallback cb) { + mCallbackHandler.setListening(cb, true); + mCallbackHandler.setSubs(mCurrentSubscriptions); + mCallbackHandler.setIsAirplaneMode(new IconState(mAirplaneMode, + TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext)); + mCallbackHandler.setNoSims(mHasNoSims); mWifiSignalController.notifyListeners(); mEthernetSignalController.notifyListeners(); for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { @@ -296,19 +289,9 @@ public class NetworkControllerImpl extends BroadcastReceiver } } - public void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) { - mSignalsChangedCallbacks.add(cb); - cb.onAirplaneModeChanged(mAirplaneMode); - cb.onNoSimVisibleChanged(mHasNoSims); - mWifiSignalController.notifyListeners(); - mEthernetSignalController.notifyListeners(); - for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { - mobileSignalController.notifyListeners(); - } - } - - public void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) { - mSignalsChangedCallbacks.remove(cb); + @Override + public void removeSignalCallback(SignalCallback cb) { + mCallbackHandler.setListening(cb, false); } @Override @@ -427,10 +410,7 @@ public class NetworkControllerImpl extends BroadcastReceiver : lhs.getSimSlotIndex() - rhs.getSimSlotIndex(); } }); - final int length = mSignalClusters.size(); - for (int i = 0; i < length; i++) { - mSignalClusters.get(i).setSubs(subscriptions); - } + mCallbackHandler.setSubs(subscriptions); mCurrentSubscriptions = subscriptions; HashMap<Integer, MobileSignalController> cachedControllers = @@ -444,8 +424,8 @@ public class NetworkControllerImpl extends BroadcastReceiver mMobileSignalControllers.put(subId, cachedControllers.remove(subId)); } else { MobileSignalController controller = new MobileSignalController(mContext, mConfig, - mHasMobileDataFeature, mPhone, mSignalsChangedCallbacks, mSignalClusters, - this, subscriptions.get(i)); + mHasMobileDataFeature, mPhone, mCallbackHandler, + this, subscriptions.get(i), mReceiverHandler.getLooper()); mMobileSignalControllers.put(subId, controller); if (subscriptions.get(i).getSimSlotIndex() == 0) { mDefaultSignalController = controller; @@ -521,17 +501,9 @@ public class NetworkControllerImpl extends BroadcastReceiver * notifyAllListeners. */ private void notifyListeners() { - int length = mSignalClusters.size(); - for (int i = 0; i < length; i++) { - mSignalClusters.get(i).setIsAirplaneMode(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON, - R.string.accessibility_airplane_mode); - mSignalClusters.get(i).setNoSims(mHasNoSims); - } - int signalsChangedLength = mSignalsChangedCallbacks.size(); - for (int i = 0; i < signalsChangedLength; i++) { - mSignalsChangedCallbacks.get(i).onAirplaneModeChanged(mAirplaneMode); - mSignalsChangedCallbacks.get(i).onNoSimVisibleChanged(mHasNoSims); - } + mCallbackHandler.setIsAirplaneMode(new IconState(mAirplaneMode, + TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, mContext)); + mCallbackHandler.setNoSims(mHasNoSims); } /** @@ -566,17 +538,10 @@ public class NetworkControllerImpl extends BroadcastReceiver private void pushConnectivityToSignals() { // We want to update all the icons, all at once, for any condition change for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { - mobileSignalController.setInetCondition( - mInetCondition ? 1 : 0, - mValidatedTransports.get(mobileSignalController.getTransportType()) ? 1 : 0); + mobileSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports); } - mWifiSignalController.setInetCondition( - mValidatedTransports.get(mWifiSignalController.getTransportType()) ? 1 : 0); - - mEthernetSignalController.setConnected( - mConnectedTransports.get(mEthernetSignalController.getTransportType())); - mEthernetSignalController.setInetCondition( - mValidatedTransports.get(mEthernetSignalController.getTransportType()) ? 1 : 0); + mWifiSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports); + mEthernetSignalController.updateConnectivity(mConnectedTransports, mValidatedTransports); } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { @@ -609,7 +574,7 @@ public class NetworkControllerImpl extends BroadcastReceiver } private boolean mDemoMode; - private int mDemoInetCondition; + private boolean mDemoInetCondition; private WifiSignalController.WifiState mDemoWifiState; @Override @@ -618,7 +583,7 @@ public class NetworkControllerImpl extends BroadcastReceiver if (DEBUG) Log.d(TAG, "Entering demo mode"); unregisterListeners(); mDemoMode = true; - mDemoInetCondition = mInetCondition ? 1 : 0; + mDemoInetCondition = mInetCondition; mDemoWifiState = mWifiSignalController.getState(); } else if (mDemoMode && command.equals(COMMAND_EXIT)) { if (DEBUG) Log.d(TAG, "Exiting demo mode"); @@ -630,24 +595,30 @@ public class NetworkControllerImpl extends BroadcastReceiver controller.resetLastState(); } mWifiSignalController.resetLastState(); - registerListeners(); + mReceiverHandler.post(mRegisterListeners); notifyAllListeners(); } else if (mDemoMode && command.equals(COMMAND_NETWORK)) { String airplane = args.getString("airplane"); if (airplane != null) { boolean show = airplane.equals("show"); - int length = mSignalClusters.size(); - for (int i = 0; i < length; i++) { - mSignalClusters.get(i).setIsAirplaneMode(show, TelephonyIcons.FLIGHT_MODE_ICON, - R.string.accessibility_airplane_mode); - } + mCallbackHandler.setIsAirplaneMode(new IconState(show, + TelephonyIcons.FLIGHT_MODE_ICON, R.string.accessibility_airplane_mode, + mContext)); } String fully = args.getString("fully"); if (fully != null) { - mDemoInetCondition = Boolean.parseBoolean(fully) ? 1 : 0; - mWifiSignalController.setInetCondition(mDemoInetCondition); + mDemoInetCondition = Boolean.parseBoolean(fully); + BitSet connected = new BitSet(); + + if (mDemoInetCondition) { + connected.set(mWifiSignalController.mTransportType); + } + mWifiSignalController.updateConnectivity(connected, connected); for (MobileSignalController controller : mMobileSignalControllers.values()) { - controller.setInetCondition(mDemoInetCondition, mDemoInetCondition); + if (mDemoInetCondition) { + connected.set(controller.mTransportType); + } + controller.updateConnectivity(connected, connected); } } String wifi = args.getString("wifi"); @@ -664,32 +635,21 @@ public class NetworkControllerImpl extends BroadcastReceiver } String sims = args.getString("sims"); if (sims != null) { - int num = Integer.parseInt(sims); - List<SubscriptionInfo> subs = new ArrayList<SubscriptionInfo>(); + int num = MathUtils.constrain(Integer.parseInt(sims), 1, 8); + List<SubscriptionInfo> subs = new ArrayList<>(); if (num != mMobileSignalControllers.size()) { mMobileSignalControllers.clear(); int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax(); for (int i = start /* get out of normal index range */; i < start + num; i++) { - SubscriptionInfo info = new SubscriptionInfo(i, "", i, "", "", 0, 0, "", 0, - null, 0, 0, ""); - subs.add(info); - mMobileSignalControllers.put(i, new MobileSignalController(mContext, - mConfig, mHasMobileDataFeature, mPhone, mSignalsChangedCallbacks, - mSignalClusters, this, info)); + subs.add(addSignalController(i, i)); } } - final int n = mSignalClusters.size(); - for (int i = 0; i < n; i++) { - mSignalClusters.get(i).setSubs(subs); - } + mCallbackHandler.setSubs(subs); } String nosim = args.getString("nosim"); if (nosim != null) { boolean show = nosim.equals("show"); - final int n = mSignalClusters.size(); - for (int i = 0; i < n; i++) { - mSignalClusters.get(i).setNoSims(show); - } + mCallbackHandler.setNoSims(show); } String mobile = args.getString("mobile"); if (mobile != null) { @@ -697,6 +657,16 @@ public class NetworkControllerImpl extends BroadcastReceiver String datatype = args.getString("datatype"); String slotString = args.getString("slot"); int slot = TextUtils.isEmpty(slotString) ? 0 : Integer.parseInt(slotString); + slot = MathUtils.constrain(slot, 0, 8); + // Ensure we have enough sim slots + List<SubscriptionInfo> subs = new ArrayList<>(); + while (mMobileSignalControllers.size() <= slot) { + int nextSlot = mMobileSignalControllers.size(); + subs.add(addSignalController(nextSlot, nextSlot)); + } + if (!subs.isEmpty()) { + mCallbackHandler.setSubs(subs); + } // Hack to index linearly for easy use. MobileSignalController controller = mMobileSignalControllers .values().toArray(new MobileSignalController[0])[slot]; @@ -733,6 +703,15 @@ public class NetworkControllerImpl extends BroadcastReceiver } } + private SubscriptionInfo addSignalController(int id, int simSlotIndex) { + SubscriptionInfo info = new SubscriptionInfo(id, "", simSlotIndex, "", "", 0, 0, "", 0, + null, 0, 0, ""); + mMobileSignalControllers.put(id, new MobileSignalController(mContext, + mConfig, mHasMobileDataFeature, mPhone, mCallbackHandler, this, info, + mReceiverHandler.getLooper())); + return info; + } + private final OnSubscriptionsChangedListener mSubscriptionListener = new OnSubscriptionsChangedListener() { @Override @@ -741,28 +720,21 @@ public class NetworkControllerImpl extends BroadcastReceiver }; }; - public interface SignalCluster { - void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription); - - void setMobileDataIndicators(boolean visible, int strengthIcon, int darkStrengthIcon, - int typeIcon, String contentDescription, String typeContentDescription, - boolean isTypeIconWide, int subId); - void setSubs(List<SubscriptionInfo> subs); - void setNoSims(boolean show); - - void setEthernetIndicators(boolean visible, int icon, String contentDescription); - - void setIsAirplaneMode(boolean is, int airplaneIcon, int contentDescription); - } + /** + * Used to register listeners from the BG Looper, this way the PhoneStateListeners that + * get created will also run on the BG Looper. + */ + private final Runnable mRegisterListeners = new Runnable() { + @Override + public void run() { + registerListeners(); + } + }; public interface EmergencyListener { void setEmergencyCallsOnly(boolean emergencyOnly); } - public interface CarrierLabelListener { - void setCarrierLabel(String label); - } - @VisibleForTesting static class Config { boolean showAtLeast3G = false; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalCallbackAdapter.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalCallbackAdapter.java new file mode 100644 index 000000000000..83a7d3dd42fe --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalCallbackAdapter.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.policy; + +import android.telephony.SubscriptionInfo; + +import com.android.systemui.statusbar.policy.NetworkController.IconState; +import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; + +import java.util.List; + + +/** + * Provides empty implementations of SignalCallback for those that only want some of + * the callbacks. + */ +public class SignalCallbackAdapter implements SignalCallback { + + @Override + public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, + boolean activityIn, boolean activityOut, String description) { + } + + @Override + public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, + int darkStatusIcon, int statusType, int qsType, boolean activityIn, + boolean activityOut, String typeContentDescription, String description, + boolean isWide, int subId) { + } + + @Override + public void setSubs(List<SubscriptionInfo> subs) { + } + + @Override + public void setNoSims(boolean show) { + } + + @Override + public void setEthernetIndicators(IconState icon) { + } + + @Override + public void setIsAirplaneMode(IconState icon) { + } + + @Override + public void setMobileDataEnabled(boolean enabled) { + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java index f3322a1f159a..e6ca64692079 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SignalController.java @@ -21,11 +21,8 @@ import android.content.Context; import android.text.format.DateFormat; import android.util.Log; -import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; -import com.android.systemui.statusbar.policy.NetworkControllerImpl.SignalCluster; - import java.io.PrintWriter; -import java.util.List; +import java.util.BitSet; /** @@ -49,24 +46,22 @@ public abstract class SignalController<T extends SignalController.State, // The owner of the SignalController (i.e. NetworkController will maintain the following // lists and call notifyListeners whenever the list has changed to ensure everyone // is aware of current state. - protected final List<NetworkSignalChangedCallback> mSignalsChangedCallbacks; - protected final List<SignalCluster> mSignalClusters; protected final NetworkControllerImpl mNetworkController; + protected final CallbackHandler mCallbackHandler; + // Save the previous HISTORY_SIZE states for logging. private final State[] mHistory; // Where to copy the next state into. private int mHistoryIndex; - public SignalController(String tag, Context context, int type, - List<NetworkSignalChangedCallback> signalCallbacks, - List<SignalCluster> signalClusters, NetworkControllerImpl networkController) { + public SignalController(String tag, Context context, int type, CallbackHandler callbackHandler, + NetworkControllerImpl networkController) { mTag = TAG + "." + tag; mNetworkController = networkController; mTransportType = type; mContext = context; - mSignalsChangedCallbacks = signalCallbacks; - mSignalClusters = signalClusters; + mCallbackHandler = callbackHandler; mCurrentState = cleanState(); mLastState = cleanState(); if (RECORD_HISTORY) { @@ -81,12 +76,8 @@ public abstract class SignalController<T extends SignalController.State, return mCurrentState; } - public int getTransportType() { - return mTransportType; - } - - public void setInetCondition(int inetCondition) { - mCurrentState.inetCondition = inetCondition; + public void updateConnectivity(BitSet connectedTransports, BitSet validatedTransports) { + mCurrentState.inetCondition = validatedTransports.get(mTransportType) ? 1 : 0; notifyListenersIfNecessary(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java index 053feb12edff..fa4d46434808 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/TelephonyIcons.java @@ -104,10 +104,7 @@ class TelephonyIcons { R.drawable.ic_qs_signal_carrier_network_change_animation } }; - static final int[] QS_DATA_R = { - R.drawable.ic_qs_signal_r, - R.drawable.ic_qs_signal_r - }; + static final int QS_DATA_R = R.drawable.ic_qs_signal_r; //***** Data connection icons @@ -123,10 +120,7 @@ class TelephonyIcons { R.drawable.stat_sys_data_fully_connected_g } }; - static final int[] QS_DATA_G = { - R.drawable.ic_qs_signal_g, - R.drawable.ic_qs_signal_g - }; + static final int QS_DATA_G = R.drawable.ic_qs_signal_g; static final int[][] DATA_3G = { { R.drawable.stat_sys_data_fully_connected_3g, @@ -139,10 +133,7 @@ class TelephonyIcons { R.drawable.stat_sys_data_fully_connected_3g } }; - static final int[] QS_DATA_3G = { - R.drawable.ic_qs_signal_3g, - R.drawable.ic_qs_signal_3g - }; + static final int QS_DATA_3G = R.drawable.ic_qs_signal_3g; static final int[][] DATA_E = { { R.drawable.stat_sys_data_fully_connected_e, @@ -155,10 +146,7 @@ class TelephonyIcons { R.drawable.stat_sys_data_fully_connected_e } }; - static final int[] QS_DATA_E = { - R.drawable.ic_qs_signal_e, - R.drawable.ic_qs_signal_e - }; + static final int QS_DATA_E = R.drawable.ic_qs_signal_e; //3.5G static final int[][] DATA_H = { @@ -172,10 +160,7 @@ class TelephonyIcons { R.drawable.stat_sys_data_fully_connected_h } }; - static final int[] QS_DATA_H = { - R.drawable.ic_qs_signal_h, - R.drawable.ic_qs_signal_h - }; + static final int QS_DATA_H = R.drawable.ic_qs_signal_h; //CDMA // Use 3G icons for EVDO data and 1x icons for 1XRTT data @@ -190,10 +175,7 @@ class TelephonyIcons { R.drawable.stat_sys_data_fully_connected_1x } }; - static final int[] QS_DATA_1X = { - R.drawable.ic_qs_signal_1x, - R.drawable.ic_qs_signal_1x - }; + static final int QS_DATA_1X = R.drawable.ic_qs_signal_1x; // LTE and eHRPD static final int[][] DATA_4G = { @@ -207,10 +189,7 @@ class TelephonyIcons { R.drawable.stat_sys_data_fully_connected_4g } }; - static final int[] QS_DATA_4G = { - R.drawable.ic_qs_signal_4g, - R.drawable.ic_qs_signal_4g - }; + static final int QS_DATA_4G = R.drawable.ic_qs_signal_4g; // LTE branded "LTE" static final int[][] DATA_LTE = { @@ -224,10 +203,7 @@ class TelephonyIcons { R.drawable.stat_sys_data_fully_connected_lte } }; - static final int[] QS_DATA_LTE = { - R.drawable.ic_qs_signal_lte, - R.drawable.ic_qs_signal_lte - }; + static final int QS_DATA_LTE = R.drawable.ic_qs_signal_lte; static final int FLIGHT_MODE_ICON = R.drawable.stat_sys_airplane_mode; static final int ROAMING_ICON = R.drawable.stat_sys_data_fully_connected_roam; @@ -264,7 +240,7 @@ class TelephonyIcons { R.string.accessibility_carrier_network_change_mode, 0, false, - null + 0 ); static final MobileIconGroup THREE_G = new MobileIconGroup( @@ -291,7 +267,7 @@ class TelephonyIcons { TelephonyIcons.TELEPHONY_NO_NETWORK, TelephonyIcons.QS_TELEPHONY_NO_NETWORK, AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0], - 0, 0, false, new int[2] + 0, 0, false, 0 ); static final MobileIconGroup E = new MobileIconGroup( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index a97ca5009341..9b1e72a1648b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -29,8 +29,7 @@ import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.AsyncChannel; -import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; -import com.android.systemui.statusbar.policy.NetworkControllerImpl.SignalCluster; +import com.android.systemui.statusbar.policy.NetworkController.IconState; import java.util.List; import java.util.Objects; @@ -43,10 +42,9 @@ public class WifiSignalController extends private final boolean mHasMobileData; public WifiSignalController(Context context, boolean hasMobileData, - List<NetworkSignalChangedCallback> signalCallbacks, - List<SignalCluster> signalClusters, NetworkControllerImpl networkController) { + CallbackHandler callbackHandler, NetworkControllerImpl networkController) { super("WifiSignalController", context, NetworkCapabilities.TRANSPORT_WIFI, - signalCallbacks, signalClusters, networkController); + callbackHandler, networkController); mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); mHasMobileData = hasMobileData; Handler handler = new WifiHandler(); @@ -82,19 +80,13 @@ public class WifiSignalController extends String wifiDesc = wifiVisible ? mCurrentState.ssid : null; boolean ssidPresent = wifiVisible && mCurrentState.ssid != null; String contentDescription = getStringIfExists(getContentDescription()); - int length = mSignalsChangedCallbacks.size(); - for (int i = 0; i < length; i++) { - mSignalsChangedCallbacks.get(i).onWifiSignalChanged(mCurrentState.enabled, - mCurrentState.connected, getQsCurrentIconId(), - ssidPresent && mCurrentState.activityIn, - ssidPresent && mCurrentState.activityOut, contentDescription, wifiDesc); - } - int signalClustersLength = mSignalClusters.size(); - for (int i = 0; i < signalClustersLength; i++) { - mSignalClusters.get(i).setWifiIndicators(wifiVisible, getCurrentIconId(), - contentDescription); - } + IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription); + IconState qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconId(), + contentDescription); + mCallbackHandler.setWifiIndicators(mCurrentState.enabled, statusIcon, qsIcon, + ssidPresent && mCurrentState.activityIn, ssidPresent && mCurrentState.activityOut, + wifiDesc); } /** diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java index 8dfa9b0b84c1..ec24d750b1ab 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java +++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java @@ -23,6 +23,7 @@ import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.res.Configuration; import android.net.Uri; import android.os.AsyncTask; import android.os.Handler; @@ -175,7 +176,14 @@ public class ZenModePanel extends LinearLayout { }); mZenConditions = (LinearLayout) findViewById(R.id.zen_conditions); + } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + if (mZenButtons != null) { + mZenButtons.updateLocale(); + } } private void confirmZenIntroduction() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java new file mode 100644 index 000000000000..c14d06f04cc4 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.statusbar.policy; + +import android.os.HandlerThread; +import android.telephony.SubscriptionInfo; +import android.test.AndroidTestCase; + +import com.android.systemui.R; +import com.android.systemui.statusbar.policy.NetworkController.IconState; +import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import com.android.systemui.statusbar.policy.NetworkControllerImpl.EmergencyListener; + +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; +import java.util.List; + +public class CallbackHandlerTest extends AndroidTestCase { + + private CallbackHandler mHandler; + private HandlerThread mHandlerThread; + + @Mock + private EmergencyListener mEmengencyListener; + @Mock + private SignalCallback mSignalCallback; + + @Override + protected void setUp() throws Exception { + super.setUp(); + + mHandlerThread = new HandlerThread("TestThread"); + mHandlerThread.start(); + mHandler = new CallbackHandler(mHandlerThread.getLooper()); + + MockitoAnnotations.initMocks(this); + mHandler.setListening(mEmengencyListener, true); + mHandler.setListening(mSignalCallback, true); + } + + public void testEmergencyListener() { + mHandler.setEmergencyCallsOnly(true); + waitForCallbacks(); + + ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class); + Mockito.verify(mEmengencyListener).setEmergencyCallsOnly(captor.capture()); + assertTrue(captor.getValue()); + } + + public void testSignalCallback_setWifiIndicators() { + boolean enabled = true; + IconState status = new IconState(true, 0, ""); + IconState qs = new IconState(true, 1, ""); + boolean in = true; + boolean out = true; + String description = "Test"; + mHandler.setWifiIndicators(enabled, status, qs, in, out, description); + waitForCallbacks(); + + ArgumentCaptor<Boolean> enableArg = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class); + ArgumentCaptor<IconState> qsArg = ArgumentCaptor.forClass(IconState.class); + ArgumentCaptor<Boolean> inArg = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class); + Mockito.verify(mSignalCallback).setWifiIndicators(enableArg.capture(), + statusArg.capture(), qsArg.capture(), inArg.capture(), outArg.capture(), + descArg.capture()); + assertEquals(enabled, (boolean) enableArg.getValue()); + assertEquals(status, statusArg.getValue()); + assertEquals(qs, qsArg.getValue()); + assertEquals(in, (boolean) inArg.getValue()); + assertEquals(out, (boolean) outArg.getValue()); + assertEquals(description, descArg.getValue()); + } + + public void testSignalCallback_setMobileDataIndicators() { + IconState status = new IconState(true, 0, ""); + IconState qs = new IconState(true, 1, ""); + int dark = 2; + boolean in = true; + boolean out = true; + String typeDescription = "Test 1"; + String description = "Test 2"; + int type = R.drawable.stat_sys_data_fully_connected_1x; + int qsType = R.drawable.ic_qs_signal_1x; + boolean wide = true; + int subId = 5; + mHandler.setMobileDataIndicators(status, qs, dark, type, qsType, in, out, typeDescription, + description, wide, subId); + waitForCallbacks(); + + ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class); + ArgumentCaptor<IconState> qsArg = ArgumentCaptor.forClass(IconState.class); + ArgumentCaptor<Integer> darkStrengthArg = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Integer> qsTypeIconArg = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<Boolean> inArg = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<String> typeContentArg = ArgumentCaptor.forClass(String.class); + ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class); + ArgumentCaptor<Boolean> wideArg = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> subIdArg = ArgumentCaptor.forClass(Integer.class); + Mockito.verify(mSignalCallback).setMobileDataIndicators(statusArg.capture(), qsArg.capture(), + darkStrengthArg.capture(), typeIconArg.capture(), qsTypeIconArg.capture(), + inArg.capture(), outArg.capture(), typeContentArg.capture(), descArg.capture(), + wideArg.capture(), subIdArg.capture()); + assertEquals(status, statusArg.getValue()); + assertEquals(qs, qsArg.getValue()); + assertEquals(dark, (int) darkStrengthArg.getValue()); + assertEquals(type, (int) typeIconArg.getValue()); + assertEquals(qsType, (int) qsTypeIconArg.getValue()); + assertEquals(in, (boolean) inArg.getValue()); + assertEquals(out, (boolean) outArg.getValue()); + assertEquals(typeDescription, typeContentArg.getValue()); + assertEquals(description, descArg.getValue()); + assertEquals(wide, (boolean) wideArg.getValue()); + assertEquals(subId, (int) subIdArg.getValue()); + } + + @SuppressWarnings("unchecked") + public void testSignalCallback_setSubs() { + List<SubscriptionInfo> subs = new ArrayList<>(); + mHandler.setSubs(subs); + waitForCallbacks(); + + ArgumentCaptor<ArrayList> subsArg = ArgumentCaptor.forClass(ArrayList.class); + Mockito.verify(mSignalCallback).setSubs(subsArg.capture()); + assertTrue(subs == subsArg.getValue()); + } + + public void testSignalCallback_setNoSims() { + boolean noSims = true; + mHandler.setNoSims(noSims); + waitForCallbacks(); + + ArgumentCaptor<Boolean> noSimsArg = ArgumentCaptor.forClass(Boolean.class); + Mockito.verify(mSignalCallback).setNoSims(noSimsArg.capture()); + assertEquals(noSims, (boolean) noSimsArg.getValue()); + } + + public void testSignalCallback_setEthernetIndicators() { + IconState state = new IconState(true, R.drawable.stat_sys_ethernet, "Test Description"); + mHandler.setEthernetIndicators(state); + waitForCallbacks(); + + ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); + Mockito.verify(mSignalCallback).setEthernetIndicators(iconArg.capture()); + assertEquals(state, iconArg.getValue()); + } + + public void testSignalCallback_setIsAirplaneMode() { + IconState state = new IconState(true, R.drawable.stat_sys_airplane_mode, "Test Description"); + mHandler.setIsAirplaneMode(state); + waitForCallbacks(); + + ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); + Mockito.verify(mSignalCallback).setIsAirplaneMode(iconArg.capture()); + assertEquals(state, iconArg.getValue()); + } + + private void waitForCallbacks() { + mHandlerThread.quitSafely(); + try { + mHandlerThread.join(); + } catch (InterruptedException e) { + } + } + +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java index 29f461e6e935..2d6bb68a03a3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java @@ -23,6 +23,7 @@ import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkCapabilities; import android.net.wifi.WifiManager; +import android.os.Looper; import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; @@ -31,12 +32,10 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.util.Log; -import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.cdma.EriInfo; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.policy.NetworkController.NetworkSignalChangedCallback; +import com.android.systemui.statusbar.policy.NetworkController.IconState; import com.android.systemui.statusbar.policy.NetworkControllerImpl.Config; -import com.android.systemui.statusbar.policy.NetworkControllerImpl.SignalCluster; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; @@ -59,8 +58,6 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected NetworkControllerImpl mNetworkController; protected MobileSignalController mMobileSignalController; protected PhoneStateListener mPhoneStateListener; - protected SignalCluster mSignalCluster; - protected NetworkSignalChangedCallback mNetworkSignalChangedCallback; private SignalStrength mSignalStrength; private ServiceState mServiceState; protected ConnectivityManager mMockCm; @@ -68,6 +65,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected SubscriptionManager mMockSm; protected TelephonyManager mMockTm; protected Config mConfig; + protected CallbackHandler mCallbackHandler; protected int mSubId; @@ -91,33 +89,36 @@ public class NetworkControllerBaseTest extends SysuiTestCase { mConfig = new Config(); mConfig.hspaDataDistinguishable = true; + mCallbackHandler = mock(CallbackHandler.class); mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm, - mConfig, mock(AccessPointControllerImpl.class), - mock(MobileDataControllerImpl.class)); + mConfig, Looper.getMainLooper(), mCallbackHandler, + mock(AccessPointControllerImpl.class), mock(MobileDataControllerImpl.class)); setupNetworkController(); } protected void setupNetworkController() { // For now just pretend to be the data sim, so we can test that too. - mSubId = SubscriptionManager.getDefaultDataSubId(); + mSubId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID; SubscriptionInfo subscription = mock(SubscriptionInfo.class); List<SubscriptionInfo> subs = new ArrayList<SubscriptionInfo>(); when(subscription.getSubscriptionId()).thenReturn(mSubId); subs.add(subscription); mNetworkController.setCurrentSubscriptions(subs); mMobileSignalController = mNetworkController.mMobileSignalControllers.get(mSubId); + mMobileSignalController.getState().dataSim = true; mPhoneStateListener = mMobileSignalController.mPhoneStateListener; - mSignalCluster = mock(SignalCluster.class); - mNetworkSignalChangedCallback = mock(NetworkSignalChangedCallback.class); - mNetworkController.addSignalCluster(mSignalCluster); - mNetworkController.addNetworkSignalChangedCallback(mNetworkSignalChangedCallback); + + // Trigger blank callbacks to always get the current state (some tests don't trigger + // changes from default state). + mNetworkController.addSignalCallback(null); } protected NetworkControllerImpl setUpNoMobileData() { when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false); NetworkControllerImpl networkControllerNoMobile = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm, - mConfig, mock(AccessPointControllerImpl.class), + mConfig, Looper.getMainLooper(), mCallbackHandler, + mock(AccessPointControllerImpl.class), mock(MobileDataControllerImpl.class)); setupNetworkController(); @@ -144,10 +145,10 @@ public class NetworkControllerBaseTest extends SysuiTestCase { setLevel(DEFAULT_LEVEL); updateDataConnectionState(TelephonyManager.DATA_CONNECTED, TelephonyManager.NETWORK_TYPE_UMTS); - setConnectivity(100, ConnectivityManager.TYPE_MOBILE, true); + setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, true, true); } - public void setConnectivity(int inetCondition, int networkType, boolean isConnected) { + public void setConnectivity(int networkType, boolean inetCondition, boolean isConnected) { Intent i = new Intent(ConnectivityManager.INET_CONDITION_ACTION); // TODO: Separate out into several NetworkCapabilities. if (isConnected) { @@ -155,7 +156,7 @@ public class NetworkControllerBaseTest extends SysuiTestCase { } else { mNetCapabilities.removeTransportType(networkType); } - if (inetCondition != 0) { + if (inetCondition) { mNetCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); } else { mNetCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED); @@ -242,34 +243,30 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected void verifyHasNoSims(boolean hasNoSimsVisible) { ArgumentCaptor<Boolean> hasNoSimsArg = ArgumentCaptor.forClass(Boolean.class); - Mockito.verify(mSignalCluster, Mockito.atLeastOnce()).setNoSims(hasNoSimsArg.capture()); - assertEquals("No sims in status bar", hasNoSimsVisible, (boolean) hasNoSimsArg.getValue()); - - Mockito.verify(mNetworkSignalChangedCallback, Mockito.atLeastOnce()) - .onNoSimVisibleChanged(hasNoSimsArg.capture()); - assertEquals("No sims in quick settings", hasNoSimsVisible, - (boolean) hasNoSimsArg.getValue()); + Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setNoSims(hasNoSimsArg.capture()); + assertEquals("No sims", hasNoSimsVisible, (boolean) hasNoSimsArg.getValue()); } protected void verifyLastQsMobileDataIndicators(boolean visible, int icon, int typeIcon, boolean dataIn, boolean dataOut) { - ArgumentCaptor<Integer> iconArg = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor<Boolean> visibleArg = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<Boolean> dataInArg = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<Boolean> dataOutArg = ArgumentCaptor.forClass(Boolean.class); - Mockito.verify(mNetworkSignalChangedCallback, Mockito.atLeastOnce()) - .onMobileDataSignalChanged(visibleArg.capture(), iconArg.capture(), - ArgumentCaptor.forClass(String.class).capture(), - typeIconArg.capture(), - dataInArg.capture(), - dataOutArg.capture(), - ArgumentCaptor.forClass(String.class).capture(), - ArgumentCaptor.forClass(String.class).capture(), - ArgumentCaptor.forClass(Boolean.class).capture()); - assertEquals("Visibility in, quick settings", visible, (boolean) visibleArg.getValue()); - assertEquals("Signal icon in, quick settings", icon, (int) iconArg.getValue()); + Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators( + ArgumentCaptor.forClass(IconState.class).capture(), + iconArg.capture(), + ArgumentCaptor.forClass(Integer.class).capture(), + ArgumentCaptor.forClass(Integer.class).capture(), + typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(), + ArgumentCaptor.forClass(String.class).capture(), + ArgumentCaptor.forClass(String.class).capture(), + ArgumentCaptor.forClass(Boolean.class).capture(), + ArgumentCaptor.forClass(Integer.class).capture()); + IconState iconState = iconArg.getValue(); + assertEquals("Visibility in, quick settings", visible, iconState.visible); + assertEquals("Signal icon in, quick settings", icon, iconState.icon); assertEquals("Data icon in, quick settings", typeIcon, (int) typeIconArg.getValue()); assertEquals("Data direction in, in quick settings", dataIn, (boolean) dataInArg.getValue()); @@ -283,29 +280,32 @@ public class NetworkControllerBaseTest extends SysuiTestCase { protected void verifyLastMobileDataIndicators(boolean visible, int strengthIcon, int darkStrengthIcon, int typeIcon) { - ArgumentCaptor<Integer> strengthIconArg = ArgumentCaptor.forClass(Integer.class); + ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); ArgumentCaptor<Integer> darkStrengthIconArg = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor<Integer> typeIconArg = ArgumentCaptor.forClass(Integer.class); - ArgumentCaptor<Boolean> visibleArg = ArgumentCaptor.forClass(Boolean.class); // TODO: Verify all fields. - Mockito.verify(mSignalCluster, Mockito.atLeastOnce()).setMobileDataIndicators( - visibleArg.capture(), strengthIconArg.capture(), darkStrengthIconArg.capture(), - typeIconArg.capture(), + Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setMobileDataIndicators( + iconArg.capture(), + ArgumentCaptor.forClass(IconState.class).capture(), + darkStrengthIconArg.capture(), typeIconArg.capture(), + ArgumentCaptor.forClass(Integer.class).capture(), + ArgumentCaptor.forClass(Boolean.class).capture(), + ArgumentCaptor.forClass(Boolean.class).capture(), ArgumentCaptor.forClass(String.class).capture(), ArgumentCaptor.forClass(String.class).capture(), ArgumentCaptor.forClass(Boolean.class).capture(), ArgumentCaptor.forClass(Integer.class).capture()); + IconState iconState = iconArg.getValue(); - assertEquals("Signal strength icon in status bar", strengthIcon, - (int) strengthIconArg.getValue()); + assertEquals("Signal strength icon in status bar", strengthIcon, iconState.icon); assertEquals("Signal strength icon (dark mode) in status bar", darkStrengthIcon, (int) darkStrengthIconArg.getValue()); assertEquals("Data icon in status bar", typeIcon, (int) typeIconArg.getValue()); - assertEquals("Visibility in status bar", visible, (boolean) visibleArg.getValue()); + assertEquals("Visibility in status bar", visible, iconState.visible); } protected void assertNetworkNameEquals(String expected) { - assertEquals("Network name", expected, mNetworkController.getMobileDataNetworkName()); + assertEquals("Network name", expected, mMobileSignalController.getState().networkName); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java index 3f9312dbd964..15752e1de7b8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java @@ -1,16 +1,17 @@ package com.android.systemui.statusbar.policy; -import org.mockito.Mockito; - +import android.os.Looper; import android.telephony.TelephonyManager; +import org.mockito.Mockito; + public class NetworkControllerDataTest extends NetworkControllerBaseTest { public void test3gDataIcon() { setupDefaultSignal(); verifyDataIndicators(TelephonyIcons.DATA_3G[1][0 /* No direction */], - TelephonyIcons.QS_DATA_3G[1]); + TelephonyIcons.QS_DATA_3G); } public void testRoamingDataIcon() { @@ -22,7 +23,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { TelephonyIcons.ROAMING_ICON); verifyLastQsMobileDataIndicators(true, TelephonyIcons.QS_TELEPHONY_SIGNAL_STRENGTH[1][DEFAULT_LEVEL], - TelephonyIcons.QS_DATA_R[1], false, false); + TelephonyIcons.QS_DATA_R, false, false); } public void test2gDataIcon() { @@ -31,7 +32,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { TelephonyManager.NETWORK_TYPE_GSM); verifyDataIndicators(TelephonyIcons.DATA_G[1][0 /* No direction */], - TelephonyIcons.QS_DATA_G[1]); + TelephonyIcons.QS_DATA_G); } public void testCdmaDataIcon() { @@ -40,7 +41,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { TelephonyManager.NETWORK_TYPE_CDMA); verifyDataIndicators(TelephonyIcons.DATA_1X[1][0 /* No direction */], - TelephonyIcons.QS_DATA_1X[1]); + TelephonyIcons.QS_DATA_1X); } public void testEdgeDataIcon() { @@ -49,7 +50,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { TelephonyManager.NETWORK_TYPE_EDGE); verifyDataIndicators(TelephonyIcons.DATA_E[1][0 /* No direction */], - TelephonyIcons.QS_DATA_E[1]); + TelephonyIcons.QS_DATA_E); } public void testLteDataIcon() { @@ -58,7 +59,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { TelephonyManager.NETWORK_TYPE_LTE); verifyDataIndicators(TelephonyIcons.DATA_LTE[1][0 /* No direction */], - TelephonyIcons.QS_DATA_LTE[1]); + TelephonyIcons.QS_DATA_LTE); } public void testHspaDataIcon() { @@ -67,14 +68,15 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { TelephonyManager.NETWORK_TYPE_HSPA); verifyDataIndicators(TelephonyIcons.DATA_H[1][0 /* No direction */], - TelephonyIcons.QS_DATA_H[1]); + TelephonyIcons.QS_DATA_H); } public void test4gDataIcon() { // Switch to showing 4g icon and re-initialize the NetworkController. mConfig.show4gForLte = true; mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm, - mConfig, Mockito.mock(AccessPointControllerImpl.class), + mConfig, Looper.getMainLooper(), mCallbackHandler, + Mockito.mock(AccessPointControllerImpl.class), Mockito.mock(MobileDataControllerImpl.class)); setupNetworkController(); @@ -83,7 +85,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { TelephonyManager.NETWORK_TYPE_LTE); verifyDataIndicators(TelephonyIcons.DATA_4G[1][0 /* No direction */], - TelephonyIcons.QS_DATA_4G[1]); + TelephonyIcons.QS_DATA_4G); } public void test4gDataIconConfigChange() { @@ -99,7 +101,7 @@ public class NetworkControllerDataTest extends NetworkControllerBaseTest { mNetworkController.handleConfigurationChanged(); verifyDataIndicators(TelephonyIcons.DATA_4G[1][0 /* No direction */], - TelephonyIcons.QS_DATA_4G[1]); + TelephonyIcons.QS_DATA_4G); } public void testDataActivity() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java index 82ced9f817a3..5d63d8dd506c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerEthernetTest.java @@ -2,6 +2,8 @@ package com.android.systemui.statusbar.policy; import android.net.NetworkCapabilities; +import com.android.systemui.statusbar.policy.NetworkController.IconState; + import org.mockito.ArgumentCaptor; import org.mockito.Mockito; @@ -24,17 +26,16 @@ public class NetworkControllerEthernetTest extends NetworkControllerBaseTest { } protected void setEthernetState(boolean connected, boolean validated) { - setConnectivity(validated ? 100 : 0, NetworkCapabilities.TRANSPORT_ETHERNET, connected); + setConnectivity(NetworkCapabilities.TRANSPORT_ETHERNET, validated, connected); } protected void verifyLastEthernetIcon(boolean visible, int icon) { - ArgumentCaptor<Boolean> visibleArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<Integer> iconArg = ArgumentCaptor.forClass(Integer.class); - - Mockito.verify(mSignalCluster, Mockito.atLeastOnce()).setEthernetIndicators( - visibleArg.capture(), iconArg.capture(), - ArgumentCaptor.forClass(String.class).capture()); - assertEquals("Ethernet visible, in status bar", visible, (boolean) visibleArg.getValue()); - assertEquals("Ethernet icon, in status bar", icon, (int) iconArg.getValue()); + ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); + + Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setEthernetIndicators( + iconArg.capture()); + IconState iconState = iconArg.getValue(); + assertEquals("Ethernet visible, in status bar", visible, iconState.visible); + assertEquals("Ethernet icon, in status bar", icon, iconState.icon); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java index 389ad6f38fd2..874fdf90916f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerSignalTest.java @@ -19,12 +19,13 @@ import static org.mockito.Mockito.mock; import android.content.Intent; import android.net.ConnectivityManager; +import android.net.NetworkCapabilities; +import android.os.Looper; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.SubscriptionInfo; import android.telephony.TelephonyManager; -import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.PhoneConstants; import com.android.internal.telephony.TelephonyIntents; import com.android.systemui.R; @@ -41,8 +42,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { Mockito.when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false); // Create a new NetworkController as this is currently handled in constructor. mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm, - mConfig, mock(AccessPointControllerImpl.class), - mock(MobileDataControllerImpl.class)); + mConfig, Looper.getMainLooper(), mCallbackHandler, + mock(AccessPointControllerImpl.class), mock(MobileDataControllerImpl.class)); setupNetworkController(); verifyLastMobileDataIndicators(false, 0, 0); @@ -61,8 +62,8 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { Mockito.when(mMockCm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)).thenReturn(false); // Create a new NetworkController as this is currently handled in constructor. mNetworkController = new NetworkControllerImpl(mContext, mMockCm, mMockTm, mMockWm, mMockSm, - mConfig, mock(AccessPointControllerImpl.class), - mock(MobileDataControllerImpl.class)); + mConfig, Looper.getMainLooper(), mCallbackHandler, + mock(AccessPointControllerImpl.class), mock(MobileDataControllerImpl.class)); setupNetworkController(); // No Subscriptions. @@ -79,13 +80,12 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { setLevel(testStrength); verifyLastMobileDataIndicators(true, - TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[1][testStrength], - DEFAULT_ICON); + TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[1][testStrength], DEFAULT_ICON); // Verify low inet number indexing. - setConnectivity(0, ConnectivityManager.TYPE_MOBILE, true); + setConnectivity(NetworkCapabilities.TRANSPORT_CELLULAR, false, true); verifyLastMobileDataIndicators(true, - TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[0][testStrength], 0); + TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[0][testStrength], DEFAULT_ICON); } } @@ -154,18 +154,12 @@ public class NetworkControllerSignalTest extends NetworkControllerBaseTest { } } - public void testNoRoamingWithoutSignal() { + public void testNoBangWithWifi() { setupDefaultSignal(); - setCdma(); - setCdmaRoaming(true); - setVoiceRegState(ServiceState.STATE_OUT_OF_SERVICE); - setDataRegState(ServiceState.STATE_OUT_OF_SERVICE); - - // This exposes the bug in b/18034542, and should be switched to the commented out - // verification below (and pass), once the bug is fixed. - verifyLastMobileDataIndicators(true, R.drawable.stat_sys_signal_null, - TelephonyIcons.ROAMING_ICON); - //verifyLastMobileDataIndicators(true, R.drawable.stat_sys_signal_null, 0 /* No Icon */); + setConnectivity(mMobileSignalController.mTransportType, false, false); + setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, true, true); + + verifyLastMobileDataIndicators(true, TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH[1][2], 0); } // Some tests of actual NetworkController code, just internals not display stuff diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java index 2e0e9a3c14f9..cecf2fde3cfb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java @@ -1,11 +1,13 @@ package com.android.systemui.statusbar.policy; import android.content.Intent; -import android.net.ConnectivityManager; +import android.net.NetworkCapabilities; import android.net.NetworkInfo; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; +import com.android.systemui.statusbar.policy.NetworkController.IconState; + import org.mockito.ArgumentCaptor; import org.mockito.Mockito; @@ -25,9 +27,9 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) { setWifiLevel(testLevel); - setConnectivity(100, ConnectivityManager.TYPE_WIFI, true); + setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, true, true); verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]); - setConnectivity(0, ConnectivityManager.TYPE_WIFI, true); + setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, false, true); verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]); } } @@ -45,10 +47,10 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) { setWifiLevel(testLevel); - setConnectivity(100, ConnectivityManager.TYPE_WIFI, true); + setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, true, true); verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel], testSsid); - setConnectivity(0, ConnectivityManager.TYPE_WIFI, true); + setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, false, true); verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[0][testLevel], testSsid); } @@ -61,7 +63,7 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { setWifiEnabled(true); setWifiState(true, testSsid); setWifiLevel(testLevel); - setConnectivity(100, ConnectivityManager.TYPE_WIFI, true); + setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, true, true); verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel], testSsid); @@ -82,13 +84,13 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { setWifiEnabled(true); setWifiState(true, testSsid); setWifiLevel(testLevel); - setConnectivity(100, ConnectivityManager.TYPE_WIFI, true); + setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, true, true); verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]); setupDefaultSignal(); setGsmRoaming(true); // Still be on wifi though. - setConnectivity(100, ConnectivityManager.TYPE_WIFI, true); + setConnectivity(NetworkCapabilities.TRANSPORT_WIFI, true, true); verifyLastMobileDataIndicators(true, TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH_ROAMING[1][DEFAULT_LEVEL], TelephonyIcons.ROAMING_ICON); @@ -133,46 +135,39 @@ public class NetworkControllerWifiTest extends NetworkControllerBaseTest { ArgumentCaptor<Boolean> inArg = ArgumentCaptor.forClass(Boolean.class); ArgumentCaptor<Boolean> outArg = ArgumentCaptor.forClass(Boolean.class); - Mockito.verify(mNetworkSignalChangedCallback, Mockito.atLeastOnce()).onWifiSignalChanged( - ArgumentCaptor.forClass(Boolean.class).capture(), - ArgumentCaptor.forClass(Boolean.class).capture(), - ArgumentCaptor.forClass(Integer.class).capture(), - inArg.capture(), outArg.capture(), - ArgumentCaptor.forClass(String.class).capture(), - ArgumentCaptor.forClass(String.class).capture()); + Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators( + Mockito.anyBoolean(), Mockito.any(IconState.class), Mockito.any(IconState.class), + inArg.capture(), outArg.capture(), Mockito.anyString()); assertEquals("WiFi data in, in quick settings", in, (boolean) inArg.getValue()); assertEquals("WiFi data out, in quick settings", out, (boolean) outArg.getValue()); } protected void verifyLastQsWifiIcon(boolean enabled, boolean connected, int icon, String description) { + ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); ArgumentCaptor<Boolean> enabledArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<Boolean> connectedArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<Integer> iconArg = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor<String> descArg = ArgumentCaptor.forClass(String.class); - Mockito.verify(mNetworkSignalChangedCallback, Mockito.atLeastOnce()).onWifiSignalChanged( - enabledArg.capture(), connectedArg.capture(), iconArg.capture(), - ArgumentCaptor.forClass(Boolean.class).capture(), - ArgumentCaptor.forClass(Boolean.class).capture(), - ArgumentCaptor.forClass(String.class).capture(), + Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators( + enabledArg.capture(), Mockito.any(IconState.class), + iconArg.capture(), Mockito.anyBoolean(), + Mockito.anyBoolean(), descArg.capture()); + IconState iconState = iconArg.getValue(); assertEquals("WiFi enabled, in quick settings", enabled, (boolean) enabledArg.getValue()); - assertEquals("WiFi connected, in quick settings", connected, - (boolean) connectedArg.getValue()); - assertEquals("WiFi signal, in quick settings", icon, (int) iconArg.getValue()); - assertEquals("WiFI desc (ssid), in quick settings", description, - (String) descArg.getValue()); + assertEquals("WiFi connected, in quick settings", connected, iconState.visible); + assertEquals("WiFi signal, in quick settings", icon, iconState.icon); + assertEquals("WiFI desc (ssid), in quick settings", description, descArg.getValue()); } protected void verifyLastWifiIcon(boolean visible, int icon) { - ArgumentCaptor<Boolean> visibleArg = ArgumentCaptor.forClass(Boolean.class); - ArgumentCaptor<Integer> iconArg = ArgumentCaptor.forClass(Integer.class); - - Mockito.verify(mSignalCluster, Mockito.atLeastOnce()).setWifiIndicators( - visibleArg.capture(), iconArg.capture(), - ArgumentCaptor.forClass(String.class).capture()); - assertEquals("WiFi visible, in status bar", visible, (boolean) visibleArg.getValue()); - assertEquals("WiFi signal, in status bar", icon, (int) iconArg.getValue()); + ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class); + + Mockito.verify(mCallbackHandler, Mockito.atLeastOnce()).setWifiIndicators( + Mockito.anyBoolean(), iconArg.capture(), Mockito.any(IconState.class), + Mockito.anyBoolean(), Mockito.anyBoolean(), Mockito.anyString()); + IconState iconState = iconArg.getValue(); + assertEquals("WiFi visible, in status bar", visible, iconState.visible); + assertEquals("WiFi signal, in status bar", icon, iconState.icon); } } diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index c32de4134e3c..e00cf5b1cf3d 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -16,8 +16,10 @@ package com.android.server; +import static com.android.internal.util.XmlUtils.readBooleanAttribute; import static com.android.internal.util.XmlUtils.readIntAttribute; import static com.android.internal.util.XmlUtils.readStringAttribute; +import static com.android.internal.util.XmlUtils.writeBooleanAttribute; import static com.android.internal.util.XmlUtils.writeIntAttribute; import static com.android.internal.util.XmlUtils.writeStringAttribute; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; @@ -37,6 +39,7 @@ import android.content.res.ObbInfo; import android.mtp.MtpStorage; import android.net.Uri; import android.os.Binder; +import android.os.DropBoxManager; import android.os.Environment; import android.os.Environment.UserEnvironment; import android.os.FileUtils; @@ -174,6 +177,7 @@ class MountService extends IMountService.Stub private static final boolean WATCHDOG_ENABLE = false; private static final String TAG = "MountService"; + private static final String TAG_STORAGE_BENCHMARK = "storage_benchmark"; private static final String VOLD_TAG = "VoldConnector"; @@ -233,6 +237,7 @@ class MountService extends IMountService.Stub public static final int VOLUME_DESTROYED = 659; public static final int MOVE_STATUS = 660; + public static final int BENCHMARK_RESULT = 661; /* * 700 series - fstrim @@ -247,6 +252,7 @@ class MountService extends IMountService.Stub private static final String TAG_VOLUMES = "volumes"; private static final String ATTR_VERSION = "version"; private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid"; + private static final String ATTR_FORCE_ADOPTABLE = "forceAdoptable"; private static final String TAG_VOLUME = "volume"; private static final String ATTR_TYPE = "type"; private static final String ATTR_FS_UUID = "fsUuid"; @@ -276,6 +282,8 @@ class MountService extends IMountService.Stub private ArrayMap<String, VolumeRecord> mRecords = new ArrayMap<>(); @GuardedBy("mLock") private String mPrimaryStorageUuid; + @GuardedBy("mLock") + private boolean mForceAdoptable; /** Map from disk ID to latches */ @GuardedBy("mLock") @@ -810,7 +818,8 @@ class MountService extends IMountService.Stub if (cooked.length != 3) break; final String id = cooked[1]; int flags = Integer.parseInt(cooked[2]); - if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false)) { + if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false) + || mForceAdoptable) { flags |= DiskInfo.FLAG_ADOPTABLE; } mDisks.put(id, new DiskInfo(id, flags)); @@ -927,6 +936,12 @@ class MountService extends IMountService.Stub break; } + case VoldResponseCode.BENCHMARK_RESULT: { + final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class); + dropBox.addText(TAG_STORAGE_BENCHMARK, raw); + break; + } + case VoldResponseCode.FstrimCompleted: { EventLogTags.writeFstrimFinish(SystemClock.elapsedRealtime()); break; @@ -1199,6 +1214,7 @@ class MountService extends IMountService.Stub private void readSettingsLocked() { mRecords.clear(); mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); + mForceAdoptable = false; FileInputStream fis = null; try { @@ -1220,6 +1236,7 @@ class MountService extends IMountService.Stub mPrimaryStorageUuid = readStringAttribute(in, ATTR_PRIMARY_STORAGE_UUID); } + mForceAdoptable = readBooleanAttribute(in, ATTR_FORCE_ADOPTABLE, false); } else if (TAG_VOLUME.equals(tag)) { final VolumeRecord rec = readVolumeRecord(in); @@ -1249,6 +1266,7 @@ class MountService extends IMountService.Stub out.startTag(null, TAG_VOLUMES); writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY); writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid); + writeBooleanAttribute(out, ATTR_FORCE_ADOPTABLE, mForceAdoptable); final int size = mRecords.size(); for (int i = 0; i < size; i++) { final VolumeRecord rec = mRecords.valueAt(i); @@ -1406,6 +1424,19 @@ class MountService extends IMountService.Stub } @Override + public long benchmark(String volId) { + enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); + waitForReady(); + + try { + final NativeDaemonEvent res = mConnector.execute("volume", "benchmark", volId); + return Long.parseLong(res.getMessage()); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + @Override public void partitionPublic(String diskId) { enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); waitForReady(); @@ -1520,6 +1551,21 @@ class MountService extends IMountService.Stub } @Override + public void setDebugFlags(int flags, int mask) { + enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); + waitForReady(); + + synchronized (mLock) { + if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) { + mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0; + } + + writeSettingsLocked(); + resetIfReadyAndConnected(); + } + } + + @Override public String getPrimaryStorageUuid() { enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); waitForReady(); @@ -3014,6 +3060,7 @@ class MountService extends IMountService.Stub pw.println(); pw.println("Primary storage UUID: " + mPrimaryStorageUuid); + pw.println("Force adoptable: " + mForceAdoptable); } synchronized (mObbMounts) { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 215839515a4e..a8ab667d9de1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2441,7 +2441,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } - long[] cpuSpeedTimes = mProcessCpuTracker.getLastCpuSpeedTimes(); final BatteryStatsImpl bstats = mBatteryStatsService.getActiveStatistics(); synchronized(bstats) { synchronized(mPidsSelfLocked) { @@ -2472,7 +2471,7 @@ public final class ActivityManagerService extends ActivityManagerNative pr.info.uid, pr.processName); } ps.addCpuTimeLocked(st.rel_utime - otherUTime, - st.rel_stime - otherSTime, cpuSpeedTimes); + st.rel_stime - otherSTime); pr.curCpuTime += st.rel_utime + st.rel_stime; } else { BatteryStatsImpl.Uid.Proc ps = st.batteryStats; @@ -2481,7 +2480,7 @@ public final class ActivityManagerService extends ActivityManagerNative bstats.mapUid(st.uid), st.name); } ps.addCpuTimeLocked(st.rel_utime - otherUTime, - st.rel_stime - otherSTime, cpuSpeedTimes); + st.rel_stime - otherSTime); } } final int userTime = mProcessCpuTracker.getLastUserTime(); @@ -2492,7 +2491,7 @@ public final class ActivityManagerService extends ActivityManagerNative final int idleTime = mProcessCpuTracker.getLastIdleTime(); bstats.finishAddingCpuLocked(perc, remainUTime, remainSTime, totalUTime, totalSTime, userTime, systemTime, - iowaitTime, irqTime, softIrqTime, idleTime, cpuSpeedTimes); + iowaitTime, irqTime, softIrqTime, idleTime); } } } @@ -5299,7 +5298,7 @@ public final class ActivityManagerService extends ActivityManagerNative private final boolean killPackageProcessesLocked(String packageName, int appId, int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart, boolean doit, boolean evenPersistent, String reason) { - ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); + ArrayList<ProcessRecord> procs = new ArrayList<>(); // Remove all processes this package may have touched: all with the // same UID (except for the system or root user), and all whose name @@ -5446,6 +5445,13 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i = providers.size() - 1; i >= 0; i--) { removeDyingProviderLocked(null, providers.get(i), true); } + + // Clean-up disabled broadcast receivers. + for (int i = mBroadcastQueues.length - 1; i >= 0; i--) { + mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked( + packageName, disabledClasses, userId, true); + } + } private final boolean forceStopPackageLocked(String packageName, int appId, @@ -5542,6 +5548,13 @@ public final class ActivityManagerService extends ActivityManagerNative // Remove transient permissions granted from/to this package/user removeUriPermissionsForPackageLocked(packageName, userId, false); + if (doit) { + for (i = mBroadcastQueues.length - 1; i >= 0; i--) { + didSomething |= mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked( + packageName, null, userId, doit); + } + } + if (packageName == null || uninstalling) { // Remove pending intents. For now we only do this when force // stopping users, because we have some problems when doing this @@ -15089,7 +15102,7 @@ public final class ActivityManagerService extends ActivityManagerNative ContentProviderRecord cpr = app.pubProviders.valueAt(i); final boolean always = app.bad || !allowRestart; boolean inLaunching = removeDyingProviderLocked(app, cpr, always); - if ((inLaunching || always) && !cpr.connections.isEmpty()) { + if ((inLaunching || always) && cpr.hasConnectionOrHandle()) { // We left the provider in the launching list, need to // restart it. restart = true; @@ -15233,7 +15246,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) { ContentProviderRecord cpr = mLaunchingProviders.get(i); if (cpr.launchingApp == app) { - if (!alwaysBad && !app.bad && !cpr.connections.isEmpty()) { + if (!alwaysBad && !app.bad && cpr.hasConnectionOrHandle()) { restart = true; } else { removeDyingProviderLocked(app, cpr, true); @@ -16130,10 +16143,7 @@ public final class ActivityManagerService extends ActivityManagerNative final int uid = intentExtras != null ? intentExtras.getInt(Intent.EXTRA_UID) : -1; if (uid >= 0) { - BatteryStatsImpl bs = mBatteryStatsService.getActiveStatistics(); - synchronized (bs) { - bs.removeUidStatsLocked(uid); - } + mBatteryStatsService.removeUid(uid); mAppOpsService.uidRemoved(uid); } break; diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 58665d7fe35e..a61223a00cb6 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -64,6 +64,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub implements PowerManagerInternal.LowPowerModeListener { static final String TAG = "BatteryStatsService"; + private boolean mFirstExternalStatsUpdate = true; static IBatteryStats sService; final BatteryStatsImpl mStats; final BatteryStatsHandler mHandler; @@ -177,6 +178,15 @@ public final class BatteryStatsService extends IBatteryStats.Stub // These are for direct use by the activity manager... + /** + * Remove a UID from the BatteryStats and BatteryStats' external dependencies. + */ + void removeUid(int uid) { + synchronized (mStats) { + mStats.removeUidStatsLocked(uid); + } + } + void addIsolatedUid(int isolatedUid, int appUid) { synchronized (mStats) { mStats.addIsolatedUidLocked(isolatedUid, appUid); @@ -1150,11 +1160,18 @@ public final class BatteryStatsService extends IBatteryStats.Stub mStats.addHistoryEventLocked(elapsedRealtime, uptime, BatteryStats.HistoryItem.EVENT_COLLECT_EXTERNAL_STATS, reason, 0); } + mStats.updateCpuTimeLocked(mFirstExternalStatsUpdate); mStats.updateKernelWakelocksLocked(); mStats.updateMobileRadioStateLocked(SystemClock.elapsedRealtime()); mStats.updateWifiStateLocked(wifiEnergyInfo); mStats.updateBluetoothStateLocked(bluetoothEnergyInfo); } + + if (mFirstExternalStatsUpdate) { + // We have read the stats for the first time, which means we have a baseline + // from which to calculate delta. + mFirstExternalStatsUpdate = false; + } } } } diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index e89ef5724dcd..745cb7e0ff7b 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -21,6 +21,7 @@ import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; +import java.util.Set; import android.app.ActivityManager; import android.app.AppGlobals; @@ -209,7 +210,7 @@ public final class BroadcastQueue { } public final boolean replaceParallelBroadcastLocked(BroadcastRecord r) { - for (int i=mParallelBroadcasts.size()-1; i>=0; i--) { + for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) { if (r.intent.filterEquals(mParallelBroadcasts.get(i).intent)) { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "***** DROPPING PARALLEL [" @@ -222,7 +223,7 @@ public final class BroadcastQueue { } public final boolean replaceOrderedBroadcastLocked(BroadcastRecord r) { - for (int i=mOrderedBroadcasts.size()-1; i>0; i--) { + for (int i = mOrderedBroadcasts.size() - 1; i > 0; i--) { if (r.intent.filterEquals(mOrderedBroadcasts.get(i).intent)) { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "***** DROPPING ORDERED [" @@ -1097,6 +1098,28 @@ public final class BroadcastQueue { mSummaryHistoryNext = ringAdvance(mSummaryHistoryNext, 1, MAX_BROADCAST_SUMMARY_HISTORY); } + boolean cleanupDisabledPackageReceiversLocked( + String packageName, Set<String> filterByClasses, int userId, boolean doit) { + boolean didSomething = false; + for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) { + didSomething |= mParallelBroadcasts.get(i).cleanupDisabledPackageReceiversLocked( + packageName, filterByClasses, userId, doit); + if (!doit && didSomething) { + return true; + } + } + + for (int i = mOrderedBroadcasts.size() - 1; i >= 0; i--) { + didSomething |= mOrderedBroadcasts.get(i).cleanupDisabledPackageReceiversLocked( + packageName, filterByClasses, userId, doit); + if (!doit && didSomething) { + return true; + } + } + + return didSomething; + } + final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) { if (r.nextReceiver > 0) { Object curReceiver = r.receivers.get(r.nextReceiver-1); @@ -1130,7 +1153,7 @@ public final class BroadcastQueue { if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0 || mPendingBroadcast != null) { boolean printed = false; - for (int i=mParallelBroadcasts.size()-1; i>=0; i--) { + for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) { BroadcastRecord br = mParallelBroadcasts.get(i); if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) { continue; @@ -1148,7 +1171,7 @@ public final class BroadcastQueue { } printed = false; needSep = true; - for (int i=mOrderedBroadcasts.size()-1; i>=0; i--) { + for (int i = mOrderedBroadcasts.size() - 1; i >= 0; i--) { BroadcastRecord br = mOrderedBroadcasts.get(i); if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) { continue; diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 9a4d7a0fb893..c050d03f0ae8 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -26,12 +26,14 @@ import android.os.Binder; import android.os.Bundle; import android.os.IBinder; import android.os.SystemClock; +import android.os.UserHandle; import android.util.PrintWriterPrinter; import android.util.TimeUtils; import java.io.PrintWriter; import java.util.Date; import java.util.List; +import java.util.Set; /** * An active intent broadcast. @@ -164,7 +166,7 @@ final class BroadcastRecord extends Binder { final int N = receivers != null ? receivers.size() : 0; String p2 = prefix + " "; PrintWriterPrinter printer = new PrintWriterPrinter(pw); - for (int i=0; i<N; i++) { + for (int i = 0; i < N; i++) { Object o = receivers.get(i); pw.print(prefix); pw.print("Receiver #"); pw.print(i); pw.print(": "); pw.println(o); @@ -205,6 +207,36 @@ final class BroadcastRecord extends Binder { state = IDLE; } + boolean cleanupDisabledPackageReceiversLocked( + String packageName, Set<String> filterByClasses, int userId, boolean doit) { + if ((userId != UserHandle.USER_ALL && this.userId != userId) || receivers == null) { + return false; + } + + boolean didSomething = false; + Object o; + for (int i = receivers.size() - 1; i >= 0; i--) { + o = receivers.get(i); + if (!(o instanceof ResolveInfo)) { + continue; + } + ActivityInfo info = ((ResolveInfo)o).activityInfo; + + final boolean sameComponent = packageName == null + || (info.applicationInfo.packageName.equals(packageName) + && (filterByClasses == null || filterByClasses.contains(info.name))); + if (sameComponent) { + if (!doit) { + return true; + } + didSomething = true; + receivers.remove(i); + } + } + + return didSomething; + } + public String toString() { return "BroadcastRecord{" + Integer.toHexString(System.identityHashCode(this)) diff --git a/services/core/java/com/android/server/am/ContentProviderRecord.java b/services/core/java/com/android/server/am/ContentProviderRecord.java index a37249da39fd..dceadf453c28 100644 --- a/services/core/java/com/android/server/am/ContentProviderRecord.java +++ b/services/core/java/com/android/server/am/ContentProviderRecord.java @@ -139,6 +139,10 @@ final class ContentProviderRecord { return (externalProcessTokenToHandle != null || externalProcessNoHandleCount > 0); } + public boolean hasConnectionOrHandle() { + return !connections.isEmpty() || hasExternalProcessHandles(); + } + void dump(PrintWriter pw, String prefix, boolean full) { if (full) { pw.print(prefix); pw.print("package="); diff --git a/services/core/java/com/android/server/am/ProviderMap.java b/services/core/java/com/android/server/am/ProviderMap.java index a1dc3e3c0769..ed8b1dd7fcd5 100644 --- a/services/core/java/com/android/server/am/ProviderMap.java +++ b/services/core/java/com/android/server/am/ProviderMap.java @@ -210,8 +210,11 @@ public final class ProviderMap { boolean collectPackageProvidersLocked(String packageName, Set<String> filterByClasses, boolean doit, boolean evenPersistent, int userId, ArrayList<ContentProviderRecord> result) { - boolean didSomething = collectPackageProvidersLocked(packageName, filterByClasses, - doit, evenPersistent, mSingletonByClass, result); + boolean didSomething = false; + if (userId == UserHandle.USER_ALL || userId == UserHandle.USER_OWNER) { + didSomething = collectPackageProvidersLocked(packageName, filterByClasses, + doit, evenPersistent, mSingletonByClass, result); + } if (!doit && didSomething) { return true; } diff --git a/services/core/java/com/android/server/location/FlpHardwareProvider.java b/services/core/java/com/android/server/location/FlpHardwareProvider.java index 1fb22bea1a6f..259ff1d4c553 100644 --- a/services/core/java/com/android/server/location/FlpHardwareProvider.java +++ b/services/core/java/com/android/server/location/FlpHardwareProvider.java @@ -48,7 +48,7 @@ public class FlpHardwareProvider { // Capabilities provided by FlpCallbacks private boolean mHaveBatchingCapabilities; private int mBatchingCapabilities; - private int mVersion; + private int mVersion = 1; private static FlpHardwareProvider sSingletonInstance = null; @@ -154,7 +154,9 @@ public class FlpHardwareProvider { private void setVersion(int version) { mVersion = version; - getGeofenceHardwareSink().setVersion(version); + if (mGeofenceHardwareSink != null) { + mGeofenceHardwareSink.setVersion(version); + } } private void maybeSendCapabilities() { @@ -480,6 +482,7 @@ public class FlpHardwareProvider { private GeofenceHardwareImpl getGeofenceHardwareSink() { if (mGeofenceHardwareSink == null) { mGeofenceHardwareSink = GeofenceHardwareImpl.getInstance(mContext); + mGeofenceHardwareSink.setVersion(mVersion); } return mGeofenceHardwareSink; diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index b505f7e131c8..7024ec8b13b9 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -17,9 +17,12 @@ package com.android.server.pm; import android.annotation.Nullable; +import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageParser; +import android.os.PowerManager; import android.os.UserHandle; +import android.os.WorkSource; import android.util.ArraySet; import android.util.Log; import android.util.Slog; @@ -50,8 +53,14 @@ final class PackageDexOptimizer { private final PackageManagerService mPackageManagerService; private ArraySet<PackageParser.Package> mDeferredDexOpt; + private final PowerManager.WakeLock mDexoptWakeLock; + private volatile boolean mSystemReady; + PackageDexOptimizer(PackageManagerService packageManagerService) { this.mPackageManagerService = packageManagerService; + PowerManager powerManager = (PowerManager)packageManagerService.mContext.getSystemService( + Context.POWER_SERVICE); + mDexoptWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*dexopt*"); } /** @@ -71,7 +80,18 @@ final class PackageDexOptimizer { done = null; } synchronized (mPackageManagerService.mInstallLock) { - return performDexOptLI(pkg, instructionSets, forceDex, defer, done); + final boolean useLock = mSystemReady; + if (useLock) { + mDexoptWakeLock.setWorkSource(new WorkSource(pkg.applicationInfo.uid)); + mDexoptWakeLock.acquire(); + } + try { + return performDexOptLI(pkg, instructionSets, forceDex, defer, done); + } finally { + if (useLock) { + mDexoptWakeLock.release(); + } + } } } @@ -242,4 +262,8 @@ final class PackageDexOptimizer { } mDeferredDexOpt.add(pkg); } + + void systemReady() { + mSystemReady = true; + } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8e0cdf1416ef..509289b6ae68 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -13486,6 +13486,7 @@ public class PackageManagerService extends IPackageManager.Stub { storage.registerListener(mStorageListener); mInstallerService.systemReady(); + mPackageDexOptimizer.systemReady(); } @Override diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index 025ede559acd..91639c5b33a6 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -189,16 +189,16 @@ void versionStylesForCompat(const std::shared_ptr<ResourceTable>& table) { } struct CompileItem { - Source source; ResourceName name; ConfigDescription config; + Source source; std::string extension; }; struct LinkItem { - Source source; ResourceName name; ConfigDescription config; + Source source; std::string originalPath; ZipFile* apk; std::u16string originalPackage; @@ -236,7 +236,8 @@ std::string buildFileReference(const LinkItem& item) { return buildFileReference(item.name, item.config, getExtension<char>(item.originalPath)); } -bool addFileReference(const std::shared_ptr<ResourceTable>& table, const CompileItem& item) { +template <typename T> +bool addFileReference(const std::shared_ptr<ResourceTable>& table, const T& item) { StringPool& pool = table->getValueStringPool(); StringPool::Ref ref = pool.makeRef(util::utf8ToUtf16(buildFileReference(item)), StringPool::Context{ 0, item.config }); @@ -334,9 +335,40 @@ bool compileXml(const AaptOptions& options, const std::shared_ptr<ResourceTable> return true; } -bool linkXml(const AaptOptions& options, const std::shared_ptr<IResolver>& resolver, - const LinkItem& item, const void* data, size_t dataLen, ZipFile* outApk, - std::queue<LinkItem>* outQueue) { +/** + * Determines if a layout should be auto generated based on SDK level. We do not + * generate a layout if there is already a layout defined whose SDK version is greater than + * the one we want to generate. + */ +bool shouldGenerateVersionedResource(const std::shared_ptr<const ResourceTable>& table, + const ResourceName& name, const ConfigDescription& config, + int sdkVersionToGenerate) { + assert(sdkVersionToGenerate > config.sdkVersion); + const ResourceTableType* type; + const ResourceEntry* entry; + std::tie(type, entry) = table->findResource(name); + assert(type && entry); + + auto iter = std::lower_bound(entry->values.begin(), entry->values.end(), config, + [](const ResourceConfigValue& lhs, const ConfigDescription& config) -> bool { + return lhs.config < config; + }); + + assert(iter != entry->values.end()); + ++iter; + + if (iter == entry->values.end()) { + return true; + } + + ConfigDescription newConfig = config; + newConfig.sdkVersion = sdkVersionToGenerate; + return newConfig < iter->config; +} + +bool linkXml(const AaptOptions& options, const std::shared_ptr<ResourceTable>& table, + const std::shared_ptr<IResolver>& resolver, const LinkItem& item, + const void* data, size_t dataLen, ZipFile* outApk, std::queue<LinkItem>* outQueue) { std::shared_ptr<android::ResXMLTree> tree = std::make_shared<android::ResXMLTree>(); if (tree->setTo(data, dataLen, false) != android::NO_ERROR) { return false; @@ -376,9 +408,21 @@ bool linkXml(const AaptOptions& options, const std::shared_ptr<IResolver>& resol if (minStrippedSdk.value() > 0) { // Something was stripped, so let's generate a new file // with the version of the smallest SDK version stripped. - LinkItem newWork = item; - newWork.config.sdkVersion = minStrippedSdk.value(); - outQueue->push(newWork); + // We can only generate a versioned layout if there doesn't exist a layout + // with sdk version greater than the current one but less than the one we + // want to generate. + if (shouldGenerateVersionedResource(table, item.name, item.config, + minStrippedSdk.value())) { + LinkItem newWork = item; + newWork.config.sdkVersion = minStrippedSdk.value(); + outQueue->push(newWork); + + if (!addFileReference(table, newWork)) { + Logger::error(options.output) << "failed to add auto-versioned resource '" + << newWork.name << "'." << std::endl; + return false; + } + } } if (outApk->add(outBuffer, buildFileReference(item).data(), ZipEntry::kCompressDeflated, @@ -604,7 +648,7 @@ static void addApkFilesToLinkQueue(const std::u16string& package, const Source& newSource.path += "/"; newSource.path += pathUtf8; outLinkQueue->push(LinkItem{ - newSource, name, value.config, pathUtf8, apk.get(), + name, value.config, newSource, pathUtf8, apk.get(), table->getPackage() }); // Now rewrite the file path. if (mangle) { @@ -743,8 +787,8 @@ bool link(const AaptOptions& options, const std::shared_ptr<ResourceTable>& outT void* uncompressedData = item.apk->uncompress(entry); assert(uncompressedData); - if (!linkXml(options, resolver, item, uncompressedData, entry->getUncompressedLen(), - &outApk, &linkQueue)) { + if (!linkXml(options, outTable, resolver, item, uncompressedData, + entry->getUncompressedLen(), &outApk, &linkQueue)) { Logger::error(options.output) << "failed to link '" << item.originalPath << "'." << std::endl; return false; @@ -862,9 +906,9 @@ bool compile(const AaptOptions& options, const std::shared_ptr<ResourceTable>& t } compileQueue.push(CompileItem{ - source, ResourceName{ table->getPackage(), *type, pathData.name }, pathData.config, + source, pathData.extension }); } diff --git a/tools/aapt2/data/Makefile b/tools/aapt2/data/Makefile index 6b5fafa1d71b..ce5201b6a85d 100644 --- a/tools/aapt2/data/Makefile +++ b/tools/aapt2/data/Makefile @@ -49,7 +49,7 @@ $(info PRIVATE_INTERMEDIATE_TABLES = $(PRIVATE_INTERMEDIATE_TABLES)) # returns: out/values-v4.apk: res/values-v4/styles.xml res/values-v4/colors.xml define make-collect-rule $(LOCAL_OUT)/$1.apk: $(filter $(LOCAL_RESOURCE_DIR)/$1/%,$(PRIVATE_RESOURCES)) - $(AAPT) compile --package $(LOCAL_PACKAGE) --binding $(LOCAL_GEN) -o $$@ $$^ + $(AAPT) compile --package $(LOCAL_PACKAGE) -o $$@ $$^ endef # Collect: out/values-v4.apk <- res/values-v4/styles.xml res/values-v4/colors.xml |