diff options
97 files changed, 2469 insertions, 1624 deletions
diff --git a/api/current.txt b/api/current.txt index b41493a9ad7e..b47a2f5511eb 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1188,7 +1188,7 @@ package android { field public static final int summaryColumn = 16843426; // 0x10102a2 field public static final int summaryOff = 16843248; // 0x10101f0 field public static final int summaryOn = 16843247; // 0x10101ef - field public static final int supportsAssistGesture = 16844011; // 0x10104eb + field public static final int supportsAssist = 16844011; // 0x10104eb field public static final int supportsRtl = 16843695; // 0x10103af field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb field public static final int supportsUploading = 16843419; // 0x101029b @@ -12486,6 +12486,25 @@ package android.graphics.drawable { enum_constant public static final android.graphics.drawable.GradientDrawable.Orientation TR_BL; } + public final class Icon implements android.os.Parcelable { + method public static android.graphics.drawable.Icon createWithBitmap(android.graphics.Bitmap); + method public static android.graphics.drawable.Icon createWithContentUri(java.lang.String); + method public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri); + method public static android.graphics.drawable.Icon createWithData(byte[], int, int); + method public static android.graphics.drawable.Icon createWithFilePath(java.lang.String); + method public static android.graphics.drawable.Icon createWithResource(android.content.res.Resources, int); + method public int describeContents(); + method public android.graphics.drawable.Drawable loadDrawable(android.content.Context); + method public void loadDrawableAsync(android.content.Context, android.os.Message); + method public void loadDrawableAsync(android.content.Context, android.os.Handler, android.graphics.drawable.Icon.OnDrawableLoadedListener); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.graphics.drawable.Icon> CREATOR; + } + + public static abstract interface Icon.OnDrawableLoadedListener { + method public abstract void onDrawableLoaded(android.graphics.drawable.Drawable); + } + public class InsetDrawable extends android.graphics.drawable.DrawableWrapper { ctor public InsetDrawable(android.graphics.drawable.Drawable, int); ctor public InsetDrawable(android.graphics.drawable.Drawable, int, int, int, int); @@ -18438,6 +18457,7 @@ package android.net { method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties); method public void onLosing(android.net.Network, int); method public void onLost(android.net.Network); + method public void onPreCheck(android.net.Network); } public static abstract interface ConnectivityManager.OnNetworkActiveListener { @@ -27163,7 +27183,8 @@ package android.provider { public static final class Telephony.Carriers implements android.provider.BaseColumns { field public static final java.lang.String APN = "apn"; field public static final java.lang.String AUTH_TYPE = "authtype"; - field public static final java.lang.String BEARER = "bearer"; + field public static final deprecated java.lang.String BEARER = "bearer"; + field public static final java.lang.String BEARER_BITMASK = "bearer_bitmask"; field public static final java.lang.String CARRIER_ENABLED = "carrier_enabled"; field public static final android.net.Uri CONTENT_URI; field public static final java.lang.String CURRENT = "current"; @@ -30306,17 +30327,17 @@ package android.telecom { method public int getHandlePresentation(); method public android.telecom.StatusHints getStatusHints(); method public int getVideoState(); + method public static boolean hasProperty(int, int); + method public boolean hasProperty(int); + method public static java.lang.String propertiesToString(int); field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000 field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000 - field public static final int CAPABILITY_GENERIC_CONFERENCE = 16384; // 0x4000 - field public static final int CAPABILITY_HIGH_DEF_AUDIO = 32768; // 0x8000 field public static final int CAPABILITY_HOLD = 1; // 0x1 field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80 field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4 field public static final int CAPABILITY_MUTE = 64; // 0x40 field public static final int CAPABILITY_RESPOND_VIA_TEXT = 32; // 0x20 field public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 4096; // 0x1000 - field public static final int CAPABILITY_SHOW_CALLBACK_NUMBER = 131072; // 0x20000 field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL = 768; // 0x300 field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 256; // 0x100 field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 512; // 0x200 @@ -30325,12 +30346,11 @@ package android.telecom { field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800 field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2 field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8 - field public static final int CAPABILITY_WIFI = 65536; // 0x10000 - } - - public class CallProperties { - ctor public CallProperties(); - field public static final int CONFERENCE = 1; // 0x1 + field public static final int PROPERTY_CONFERENCE = 1; // 0x1 + field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4 + field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2 + field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10 + field public static final int PROPERTY_WIFI = 8; // 0x8 } public final class CameraCapabilities implements android.os.Parcelable { diff --git a/api/system-current.txt b/api/system-current.txt index 09411dbfeb04..99c8c904e89a 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1263,7 +1263,7 @@ package android { field public static final int summaryColumn = 16843426; // 0x10102a2 field public static final int summaryOff = 16843248; // 0x10101f0 field public static final int summaryOn = 16843247; // 0x10101ef - field public static final int supportsAssistGesture = 16844011; // 0x10104eb + field public static final int supportsAssist = 16844011; // 0x10104eb field public static final int supportsRtl = 16843695; // 0x10103af field public static final int supportsSwitchingToNextInputMethod = 16843755; // 0x10103eb field public static final int supportsUploading = 16843419; // 0x101029b @@ -12780,6 +12780,25 @@ package android.graphics.drawable { enum_constant public static final android.graphics.drawable.GradientDrawable.Orientation TR_BL; } + public final class Icon implements android.os.Parcelable { + method public static android.graphics.drawable.Icon createWithBitmap(android.graphics.Bitmap); + method public static android.graphics.drawable.Icon createWithContentUri(java.lang.String); + method public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri); + method public static android.graphics.drawable.Icon createWithData(byte[], int, int); + method public static android.graphics.drawable.Icon createWithFilePath(java.lang.String); + method public static android.graphics.drawable.Icon createWithResource(android.content.res.Resources, int); + method public int describeContents(); + method public android.graphics.drawable.Drawable loadDrawable(android.content.Context); + method public void loadDrawableAsync(android.content.Context, android.os.Message); + method public void loadDrawableAsync(android.content.Context, android.os.Handler, android.graphics.drawable.Icon.OnDrawableLoadedListener); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.graphics.drawable.Icon> CREATOR; + } + + public static abstract interface Icon.OnDrawableLoadedListener { + method public abstract void onDrawableLoaded(android.graphics.drawable.Drawable); + } + public class InsetDrawable extends android.graphics.drawable.DrawableWrapper { ctor public InsetDrawable(android.graphics.drawable.Drawable, int); ctor public InsetDrawable(android.graphics.drawable.Drawable, int, int, int, int); @@ -19906,6 +19925,7 @@ package android.net { method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties); method public void onLosing(android.net.Network, int); method public void onLost(android.net.Network); + method public void onPreCheck(android.net.Network); } public static abstract interface ConnectivityManager.OnNetworkActiveListener { @@ -29176,7 +29196,8 @@ package android.provider { public static final class Telephony.Carriers implements android.provider.BaseColumns { field public static final java.lang.String APN = "apn"; field public static final java.lang.String AUTH_TYPE = "authtype"; - field public static final java.lang.String BEARER = "bearer"; + field public static final deprecated java.lang.String BEARER = "bearer"; + field public static final java.lang.String BEARER_BITMASK = "bearer_bitmask"; field public static final java.lang.String CARRIER_ENABLED = "carrier_enabled"; field public static final android.net.Uri CONTENT_URI; field public static final java.lang.String CURRENT = "current"; @@ -32422,17 +32443,17 @@ package android.telecom { method public int getHandlePresentation(); method public android.telecom.StatusHints getStatusHints(); method public int getVideoState(); + method public static boolean hasProperty(int, int); + method public boolean hasProperty(int); + method public static java.lang.String propertiesToString(int); field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000 field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000 - field public static final int CAPABILITY_GENERIC_CONFERENCE = 16384; // 0x4000 - field public static final int CAPABILITY_HIGH_DEF_AUDIO = 32768; // 0x8000 field public static final int CAPABILITY_HOLD = 1; // 0x1 field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80 field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4 field public static final int CAPABILITY_MUTE = 64; // 0x40 field public static final int CAPABILITY_RESPOND_VIA_TEXT = 32; // 0x20 field public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 4096; // 0x1000 - field public static final int CAPABILITY_SHOW_CALLBACK_NUMBER = 131072; // 0x20000 field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL = 768; // 0x300 field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 256; // 0x100 field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 512; // 0x200 @@ -32441,18 +32462,17 @@ package android.telecom { field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800 field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2 field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8 - field public static final int CAPABILITY_WIFI = 65536; // 0x10000 + field public static final int PROPERTY_CONFERENCE = 1; // 0x1 + field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4 + field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2 + field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10 + field public static final int PROPERTY_WIFI = 8; // 0x8 } public static abstract deprecated class Call.Listener extends android.telecom.Call.Callback { ctor public Call.Listener(); } - public class CallProperties { - ctor public CallProperties(); - field public static final int CONFERENCE = 1; // 0x1 - } - public final class CameraCapabilities implements android.os.Parcelable { ctor public CameraCapabilities(int, int); method public int describeContents(); diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 9d1d31214cb7..b0fda9c25190 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -26,6 +26,10 @@ import android.os.Parcelable; import android.os.RemoteException; import android.os.UserHandle; import android.os.WorkSource; +import android.text.TextUtils; +import libcore.util.ZoneInfoDB; + +import java.io.IOException; /** * This class provides access to the system alarm services. These allow you @@ -151,6 +155,7 @@ public class AlarmManager private final IAlarmManager mService; private final boolean mAlwaysExact; + private final int mTargetSdkVersion; /** @@ -159,8 +164,8 @@ public class AlarmManager AlarmManager(IAlarmManager service, Context ctx) { mService = service; - final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion; - mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KITKAT); + mTargetSdkVersion = ctx.getApplicationInfo().targetSdkVersion; + mAlwaysExact = (mTargetSdkVersion < Build.VERSION_CODES.KITKAT); } private long legacyExactLength() { @@ -585,12 +590,38 @@ public class AlarmManager } /** - * Set the system default time zone. - * Requires the permission android.permission.SET_TIME_ZONE. - * - * @param timeZone in the format understood by {@link java.util.TimeZone} + * Sets the system's persistent default time zone. This is the time zone for all apps, even + * after a reboot. Use {@link java.util.TimeZone#setDefault} if you just want to change the + * time zone within your app, and even then prefer to pass an explicit + * {@link java.util.TimeZone} to APIs that require it rather than changing the time zone for + * all threads. + * + * <p> On android M and above, it is an error to pass in a non-Olson timezone to this + * function. Note that this is a bad idea on all Android releases because POSIX and + * the {@code TimeZone} class have opposite interpretations of {@code '+'} and {@code '-'} + * in the same non-Olson ID. + * + * @param timeZone one of the Olson ids from the list returned by + * {@link java.util.TimeZone#getAvailableIDs} */ public void setTimeZone(String timeZone) { + if (TextUtils.isEmpty(timeZone)) { + return; + } + + // Reject this timezone if it isn't an Olson zone we recognize. + if (mTargetSdkVersion >= Build.VERSION_CODES.MNC) { + boolean hasTimeZone = false; + try { + hasTimeZone = ZoneInfoDB.getInstance().hasTimeZone(timeZone); + } catch (IOException ignored) { + } + + if (!hasTimeZone) { + throw new IllegalArgumentException("Timezone: " + timeZone + " is not an Olson ID"); + } + } + try { mService.setTimeZone(timeZone); } catch (RemoteException ex) { diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 8a3c9c8a865a..6b161b910f05 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -223,8 +223,10 @@ public class AppOpsManager { public static final int OP_PROCESS_OUTGOING_CALLS = 54; /** @hide User the fingerprint API. */ public static final int OP_USE_FINGERPRINT = 55; + /** @hide Access to body sensors such as heart rate, etc. */ + public static final int OP_BODY_SENSORS = 56; /** @hide */ - public static final int _NUM_OP = 56; + public static final int _NUM_OP = 57; /** Access to coarse location information. */ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location"; @@ -280,9 +282,6 @@ public class AppOpsManager { /** @hide Allows an application to send SMS messages. */ public static final String OPSTR_SEND_SMS = "android:send_sms"; - /** @hide Allows an application to add system alert windows. */ - public static final String OPSTR_SYSTEM_ALERT_WINDOW - = "android:system_alert_window"; /** @hide Required to be able to access the camera device. */ public static final String OPSTR_CAMERA = "android:camera"; @@ -295,6 +294,15 @@ public class AppOpsManager { /** @hide Required to access phone state related information. */ public static final String OPSTR_ADD_VOICEMAIL = "android:add_voicemail"; + /** @hide Access APIs for SIP calling over VOIP or WiFi */ + public static final String OPSTR_USE_SIP + = "android:use_sip"; + /** @hide Use the fingerprint API. */ + public static final String OPSTR_USE_FINGERPRINT + = "android:use_fingerprint"; + /** @hide Access to body sensors such as heart rate, etc. */ + public static final String OPSTR_BODY_SENSORS + = "android:body_sensors"; /** * This maps each operation to the operation that serves as the @@ -360,7 +368,8 @@ public class AppOpsManager { OP_ADD_VOICEMAIL, OP_USE_SIP, OP_PROCESS_OUTGOING_CALLS, - OP_USE_FINGERPRINT + OP_USE_FINGERPRINT, + OP_BODY_SENSORS }; /** @@ -372,30 +381,30 @@ public class AppOpsManager { OPSTR_FINE_LOCATION, null, null, + OPSTR_READ_CONTACTS, + OPSTR_WRITE_CONTACTS, + OPSTR_READ_CALL_LOG, + OPSTR_WRITE_CALL_LOG, + OPSTR_READ_CALENDAR, + OPSTR_WRITE_CALENDAR, null, null, null, + OPSTR_CALL_PHONE, + OPSTR_READ_SMS, null, + OPSTR_RECEIVE_SMS, null, + OPSTR_RECEIVE_MMS, + OPSTR_RECEIVE_WAP_PUSH, + OPSTR_SEND_SMS, null, null, null, null, null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, - null, + OPSTR_CAMERA, + OPSTR_RECORD_AUDIO, null, null, null, @@ -419,11 +428,12 @@ public class AppOpsManager { null, null, null, + OPSTR_READ_PHONE_STATE, + OPSTR_ADD_VOICEMAIL, + OPSTR_USE_SIP, null, - null, - null, - null, - null + OPSTR_USE_FINGERPRINT, + OPSTR_BODY_SENSORS }; /** @@ -486,7 +496,8 @@ public class AppOpsManager { "ADD_VOICEMAIL", "USE_SIP", "PROCESS_OUTGOING_CALLS", - "USE_FINGERPRINT" + "USE_FINGERPRINT", + "BODY_SENSORS" }; /** @@ -549,7 +560,8 @@ public class AppOpsManager { Manifest.permission.ADD_VOICEMAIL, Manifest.permission.USE_SIP, Manifest.permission.PROCESS_OUTGOING_CALLS, - Manifest.permission.USE_FINGERPRINT + Manifest.permission.USE_FINGERPRINT, + Manifest.permission.BODY_SENSORS }; /** @@ -613,7 +625,8 @@ public class AppOpsManager { null, // ADD_VOICEMAIL null, // USE_SIP null, // PROCESS_OUTGOING_CALLS - null // USE_FINGERPRINT + null, // USE_FINGERPRINT + null // BODY_SENSORS }; /** @@ -676,7 +689,8 @@ public class AppOpsManager { false, //ADD_VOICEMAIL false, // USE_SIP false, // PROCESS_OUTGOING_CALLS - false // USE_FINGERPRINT + false, // USE_FINGERPRINT + false // BODY_SENSORS }; /** @@ -738,6 +752,7 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED, + AppOpsManager.MODE_ALLOWED, AppOpsManager.MODE_ALLOWED }; @@ -804,37 +819,19 @@ public class AppOpsManager { false, false, false, + false, false }; /** - * This is a mapping from a permission name to public app op name. + * Mapping from an app op name to the app op code. */ - private static final ArrayMap<String, String> sPermToOp = new ArrayMap<>(); - static { - sPermToOp.put(Manifest.permission.ACCESS_COARSE_LOCATION, OPSTR_COARSE_LOCATION); - sPermToOp.put(Manifest.permission.ACCESS_FINE_LOCATION, OPSTR_FINE_LOCATION); - sPermToOp.put(Manifest.permission.PACKAGE_USAGE_STATS, OPSTR_GET_USAGE_STATS); - sPermToOp.put(Manifest.permission.READ_CONTACTS, OPSTR_READ_CONTACTS); - sPermToOp.put(Manifest.permission.WRITE_CONTACTS, OPSTR_WRITE_CONTACTS); - sPermToOp.put(Manifest.permission.READ_CALL_LOG, OPSTR_READ_CALL_LOG); - sPermToOp.put(Manifest.permission.WRITE_CALL_LOG, OPSTR_WRITE_CALL_LOG); - sPermToOp.put(Manifest.permission.READ_CALENDAR, OPSTR_READ_CALENDAR); - sPermToOp.put(Manifest.permission.WRITE_CALENDAR, OPSTR_WRITE_CALENDAR); - sPermToOp.put(Manifest.permission.CALL_PHONE, OPSTR_CALL_PHONE); - sPermToOp.put(Manifest.permission.READ_SMS, OPSTR_READ_SMS); - sPermToOp.put(Manifest.permission.RECEIVE_SMS, OPSTR_RECEIVE_SMS); - sPermToOp.put(Manifest.permission.RECEIVE_MMS, OPSTR_RECEIVE_MMS); - sPermToOp.put(Manifest.permission.RECEIVE_WAP_PUSH, OPSTR_RECEIVE_WAP_PUSH); - sPermToOp.put(Manifest.permission.SEND_SMS, OPSTR_SEND_SMS); - sPermToOp.put(Manifest.permission.SYSTEM_ALERT_WINDOW, OPSTR_SYSTEM_ALERT_WINDOW); - sPermToOp.put(Manifest.permission.CAMERA, OPSTR_CAMERA); - sPermToOp.put(Manifest.permission.RECORD_AUDIO, OPSTR_RECORD_AUDIO); - sPermToOp.put(Manifest.permission.READ_PHONE_STATE, OPSTR_READ_PHONE_STATE); - sPermToOp.put(Manifest.permission.ADD_VOICEMAIL, OPSTR_ADD_VOICEMAIL); - } + private static HashMap<String, Integer> sOpStrToOp = new HashMap<>(); - private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>(); + /** + * Mapping from a permission to the corresponding app op. + */ + private static HashMap<String, Integer> sPermToOp = new HashMap<>(); static { if (sOpToSwitch.length != _NUM_OP) { @@ -874,6 +871,11 @@ public class AppOpsManager { sOpStrToOp.put(sOpToString[i], i); } } + for (int i=0; i<_NUM_OP; i++) { + if (sOpPerms[i] != null) { + sPermToOp.put(sOpPerms[i], i); + } + } } /** @@ -922,6 +924,14 @@ public class AppOpsManager { } /** + * Retrieve the app op code for a permission, or null if there is not one. + * @hide + */ + public static int permissionToOpCode(String permission) { + return sPermToOp.get(permission); + } + + /** * Retrieve whether the op allows the system (and system ui) to * bypass the user restriction. * @hide @@ -1185,7 +1195,11 @@ public class AppOpsManager { */ @SystemApi public static String permissionToOp(String permission) { - return sPermToOp.get(permission); + final Integer opCode = sPermToOp.get(permission); + if (opCode == null) { + return null; + } + return sOpToString[opCode]; } /** diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index cf9813fcaf8d..9f71ea5c805d 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2926,7 +2926,7 @@ public class DevicePolicyManager { * the user has already been set up. */ @SystemApi - public boolean setActiveProfileOwner(ComponentName admin, String ownerName) + public boolean setActiveProfileOwner(ComponentName admin, @Deprecated String ownerName) throws IllegalArgumentException { if (mService != null) { try { @@ -2992,8 +2992,8 @@ public class DevicePolicyManager { * @throws IllegalArgumentException if admin is null, the package isn't installed, or the * preconditions mentioned are not met. */ - public boolean setProfileOwner(ComponentName admin, String ownerName, int userHandle) - throws IllegalArgumentException { + public boolean setProfileOwner(ComponentName admin, @Deprecated String ownerName, + int userHandle) throws IllegalArgumentException { if (admin == null) { throw new NullPointerException("admin cannot be null"); } diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java index 00248ccbbf32..1205708478a7 100644 --- a/core/java/android/appwidget/AppWidgetManager.java +++ b/core/java/android/appwidget/AppWidgetManager.java @@ -1053,6 +1053,20 @@ public class AppWidgetManager { } } + /** + * @hide + */ + public boolean isBoundWidgetPackage(String packageName, int userId) { + if (mService == null) { + return false; + } + try { + return mService.isBoundWidgetPackage(packageName, userId); + } catch (RemoteException re) { + throw new RuntimeException("system server dead?", re); + } + } + private boolean bindAppWidgetIdIfAllowed(int appWidgetId, int profileId, ComponentName provider, Bundle options) { if (mService == null) { diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 3044a9494a92..9b4dcc6b3467 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -34,6 +34,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.app.ActivityThread; import android.os.SystemProperties; +import android.provider.Settings; import android.os.Binder; import android.util.Log; import android.util.Pair; @@ -389,7 +390,7 @@ public final class BluetoothAdapter { * @hide */ public static final String ACTION_BLE_STATE_CHANGED = - "anrdoid.bluetooth.adapter.action.BLE_STATE_CHANGED"; + "android.bluetooth.adapter.action.BLE_STATE_CHANGED"; /** * Broadcast Action: The notifys Bluetooth ACL connected event. This will be @@ -632,24 +633,6 @@ public final class BluetoothAdapter { } /** - * Returns true if LE only mode is enabled, that is apps - * have authorization to turn only BT ON and the calling - * app has privilage to do so - */ - private boolean isLEAlwaysOnEnabled() { - boolean ret = false; - if (SystemProperties.getBoolean("ro.bluetooth.blealwayson", true) == true) { - Log.v(TAG, "LE always on mode is enabled"); - // TODO: System API authorization check - ret = true; - } else { - Log.v(TAG, "LE always on mode is disabled"); - ret = false; - } - return ret; - } - - /** * Turns off Bluetooth LE which was earlier turned on by calling EnableBLE(). * * <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition @@ -676,7 +659,7 @@ public final class BluetoothAdapter { * @hide */ public boolean disableBLE() { - if (isLEAlwaysOnEnabled() != true) return false; + if (!isBleScanAlwaysAvailable()) return false; int state = getLeState(); if (state == BluetoothAdapter.STATE_ON) { @@ -738,7 +721,7 @@ public final class BluetoothAdapter { * @hide */ public boolean enableBLE() { - if (isLEAlwaysOnEnabled() != true) return false; + if (!isBleScanAlwaysAvailable()) return false; if (isLeEnabled() == true) { if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!"); @@ -1243,8 +1226,12 @@ public final class BluetoothAdapter { */ @SystemApi public boolean isBleScanAlwaysAvailable() { - // TODO: implement after Settings UI change. - return false; + try { + return mManagerService.isBleScanAlwaysAvailable(); + } catch (RemoteException e) { + Log.e(TAG, "remote expection when calling isBleScanAlwaysAvailable", e); + return false; + } } /** diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl index 8d1ce990a562..0b81ee8c547e 100644 --- a/core/java/android/bluetooth/IBluetoothManager.aidl +++ b/core/java/android/bluetooth/IBluetoothManager.aidl @@ -44,6 +44,8 @@ interface IBluetoothManager String getAddress(); String getName(); + + boolean isBleScanAlwaysAvailable(); int updateBleAppCount(IBinder b, boolean enable); boolean isBleAppPresent(); } diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java index 123514eb9374..dad486dd9416 100644 --- a/core/java/android/bluetooth/le/ScanSettings.java +++ b/core/java/android/bluetooth/le/ScanSettings.java @@ -187,7 +187,7 @@ public final class ScanSettings implements Parcelable { mScanResultType = scanResultType; mReportDelayMillis = reportDelayMillis; mNumOfMatchesPerFilter = numOfMatchesPerFilter; - mMatchMode = numOfMatchesPerFilter; + mMatchMode = matchMode; } private ScanSettings(Parcel in) { diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java index e50b0ff17a0a..96000dd84c6f 100644 --- a/core/java/android/content/pm/IntentFilterVerificationInfo.java +++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java @@ -48,19 +48,18 @@ public final class IntentFilterVerificationInfo implements Parcelable { private static final String ATTR_PACKAGE_NAME = "packageName"; private static final String ATTR_STATUS = "status"; - private ArrayList<String> mDomains; + private ArraySet<String> mDomains = new ArraySet<>(); private String mPackageName; private int mMainStatus; public IntentFilterVerificationInfo() { mPackageName = null; - mDomains = new ArrayList<>(); mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; } public IntentFilterVerificationInfo(String packageName, ArrayList<String> domains) { mPackageName = packageName; - mDomains = domains; + mDomains.addAll(domains); mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; } @@ -73,14 +72,6 @@ public final class IntentFilterVerificationInfo implements Parcelable { readFromParcel(source); } - public ArrayList<String> getDomains() { - return mDomains; - } - - public ArraySet<String> getDomainsSet() { - return new ArraySet<>(mDomains); - } - public String getPackageName() { return mPackageName; } @@ -98,6 +89,14 @@ public final class IntentFilterVerificationInfo implements Parcelable { } } + public ArraySet<String> getDomains() { + return mDomains; + } + + public void setDomains(ArrayList<String> list) { + mDomains = new ArraySet<>(list); + } + public String getDomainsString() { StringBuilder sb = new StringBuilder(); for (String str : mDomains) { @@ -145,7 +144,6 @@ public final class IntentFilterVerificationInfo implements Parcelable { } mMainStatus = status; - mDomains = new ArrayList<>(); int outerDepth = parser.getDepth(); int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT @@ -201,15 +199,16 @@ public final class IntentFilterVerificationInfo implements Parcelable { private void readFromParcel(Parcel source) { mPackageName = source.readString(); mMainStatus = source.readInt(); - mDomains = new ArrayList<>(); - source.readStringList(mDomains); + ArrayList<String> list = new ArrayList<>(); + source.readStringList(list); + mDomains.addAll(list); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mPackageName); dest.writeInt(mMainStatus); - dest.writeStringList(mDomains); + dest.writeStringList(new ArrayList<>(mDomains)); } public static final Creator<IntentFilterVerificationInfo> CREATOR = @@ -221,5 +220,4 @@ public final class IntentFilterVerificationInfo implements Parcelable { return new IntentFilterVerificationInfo[size]; } }; - } diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 11037fd69604..22a9e9c8a1c8 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -41,16 +41,19 @@ import java.util.List; */ public class SystemSensorManager extends SensorManager { private static native void nativeClassInit(); - private static native int nativeGetNextSensor(Sensor sensor, int next); - private static native int nativeEnableDataInjection(boolean enable); + private static native long nativeCreate(String opPackageName); + private static native int nativeGetNextSensor(long nativeInstance, Sensor sensor, int next); + private static native int nativeEnableDataInjection(long nativeInstance, boolean enable); private static boolean sSensorModuleInitialized = false; - private static final Object sSensorModuleLock = new Object(); - private static final ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>(); - private static final SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>(); private static InjectEventQueue mInjectEventQueue = null; private static boolean mDataInjectionMode = false; + private final Object mLock = new Object(); + + private final ArrayList<Sensor> mFullSensorsList = new ArrayList<>(); + private final SparseArray<Sensor> mHandleToSensor = new SparseArray<>(); + // Listener list private final HashMap<SensorEventListener, SensorEventQueue> mSensorListeners = new HashMap<SensorEventListener, SensorEventQueue>(); @@ -60,44 +63,44 @@ public class SystemSensorManager extends SensorManager { // Looper associated with the context in which this instance was created. private final Looper mMainLooper; private final int mTargetSdkLevel; - private final String mPackageName; + private final Context mContext; private final boolean mHasDataInjectionPermissions; + private final long mNativeInstance; /** {@hide} */ public SystemSensorManager(Context context, Looper mainLooper) { mMainLooper = mainLooper; mTargetSdkLevel = context.getApplicationInfo().targetSdkVersion; - mPackageName = context.getPackageName(); - synchronized(sSensorModuleLock) { + mContext = context; + mNativeInstance = nativeCreate(context.getOpPackageName()); + + synchronized(mLock) { if (!sSensorModuleInitialized) { sSensorModuleInitialized = true; - nativeClassInit(); - - // initialize the sensor list - final ArrayList<Sensor> fullList = sFullSensorsList; - int i = 0; - do { - Sensor sensor = new Sensor(); - i = nativeGetNextSensor(sensor, i); - if (i>=0) { - //Log.d(TAG, "found sensor: " + sensor.getName() + - // ", handle=" + sensor.getHandle()); - fullList.add(sensor); - sHandleToSensor.append(sensor.getHandle(), sensor); - } - } while (i>0); } mHasDataInjectionPermissions = context.checkSelfPermission( Manifest.permission.HARDWARE_TEST) == PackageManager.PERMISSION_GRANTED; } + + // initialize the sensor list + int i = 0; + while(true) { + Sensor sensor = new Sensor(); + i = nativeGetNextSensor(mNativeInstance, sensor, i); + if (i <= 0) { + break; + } + mFullSensorsList.add(sensor); + mHandleToSensor.append(sensor.getHandle(), sensor); + } } /** @hide */ @Override protected List<Sensor> getFullSensorList() { - return sFullSensorsList; + return mFullSensorsList; } @@ -232,8 +235,8 @@ public class SystemSensorManager extends SensorManager { throw new SecurityException("Permission denial. Calling enableDataInjection without " + Manifest.permission.HARDWARE_TEST); } - synchronized (sSensorModuleLock) { - int ret = nativeEnableDataInjection(enable); + synchronized (mLock) { + int ret = nativeEnableDataInjection(mNativeInstance, enable); // The HAL does not support injection. Ignore. if (ret != 0) { Log.e(TAG, "HAL does not support data injection"); @@ -255,7 +258,7 @@ public class SystemSensorManager extends SensorManager { throw new SecurityException("Permission denial. Calling injectSensorData without " + Manifest.permission.HARDWARE_TEST); } - synchronized (sSensorModuleLock) { + synchronized (mLock) { if (!mDataInjectionMode) { Log.e(TAG, "Data injection mode not activated before calling injectSensorData"); return false; @@ -284,15 +287,17 @@ public class SystemSensorManager extends SensorManager { * SensorManager instance. */ private static abstract class BaseEventQueue { - private native long nativeInitBaseEventQueue(WeakReference<BaseEventQueue> eventQWeak, - MessageQueue msgQ, float[] scratch, String packageName, int mode); + private static native long nativeInitBaseEventQueue(long nativeManager, + WeakReference<BaseEventQueue> eventQWeak, MessageQueue msgQ, float[] scratch, + String packageName, int mode, String opPackageName); private static native int nativeEnableSensor(long eventQ, int handle, int rateUs, int maxBatchReportLatencyUs); private static native int nativeDisableSensor(long eventQ, int handle); private static native void nativeDestroySensorEventQueue(long eventQ); private static native int nativeFlushSensor(long eventQ); private static native int nativeInjectSensorData(long eventQ, int handle, - float[] values,int accuracy, long timestamp); + float[] values,int accuracy, long timestamp); + private long nSensorEventQueue; private final SparseBooleanArray mActiveSensors = new SparseBooleanArray(); protected final SparseIntArray mSensorAccuracies = new SparseIntArray(); @@ -305,8 +310,9 @@ public class SystemSensorManager extends SensorManager { protected static final int OPERATING_MODE_DATA_INJECTION = 1; BaseEventQueue(Looper looper, SystemSensorManager manager, int mode) { - nSensorEventQueue = nativeInitBaseEventQueue(new WeakReference<BaseEventQueue>(this), - looper.getQueue(), mScratch, manager.mPackageName, mode); + nSensorEventQueue = nativeInitBaseEventQueue(manager.mNativeInstance, + new WeakReference<>(this), looper.getQueue(), mScratch, + manager.mContext.getPackageName(), mode, manager.mContext.getOpPackageName()); mCloseGuard.open("dispose"); mManager = manager; } @@ -339,7 +345,7 @@ public class SystemSensorManager extends SensorManager { for (int i=0 ; i<mActiveSensors.size(); i++) { if (mActiveSensors.valueAt(i) == true) { int handle = mActiveSensors.keyAt(i); - Sensor sensor = sHandleToSensor.get(handle); + Sensor sensor = mManager.mHandleToSensor.get(handle); if (sensor != null) { disableSensor(sensor); mActiveSensors.put(handle, false); @@ -452,7 +458,7 @@ public class SystemSensorManager extends SensorManager { @Override protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy, long timestamp) { - final Sensor sensor = sHandleToSensor.get(handle); + final Sensor sensor = mManager.mHandleToSensor.get(handle); SensorEvent t = null; synchronized (mSensorsEvents) { t = mSensorsEvents.get(handle); @@ -481,7 +487,7 @@ public class SystemSensorManager extends SensorManager { @SuppressWarnings("unused") protected void dispatchFlushCompleteEvent(int handle) { if (mListener instanceof SensorEventListener2) { - final Sensor sensor = sHandleToSensor.get(handle); + final Sensor sensor = mManager.mHandleToSensor.get(handle); ((SensorEventListener2)mListener).onFlushCompleted(sensor); } return; @@ -519,7 +525,7 @@ public class SystemSensorManager extends SensorManager { @Override protected void dispatchSensorEvent(int handle, float[] values, int accuracy, long timestamp) { - final Sensor sensor = sHandleToSensor.get(handle); + final Sensor sensor = mManager.mHandleToSensor.get(handle); TriggerEvent t = null; synchronized (mTriggerEvents) { t = mTriggerEvents.get(handle); @@ -546,7 +552,7 @@ public class SystemSensorManager extends SensorManager { } } - static final class InjectEventQueue extends BaseEventQueue { + final class InjectEventQueue extends BaseEventQueue { public InjectEventQueue(Looper looper, SystemSensorManager manager) { super(looper, manager, OPERATING_MODE_DATA_INJECTION); } diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index f6791a42fa7a..e9564b3fd59f 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -580,7 +580,8 @@ public abstract class CameraDevice implements AutoCloseable { * indicating that the camera device is in use already. * * <p> - * This error can be produced when opening the camera fails. + * This error can be produced when opening the camera fails due to the camera + * being used by a higher-priority camera API client. * </p> * * @see #onError @@ -678,7 +679,7 @@ public abstract class CameraDevice implements AutoCloseable { * {@link CameraAccessException}. The disconnection could be due to a * change in security policy or permissions; the physical disconnection * of a removable camera device; or the camera being needed for a - * higher-priority use case.</p> + * higher-priority camera API client.</p> * * <p>There may still be capture callbacks that are invoked * after this method is called, or new image buffers that are delivered @@ -688,8 +689,9 @@ public abstract class CameraDevice implements AutoCloseable { * about the disconnection.</p> * * <p>You should clean up the camera with {@link CameraDevice#close} after - * this happens, as it is not recoverable until opening the camera again - * after it becomes {@link CameraManager.AvailabilityCallback#onCameraAvailable available}. + * this happens, as it is not recoverable until the camera can be opened + * again. For most use cases, this will be when the camera again becomes + * {@link CameraManager.AvailabilityCallback#onCameraAvailable available}. * </p> * * @param camera the device that has been disconnected diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index 1a00a05b231d..e2d2f61d48c4 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -77,8 +77,8 @@ public final class CameraManager { } /** - * Return the list of currently connected camera devices by - * identifier. + * Return the list of currently connected camera devices by identifier, including + * cameras that may be in use by other camera API clients. * * <p>Non-removable cameras use integers starting at 0 for their * identifiers, while removable cameras have a unique identifier for each @@ -103,6 +103,11 @@ public final class CameraManager { * <p>The first time a callback is registered, it is immediately called * with the availability status of all currently known camera devices.</p> * + * <p>{@link AvailabilityCallback#onCameraUnavailable(String)} will be called whenever a camera + * device is opened by any camera API client. As of API level 23, other camera API clients may + * still be able to open such a camera device, evicting the existing client if they have higher + * priority than the existing client of a camera device. See open() for more details.</p> + * * <p>Since this callback will be registered with the camera service, remember to unregister it * once it is no longer needed; otherwise the callback will continue to receive events * indefinitely and it may prevent other resources from being released. Specifically, the @@ -259,14 +264,14 @@ public final class CameraManager { } /** - * Helper for openning a connection to a camera with the given ID. + * Helper for opening a connection to a camera with the given ID. * * @param cameraId The unique identifier of the camera device to open * @param callback The callback for the camera. Must not be null. * @param handler The handler to invoke the callback on. Must not be null. * * @throws CameraAccessException if the camera is disabled by device policy, - * or too many camera devices are already open, or the cameraId does not match + * too many camera devices are already open, or the cameraId does not match * any currently available camera device. * * @throws SecurityException if the application does not have permission to @@ -330,7 +335,8 @@ public final class CameraManager { deviceImpl.setRemoteFailure(e); if (e.getReason() == CameraAccessException.CAMERA_DISABLED || - e.getReason() == CameraAccessException.CAMERA_DISCONNECTED) { + e.getReason() == CameraAccessException.CAMERA_DISCONNECTED || + e.getReason() == CameraAccessException.CAMERA_IN_USE) { // Per API docs, these failures call onError and throw throw e.asChecked(); } @@ -369,7 +375,19 @@ public final class CameraManager { * <p>Use {@link #getCameraIdList} to get the list of available camera * devices. Note that even if an id is listed, open may fail if the device * is disconnected between the calls to {@link #getCameraIdList} and - * {@link #openCamera}.</p> + * {@link #openCamera}, or if a higher-priority camera API client begins using the + * camera device.</p> + * + * <p>As of API level 23, devices for which the + * {@link AvailabilityCallback#onCameraUnavailable(String)} callback has been called due to the + * device being in use by a lower-priority, background camera API client can still potentially + * be opened by calling this method when the calling camera API client has a higher priority + * than the current camera API client using this device. In general, if the top, foreground + * activity is running within your application process, your process will be given the highest + * priority when accessing the camera, and this method will succeed even if the camera device is + * in use by another camera API client. Any lower-priority application that loses control of the + * camera in this way will receive an + * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback.</p> * * <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will * be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up @@ -401,7 +419,7 @@ public final class CameraManager { * {@code null} to use the current thread's {@link android.os.Looper looper}. * * @throws CameraAccessException if the camera is disabled by device policy, - * or the camera has become or was disconnected. + * has been disconnected, or is being used by a higher-priority camera API client. * * @throws IllegalArgumentException if cameraId or the callback was null, * or the cameraId does not match any currently or previously available @@ -477,8 +495,7 @@ public final class CameraManager { } /** - * A callback for camera devices becoming available or - * unavailable to open. + * A callback for camera devices becoming available or unavailable to open. * * <p>Cameras become available when they are no longer in use, or when a new * removable camera is connected. They become unavailable when some diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index c531e7ec8f69..2d63e3f5747f 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -341,7 +341,8 @@ public class ConnectivityManager { * one. This is used by applications needing to talk to the carrier's * Multimedia Messaging Service servers. * - * @deprecated Applications should instead use {@link #requestNetwork} to request a network that + * @deprecated Applications should instead use + * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that * provides the {@link NetworkCapabilities#NET_CAPABILITY_MMS} capability. */ public static final int TYPE_MOBILE_MMS = 2; @@ -351,7 +352,8 @@ public class ConnectivityManager { * one. This is used by applications needing to talk to the carrier's * Secure User Plane Location servers for help locating the device. * - * @deprecated Applications should instead use {@link #requestNetwork} to request a network that + * @deprecated Applications should instead use + * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that * provides the {@link NetworkCapabilities#NET_CAPABILITY_SUPL} capability. */ public static final int TYPE_MOBILE_SUPL = 3; @@ -367,7 +369,8 @@ public class ConnectivityManager { * same network interface as {@link #TYPE_MOBILE} but the routing setup * is different. * - * @deprecated Applications should instead use {@link #requestNetwork} to request a network that + * @deprecated Applications should instead use + * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that * uses the {@link NetworkCapabilities#TRANSPORT_CELLULAR} transport. */ public static final int TYPE_MOBILE_HIPRI = 5; @@ -910,7 +913,8 @@ public class ConnectivityManager { * implementation+feature combination, except that the value {@code -1} * always indicates failure. * - * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api. + * @deprecated Deprecated in favor of the cleaner + * {@link #requestNetwork(NetworkRequest, NetworkCallback)} API. * @removed */ public int startUsingNetworkFeature(int networkType, String feature) { @@ -958,7 +962,7 @@ public class ConnectivityManager { * implementation+feature combination, except that the value {@code -1} * always indicates failure. * - * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api. + * @deprecated Deprecated in favor of the cleaner {@link unregisterNetworkCallback} API. * @removed */ public int stopUsingNetworkFeature(int networkType, String feature) { @@ -1236,8 +1240,9 @@ public class ConnectivityManager { * @param hostAddress the IP address of the host to which the route is desired * @return {@code true} on success, {@code false} on failure * - * @deprecated Deprecated in favor of the {@link #requestNetwork}, - * {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} api. + * @deprecated Deprecated in favor of the + * {@link #requestNetwork(NetworkRequest, NetworkCallback)}, + * {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} API. * @removed */ public boolean requestRouteToHost(int networkType, int hostAddress) { @@ -1256,7 +1261,7 @@ public class ConnectivityManager { * @return {@code true} on success, {@code false} on failure * @hide * @deprecated Deprecated in favor of the {@link #requestNetwork} and - * {@link #bindProcessToNetwork} api. + * {@link #bindProcessToNetwork} API. * @removed */ public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) { @@ -2144,14 +2149,22 @@ public class ConnectivityManager { public static final int CANCELED = 8; /** - * @hide - * Called whenever the framework connects to a network that it may use to - * satisfy this request + * Called when the framework connects to a new network to evaluate whether it satisfies this + * request. If evaluation succeeds, this callback may be followed by an {@link #onAvailable} + * callback. There is no guarantee that this new network will satisfy any requests, or that + * the network will stay connected for longer than the time necessary to evaluate it. + * <p> + * Most applications <b>should not</b> act on this callback, and should instead use + * {@link #onAvailable}. This callback is intended for use by applications that can assist + * the framework in properly evaluating the network — for example, an application that + * can automatically log in to a captive portal without user intervention. + * + * @param network The {@link Network} of the network that is being evaluated. */ public void onPreCheck(Network network) {} /** - * Called when the framework connects and has declared new network ready for use. + * Called when the framework connects and has declared a new network ready for use. * This callback may be called more than once if the {@link Network} that is * satisfying the request changes. * @@ -2251,116 +2264,82 @@ public class ConnectivityManager { @Override public void handleMessage(Message message) { Log.d(TAG, "CM callback handler got msg " + message.what); + NetworkRequest request = (NetworkRequest) getObject(message, NetworkRequest.class); + Network network = (Network) getObject(message, Network.class); switch (message.what) { case CALLBACK_PRECHECK: { - NetworkRequest request = (NetworkRequest)getObject(message, - NetworkRequest.class); - NetworkCallback callbacks = getCallbacks(request); - if (callbacks != null) { - callbacks.onPreCheck((Network)getObject(message, Network.class)); - } else { - Log.e(TAG, "callback not found for PRECHECK message"); + NetworkCallback callback = getCallback(request, "PRECHECK"); + if (callback != null) { + callback.onPreCheck(network); } break; } case CALLBACK_AVAILABLE: { - NetworkRequest request = (NetworkRequest)getObject(message, - NetworkRequest.class); - NetworkCallback callbacks = getCallbacks(request); - if (callbacks != null) { - callbacks.onAvailable((Network)getObject(message, Network.class)); - } else { - Log.e(TAG, "callback not found for AVAILABLE message"); + NetworkCallback callback = getCallback(request, "AVAILABLE"); + if (callback != null) { + callback.onAvailable(network); } break; } case CALLBACK_LOSING: { - NetworkRequest request = (NetworkRequest)getObject(message, - NetworkRequest.class); - NetworkCallback callbacks = getCallbacks(request); - if (callbacks != null) { - callbacks.onLosing((Network)getObject(message, Network.class), - message.arg1); - } else { - Log.e(TAG, "callback not found for LOSING message"); + NetworkCallback callback = getCallback(request, "LOSING"); + if (callback != null) { + callback.onLosing(network, message.arg1); } break; } case CALLBACK_LOST: { - NetworkRequest request = (NetworkRequest)getObject(message, - NetworkRequest.class); - - NetworkCallback callbacks = getCallbacks(request); - if (callbacks != null) { - callbacks.onLost((Network)getObject(message, Network.class)); - } else { - Log.e(TAG, "callback not found for LOST message"); + NetworkCallback callback = getCallback(request, "LOST"); + if (callback != null) { + callback.onLost(network); } break; } case CALLBACK_UNAVAIL: { - NetworkRequest request = (NetworkRequest)getObject(message, - NetworkRequest.class); - NetworkCallback callbacks = null; - synchronized(mCallbackMap) { - callbacks = mCallbackMap.get(request); - } - if (callbacks != null) { - callbacks.onUnavailable(); - } else { - Log.e(TAG, "callback not found for UNAVAIL message"); + NetworkCallback callback = getCallback(request, "UNAVAIL"); + if (callback != null) { + callback.onUnavailable(); } break; } case CALLBACK_CAP_CHANGED: { - NetworkRequest request = (NetworkRequest)getObject(message, - NetworkRequest.class); - NetworkCallback callbacks = getCallbacks(request); - if (callbacks != null) { - Network network = (Network)getObject(message, Network.class); + NetworkCallback callback = getCallback(request, "CAP_CHANGED"); + if (callback != null) { NetworkCapabilities cap = (NetworkCapabilities)getObject(message, NetworkCapabilities.class); - callbacks.onCapabilitiesChanged(network, cap); - } else { - Log.e(TAG, "callback not found for CAP_CHANGED message"); + callback.onCapabilitiesChanged(network, cap); } break; } case CALLBACK_IP_CHANGED: { - NetworkRequest request = (NetworkRequest)getObject(message, - NetworkRequest.class); - NetworkCallback callbacks = getCallbacks(request); - if (callbacks != null) { - Network network = (Network)getObject(message, Network.class); + NetworkCallback callback = getCallback(request, "IP_CHANGED"); + if (callback != null) { LinkProperties lp = (LinkProperties)getObject(message, LinkProperties.class); - callbacks.onLinkPropertiesChanged(network, lp); - } else { - Log.e(TAG, "callback not found for IP_CHANGED message"); + callback.onLinkPropertiesChanged(network, lp); } break; } case CALLBACK_RELEASED: { - NetworkRequest req = (NetworkRequest)getObject(message, NetworkRequest.class); - NetworkCallback callbacks = null; + NetworkCallback callback = null; synchronized(mCallbackMap) { - callbacks = mCallbackMap.remove(req); + callback = mCallbackMap.remove(request); } - if (callbacks != null) { + if (callback != null) { synchronized(mRefCount) { if (mRefCount.decrementAndGet() == 0) { getLooper().quit(); } } } else { - Log.e(TAG, "callback not found for CANCELED message"); + Log.e(TAG, "callback not found for RELEASED message"); } break; } case CALLBACK_EXIT: { - Log.d(TAG, "Listener quiting"); + Log.d(TAG, "Listener quitting"); getLooper().quit(); break; } @@ -2374,10 +2353,16 @@ public class ConnectivityManager { private Object getObject(Message msg, Class c) { return msg.getData().getParcelable(c.getSimpleName()); } - private NetworkCallback getCallbacks(NetworkRequest req) { + + private NetworkCallback getCallback(NetworkRequest req, String name) { + NetworkCallback callback; synchronized(mCallbackMap) { - return mCallbackMap.get(req); + callback = mCallbackMap.get(req); + } + if (callback == null) { + Log.e(TAG, "callback not found for " + name + " message"); } + return callback; } } @@ -2601,10 +2586,10 @@ public class ConnectivityManager { /** * Unregisters callbacks about and possibly releases networks originating from - * {@link #requestNetwork} and {@link #registerNetworkCallback} calls. If the - * given {@code NetworkCallback} had previously been used with {@code #requestNetwork}, - * any networks that had been connected to only to satisfy that request will be - * disconnected. + * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and {@link #registerNetworkCallback} + * calls. If the given {@code NetworkCallback} had previously been used with + * {@code #requestNetwork}, any networks that had been connected to only to satisfy that request + * will be disconnected. * * @param networkCallback The {@link NetworkCallback} used when making the request. */ diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 8c8bfab9c76d..ab70485d38bc 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -37,6 +37,11 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ public NetworkCapabilities() { + clearAll(); + mNetworkCapabilities = + (1 << NET_CAPABILITY_NOT_RESTRICTED) | + (1 << NET_CAPABILITY_TRUSTED) | + (1 << NET_CAPABILITY_NOT_VPN); } public NetworkCapabilities(NetworkCapabilities nc) { @@ -50,11 +55,21 @@ public final class NetworkCapabilities implements Parcelable { } /** + * Completely clears the contents of this object, removing even the capabilities that are set + * by default when the object is constructed. + * @hide + */ + public void clearAll() { + mNetworkCapabilities = mTransportTypes = 0; + mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = 0; + mNetworkSpecifier = null; + } + + /** * Represents the network's capabilities. If any are specified they will be satisfied * by any Network that matches all of them. */ - private long mNetworkCapabilities = (1 << NET_CAPABILITY_NOT_RESTRICTED) | - (1 << NET_CAPABILITY_TRUSTED) | (1 << NET_CAPABILITY_NOT_VPN); + private long mNetworkCapabilities; /** * Indicates this is a network that has the ability to reach the diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index e3e16eb1747b..8e0584a3e0bc 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -20,6 +20,7 @@ import android.app.admin.DevicePolicyManager; import android.content.Context; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; +import android.text.TextUtils; import android.util.Log; import java.io.File; @@ -242,6 +243,15 @@ public class Environment { return DATA_DIRECTORY; } + /** {@hide} */ + public static File getDataAppDirectory(String volumeUuid) { + if (TextUtils.isEmpty(volumeUuid)) { + return new File("/data/app"); + } else { + return new File("/mnt/expand/" + volumeUuid + "/app"); + } + } + /** * Return the primary external storage directory. This directory may not * currently be accessible if it has been mounted by the user on their diff --git a/core/java/android/os/IPermissionController.aidl b/core/java/android/os/IPermissionController.aidl index 0cc160309bd8..5e8590af11f1 100644 --- a/core/java/android/os/IPermissionController.aidl +++ b/core/java/android/os/IPermissionController.aidl @@ -21,4 +21,5 @@ package android.os; interface IPermissionController { boolean checkPermission(String permission, int pid, int uid); String[] getPackagesForUid(int uid); + boolean isRuntimePermission(String permission); } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 8c1f44fa1a69..1273772bc4ec 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -502,7 +502,25 @@ public final class Parcel { * {@SystemApi} */ public final void writeBlob(byte[] b) { - nativeWriteBlob(mNativePtr, b, 0, (b != null) ? b.length : 0); + writeBlob(b, 0, (b != null) ? b.length : 0); + } + + /** + * Write a blob of data into the parcel at the current {@link #dataPosition}, + * growing {@link #dataCapacity} if needed. + * @param b Bytes to place into the parcel. + * @param offset Index of first byte to be written. + * @param len Number of bytes to write. + * {@hide} + * {@SystemApi} + */ + public final void writeBlob(byte[] b, int offset, int len) { + if (b == null) { + writeInt(-1); + return; + } + Arrays.checkOffsetAndCount(b.length, offset, len); + nativeWriteBlob(mNativePtr, b, offset, len); } /** diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java index 4bc97c9cbcb5..997d58673da3 100644 --- a/core/java/android/service/voice/VoiceInteractionServiceInfo.java +++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java @@ -43,7 +43,7 @@ public class VoiceInteractionServiceInfo { private String mSessionService; private String mRecognitionService; private String mSettingsActivity; - private boolean mSupportsAssistGesture; + private boolean mSupportsAssist; public VoiceInteractionServiceInfo(PackageManager pm, ComponentName comp) throws PackageManager.NameNotFoundException { @@ -95,8 +95,8 @@ public class VoiceInteractionServiceInfo { com.android.internal.R.styleable.VoiceInteractionService_recognitionService); mSettingsActivity = array.getString( com.android.internal.R.styleable.VoiceInteractionService_settingsActivity); - mSupportsAssistGesture = array.getBoolean( - com.android.internal.R.styleable.VoiceInteractionService_supportsAssistGesture, + mSupportsAssist = array.getBoolean( + com.android.internal.R.styleable.VoiceInteractionService_supportsAssist, false); array.recycle(); if (mSessionService == null) { @@ -145,7 +145,7 @@ public class VoiceInteractionServiceInfo { return mSettingsActivity; } - public boolean getSupportsAssistGesture() { - return mSupportsAssistGesture; + public boolean getSupportsAssist() { + return mSupportsAssist; } } diff --git a/core/java/android/view/GhostView.java b/core/java/android/view/GhostView.java index d58e7c03848c..bc38e1a271f1 100644 --- a/core/java/android/view/GhostView.java +++ b/core/java/android/view/GhostView.java @@ -41,7 +41,7 @@ public class GhostView extends View { final ViewGroup parent = (ViewGroup) mView.getParent(); setGhostedVisibility(View.INVISIBLE); parent.mRecreateDisplayList = true; - parent.getDisplayList(); + parent.updateDisplayListIfDirty(); } @Override @@ -49,7 +49,7 @@ public class GhostView extends View { if (canvas instanceof DisplayListCanvas) { DisplayListCanvas dlCanvas = (DisplayListCanvas) canvas; mView.mRecreateDisplayList = true; - RenderNode renderNode = mView.getDisplayList(); + RenderNode renderNode = mView.updateDisplayListIfDirty(); if (renderNode.isValid()) { dlCanvas.insertReorderBarrier(); // enable shadow for this rendernode dlCanvas.drawRenderNode(renderNode); @@ -84,7 +84,7 @@ public class GhostView extends View { final ViewGroup parent = (ViewGroup) mView.getParent(); if (parent != null) { parent.mRecreateDisplayList = true; - parent.getDisplayList(); + parent.updateDisplayListIfDirty(); } } } diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java index 6db46e9fda0d..85b22fbb355b 100644 --- a/core/java/android/view/TextureView.java +++ b/core/java/android/view/TextureView.java @@ -246,6 +246,9 @@ public class TextureView extends View { mSurface = null; mLayer = null; + // Make sure if/when new layer gets re-created, transform matrix will + // be re-applied. + mMatrixChanged = true; mHadSurface = true; } } diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 87d5d9a4b1aa..390d81dede51 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -269,7 +269,7 @@ public class ThreadedRenderer extends HardwareRenderer { view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED) == View.PFLAG_INVALIDATED; view.mPrivateFlags &= ~View.PFLAG_INVALIDATED; - view.getDisplayList(); + view.updateDisplayListIfDirty(); view.mRecreateDisplayList = false; } @@ -285,7 +285,7 @@ public class ThreadedRenderer extends HardwareRenderer { callbacks.onHardwarePreDraw(canvas); canvas.insertReorderBarrier(); - canvas.drawRenderNode(view.getDisplayList()); + canvas.drawRenderNode(view.updateDisplayListIfDirty()); canvas.insertInorderBarrier(); callbacks.onHardwarePostDraw(canvas); @@ -360,7 +360,7 @@ public class ThreadedRenderer extends HardwareRenderer { @Override boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) { return nCopyLayerInto(mNativeProxy, - layer.getDeferredLayerUpdater(), bitmap.getSkBitmap()); + layer.getDeferredLayerUpdater(), bitmap); } @Override @@ -531,7 +531,7 @@ public class ThreadedRenderer extends HardwareRenderer { private static native long nCreateTextureLayer(long nativeProxy); private static native void nBuildLayer(long nativeProxy, long node); - private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap); + private static native boolean nCopyLayerInto(long nativeProxy, long layer, Bitmap bitmap); private static native void nPushLayerUpdate(long nativeProxy, long layer); private static native void nCancelLayerUpdate(long nativeProxy, long layer); private static native void nDetachSurfaceTexture(long nativeProxy, long layer); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 81ad5ad7512d..963b7a6c6a40 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -11091,25 +11091,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * <p>Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is - * completely transparent and 1 means the view is completely opaque.</p> + * Sets the opacity of the view to a value from 0 to 1, where 0 means the view is + * completely transparent and 1 means the view is completely opaque. * - * <p> Note that setting alpha to a translucent value (0 < alpha < 1) can have significant - * performance implications, especially for large views. It is best to use the alpha property - * sparingly and transiently, as in the case of fading animations.</p> + * <p class="note"><strong>Note:</strong> setting alpha to a translucent value (0 < alpha < 1) + * can have significant performance implications, especially for large views. It is best to use + * the alpha property sparingly and transiently, as in the case of fading animations.</p> * * <p>For a view with a frequently changing alpha, such as during a fading animation, it is * strongly recommended for performance reasons to either override - * {@link #hasOverlappingRendering()} to return false if appropriate, or setting a - * {@link #setLayerType(int, android.graphics.Paint) layer type} on the view.</p> + * {@link #hasOverlappingRendering()} to return <code>false</code> if appropriate, or setting a + * {@link #setLayerType(int, android.graphics.Paint) layer type} on the view for the duration + * of the animation. On versions {@link android.os.Build.VERSION_CODES#MNC} and below, + * the default path for rendering an unlayered View with alpha could add multiple milliseconds + * of rendering cost, even for simple or small views. Starting with + * {@link android.os.Build.VERSION_CODES#MNC}, {@link #LAYER_TYPE_HARDWARE} is automatically + * applied to the view at the rendering level.</p> * * <p>If this view overrides {@link #onSetAlpha(int)} to return true, then this view is * responsible for applying the opacity itself.</p> * - * <p>Note that if the view is backed by a - * {@link #setLayerType(int, android.graphics.Paint) layer} and is associated with a - * {@link #setLayerPaint(android.graphics.Paint) layer paint}, setting an alpha value less than - * 1.0 will supersede the alpha of the layer paint.</p> + * <p>On versions {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1} and below, note that if + * the view is backed by a {@link #setLayerType(int, android.graphics.Paint) layer} and is + * associated with a {@link #setLayerPaint(android.graphics.Paint) layer paint}, setting an + * alpha value less than 1.0 will supersede the alpha of the layer paint.</p> + * + * <p>Starting with {@link android.os.Build.VERSION_CODES#MNC}, setting a translucent alpha + * value will clip a View to its bounds, unless the View returns <code>false</code> from + * {@link #hasOverlappingRendering}.</p> * * @param alpha The opacity of the view. * @@ -14702,11 +14711,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return !(mAttachInfo == null || mAttachInfo.mHardwareRenderer == null); } - private void updateDisplayListIfDirty() { + /** + * Gets the RenderNode for the view, and updates its DisplayList (if needed and supported) + * @hide + */ + @NonNull + public RenderNode updateDisplayListIfDirty() { final RenderNode renderNode = mRenderNode; if (!canHaveDisplayList()) { // can't populate RenderNode, don't try - return; + return renderNode; } if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0 @@ -14720,7 +14734,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags &= ~PFLAG_DIRTY_MASK; dispatchGetDisplayList(); - return; // no work needed + return renderNode; // no work needed } // If we got here, we're recreating it. Mark it as such to ensure that @@ -14769,19 +14783,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID; mPrivateFlags &= ~PFLAG_DIRTY_MASK; } - } - - /** - * Returns a RenderNode with View draw content recorded, which can be - * used to draw this view again without executing its draw method. - * - * @return A RenderNode ready to replay, or null if caching is not enabled. - * - * @hide - */ - public RenderNode getDisplayList() { - updateDisplayListIfDirty(); - return mRenderNode; + return renderNode; } private void resetDisplayList() { @@ -15543,7 +15545,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (drawingWithRenderNode) { // Delay getting the display list until animation-driven alpha values are // set up and possibly passed on to the view - renderNode = getDisplayList(); + renderNode = updateDisplayListIfDirty(); if (!renderNode.isValid()) { // Uncommon, but possible. If a view is removed from the hierarchy during the call // to getDisplayList(), the display list will be marked invalid and we should not diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index d0d42010b3ec..ef57dc32c7dd 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3524,10 +3524,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } private void recreateChildDisplayList(View child) { - child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) - == PFLAG_INVALIDATED; + child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0; child.mPrivateFlags &= ~PFLAG_INVALIDATED; - child.getDisplayList(); + child.updateDisplayListIfDirty(); child.mRecreateDisplayList = false; } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4158340e00a1..fda6e6394fab 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2322,10 +2322,8 @@ public final class ViewRootImpl implements ViewParent, * @hide */ void outputDisplayList(View view) { - RenderNode renderNode = view.getDisplayList(); - if (renderNode != null) { - renderNode.output(); - } + RenderNode renderNode = view.updateDisplayListIfDirty(); + renderNode.output(); } /** diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index c9d9a8c0513a..1df43d058040 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -3638,11 +3638,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te startNestedScroll(SCROLL_AXIS_VERTICAL); - if (mFastScroll != null) { - boolean intercepted = mFastScroll.onTouchEvent(ev); - if (intercepted) { - return true; - } + if (mFastScroll != null && mFastScroll.onTouchEvent(ev)) { + return true; } initVelocityTrackerIfNotExists(); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 35e73892a6ea..9b36b840de0c 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -578,7 +578,12 @@ public class Editor { } private void hideCursorControllers() { - if (mSuggestionsPopupWindow != null && !mSuggestionsPopupWindow.isShowingUp()) { + // When mTextView is not ExtractEditText, we need to distinguish two kinds of focus-lost. + // One is the true focus lost where suggestions pop-up (if any) should be dismissed, and the + // other is an side effect of showing the suggestions pop-up itself. We use isShowingUp() + // to distinguish one from the other. + if (mSuggestionsPopupWindow != null && ((mTextView instanceof ExtractEditText) || + !mSuggestionsPopupWindow.isShowingUp())) { // Should be done before hide insertion point controller since it triggers a show of it mSuggestionsPopupWindow.hide(); } @@ -1397,12 +1402,11 @@ public class Editor { InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) { if (imm.isActive(mTextView)) { - boolean reported = false; if (ims.mContentChanged || ims.mSelectionModeChanged) { // We are in extract mode and the content has changed // in some way... just report complete new text to the // input method. - reported = reportExtractedText(); + reportExtractedText(); } } } @@ -1919,10 +1923,6 @@ public class Editor { mSuggestionsPopupWindow.show(); } - boolean areSuggestionsShown() { - return mSuggestionsPopupWindow != null && mSuggestionsPopupWindow.isShowing(); - } - void onScrollChanged() { if (mPositionListener != null) { mPositionListener.onScrollChanged(); @@ -4620,8 +4620,6 @@ public class Editor { } static class InputMethodState { - Rect mCursorRectInWindow = new Rect(); - float[] mTmpOffset = new float[2]; ExtractedTextRequest mExtractedTextRequest; final ExtractedText mExtractedText = new ExtractedText(); int mBatchEditNesting; diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index 552b274065f9..f06f3c218276 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -1389,7 +1389,8 @@ class FastScroller { // to intercept events. If it does, we will receive a CANCEL // event. if (!mList.isInScrollingContainer()) { - beginDrag(); + // This will get dispatched to onTouchEvent(). Start + // dragging there. return true; } @@ -1406,6 +1407,8 @@ class FastScroller { final float pos = getPosFromMotionEvent(mInitialTouchY); scrollTo(pos); + // This may get dispatched to onTouchEvent(), but it + // doesn't really matter since we'll already be in a drag. return onTouchEvent(ev); } break; @@ -1440,6 +1443,15 @@ class FastScroller { } switch (me.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + if (isPointInside(me.getX(), me.getY())) { + if (!mList.isInScrollingContainer()) { + beginDrag(); + return true; + } + } + } break; + case MotionEvent.ACTION_UP: { if (mPendingDrag >= 0) { // Allow a tap to scroll. diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index 99bf9f316162..86c1b2f65873 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -30,6 +30,7 @@ interface IAppOpsService { void startWatchingMode(int op, String packageName, IAppOpsCallback callback); void stopWatchingMode(IAppOpsCallback callback); IBinder getToken(IBinder clientToken); + int permissionToOpCode(String permission); // Remaining methods are only used in Java. int checkPackage(int uid, String packageName); @@ -42,5 +43,4 @@ interface IAppOpsService { void setUserRestrictions(in Bundle restrictions, int userHandle); void removeUser(int userHandle); - } diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl index d149c5b9d497..4c6db24e4043 100644 --- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl +++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl @@ -105,5 +105,5 @@ interface IVoiceInteractionManagerService { * Indicates whether the currently active voice interaction service is capable of handling the * assist gesture. */ - boolean activeServiceSupportsAssistGesture(); + boolean activeServiceSupportsAssist(); } diff --git a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl index 008d38b4893a..7d3db02d6bca 100644 --- a/core/java/com/android/internal/appwidget/IAppWidgetService.aidl +++ b/core/java/com/android/internal/appwidget/IAppWidgetService.aidl @@ -65,5 +65,6 @@ interface IAppWidgetService { in IBinder connection); void unbindRemoteViewsService(String callingPackage, int appWidgetId, in Intent intent); int[] getAppWidgetIds(in ComponentName providerComponent); + boolean isBoundWidgetPackage(String packageName, int userId); } diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java index 3f96174bd27b..6173832d8596 100644 --- a/core/java/com/android/internal/logging/MetricsLogger.java +++ b/core/java/com/android/internal/logging/MetricsLogger.java @@ -32,6 +32,7 @@ public class MetricsLogger implements MetricsConstants { public static final int NOTIFICATION_ZEN_MODE_EXTERNAL_RULE = 145; public static final int ACTION_BAN_APP_NOTES = 146; public static final int NOTIFICATION_ZEN_MODE_EVENT_RULE = 147; + public static final int ACTION_DISMISS_ALL_NOTES = 148; public static void visible(Context context, int category) throws IllegalArgumentException { if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) { diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java index 191662c1e621..6d4e0583b4e1 100644 --- a/core/java/com/android/internal/widget/SwipeDismissLayout.java +++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java @@ -216,6 +216,8 @@ public class SwipeDismissLayout extends FrameLayout { if (mVelocityTracker == null) { return super.onTouchEvent(ev); } + // offset because the view is translated during swipe + ev.offsetLocation(mTranslationX, 0); switch (ev.getActionMasked()) { case MotionEvent.ACTION_UP: updateDismiss(ev); diff --git a/core/jni/android/graphics/Shader.cpp b/core/jni/android/graphics/Shader.cpp index 6591d293cd34..d0f75917106a 100644 --- a/core/jni/android/graphics/Shader.cpp +++ b/core/jni/android/graphics/Shader.cpp @@ -80,11 +80,12 @@ static jlong Shader_setLocalMatrix(JNIEnv* env, jobject o, jlong shaderHandle, j /////////////////////////////////////////////////////////////////////////////////////////////// -static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jlong bitmapHandle, +static jlong BitmapShader_constructor(JNIEnv* env, jobject o, jobject jbitmap, jint tileModeX, jint tileModeY) { - const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle); - SkShader* s = SkShader::CreateBitmapShader(*bitmap, + SkBitmap bitmap; + GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); + SkShader* s = SkShader::CreateBitmapShader(bitmap, (SkShader::TileMode)tileModeX, (SkShader::TileMode)tileModeY); @@ -249,7 +250,7 @@ static JNINativeMethod gShaderMethods[] = { }; static JNINativeMethod gBitmapShaderMethods[] = { - { "nativeCreate", "(JII)J", (void*)BitmapShader_constructor }, + { "nativeCreate", "(Landroid/graphics/Bitmap;II)J", (void*)BitmapShader_constructor }, }; static JNINativeMethod gLinearGradientMethods[] = { diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp index 5d08532f0905..50a106917ee8 100644 --- a/core/jni/android_graphics_Canvas.cpp +++ b/core/jni/android_graphics_Canvas.cpp @@ -40,22 +40,21 @@ static void finalizer(JNIEnv* env, jobject clazz, jlong canvasHandle) { // Native wrapper constructor used by Canvas(Bitmap) static jlong initRaster(JNIEnv* env, jobject, jobject jbitmap) { - SkBitmap* bitmap = nullptr; + SkBitmap bitmap; if (jbitmap != NULL) { - bitmap = GraphicsJNI::getSkBitmapDeprecated(env, jbitmap); + GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); } - return reinterpret_cast<jlong>(Canvas::create_canvas( - bitmap ? *bitmap : SkBitmap())); + return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap)); } // Set the given bitmap as the new draw target (wrapped in a new SkCanvas), // optionally copying canvas matrix & clip state. static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap) { - SkBitmap* bitmap = nullptr; + SkBitmap bitmap; if (jbitmap != NULL) { - bitmap = GraphicsJNI::getSkBitmapDeprecated(env, jbitmap); + GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); } - get_canvas(canvasHandle)->setBitmap(bitmap ? *bitmap : SkBitmap()); + get_canvas(canvasHandle)->setBitmap(bitmap); } static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) { diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index 0cf596c166e1..f5f8b1f6324d 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -134,13 +134,21 @@ getInternedString(JNIEnv *env, const String8* string) { return internedString; } +static jlong +nativeCreate +(JNIEnv *env, jclass clazz, jstring opPackageName) +{ + ScopedUtfChars opPackageNameUtf(env, opPackageName); + return (jlong) new SensorManager(String16(opPackageNameUtf.c_str())); +} + static jint -nativeGetNextSensor(JNIEnv *env, jclass clazz, jobject sensor, jint next) +nativeGetNextSensor(JNIEnv *env, jclass clazz, jlong sensorManager, jobject sensor, jint next) { - SensorManager& mgr(SensorManager::getInstance()); + SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager); Sensor const* const* sensorList; - size_t count = mgr.getSensorList(&sensorList); + size_t count = mgr->getSensorList(&sensorList); if (size_t(next) >= count) { return -1; } @@ -174,9 +182,10 @@ nativeGetNextSensor(JNIEnv *env, jclass clazz, jobject sensor, jint next) return size_t(next) < count ? next : 0; } -static int nativeEnableDataInjection(JNIEnv *_env, jclass _this, jboolean enable) { - SensorManager& mgr(SensorManager::getInstance()); - return mgr.enableDataInjection(enable); +static int nativeEnableDataInjection(JNIEnv *_env, jclass _this, jlong sensorManager, + jboolean enable) { + SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager); + return mgr->enableDataInjection(enable); } //---------------------------------------------------------------------------- @@ -281,12 +290,12 @@ private: } }; -static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jobject eventQWeak, jobject msgQ, - jfloatArray scratch, jstring packageName, jint mode) { - SensorManager& mgr(SensorManager::getInstance()); +static jlong nativeInitSensorEventQueue(JNIEnv *env, jclass clazz, jlong sensorManager, + jobject eventQWeak, jobject msgQ, jfloatArray scratch, jstring packageName, jint mode) { + SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager); ScopedUtfChars packageUtf(env, packageName); String8 clientName(packageUtf.c_str()); - sp<SensorEventQueue> queue(mgr.createEventQueue(clientName, mode)); + sp<SensorEventQueue> queue(mgr->createEventQueue(clientName, mode)); sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, msgQ); if (messageQueue == NULL) { @@ -339,20 +348,23 @@ static JNINativeMethod gSystemSensorManagerMethods[] = { {"nativeClassInit", "()V", (void*)nativeClassInit }, + {"nativeCreate", + "(Ljava/lang/String;)J", + (void*)nativeCreate }, {"nativeGetNextSensor", - "(Landroid/hardware/Sensor;I)I", + "(JLandroid/hardware/Sensor;I)I", (void*)nativeGetNextSensor }, {"nativeEnableDataInjection", - "(Z)I", + "(JZ)I", (void*)nativeEnableDataInjection }, }; static JNINativeMethod gBaseEventQueueMethods[] = { {"nativeInitBaseEventQueue", - "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;[FLjava/lang/String;I)J", - (void*)nativeInitSensorEventQueue }, + "(JLjava/lang/ref/WeakReference;Landroid/os/MessageQueue;[FLjava/lang/String;ILjava/lang/String;)J", + (void*)nativeInitSensorEventQueue }, {"nativeEnableSensor", "(JIII)I", diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index d4fb572c11e7..2692ad82c70b 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -270,49 +270,10 @@ static void read_mapinfo(FILE *fp, stats_t* stats) if ((strstr(name, "[heap]") == name)) { whichHeap = HEAP_NATIVE; - } else if (strncmp(name, "/dev/ashmem", 11) == 0) { - if (strncmp(name, "/dev/ashmem/dalvik-", 19) == 0) { - whichHeap = HEAP_DALVIK_OTHER; - if (strstr(name, "/dev/ashmem/dalvik-LinearAlloc") == name) { - subHeap = HEAP_DALVIK_LINEARALLOC; - } else if ((strstr(name, "/dev/ashmem/dalvik-alloc space") == name) || - (strstr(name, "/dev/ashmem/dalvik-main space") == name)) { - // This is the regular Dalvik heap. - whichHeap = HEAP_DALVIK; - subHeap = HEAP_DALVIK_NORMAL; - } else if (strstr(name, "/dev/ashmem/dalvik-large object space") == name) { - whichHeap = HEAP_DALVIK; - subHeap = HEAP_DALVIK_LARGE; - } else if (strstr(name, "/dev/ashmem/dalvik-non moving space") == name) { - whichHeap = HEAP_DALVIK; - subHeap = HEAP_DALVIK_NON_MOVING; - } else if (strstr(name, "/dev/ashmem/dalvik-zygote space") == name) { - whichHeap = HEAP_DALVIK; - subHeap = HEAP_DALVIK_ZYGOTE; - } else if (strstr(name, "/dev/ashmem/dalvik-indirect ref") == name) { - subHeap = HEAP_DALVIK_INDIRECT_REFERENCE_TABLE; - } else if (strstr(name, "/dev/ashmem/dalvik-jit-code-cache") == name) { - subHeap = HEAP_DALVIK_CODE_CACHE; - } else { - subHeap = HEAP_DALVIK_ACCOUNTING; // Default to accounting. - } - } else if (strncmp(name, "/dev/ashmem/CursorWindow", 24) == 0) { - whichHeap = HEAP_CURSOR; - } else if (strncmp(name, "/dev/ashmem/libc malloc", 23) == 0) { - whichHeap = HEAP_NATIVE; - } else { - whichHeap = HEAP_ASHMEM; - } } else if (strncmp(name, "[anon:libc_malloc]", 18) == 0) { whichHeap = HEAP_NATIVE; } else if (strncmp(name, "[stack", 6) == 0) { whichHeap = HEAP_STACK; - } else if (strncmp(name, "/dev/", 5) == 0) { - if (strncmp(name, "/dev/kgsl-3d0", 13) == 0) { - whichHeap = HEAP_GL_DEV; - } else { - whichHeap = HEAP_UNKNOWN_DEV; - } } else if (nameLen > 3 && strcmp(name+nameLen-3, ".so") == 0) { whichHeap = HEAP_SO; is_swappable = true; @@ -325,7 +286,7 @@ static void read_mapinfo(FILE *fp, stats_t* stats) } else if (nameLen > 4 && strcmp(name+nameLen-4, ".ttf") == 0) { whichHeap = HEAP_TTF; is_swappable = true; - } else if ((nameLen > 4 && strcmp(name+nameLen-4, ".dex") == 0) || + } else if ((nameLen > 4 && strstr(name, ".dex") != NULL) || (nameLen > 5 && strcmp(name+nameLen-5, ".odex") == 0)) { whichHeap = HEAP_DEX; is_swappable = true; @@ -335,6 +296,45 @@ static void read_mapinfo(FILE *fp, stats_t* stats) } else if (nameLen > 4 && strcmp(name+nameLen-4, ".art") == 0) { whichHeap = HEAP_ART; is_swappable = true; + } else if (strncmp(name, "/dev/", 5) == 0) { + if (strncmp(name, "/dev/kgsl-3d0", 13) == 0) { + whichHeap = HEAP_GL_DEV; + } else if (strncmp(name, "/dev/ashmem", 11) == 0) { + if (strncmp(name, "/dev/ashmem/dalvik-", 19) == 0) { + whichHeap = HEAP_DALVIK_OTHER; + if (strstr(name, "/dev/ashmem/dalvik-LinearAlloc") == name) { + subHeap = HEAP_DALVIK_LINEARALLOC; + } else if ((strstr(name, "/dev/ashmem/dalvik-alloc space") == name) || + (strstr(name, "/dev/ashmem/dalvik-main space") == name)) { + // This is the regular Dalvik heap. + whichHeap = HEAP_DALVIK; + subHeap = HEAP_DALVIK_NORMAL; + } else if (strstr(name, "/dev/ashmem/dalvik-large object space") == name) { + whichHeap = HEAP_DALVIK; + subHeap = HEAP_DALVIK_LARGE; + } else if (strstr(name, "/dev/ashmem/dalvik-non moving space") == name) { + whichHeap = HEAP_DALVIK; + subHeap = HEAP_DALVIK_NON_MOVING; + } else if (strstr(name, "/dev/ashmem/dalvik-zygote space") == name) { + whichHeap = HEAP_DALVIK; + subHeap = HEAP_DALVIK_ZYGOTE; + } else if (strstr(name, "/dev/ashmem/dalvik-indirect ref") == name) { + subHeap = HEAP_DALVIK_INDIRECT_REFERENCE_TABLE; + } else if (strstr(name, "/dev/ashmem/dalvik-jit-code-cache") == name) { + subHeap = HEAP_DALVIK_CODE_CACHE; + } else { + subHeap = HEAP_DALVIK_ACCOUNTING; // Default to accounting. + } + } else if (strncmp(name, "/dev/ashmem/CursorWindow", 24) == 0) { + whichHeap = HEAP_CURSOR; + } else if (strncmp(name, "/dev/ashmem/libc malloc", 23) == 0) { + whichHeap = HEAP_NATIVE; + } else { + whichHeap = HEAP_ASHMEM; + } + } else { + whichHeap = HEAP_UNKNOWN_DEV; + } } else if (strncmp(name, "[anon:", 6) == 0) { whichHeap = HEAP_UNKNOWN; } else if (nameLen > 0) { diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 11b38054433d..4ccbb412eb15 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -21,6 +21,7 @@ #include "jni.h" #include <nativehelper/JNIHelp.h> #include "core_jni_helpers.h" +#include <GraphicsJNI.h> #include <ScopedPrimitiveArray.h> #include <EGL/egl.h> @@ -347,10 +348,11 @@ static void android_view_ThreadedRenderer_buildLayer(JNIEnv* env, jobject clazz, } static jboolean android_view_ThreadedRenderer_copyLayerInto(JNIEnv* env, jobject clazz, - jlong proxyPtr, jlong layerPtr, jlong bitmapPtr) { + jlong proxyPtr, jlong layerPtr, jobject jbitmap) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); DeferredLayerUpdater* layer = reinterpret_cast<DeferredLayerUpdater*>(layerPtr); - SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapPtr); + SkBitmap bitmap; + GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap); return proxy->copyLayerInto(layer, bitmap); } @@ -458,7 +460,7 @@ static JNINativeMethod gMethods[] = { { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor }, { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer }, { "nBuildLayer", "(JJ)V", (void*) android_view_ThreadedRenderer_buildLayer }, - { "nCopyLayerInto", "(JJJ)Z", (void*) android_view_ThreadedRenderer_copyLayerInto }, + { "nCopyLayerInto", "(JJLandroid/graphics/Bitmap;)Z", (void*) android_view_ThreadedRenderer_copyLayerInto }, { "nPushLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_pushLayerUpdate }, { "nCancelLayerUpdate", "(JJ)V", (void*) android_view_ThreadedRenderer_cancelLayerUpdate }, { "nDetachSurfaceTexture", "(JJ)V", (void*) android_view_ThreadedRenderer_detachSurfaceTexture }, diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 52b31b2e7294..fe5862be8bab 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -7304,8 +7304,8 @@ <attr name="recognitionService" format="string" /> <attr name="settingsActivity" /> <!-- Flag indicating whether this voice interaction service is capable of handling the - assist gesture. --> - <attr name="supportsAssistGesture" format="boolean" /> + assist action. --> + <attr name="supportsAssist" format="boolean" /> </declare-styleable> <!-- Use <code>voice-enrollment-application</code> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index cf21a1c50cb1..79b81a797057 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2663,7 +2663,7 @@ <public type="attr" name="autoVerify" /> <public type="attr" name="breakStrategy" /> - <public type="attr" name="supportsAssistGesture" /> + <public type="attr" name="supportsAssist" /> <public type="attr" name="thumbPosition" /> <!-- Placeholder for a removed attribute. Remove this before M release. --> diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml index dc364a1239f6..0aa588bef6ac 100644 --- a/data/fonts/fallback_fonts.xml +++ b/data/fonts/fallback_fonts.xml @@ -402,12 +402,12 @@ </family> <family> <fileset> - <file lang="zh-Hans">NotoSansHans-Regular.otf</file> + <file lang="zh-Hans">NotoSansSC-Regular.otf</file> </fileset> </family> <family> <fileset> - <file lang="zh-Hant">NotoSansHant-Regular.otf</file> + <file lang="zh-Hant">NotoSansTC-Regular.otf</file> </fileset> </family> <family> diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 44ea1c95ba40..f903aabdf937 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -329,10 +329,10 @@ <font weight="400" style="normal">NotoSansYi-Regular.ttf</font> </family> <family lang="zh-Hans"> - <font weight="400" style="normal">NotoSansHans-Regular.otf</font> + <font weight="400" style="normal">NotoSansSC-Regular.otf</font> </family> <family lang="zh-Hant"> - <font weight="400" style="normal">NotoSansHant-Regular.otf</font> + <font weight="400" style="normal">NotoSansTC-Regular.otf</font> </family> <family lang="ja"> <font weight="400" style="normal">NotoSansJP-Regular.otf</font> diff --git a/graphics/java/android/graphics/BitmapShader.java b/graphics/java/android/graphics/BitmapShader.java index f2f890e0c5a7..bd74bc88e33f 100644 --- a/graphics/java/android/graphics/BitmapShader.java +++ b/graphics/java/android/graphics/BitmapShader.java @@ -42,8 +42,7 @@ public class BitmapShader extends Shader { mBitmap = bitmap; mTileX = tileX; mTileY = tileY; - final long b = bitmap.getSkBitmap(); - init(nativeCreate(b, tileX.nativeInt, tileY.nativeInt)); + init(nativeCreate(bitmap, tileX.nativeInt, tileY.nativeInt)); } /** @@ -56,6 +55,6 @@ public class BitmapShader extends Shader { return copy; } - private static native long nativeCreate(long native_bitmap, int shaderTileModeX, + private static native long nativeCreate(Bitmap bitmap, int shaderTileModeX, int shaderTileModeY); } diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 9fe8e0cd9b16..7a1ecf7e5f27 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -81,10 +81,6 @@ public class Canvas { */ protected int mScreenDensity = Bitmap.DENSITY_NONE; - // Used by native code - @SuppressWarnings("UnusedDeclaration") - private int mSurfaceFormat; - // Maximum bitmap size as defined in Skia's native code // (see SkCanvas.cpp, SkDraw.cpp) private static final int MAXMIMUM_BITMAP_SIZE = 32766; diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java new file mode 100644 index 000000000000..47a1f774345c --- /dev/null +++ b/graphics/java/android/graphics/drawable/Icon.java @@ -0,0 +1,482 @@ +/* + * 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 android.graphics.drawable; + +import android.annotation.DrawableRes; +import android.content.ContentResolver; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Handler; +import android.os.Message; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.lang.IllegalArgumentException; +import java.lang.Override; + +/** + * An umbrella container for several serializable graphics representations, including Bitmaps, + * compressed bitmap images (e.g. JPG or PNG), and drawable resources (including vectors). + * + * <a href="https://developer.android.com/training/displaying-bitmaps/index.html">Much ink</a> + * has been spilled on the best way to load images, and many clients may have different needs when + * it comes to threading and fetching. This class is therefore focused on encapsulation rather than + * behavior. + */ + +public final class Icon implements Parcelable { + private static final String TAG = "Icon"; + + private static final int TYPE_BITMAP = 1; + private static final int TYPE_RESOURCE = 2; + private static final int TYPE_DATA = 3; + private static final int TYPE_URI = 4; + + private final int mType; + + // To avoid adding unnecessary overhead, we have a few basic objects that get repurposed + // based on the value of mType. + + // TYPE_BITMAP: Bitmap + // TYPE_RESOURCE: Resources + // TYPE_DATA: DataBytes + private Object mObj1; + + // TYPE_RESOURCE: package name + // TYPE_URI: uri string + private String mString1; + + // TYPE_RESOURCE: resId + // TYPE_DATA: data length + private int mInt1; + + // TYPE_DATA: data offset + private int mInt2; + + // Internal accessors for different mType variants + private Bitmap getBitmap() { + if (mType != TYPE_BITMAP) { + throw new IllegalStateException("called getBitmap() on " + this); + } + return (Bitmap) mObj1; + } + + private int getDataLength() { + if (mType != TYPE_DATA) { + throw new IllegalStateException("called getDataLength() on " + this); + } + synchronized (this) { + return mInt1; + } + } + + private int getDataOffset() { + if (mType != TYPE_DATA) { + throw new IllegalStateException("called getDataOffset() on " + this); + } + synchronized (this) { + return mInt2; + } + } + + private byte[] getDataBytes() { + if (mType != TYPE_DATA) { + throw new IllegalStateException("called getDataBytes() on " + this); + } + synchronized (this) { + return (byte[]) mObj1; + } + } + + private Resources getResources() { + if (mType != TYPE_RESOURCE) { + throw new IllegalStateException("called getResources() on " + this); + } + return (Resources) mObj1; + } + + private String getResPackage() { + if (mType != TYPE_RESOURCE) { + throw new IllegalStateException("called getResPackage() on " + this); + } + return mString1; + } + + private int getResId() { + if (mType != TYPE_RESOURCE) { + throw new IllegalStateException("called getResId() on " + this); + } + return mInt1; + } + + private String getUriString() { + if (mType != TYPE_URI) { + throw new IllegalStateException("called getUriString() on " + this); + } + return mString1; + } + + private Uri getUri() { + return Uri.parse(getUriString()); + } + + // Convert a int32 into a four-char string + private static final String typeToString(int x) { + switch (x) { + case TYPE_BITMAP: return "BITMAP"; + case TYPE_DATA: return "DATA"; + case TYPE_RESOURCE: return "RESOURCE"; + case TYPE_URI: return "URI"; + default: return "UNKNOWN"; + } + } + + /** + * Invokes {@link #loadDrawable(Context)} on the given {@link android.os.Handler Handler} + * and then sends <code>andThen</code> to the same Handler when finished. + * + * @param context {@link android.content.Context Context} in which to load the drawable; see + * {@link #loadDrawable(Context)} + * @param andThen {@link android.os.Message} to send to its target once the drawable + * is available. The {@link android.os.Message#obj obj} + * property is populated with the Drawable. + */ + public void loadDrawableAsync(Context context, Message andThen) { + if (andThen.getTarget() == null) { + throw new IllegalArgumentException("callback message must have a target handler"); + } + new LoadDrawableTask(context, andThen).runAsync(); + } + + /** + * Invokes {@link #loadDrawable(Context)} on a background thread + * and then runs <code>andThen</code> on the UI thread when finished. + * + * @param context {@link android.content.Context Context} in which to load the drawable; see + * {@link #loadDrawable(Context)} + * @param handler {@link android.os.Handler} on which to run <code>andThen</code>. + * @param listener a callback to run on the provided + * Handler once the drawable is available. + */ + public void loadDrawableAsync(Context context, Handler handler, + final OnDrawableLoadedListener listener) { + new LoadDrawableTask(context, handler, listener).runAsync(); + } + + /** + * Returns a Drawable that can be used to draw the image inside this Icon, constructing it + * if necessary. Depending on the type of image, this may not be something you want to do on + * the UI thread, so consider using + * {@link #loadDrawableAsync(Context, Message) loadDrawableAsync} instead. + * + * @param context {@link android.content.Context Context} in which to load the drawable; used + * to access {@link android.content.res.Resources Resources}, for example. + * @return A fresh instance of a drawable for this image, yours to keep. + */ + public Drawable loadDrawable(Context context) { + switch (mType) { + case TYPE_BITMAP: + return new BitmapDrawable(context.getResources(), getBitmap()); + case TYPE_RESOURCE: + if (getResources() == null) { + if (getResPackage() == null || "android".equals(getResPackage())) { + mObj1 = Resources.getSystem(); + } else { + final PackageManager pm = context.getPackageManager(); + try { + mObj1 = pm.getResourcesForApplication(getResPackage()); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, + String.format("Unable to find package '%s'", getResPackage()), + e); + break; + } + } + } + return getResources().getDrawable(getResId(), context.getTheme()); + case TYPE_DATA: + return new BitmapDrawable(context.getResources(), + BitmapFactory.decodeByteArray(getDataBytes(), getDataOffset(), getDataLength()) + ); + case TYPE_URI: + final Uri uri = getUri(); + final String scheme = uri.getScheme(); + InputStream is = null; + if (ContentResolver.SCHEME_CONTENT.equals(scheme) + || ContentResolver.SCHEME_FILE.equals(scheme)) { + try { + is = context.getContentResolver().openInputStream(uri); + } catch (Exception e) { + Log.w(TAG, "Unable to load image from URI: " + uri, e); + } + } else { + try { + is = new FileInputStream(new File(mString1)); + } catch (FileNotFoundException e) { + Log.w(TAG, "Unable to load image from path: " + uri, e); + } + } + if (is != null) { + return new BitmapDrawable(context.getResources(), + BitmapFactory.decodeStream(is)); + } + break; + } + return null; + } + + private Icon(int mType) { + this.mType = mType; + } + + /** + * Create a Icon pointing to a drawable resource. + * @param res Resources for a package containing the resource in question + * @param resid ID of the drawable resource + */ + public static Icon createWithResource(Resources res, @DrawableRes int resid) { + final Icon rep = new Icon(TYPE_RESOURCE); + rep.mObj1 = res; + rep.mInt1 = resid; + rep.mString1 = res.getResourcePackageName(resid); + return rep; + } + + /** + * Create a Icon pointing to a bitmap in memory. + * @param bits A valid {@link android.graphics.Bitmap} object + */ + public static Icon createWithBitmap(Bitmap bits) { + final Icon rep = new Icon(TYPE_BITMAP); + rep.mObj1 = bits; + return rep; + } + + /** + * Create a Icon pointing to a compressed bitmap stored in a byte array. + * @param data Byte array storing compressed bitmap data of a type that + * {@link android.graphics.BitmapFactory} + * can decode (see {@link android.graphics.Bitmap.CompressFormat}). + * @param offset Offset into <code>data</code> at which the bitmap data starts + * @param length Length of the bitmap data + */ + public static Icon createWithData(byte[] data, int offset, int length) { + final Icon rep = new Icon(TYPE_DATA); + rep.mObj1 = data; + rep.mInt1 = length; + rep.mInt2 = offset; + return rep; + } + + /** + * Create a Icon pointing to a content specified by URI. + * + * @param uri A uri referring to local content:// or file:// image data. + */ + public static Icon createWithContentUri(String uri) { + final Icon rep = new Icon(TYPE_URI); + rep.mString1 = uri; + return rep; + } + + /** + * Create a Icon pointing to a content specified by URI. + * + * @param uri A uri referring to local content:// or file:// image data. + */ + public static Icon createWithContentUri(Uri uri) { + final Icon rep = new Icon(TYPE_URI); + rep.mString1 = uri.toString(); + return rep; + } + + /** + * Create a Icon pointing to + * + * @param path A path to a file that contains compressed bitmap data of + * a type that {@link android.graphics.BitmapFactory} can decode. + */ + public static Icon createWithFilePath(String path) { + final Icon rep = new Icon(TYPE_URI); + rep.mString1 = path; + return rep; + } + + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("Icon(typ=").append(typeToString(mType)); + switch (mType) { + case TYPE_BITMAP: + sb.append(" size=") + .append(getBitmap().getWidth()) + .append("x") + .append(getBitmap().getHeight()); + break; + case TYPE_RESOURCE: + sb.append(" pkg=") + .append(getResPackage()) + .append(" id=") + .append(String.format("%08x", getResId())); + break; + case TYPE_DATA: + sb.append(" len=").append(getDataLength()); + if (getDataOffset() != 0) { + sb.append(" off=").append(getDataOffset()); + } + break; + case TYPE_URI: + sb.append(" uri=").append(getUriString()); + break; + } + sb.append(")"); + return sb.toString(); + } + + /** + * Parcelable interface + */ + public int describeContents() { + return (mType == TYPE_BITMAP || mType == TYPE_DATA) + ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0; + } + + // ===== Parcelable interface ====== + + private Icon(Parcel in) { + this(in.readInt()); + switch (mType) { + case TYPE_BITMAP: + final Bitmap bits = Bitmap.CREATOR.createFromParcel(in); + mObj1 = bits; + break; + case TYPE_RESOURCE: + final String pkg = in.readString(); + final int resId = in.readInt(); + mString1 = pkg; + mInt1 = resId; + break; + case TYPE_DATA: + final int len = in.readInt(); + final byte[] a = in.readBlob(); + if (len != a.length) { + throw new RuntimeException("internal unparceling error: blob length (" + + a.length + ") != expected length (" + len + ")"); + } + mInt1 = len; + mObj1 = a; + break; + case TYPE_URI: + final String uri = in.readString(); + mString1 = uri; + break; + default: + throw new RuntimeException("invalid " + + this.getClass().getSimpleName() + " type in parcel: " + mType); + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + switch (mType) { + case TYPE_BITMAP: + final Bitmap bits = getBitmap(); + dest.writeInt(TYPE_BITMAP); + getBitmap().writeToParcel(dest, flags); + break; + case TYPE_RESOURCE: + dest.writeInt(TYPE_RESOURCE); + dest.writeString(getResPackage()); + dest.writeInt(getResId()); + break; + case TYPE_DATA: + dest.writeInt(TYPE_DATA); + dest.writeInt(getDataLength()); + dest.writeBlob(getDataBytes(), getDataOffset(), getDataLength()); + break; + case TYPE_URI: + dest.writeInt(TYPE_URI); + dest.writeString(getUriString()); + break; + } + } + + public static final Parcelable.Creator<Icon> CREATOR + = new Parcelable.Creator<Icon>() { + public Icon createFromParcel(Parcel in) { + return new Icon(in); + } + + public Icon[] newArray(int size) { + return new Icon[size]; + } + }; + + /** + * Implement this interface to receive notification when + * {@link #loadDrawableAsync(Context, Handler, OnDrawableLoadedListener) loadDrawableAsync} + * is finished and your Drawable is ready. + */ + public interface OnDrawableLoadedListener { + void onDrawableLoaded(Drawable d); + } + + /** + * Wrapper around loadDrawable that does its work on a pooled thread and then + * fires back the given (targeted) Message. + */ + private class LoadDrawableTask implements Runnable { + final Context mContext; + final Message mMessage; + + public LoadDrawableTask(Context context, final Handler handler, + final OnDrawableLoadedListener listener) { + mContext = context; + mMessage = Message.obtain(handler, new Runnable() { + @Override + public void run() { + listener.onDrawableLoaded((Drawable) mMessage.obj); + } + }); + } + + public LoadDrawableTask(Context context, Message message) { + mContext = context; + mMessage = message; + } + + @Override + public void run() { + mMessage.obj = loadDrawable(mContext); + mMessage.sendToTarget(); + } + + public void runAsync() { + AsyncTask.THREAD_POOL_EXECUTOR.execute(this); + } + } +} diff --git a/graphics/tests/graphicstests/AndroidManifest.xml b/graphics/tests/graphicstests/AndroidManifest.xml index 5fb5959bc068..e019e2806a94 100644 --- a/graphics/tests/graphicstests/AndroidManifest.xml +++ b/graphics/tests/graphicstests/AndroidManifest.xml @@ -24,6 +24,7 @@ <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> <uses-permission android:name="android.permission.WRITE_APN_SETTINGS" /> <uses-permission android:name="android.permission.BROADCAST_STICKY" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application> diff --git a/graphics/tests/graphicstests/res/drawable-nodpi/landscape.png b/graphics/tests/graphicstests/res/drawable-nodpi/landscape.png Binary files differnew file mode 100644 index 000000000000..ddb31806eca9 --- /dev/null +++ b/graphics/tests/graphicstests/res/drawable-nodpi/landscape.png diff --git a/graphics/tests/graphicstests/src/android/graphics/drawable/IconTest.java b/graphics/tests/graphicstests/src/android/graphics/drawable/IconTest.java new file mode 100644 index 000000000000..2b9bf50b3f0d --- /dev/null +++ b/graphics/tests/graphicstests/src/android/graphics/drawable/IconTest.java @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.graphics.drawable; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Parcel; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Log; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.lang.Override; +import java.util.Arrays; +import java.util.ArrayList; + +import junit.framework.TestCase; + +import com.android.frameworks.graphicstests.R; + +public class IconTest extends AndroidTestCase { + public static final String TAG = IconTest.class.getSimpleName(); + public static void L(String s, Object... parts) { + Log.d(TAG, (parts.length == 0) ? s : String.format(s, parts)); + } + + @SmallTest + public void testWithBitmap() throws Exception { + final Bitmap bm1 = Bitmap.createBitmap(100, 200, Bitmap.Config.ARGB_8888); + final Bitmap bm2 = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565); + final Bitmap bm3 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape)) + .getBitmap(); + + final Canvas can1 = new Canvas(bm1); + can1.drawColor(0xFFFF0000); + final Canvas can2 = new Canvas(bm2); + can2.drawColor(0xFF00FF00); + + final Icon im1 = Icon.createWithBitmap(bm1); + final Icon im2 = Icon.createWithBitmap(bm2); + final Icon im3 = Icon.createWithBitmap(bm3); + + final Drawable draw1 = im1.loadDrawable(mContext); + final Drawable draw2 = im2.loadDrawable(mContext); + final Drawable draw3 = im3.loadDrawable(mContext); + + final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(), + draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + final Bitmap test2 = Bitmap.createBitmap(draw2.getIntrinsicWidth(), + draw2.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + final Bitmap test3 = Bitmap.createBitmap(draw3.getIntrinsicWidth(), + draw3.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + + draw1.setBounds(0, 0, draw1.getIntrinsicWidth(), draw1.getIntrinsicHeight()); + draw1.draw(new Canvas(test1)); + + draw2.setBounds(0, 0, draw2.getIntrinsicWidth(), draw2.getIntrinsicHeight()); + draw2.draw(new Canvas(test2)); + + draw3.setBounds(0, 0, draw3.getIntrinsicWidth(), draw3.getIntrinsicHeight()); + draw3.draw(new Canvas(test3)); + + final File dir = getContext().getExternalFilesDir(null); + L("writing temp bitmaps to %s...", dir); + + bm1.compress(Bitmap.CompressFormat.PNG, 100, + new FileOutputStream(new File(dir, "bitmap1-original.png"))); + test1.compress(Bitmap.CompressFormat.PNG, 100, + new FileOutputStream(new File(dir, "bitmap1-test.png"))); + if (!equalBitmaps(bm1, test1)) { + findBitmapDifferences(bm1, test1); + fail("bitmap1 differs, check " + dir); + } + + bm2.compress(Bitmap.CompressFormat.PNG, 100, + new FileOutputStream(new File(dir, "bitmap2-original.png"))); + test2.compress(Bitmap.CompressFormat.PNG, 100, + new FileOutputStream(new File(dir, "bitmap2-test.png"))); + if (!equalBitmaps(bm2, test2)) { + findBitmapDifferences(bm2, test2); + fail("bitmap2 differs, check " + dir); + } + + bm3.compress(Bitmap.CompressFormat.PNG, 100, + new FileOutputStream(new File(dir, "bitmap3-original.png"))); + test3.compress(Bitmap.CompressFormat.PNG, 100, + new FileOutputStream(new File(dir, "bitmap3-test.png"))); + if (!equalBitmaps(bm3, test3)) { + findBitmapDifferences(bm3, test3); + fail("bitmap3 differs, check " + dir); + } + } + + @SmallTest + public void testWithBitmapResource() throws Exception { + final Bitmap res1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape)) + .getBitmap(); + + final Icon im1 = Icon.createWithResource(getContext().getResources(), + R.drawable.landscape); + final Drawable draw1 = im1.loadDrawable(mContext); + final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(), + draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + draw1.setBounds(0, 0, test1.getWidth(), test1.getHeight()); + draw1.draw(new Canvas(test1)); + + final File dir = getContext().getExternalFilesDir(null); + res1.compress(Bitmap.CompressFormat.PNG, 100, + new FileOutputStream(new File(dir, "res1-original.png"))); + test1.compress(Bitmap.CompressFormat.PNG, 100, + new FileOutputStream(new File(dir, "res1-test.png"))); + if (!equalBitmaps(res1, test1)) { + findBitmapDifferences(res1, test1); + fail("res1 differs, check " + dir); + } + } + + @SmallTest + public void testWithFile() throws Exception { + final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape)) + .getBitmap(); + final File dir = getContext().getExternalFilesDir(null); + final File file1 = new File(dir, "file1-original.png"); + bit1.compress(Bitmap.CompressFormat.PNG, 100, + new FileOutputStream(file1)); + + final Icon im1 = Icon.createWithFilePath(file1.toString()); + final Drawable draw1 = im1.loadDrawable(mContext); + final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(), + draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + draw1.setBounds(0, 0, test1.getWidth(), test1.getHeight()); + draw1.draw(new Canvas(test1)); + + test1.compress(Bitmap.CompressFormat.PNG, 100, + new FileOutputStream(new File(dir, "file1-test.png"))); + if (!equalBitmaps(bit1, test1)) { + findBitmapDifferences(bit1, test1); + fail("testWithFile: file1 differs, check " + dir); + } + } + + @SmallTest + public void testAsync() throws Exception { + final Bitmap bit1 = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape)) + .getBitmap(); + final File dir = getContext().getExternalFilesDir(null); + final File file1 = new File(dir, "async-original.png"); + bit1.compress(Bitmap.CompressFormat.PNG, 100, + new FileOutputStream(file1)); + + final Icon im1 = Icon.createWithFilePath(file1.toString()); + final HandlerThread thd = new HandlerThread("testAsync"); + thd.start(); + final Handler h = new Handler(thd.getLooper()); + L(TAG, "asyncTest: dispatching load to thread: " + thd); + im1.loadDrawableAsync(mContext, h, new Icon.OnDrawableLoadedListener() { + @Override + public void onDrawableLoaded(Drawable draw1) { + L(TAG, "asyncTest: thread: loading drawable"); + L(TAG, "asyncTest: thread: loaded: %dx%d", draw1.getIntrinsicWidth(), + draw1.getIntrinsicHeight()); + final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(), + draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + draw1.setBounds(0, 0, test1.getWidth(), test1.getHeight()); + draw1.draw(new Canvas(test1)); + + try { + test1.compress(Bitmap.CompressFormat.PNG, 100, + new FileOutputStream(new File(dir, "async-test.png"))); + } catch (java.io.FileNotFoundException ex) { + fail("couldn't create test file: " + ex); + } + if (!equalBitmaps(bit1, test1)) { + findBitmapDifferences(bit1, test1); + fail("testAsync: file1 differs, check " + dir); + } + } + }); + L(TAG, "asyncTest: awaiting result"); + Thread.sleep(500); // ;_; + assertTrue("async-test.png does not exist!", new File(dir, "async-test.png").exists()); + L(TAG, "asyncTest: done"); + } + + @SmallTest + public void testParcel() throws Exception { + final Bitmap originalbits = ((BitmapDrawable) getContext().getDrawable(R.drawable.landscape)) + .getBitmap(); + + final ByteArrayOutputStream ostream = new ByteArrayOutputStream( + originalbits.getWidth() * originalbits.getHeight() * 2); // guess 50% compression + originalbits.compress(Bitmap.CompressFormat.PNG, 100, ostream); + final byte[] pngdata = ostream.toByteArray(); + + L("starting testParcel; bitmap: %d bytes, PNG: %d bytes", + originalbits.getByteCount(), + pngdata.length); + + final File dir = getContext().getExternalFilesDir(null); + final File originalfile = new File(dir, "parcel-original.png"); + new FileOutputStream(originalfile).write(pngdata); + + ArrayList<Icon> imgs = new ArrayList<>(); + final Icon file1 = Icon.createWithFilePath(originalfile.getAbsolutePath()); + imgs.add(file1); + final Icon bit1 = Icon.createWithBitmap(originalbits); + imgs.add(bit1); + final Icon data1 = Icon.createWithData(pngdata, 0, pngdata.length); + imgs.add(data1); + final Icon res1 = Icon.createWithResource(getContext().getResources(), R.drawable.landscape); + imgs.add(res1); + + ArrayList<Icon> test = new ArrayList<>(); + final Parcel parcel = Parcel.obtain(); + int pos = 0; + parcel.writeInt(imgs.size()); + for (Icon img : imgs) { + img.writeToParcel(parcel, 0); + L("used %d bytes parceling: %s", parcel.dataPosition() - pos, img); + pos = parcel.dataPosition(); + } + + parcel.setDataPosition(0); // rewind + final int N = parcel.readInt(); + for (int i=0; i<N; i++) { + Icon img = Icon.CREATOR.createFromParcel(parcel); + L("test %d: read from parcel: %s", i, img); + final File testfile = new File(dir, + String.format("parcel-test%02d.png", i)); + + final Drawable draw1 = img.loadDrawable(mContext); + if (draw1 == null) { + fail("null drawable from img: " + img); + } + final Bitmap test1 = Bitmap.createBitmap(draw1.getIntrinsicWidth(), + draw1.getIntrinsicHeight(), Bitmap.Config.ARGB_8888); + draw1.setBounds(0, 0, test1.getWidth(), test1.getHeight()); + draw1.draw(new Canvas(test1)); + + try { + test1.compress(Bitmap.CompressFormat.PNG, 100, + new FileOutputStream(testfile)); + } catch (java.io.FileNotFoundException ex) { + fail("couldn't create test file " + testfile + ": " + ex); + } + if (!equalBitmaps(originalbits, test1)) { + findBitmapDifferences(originalbits, test1); + fail(testfile + " differs from original: " + originalfile); + } + + } + } + + + // ======== utils ======== + + static final char[] GRADIENT = " .:;+=xX$#".toCharArray(); + static float[] hsv = new float[3]; + static char colorToChar(int color) { + int sum = ((color >> 16) & 0xff) + + ((color >> 8) & 0xff) + + ((color) & 0xff); + return GRADIENT[sum * (GRADIENT.length-1) / (3*0xff)]; + } + static void printBits(int[] a, int w, int h) { + final StringBuilder sb = new StringBuilder(); + for (int i=0; i<w; i++) { + for (int j=0; j<h; j++) { + sb.append(colorToChar(a[i+w*j])); + } + sb.append('\n'); + } + L(sb.toString()); + } + static void printBits(Bitmap a) { + final int w = a.getWidth(); + final int h = a.getHeight(); + int[] aPix = new int[w * h]; + printBits(aPix, w, h); + } + boolean equalBitmaps(Bitmap a, Bitmap b) { + if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) return false; + + final int w = a.getWidth(); + final int h = a.getHeight(); + int[] aPix = new int[w * h]; + int[] bPix = new int[w * h]; + + a.getPixels(aPix, 0, w, 0, 0, w, h); + b.getPixels(bPix, 0, w, 0, 0, w, h); + + return Arrays.equals(aPix, bPix); + } + + void findBitmapDifferences(Bitmap a, Bitmap b) { + if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) { + L("different sizes: %dx%d vs %dx%d", + a.getWidth(), a.getHeight(), b.getWidth(), b.getHeight()); + return; + } + + final int w = a.getWidth(); + final int h = a.getHeight(); + int[] aPix = new int[w * h]; + int[] bPix = new int[w * h]; + + a.getPixels(aPix, 0, w, 0, 0, w, h); + b.getPixels(bPix, 0, w, 0, 0, w, h); + + L("bitmap a (%dx%d)", w, h); + printBits(aPix, w, h); + L("bitmap b (%dx%d)", w, h); + printBits(bPix, w, h); + + StringBuffer sb = new StringBuffer("Different pixels: "); + for (int i=0; i<w; i++) { + for (int j=0; j<h; j++) { + if (aPix[i+w*j] != bPix[i+w*j]) { + sb.append(" ").append(i).append(",").append(j); + } + } + } + L(sb.toString()); + } +} diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index cc872412251e..d15fa3941766 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -293,11 +293,11 @@ CREATE_BRIDGE3(copyLayerInto, CanvasContext* context, DeferredLayerUpdater* laye return (void*) success; } -bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) { +bool RenderProxy::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap) { SETUP_TASK(copyLayerInto); args->context = mContext; args->layer = layer; - args->bitmap = bitmap; + args->bitmap = &bitmap; return (bool) postAndWait(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 29c6f0818cb0..cc475faeddf3 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -83,7 +83,7 @@ public: ANDROID_API DeferredLayerUpdater* createTextureLayer(); ANDROID_API void buildLayer(RenderNode* node); - ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap); + ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap& bitmap); ANDROID_API void pushLayerUpdate(DeferredLayerUpdater* layer); ANDROID_API void cancelLayerUpdate(DeferredLayerUpdater* layer); ANDROID_API void detachSurfaceTexture(DeferredLayerUpdater* layer); diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index a33fa591d956..77adb39d3a3c 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1953,21 +1953,16 @@ public class MediaPlayer implements SubtitleController.Listener TrackInfo(Parcel in) { mTrackType = in.readInt(); - // TODO: parcel in the full MediaFormat + // TODO: parcel in the full MediaFormat; currently we are using createSubtitleFormat + // even for audio/video tracks, meaning we only set the mime and language. + String mime = in.readString(); String language = in.readString(); + mFormat = MediaFormat.createSubtitleFormat(mime, language); - if (mTrackType == MEDIA_TRACK_TYPE_TIMEDTEXT) { - mFormat = MediaFormat.createSubtitleFormat( - MEDIA_MIMETYPE_TEXT_SUBRIP, language); - } else if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) { - String mime = in.readString(); - mFormat = MediaFormat.createSubtitleFormat(mime, language); + if (mTrackType == MEDIA_TRACK_TYPE_SUBTITLE) { mFormat.setInteger(MediaFormat.KEY_IS_AUTOSELECT, in.readInt()); mFormat.setInteger(MediaFormat.KEY_IS_DEFAULT, in.readInt()); mFormat.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, in.readInt()); - } else { - mFormat = new MediaFormat(); - mFormat.setString(MediaFormat.KEY_LANGUAGE, language); } } diff --git a/media/java/android/media/MediaSync.java b/media/java/android/media/MediaSync.java index dc6760d59f71..ecc87e7ebba1 100644 --- a/media/java/android/media/MediaSync.java +++ b/media/java/android/media/MediaSync.java @@ -199,6 +199,7 @@ final public class MediaSync { private final Object mAudioLock = new Object(); private AudioTrack mAudioTrack = null; private List<AudioBuffer> mAudioBuffers = new LinkedList<AudioBuffer>(); + // this is only used for paused/running decisions, so it is not affected by clock drift private float mPlaybackRate = 0.0f; private long mNativeContext; @@ -459,36 +460,11 @@ final public class MediaSync { * @throws IllegalArgumentException if the settings are not supported. */ public void setPlaybackSettings(@NonNull PlaybackSettings settings) { - float rate; - try { - rate = settings.getSpeed(); - - // rate is specified - if (mAudioTrack != null) { - try { - if (rate == 0.0) { - mAudioTrack.pause(); - } else { - mAudioTrack.setPlaybackSettings(settings); - mAudioTrack.play(); - } - } catch (IllegalStateException e) { - throw e; - } - } - - synchronized(mAudioLock) { - mPlaybackRate = rate; - } - if (mPlaybackRate != 0.0 && mAudioThread != null) { - postRenderAudio(0); - } - native_setPlaybackRate(mPlaybackRate); - } catch (IllegalStateException e) { - // rate is not specified; still, propagate settings to audio track - if (mAudioTrack != null) { - mAudioTrack.setPlaybackSettings(settings); - } + synchronized(mAudioLock) { + mPlaybackRate = native_setPlaybackSettings(settings);; + } + if (mPlaybackRate != 0.0 && mAudioThread != null) { + postRenderAudio(0); } } @@ -501,18 +477,9 @@ final public class MediaSync { * been initialized. */ @NonNull - public PlaybackSettings getPlaybackSettings() { - if (mAudioTrack != null) { - return mAudioTrack.getPlaybackSettings(); - } else { - PlaybackSettings settings = new PlaybackSettings(); - settings.allowDefaults(); - settings.setSpeed(mPlaybackRate); - return settings; - } - } + public native PlaybackSettings getPlaybackSettings(); - private native final void native_setPlaybackRate(float rate); + private native float native_setPlaybackSettings(@NonNull PlaybackSettings settings); /** * Sets A/V sync mode. @@ -523,7 +490,16 @@ final public class MediaSync { * initialized. * @throws IllegalArgumentException if settings are not supported. */ - public native void setSyncSettings(@NonNull SyncSettings settings); + public void setSyncSettings(@NonNull SyncSettings settings) { + synchronized(mAudioLock) { + mPlaybackRate = native_setSyncSettings(settings);; + } + if (mPlaybackRate != 0.0 && mAudioThread != null) { + postRenderAudio(0); + } + } + + private native float native_setSyncSettings(@NonNull SyncSettings settings); /** * Gets the A/V sync mode. diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 2c61779bc3ed..5b55a612e33a 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -174,6 +174,8 @@ static void process_media_player_call(JNIEnv *env, jobject thiz, status_t opStat } else { // Throw exception! if ( opStatus == (status_t) INVALID_OPERATION ) { jniThrowException(env, "java/lang/IllegalStateException", NULL); + } else if ( opStatus == (status_t) BAD_VALUE ) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); } else if ( opStatus == (status_t) PERMISSION_DENIED ) { jniThrowException(env, "java/lang/SecurityException", NULL); } else if ( opStatus != (status_t) OK ) { @@ -442,8 +444,33 @@ android_media_MediaPlayer_setPlaybackSettings(JNIEnv *env, jobject thiz, jobject pbs.audioFallbackModeSet, pbs.audioRate.mFallbackMode, pbs.audioStretchModeSet, pbs.audioRate.mStretchMode); - // TODO: pass playback settings to mediaplayer when audiotrack supports it - process_media_player_call(env, thiz, mp->setPlaybackRate(pbs.audioRate.mSpeed), NULL, NULL); + AudioPlaybackRate rate; + status_t err = mp->getPlaybackSettings(&rate); + if (err == OK) { + bool updatedRate = false; + if (pbs.speedSet) { + rate.mSpeed = pbs.audioRate.mSpeed; + updatedRate = true; + } + if (pbs.pitchSet) { + rate.mPitch = pbs.audioRate.mPitch; + updatedRate = true; + } + if (pbs.audioFallbackModeSet) { + rate.mFallbackMode = pbs.audioRate.mFallbackMode; + updatedRate = true; + } + if (pbs.audioStretchModeSet) { + rate.mStretchMode = pbs.audioRate.mStretchMode; + updatedRate = true; + } + if (updatedRate) { + err = mp->setPlaybackSettings(rate); + } + } + process_media_player_call( + env, thiz, err, + "java/lang/IllegalStateException", "unexpected error"); } static jobject @@ -457,15 +484,9 @@ android_media_MediaPlayer_getPlaybackSettings(JNIEnv *env, jobject thiz) PlaybackSettings pbs; AudioPlaybackRate &audioRate = pbs.audioRate; - - audioRate.mSpeed = 1.0f; - audioRate.mPitch = 1.0f; - audioRate.mFallbackMode = AUDIO_TIMESTRETCH_FALLBACK_DEFAULT; - audioRate.mStretchMode = AUDIO_TIMESTRETCH_STRETCH_DEFAULT; - - // TODO: get this from mediaplayer when audiotrack supports it - // process_media_player_call( - // env, thiz, mp->getPlaybackSettings(&audioRate), NULL, NULL); + process_media_player_call( + env, thiz, mp->getPlaybackSettings(&audioRate), + "java/lang/IllegalStateException", "unexpected error"); ALOGV("getPlaybackSettings: %f %f %d %d", audioRate.mSpeed, audioRate.mPitch, audioRate.mFallbackMode, audioRate.mStretchMode); @@ -489,13 +510,35 @@ android_media_MediaPlayer_setSyncSettings(JNIEnv *env, jobject thiz, jobject set SyncSettings scs; scs.fillFromJobject(env, gSyncSettingsFields, settings); ALOGV("setSyncSettings: %d:%d %d:%d %d:%f %d:%f", - scs.syncSourceSet, scs.syncSource, - scs.audioAdjustModeSet, scs.audioAdjustMode, - scs.toleranceSet, scs.tolerance, - scs.frameRateSet, scs.frameRate); - - // TODO: pass sync settings to mediaplayer when it supports it - // process_media_player_call(env, thiz, mp->setSyncSettings(scs), NULL, NULL); + scs.syncSourceSet, scs.sync.mSource, + scs.audioAdjustModeSet, scs.sync.mAudioAdjustMode, + scs.toleranceSet, scs.sync.mTolerance, + scs.frameRateSet, scs.frameRate); + + AVSyncSettings avsync; + float videoFrameRate; + status_t err = mp->getSyncSettings(&avsync, &videoFrameRate); + if (err == OK) { + bool updatedSync = scs.frameRateSet; + if (scs.syncSourceSet) { + avsync.mSource = scs.sync.mSource; + updatedSync = true; + } + if (scs.audioAdjustModeSet) { + avsync.mAudioAdjustMode = scs.sync.mAudioAdjustMode; + updatedSync = true; + } + if (scs.toleranceSet) { + avsync.mTolerance = scs.sync.mTolerance; + updatedSync = true; + } + if (updatedSync) { + err = mp->setSyncSettings(avsync, scs.frameRateSet ? scs.frameRate : -1.f); + } + } + process_media_player_call( + env, thiz, err, + "java/lang/IllegalStateException", "unexpected error"); } static jobject @@ -508,21 +551,27 @@ android_media_MediaPlayer_getSyncSettings(JNIEnv *env, jobject thiz) } SyncSettings scs; - scs.syncSource = 0; // SYNC_SOURCE_DEFAULT - scs.audioAdjustMode = 0; // AUDIO_ADJUST_MODE_DEFAULT - scs.tolerance = 0.f; - scs.frameRate = 0.f; - - // TODO: get this from mediaplayer when it supports it - // process_media_player_call( - // env, thiz, mp->getSyncSettings(&scs), NULL, NULL); + scs.frameRate = -1.f; + process_media_player_call( + env, thiz, mp->getSyncSettings(&scs.sync, &scs.frameRate), + "java/lang/IllegalStateException", "unexpected error"); + ALOGV("getSyncSettings: %d %d %f %f", - scs.syncSource, scs.audioAdjustMode, scs.tolerance, scs.frameRate); + scs.sync.mSource, scs.sync.mAudioAdjustMode, scs.sync.mTolerance, scs.frameRate); + + // sanity check settings + if (scs.sync.mSource >= AVSYNC_SOURCE_MAX + || scs.sync.mAudioAdjustMode >= AVSYNC_AUDIO_ADJUST_MODE_MAX + || scs.sync.mTolerance < 0.f + || scs.sync.mTolerance >= AVSYNC_TOLERANCE_MAX) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return NULL; + } scs.syncSourceSet = true; scs.audioAdjustModeSet = true; scs.toleranceSet = true; - scs.frameRateSet = false; + scs.frameRateSet = scs.frameRate >= 0.f; return scs.asJobject(env, gSyncSettingsFields); } diff --git a/media/jni/android_media_MediaSync.cpp b/media/jni/android_media_MediaSync.cpp index f1922625ca0d..8ad4b7138552 100644 --- a/media/jni/android_media_MediaSync.cpp +++ b/media/jni/android_media_MediaSync.cpp @@ -21,6 +21,7 @@ #include "android_media_MediaSync.h" #include "android_media_AudioTrack.h" +#include "android_media_PlaybackSettings.h" #include "android_media_SyncSettings.h" #include "android_runtime/AndroidRuntime.h" #include "android_runtime/android_view_Surface.h" @@ -29,6 +30,7 @@ #include <gui/Surface.h> +#include <media/AudioResamplerPublic.h> #include <media/AudioTrack.h> #include <media/stagefright/MediaClock.h> #include <media/stagefright/MediaSync.h> @@ -47,6 +49,7 @@ struct fields_t { }; static fields_t gFields; +static PlaybackSettings::fields_t gPlaybackSettingsFields; static SyncSettings::fields_t gSyncSettingsFields; //////////////////////////////////////////////////////////////////////////////// @@ -62,10 +65,8 @@ status_t JMediaSync::configureSurface(const sp<IGraphicBufferProducer> &bufferPr return mSync->configureSurface(bufferProducer); } -status_t JMediaSync::configureAudioTrack( - const sp<AudioTrack> &audioTrack, - int32_t nativeSampleRateInHz) { - return mSync->configureAudioTrack(audioTrack, nativeSampleRateInHz); +status_t JMediaSync::configureAudioTrack(const sp<AudioTrack> &audioTrack) { + return mSync->configureAudioTrack(audioTrack); } status_t JMediaSync::createInputSurface( @@ -73,14 +74,34 @@ status_t JMediaSync::createInputSurface( return mSync->createInputSurface(bufferProducer); } -status_t JMediaSync::setPlaybackRate(float rate) { - return mSync->setPlaybackRate(rate); -} - sp<const MediaClock> JMediaSync::getMediaClock() { return mSync->getMediaClock(); } +status_t JMediaSync::setPlaybackSettings(const AudioPlaybackRate& rate) { + return mSync->setPlaybackSettings(rate); +} + +void JMediaSync::getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */) { + mSync->getPlaybackSettings(rate); +} + +status_t JMediaSync::setSyncSettings(const AVSyncSettings& syncSettings) { + return mSync->setSyncSettings(syncSettings); +} + +void JMediaSync::getSyncSettings(AVSyncSettings* syncSettings /* nonnull */) { + mSync->getSyncSettings(syncSettings); +} + +status_t JMediaSync::setVideoFrameRateHint(float rate) { + return mSync->setVideoFrameRateHint(rate); +} + +float JMediaSync::getVideoFrameRate() { + return mSync->getVideoFrameRate(); +} + status_t JMediaSync::updateQueuedAudioData( int sizeInBytes, int64_t presentationTimeUs) { return mSync->updateQueuedAudioData(sizeInBytes, presentationTimeUs); @@ -176,7 +197,7 @@ static void android_media_MediaSync_native_configureSurface( } static void android_media_MediaSync_native_configureAudioTrack( - JNIEnv *env, jobject thiz, jobject jaudioTrack, jint nativeSampleRateInHz) { + JNIEnv *env, jobject thiz, jobject jaudioTrack) { ALOGV("android_media_MediaSync_configureAudioTrack"); sp<JMediaSync> sync = getMediaSync(env, thiz); @@ -194,7 +215,7 @@ static void android_media_MediaSync_native_configureAudioTrack( } } - status_t err = sync->configureAudioTrack(audioTrack, nativeSampleRateInHz); + status_t err = sync->configureAudioTrack(audioTrack); if (err == INVALID_OPERATION) { throwExceptionAsNecessary( @@ -287,29 +308,132 @@ static jlong android_media_MediaSync_native_getPlayTimeForPendingAudioFrames( return (jlong)playTimeUs; } -static void -android_media_MediaSync_setSyncSettings(JNIEnv *env, jobject thiz, jobject settings) -{ +static jfloat android_media_MediaSync_setPlaybackSettings( + JNIEnv *env, jobject thiz, jobject settings) { sp<JMediaSync> sync = getMediaSync(env, thiz); if (sync == NULL) { throwExceptionAsNecessary(env, INVALID_OPERATION); - return; + return (jfloat)0.f; + } + + PlaybackSettings pbs; + pbs.fillFromJobject(env, gPlaybackSettingsFields, settings); + ALOGV("setPlaybackSettings: %d:%f %d:%f %d:%u %d:%u", + pbs.speedSet, pbs.audioRate.mSpeed, + pbs.pitchSet, pbs.audioRate.mPitch, + pbs.audioFallbackModeSet, pbs.audioRate.mFallbackMode, + pbs.audioStretchModeSet, pbs.audioRate.mStretchMode); + + AudioPlaybackRate rate; + sync->getPlaybackSettings(&rate); + bool updatedRate = false; + if (pbs.speedSet) { + rate.mSpeed = pbs.audioRate.mSpeed; + updatedRate = true; + } + if (pbs.pitchSet) { + rate.mPitch = pbs.audioRate.mPitch; + updatedRate = true; + } + if (pbs.audioFallbackModeSet) { + rate.mFallbackMode = pbs.audioRate.mFallbackMode; + updatedRate = true; + } + if (pbs.audioStretchModeSet) { + rate.mStretchMode = pbs.audioRate.mStretchMode; + updatedRate = true; + } + if (updatedRate) { + status_t err = sync->setPlaybackSettings(rate); + if (err != OK) { + throwExceptionAsNecessary(env, err); + return (jfloat)0.f; + } + } + + sp<const MediaClock> mediaClock = sync->getMediaClock(); + if (mediaClock == NULL) { + return (jfloat)0.f; + } + + return (jfloat)mediaClock->getPlaybackRate(); +} + +static jobject android_media_MediaSync_getPlaybackSettings( + JNIEnv *env, jobject thiz) { + sp<JMediaSync> sync = getMediaSync(env, thiz); + if (sync == NULL) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return NULL; + } + + PlaybackSettings pbs; + AudioPlaybackRate &audioRate = pbs.audioRate; + sync->getPlaybackSettings(&audioRate); + ALOGV("getPlaybackSettings: %f %f %d %d", + audioRate.mSpeed, audioRate.mPitch, audioRate.mFallbackMode, audioRate.mStretchMode); + + pbs.speedSet = true; + pbs.pitchSet = true; + pbs.audioFallbackModeSet = true; + pbs.audioStretchModeSet = true; + + return pbs.asJobject(env, gPlaybackSettingsFields); +} + +static jfloat android_media_MediaSync_setSyncSettings( + JNIEnv *env, jobject thiz, jobject settings) { + sp<JMediaSync> sync = getMediaSync(env, thiz); + if (sync == NULL) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return (jfloat)0.f; } SyncSettings scs; scs.fillFromJobject(env, gSyncSettingsFields, settings); ALOGV("setSyncSettings: %d:%d %d:%d %d:%f %d:%f", - scs.syncSourceSet, scs.syncSource, - scs.audioAdjustModeSet, scs.audioAdjustMode, - scs.toleranceSet, scs.tolerance, + scs.syncSourceSet, scs.sync.mSource, + scs.audioAdjustModeSet, scs.sync.mAudioAdjustMode, + scs.toleranceSet, scs.sync.mTolerance, scs.frameRateSet, scs.frameRate); - // TODO: pass sync settings to mediasync when it supports it + AVSyncSettings avsync; + sync->getSyncSettings(&avsync); + bool updatedSync = false; + status_t err = OK; + if (scs.syncSourceSet) { + avsync.mSource = scs.sync.mSource; + updatedSync = true; + } + if (scs.audioAdjustModeSet) { + avsync.mAudioAdjustMode = scs.sync.mAudioAdjustMode; + updatedSync = true; + } + if (scs.toleranceSet) { + avsync.mTolerance = scs.sync.mTolerance; + updatedSync = true; + } + if (updatedSync) { + err = sync->setSyncSettings(avsync); + } + + if (scs.frameRateSet && err == OK) { + err = sync->setVideoFrameRateHint(scs.frameRate); + } + if (err != OK) { + throwExceptionAsNecessary(env, err); + return (jfloat)0.f; + } + + sp<const MediaClock> mediaClock = sync->getMediaClock(); + if (mediaClock == NULL) { + return (jfloat)0.f; + } + + return (jfloat)mediaClock->getPlaybackRate(); } -static jobject -android_media_MediaSync_getSyncSettings(JNIEnv *env, jobject thiz) -{ +static jobject android_media_MediaSync_getSyncSettings(JNIEnv *env, jobject thiz) { sp<JMediaSync> sync = getMediaSync(env, thiz); if (sync == NULL) { throwExceptionAsNecessary(env, INVALID_OPERATION); @@ -317,21 +441,25 @@ android_media_MediaSync_getSyncSettings(JNIEnv *env, jobject thiz) } SyncSettings scs; - scs.syncSource = 0; // SYNC_SOURCE_DEFAULT - scs.audioAdjustMode = 0; // AUDIO_ADJUST_MODE_DEFAULT - scs.tolerance = 0.f; - scs.frameRate = 0.f; - - // TODO: get this from mediaplayer when it supports it - // process_media_player_call( - // env, thiz, mp->getSyncSettings(&scs), NULL, NULL); + sync->getSyncSettings(&scs.sync); + scs.frameRate = sync->getVideoFrameRate(); + ALOGV("getSyncSettings: %d %d %f %f", - scs.syncSource, scs.audioAdjustMode, scs.tolerance, scs.frameRate); + scs.sync.mSource, scs.sync.mAudioAdjustMode, scs.sync.mTolerance, scs.frameRate); + + // sanity check settings + if (scs.sync.mSource >= AVSYNC_SOURCE_MAX + || scs.sync.mAudioAdjustMode >= AVSYNC_AUDIO_ADJUST_MODE_MAX + || scs.sync.mTolerance < 0.f + || scs.sync.mTolerance >= AVSYNC_TOLERANCE_MAX) { + throwExceptionAsNecessary(env, INVALID_OPERATION); + return NULL; + } scs.syncSourceSet = true; scs.audioAdjustModeSet = true; scs.toleranceSet = true; - scs.frameRateSet = false; + scs.frameRateSet = scs.frameRate >= 0.f; return scs.asJobject(env, gSyncSettingsFields); } @@ -359,6 +487,7 @@ static void android_media_MediaSync_native_init(JNIEnv *env) { CHECK(gFields.mediaTimestampClockRateID != NULL); gSyncSettingsFields.init(env); + gPlaybackSettingsFields.init(env); } static void android_media_MediaSync_native_setup(JNIEnv *env, jobject thiz) { @@ -367,21 +496,6 @@ static void android_media_MediaSync_native_setup(JNIEnv *env, jobject thiz) { setMediaSync(env, thiz, sync); } -static void android_media_MediaSync_native_setPlaybackRate( - JNIEnv *env, jobject thiz, jfloat rate) { - sp<JMediaSync> sync = getMediaSync(env, thiz); - if (sync == NULL) { - throwExceptionAsNecessary(env, INVALID_OPERATION); - return; - } - - status_t err = sync->setPlaybackRate(rate); - if (err != NO_ERROR) { - throwExceptionAsNecessary(env, err); - return; - } -} - static void android_media_MediaSync_native_finalize(JNIEnv *env, jobject thiz) { android_media_MediaSync_release(env, thiz); } @@ -416,11 +530,17 @@ static JNINativeMethod gMethods[] = { { "native_release", "()V", (void *)android_media_MediaSync_release }, - { "native_setPlaybackRate", "(F)V", (void *)android_media_MediaSync_native_setPlaybackRate }, + { "native_setPlaybackSettings", "(Landroid/media/PlaybackSettings;)F", + (void *)android_media_MediaSync_setPlaybackSettings }, + + { "getPlaybackSettings", "()Landroid/media/PlaybackSettings;", + (void *)android_media_MediaSync_getPlaybackSettings }, - { "setSyncSettings", "(Landroid/media/SyncSettings;)V", (void *)android_media_MediaSync_setSyncSettings}, + { "native_setSyncSettings", "(Landroid/media/SyncSettings;)F", + (void *)android_media_MediaSync_setSyncSettings }, - { "getSyncSettings", "()Landroid/media/SyncSettings;", (void *)android_media_MediaSync_getSyncSettings}, + { "getSyncSettings", "()Landroid/media/SyncSettings;", + (void *)android_media_MediaSync_getSyncSettings }, { "native_finalize", "()V", (void *)android_media_MediaSync_native_finalize }, }; diff --git a/media/jni/android_media_MediaSync.h b/media/jni/android_media_MediaSync.h index cf81a72a1fbf..80f5d637ddf7 100644 --- a/media/jni/android_media_MediaSync.h +++ b/media/jni/android_media_MediaSync.h @@ -18,11 +18,13 @@ #define _ANDROID_MEDIA_MEDIASYNC_H_ #include <media/stagefright/foundation/ABase.h> +#include <media/stagefright/MediaSync.h> #include <utils/Errors.h> #include <utils/RefBase.h> namespace android { +struct AudioPlaybackRate; class AudioTrack; struct IGraphicBufferProducer; struct MediaClock; @@ -32,17 +34,21 @@ struct JMediaSync : public RefBase { JMediaSync(); status_t configureSurface(const sp<IGraphicBufferProducer> &bufferProducer); - status_t configureAudioTrack( - const sp<AudioTrack> &audioTrack, int32_t nativeSampleRateInHz); + status_t configureAudioTrack(const sp<AudioTrack> &audioTrack); status_t createInputSurface(sp<IGraphicBufferProducer>* bufferProducer); status_t updateQueuedAudioData(int sizeInBytes, int64_t presentationTimeUs); - status_t setPlaybackRate(float rate); - status_t getPlayTimeForPendingAudioFrames(int64_t *outTimeUs); + status_t setPlaybackSettings(const AudioPlaybackRate& rate); + void getPlaybackSettings(AudioPlaybackRate* rate /* nonnull */); + status_t setSyncSettings(const AVSyncSettings& syncSettings); + void getSyncSettings(AVSyncSettings* syncSettings /* nonnull */); + status_t setVideoFrameRateHint(float rate); + float getVideoFrameRate(); + sp<const MediaClock> getMediaClock(); protected: diff --git a/media/jni/android_media_SyncSettings.cpp b/media/jni/android_media_SyncSettings.cpp index 2f0605e03295..5da35e751f57 100644 --- a/media/jni/android_media_SyncSettings.cpp +++ b/media/jni/android_media_SyncSettings.cpp @@ -57,9 +57,9 @@ void SyncSettings::fields_t::exit(JNIEnv *env) { } void SyncSettings::fillFromJobject(JNIEnv *env, const fields_t& fields, jobject settings) { - syncSource = env->GetIntField(settings, fields.sync_source); - audioAdjustMode = env->GetIntField(settings, fields.audio_adjust_mode); - tolerance = env->GetFloatField(settings, fields.tolerance); + sync.mSource = (AVSyncSource)env->GetIntField(settings, fields.sync_source); + sync.mAudioAdjustMode = (AVSyncAudioAdjustMode)env->GetIntField(settings, fields.audio_adjust_mode); + sync.mTolerance = env->GetFloatField(settings, fields.tolerance); frameRate = env->GetFloatField(settings, fields.frame_rate); int set = env->GetIntField(settings, fields.set); @@ -74,9 +74,9 @@ jobject SyncSettings::asJobject(JNIEnv *env, const fields_t& fields) { if (settings == NULL) { return NULL; } - env->SetIntField(settings, fields.sync_source, (jint)syncSource); - env->SetIntField(settings, fields.audio_adjust_mode, (jint)audioAdjustMode); - env->SetFloatField(settings, fields.tolerance, (jfloat)tolerance); + env->SetIntField(settings, fields.sync_source, (jint)sync.mSource); + env->SetIntField(settings, fields.audio_adjust_mode, (jint)sync.mAudioAdjustMode); + env->SetFloatField(settings, fields.tolerance, (jfloat)sync.mTolerance); env->SetFloatField(settings, fields.frame_rate, (jfloat)frameRate); env->SetIntField( settings, fields.set, diff --git a/media/jni/android_media_SyncSettings.h b/media/jni/android_media_SyncSettings.h index 586533f25bd1..23530db4eabb 100644 --- a/media/jni/android_media_SyncSettings.h +++ b/media/jni/android_media_SyncSettings.h @@ -19,13 +19,12 @@ #include "jni.h" +#include <media/stagefright/MediaSync.h> + namespace android { struct SyncSettings { - // keep this here until it is implemented - int syncSource; - int audioAdjustMode; - float tolerance; + AVSyncSettings sync; float frameRate; bool syncSourceSet; diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp index 73b52aa2c604..4e7c6bededba 100644 --- a/native/android/sensor.cpp +++ b/native/android/sensor.cpp @@ -38,11 +38,6 @@ using android::String8; /*****************************************************************************/ -ASensorManager* ASensorManager_getInstance() -{ - return &SensorManager::getInstance(); -} - int ASensorManager_getSensorList(ASensorManager* manager, ASensorList* list) { diff --git a/packages/IntentFilterVerifier/Android.mk b/packages/IntentFilterVerifier/Android.mk deleted file mode 100644 index 99feda51c9f3..000000000000 --- a/packages/IntentFilterVerifier/Android.mk +++ /dev/null @@ -1,22 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -# Build the IntentFilterVerifier. -include $(CLEAR_VARS) - -LOCAL_STATIC_JAVA_LIBRARIES := \ - volley \ - -LOCAL_JAVA_LIBRARIES += org.apache.http.legacy - -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -LOCAL_PACKAGE_NAME := IntentFilterVerifier - -LOCAL_PRIVILEGED_MODULE := true - -LOCAL_PROGUARD_FLAGS := $(proguard.flags) - -include $(BUILD_PACKAGE) - -# Build the test package. -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/packages/IntentFilterVerifier/AndroidManifest.xml b/packages/IntentFilterVerifier/AndroidManifest.xml deleted file mode 100644 index 3829cc561b1b..000000000000 --- a/packages/IntentFilterVerifier/AndroidManifest.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.verifier.intentfilter" - coreApp="true"> - <uses-permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/> - <uses-permission android:name="android.permission.INTERNET"/> - <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> - - <application - android:label="@string/service_name" - android:allowBackup="false"> - - <receiver - android:name="com.android.verifier.intentfilter.IntentVerificationReceiver" - android:permission="android.permission.BIND_INTENT_FILTER_VERIFIER" > - <intent-filter - android:priority="-1" > - <action android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION" /> - <data android:mimeType="application/vnd.android.package-archive" /> - </intent-filter> - </receiver> - - <service android:name=".IntentVerificationService" - android:label="@string/service_name" - android:exported="false"/> - - </application> - -</manifest> diff --git a/packages/IntentFilterVerifier/CleanSpec.mk b/packages/IntentFilterVerifier/CleanSpec.mk deleted file mode 100644 index e4575ae74754..000000000000 --- a/packages/IntentFilterVerifier/CleanSpec.mk +++ /dev/null @@ -1,49 +0,0 @@ -# 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. -# - -# If you don't need to do a full clean build but would like to touch -# a file or delete some intermediate files, add a clean step to the end -# of the list. These steps will only be run once, if they haven't been -# run before. -# -# E.g.: -# $(call add-clean-step, touch -c external/sqlite/sqlite3.h) -# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates) -# -# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with -# files that are missing or have been moved. -# -# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory. -# Use $(OUT_DIR) to refer to the "out" directory. -# -# If you need to re-do something that's already mentioned, just copy -# the command and add it to the bottom of the list. E.g., if a change -# that you made last week required touching a file and a change you -# made today requires touching the same file, just copy the old -# touch step and add it to the end of the list. -# -# ***************************************************************** -# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER -# ***************************************************************** - -# For example: -#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) -#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates) -#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f) -#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*) - -# ****************************************************************** -# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER -# ****************************************************************** diff --git a/packages/IntentFilterVerifier/proguard.flags b/packages/IntentFilterVerifier/proguard.flags deleted file mode 100644 index 6e4bec388e6d..000000000000 --- a/packages/IntentFilterVerifier/proguard.flags +++ /dev/null @@ -1 +0,0 @@ --verbose
\ No newline at end of file diff --git a/packages/IntentFilterVerifier/res/values/strings.xml b/packages/IntentFilterVerifier/res/values/strings.xml deleted file mode 100644 index 22f3cd522d58..000000000000 --- a/packages/IntentFilterVerifier/res/values/strings.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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. ---> - -<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <!-- Package name shown to users when they look at installed applications - and running processes. This service verifies packages that are - requested to be installed. [CHAR LIMIT=50] --> - <string name="service_name">Basic Intent Filter Verification Service</string> - -</resources> diff --git a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationReceiver.java b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationReceiver.java deleted file mode 100644 index de25f8c3a8b2..000000000000 --- a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationReceiver.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.verifier.intentfilter; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Bundle; -import android.util.Log; -import android.util.Slog; - -import java.util.Arrays; -import java.util.List; - -public class IntentVerificationReceiver extends BroadcastReceiver { - static final String TAG = IntentVerificationReceiver.class.getName(); - - @Override - public void onReceive(Context context, Intent intent) { - final String action = intent.getAction(); - if (Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION.equals(action)) { - Bundle extras = intent.getExtras(); - if (extras != null) { - int verificationId = extras.getInt( - PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID); - String hosts = extras.getString( - PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS); - - Log.d(TAG, "Received IntentFilter verification broadcast with verificationId: " - + verificationId); - - if (canDoVerification(context)) { - Intent serviceIntent = new Intent(context, IntentVerificationService.class); - serviceIntent.fillIn(intent, 0); - serviceIntent.putExtras(intent.getExtras()); - - Slog.d(TAG, "Starting Intent Verification Service."); - - context.startService(serviceIntent); - } else { - sendVerificationFailure(context, verificationId, hosts); - } - } - - } else { - Log.w(TAG, "Unexpected action: " + action); - } - } - - private void sendVerificationFailure(Context context, int verificationId, String hosts) { - List<String> list = Arrays.asList(hosts.split(" ")); - context.getPackageManager().verifyIntentFilter( - verificationId, PackageManager.INTENT_FILTER_VERIFICATION_FAILURE, list); - - Log.d(TAG, "No network! Failing IntentFilter verification with verificationId: " + - verificationId + " and hosts: " + hosts); - } - - private boolean canDoVerification(Context context) { - return hasNetwork(context); - } - - public boolean hasNetwork(Context context) { - ConnectivityManager cm = - (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - if (cm != null) { - NetworkInfo info = cm.getActiveNetworkInfo(); - return (info != null) && info.isConnected(); - } else { - return false; - } - } -} diff --git a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationRequest.java b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationRequest.java deleted file mode 100644 index 8f9c86f06aeb..000000000000 --- a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationRequest.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * 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.verifier.intentfilter; - -import com.android.volley.Response; -import com.android.volley.toolbox.JsonArrayRequest; -import org.json.JSONArray; - -public class IntentVerificationRequest extends JsonArrayRequest { - - public IntentVerificationRequest(String url, Response.Listener<JSONArray> listener, - Response.ErrorListener errorListener) { - super(url, listener, errorListener); - } -} diff --git a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationService.java b/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationService.java deleted file mode 100644 index 3e4db6c0eb72..000000000000 --- a/packages/IntentFilterVerifier/src/com/android/verifier/intentfilter/IntentVerificationService.java +++ /dev/null @@ -1,468 +0,0 @@ -/* - * 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.verifier.intentfilter; - -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.Signature; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.text.TextUtils; -import android.util.Log; -import android.util.Slog; -import com.android.volley.RequestQueue; -import com.android.volley.Response; -import com.android.volley.VolleyError; -import com.android.volley.toolbox.Volley; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.net.MalformedURLException; -import java.net.URL; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; - -public class IntentVerificationService extends Service { - private static final String TAG = "IntentVerificationService"; - - private static final String WELL_KNOWN_ASSOCIATIONS_JSON = "/.well-known/associations.json"; - private static final String DEFAULT_SCHEME = "https"; - - private static final String JSON_KEY_TARGET = "target"; - private static final String JSON_KEY_NAMESPACE = "namespace"; - private static final String JSON_KEY_PACKAGE_NAME = "package_name"; - private static final String JSON_KEY_CERT_FINGERPRINTS = "sha256_cert_fingerprints"; - - private static final String JSON_VAL_ANDROID_APP = "android_app"; - - private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'A', 'B', 'C', 'D', 'E', 'F' }; - - private ConnectivityManager mConnectivityManager; - private Looper mHandlerLooper; - private VerificationHandler mHandler; - private RequestQueue mRequestQueue; - - private static class VerificationState { - public final int verificationId; - public final String hosts; - public final String packageName; - public final Set<String> fingerprints; - public int responseCode = PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS; - public int counter; - public int numberOfHosts; - public ArrayList<String> failedHosts = new ArrayList<>(); - - private final Object lock = new Object(); - - public VerificationState(int id, String h, String p, Set<String> fps) { - verificationId = id; - hosts = h; - packageName = p; - fingerprints = fps; - numberOfHosts = hosts.split(" ").length; - } - public boolean setResponseCodeAndCheckMax(int code) { - synchronized (lock) { - if (code == PackageManager.INTENT_FILTER_VERIFICATION_FAILURE) { - responseCode = code; - counter++; - } else if (code == PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS) { - counter++; - } - return (counter == numberOfHosts); - } - } - - public void addFailedHost(String host) { - synchronized (failedHosts) { - failedHosts.add(host); - } - } - - public ArrayList<String> getFailedHosts() { - return failedHosts; - } - } - - private HashMap<Integer, VerificationState> mVerificationMap = - new HashMap<Integer, VerificationState>(); - - private class VerificationHandler extends Handler { - private static final int MSG_STOP_SERVICE = 0; - private static final int MSG_VERIFY_INTENT_START = 1; - private static final int MSG_VERIFY_INTENT_DONE = 2; - - private static final long SHUTDOWN_DELAY_MILLIS = 8 * 1000; - - private final Context mContext; - - public VerificationHandler(Context context, Looper looper) { - super(looper); - - mContext = context; - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_VERIFY_INTENT_START: - final Intent intent = (Intent) msg.obj; - Bundle extras = intent.getExtras(); - boolean immediate = false; - - if (extras != null) { - immediate = doVerification(extras); - } - - // There was no network, so we can stop soon - if (immediate) { - stopDelayed(); - } - break; - - case MSG_VERIFY_INTENT_DONE: - VerificationState vs = (VerificationState) msg.obj; - processVerificationDone(mContext, vs); - clearVerificationState(vs); - break; - - case MSG_STOP_SERVICE: - stopSelf(); - break; - - default: - Slog.i(TAG, "Unknown message posted " + msg.toString()); - break; - - } - } - - private void stopDelayed() { - removeMessages(MSG_STOP_SERVICE); - sendEmptyMessageDelayed(MSG_STOP_SERVICE, SHUTDOWN_DELAY_MILLIS); - } - } - - private VerificationState getVerificationState(int id, String hosts, String packageName, - Set<String> fingerprints) { - synchronized (mVerificationMap) { - VerificationState vs = mVerificationMap.get(id); - if (vs == null) { - vs = new VerificationState(id, hosts, packageName, fingerprints); - } - return vs; - } - } - - private void clearVerificationState(VerificationState vs) { - mVerificationMap.remove(vs); - } - - private boolean doVerification(Bundle extras) { - String scheme = extras.getString(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME); - if (TextUtils.isEmpty(scheme)) { - scheme = DEFAULT_SCHEME; - } - - int verificationId = extras.getInt(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID); - String hosts = extras.getString(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS); - String packageName = extras.getString( - PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME); - - Set<String> fingerprints = getFingerprints(packageName); - - Log.d(TAG, "Received IntentFilter verification broadcast with verificationId:" + - verificationId + " hosts:'" + hosts + "' scheme:" + scheme); - - VerificationState vs = getVerificationState(verificationId, hosts, packageName, - fingerprints); - - if (hasNetwork()) { - sendNetworkVerifications(scheme, vs); - return false; - } - - // No network, so fail immediately - sendFailureResponseIfNeeded(vs); - - return true; - } - - private Set<String> getFingerprints(String packageName) { - Context context = getApplicationContext(); - try { - Signature[] signatures = context.getPackageManager().getPackageInfo(packageName, - PackageManager.GET_SIGNATURES).signatures; - if (signatures.length > 0) { - HashSet<String> result = new HashSet<String>(); - for (Signature sig : signatures) { - String fingerprint = computeNormalizedSha256Fingerprint(sig.toByteArray()); - result.add(fingerprint); - } - return result; - } - } catch (PackageManager.NameNotFoundException e) { - Log.e(TAG, "Cannot get signatures for package name: " + packageName); - } - return Collections.EMPTY_SET; - } - - private static String computeNormalizedSha256Fingerprint(byte[] signature) { - MessageDigest digester; - try { - digester = MessageDigest.getInstance("SHA-256"); - } catch (NoSuchAlgorithmException e) { - throw new AssertionError("No SHA-256 implementation found."); - } - digester.update(signature); - return byteArrayToHexString(digester.digest()); - } - - private static String byteArrayToHexString(byte[] array) { - if (array.length == 0) { - return ""; - } - char[] buf = new char[array.length * 3 - 1]; - - int bufIndex = 0; - for (int i = 0; i < array.length; i++) { - byte b = array[i]; - if (i > 0) { - buf[bufIndex++] = ':'; - } - buf[bufIndex++] = HEX_DIGITS[(b >>> 4) & 0x0F]; - buf[bufIndex++] = HEX_DIGITS[b & 0x0F]; - } - return new String(buf); - } - - private static String getAssociationPath() { - return WELL_KNOWN_ASSOCIATIONS_JSON; - } - - private void sendNetworkVerifications(String scheme, final VerificationState vs) { - final int verificationId = vs.verificationId; - final String hosts = vs.hosts; - - String[] array = hosts.split(" "); - for (final String host : array) { - try { - final URL url = new URL(scheme, host, getAssociationPath()); - final String urlStr = url.toString(); - Log.d(TAG, "Using verification URL: " + urlStr); - IntentVerificationRequest req = new IntentVerificationRequest(urlStr, - new Response.Listener<JSONArray>() { - @Override - public void onResponse(JSONArray response) { - Log.d(TAG, "From: " + urlStr + " received response: " - + response.toString()); - handleResponse(vs, host, response); - } - }, new Response.ErrorListener() { - @Override - public void onErrorResponse(VolleyError error) { - Slog.d(TAG, "From: " + urlStr + " got error: " + error.getMessage() - + (error.networkResponse != null ? " with status code: " - + error.networkResponse.statusCode : "")); - handleError(vs, host); - } - } - ); - mRequestQueue.add(req); - } catch (MalformedURLException e) { - Log.w(TAG, "Cannot send verificationId: " + verificationId + " to host: " + host); - } - } - } - - private void handleError(VerificationState vs, String host) { - vs.addFailedHost(host); - sendFailureResponseIfNeeded(vs); - } - - private void handleResponse(VerificationState vs, String host, JSONArray response) { - try { - if (response.length() == 0) { - Log.d(TAG, "Domain response is empty!"); - handleError(vs, host); - return; - } - - JSONObject firstRelation = (JSONObject) response.get(0); - if (firstRelation == null) { - Log.d(TAG, "Domain response is should have a relation!"); - handleError(vs, host); - return; - } - - JSONObject target = (JSONObject) firstRelation.get(JSON_KEY_TARGET); - if (target == null) { - Log.d(TAG, "Domain response target is empty!"); - handleError(vs, host); - return; - } - - String nameSpace = target.getString(JSON_KEY_NAMESPACE); - if (TextUtils.isEmpty(nameSpace) || !nameSpace.equals(JSON_VAL_ANDROID_APP)) { - Log.d(TAG, "Domain response target name space is not valid: " + nameSpace); - handleError(vs, host); - return; - } - - String packageName = target.getString(JSON_KEY_PACKAGE_NAME); - JSONArray certFingerprints = target.getJSONArray(JSON_KEY_CERT_FINGERPRINTS); - - // Early exits is the JSON response is not correct for the package name or signature - if (TextUtils.isEmpty(packageName)) { - Log.d(TAG, "Domain response has empty package name!"); - handleError(vs, host); - return; - } - if (certFingerprints.length() == 0) { - Log.d(TAG, "Domain response has empty cert signature!"); - handleError(vs, host); - return; - } - // Now do the real test on package name and signature - if (!packageName.equalsIgnoreCase(vs.packageName)) { - Log.d(TAG, "Domain response has package name mismatch!" + packageName + - " vs " + vs.packageName); - handleError(vs, host); - return; - } - final int count = certFingerprints.length(); - for (int i = 0; i < count; i++) { - String fingerprint = certFingerprints.getString(i); - if (!vs.fingerprints.contains(fingerprint)) { - Log.d(TAG, "Domain response has cert fingerprint mismatch! " + - "The domain fingerprint '" + fingerprint + "' is not from the App"); - handleError(vs, host); - return; - } - } - sendSuccessResponseIfNeeded(vs); - } catch (JSONException e) { - Log.d(TAG, "Domain response is not well formed", e); - handleError(vs, host); - } - } - - private void sendSuccessResponseIfNeeded(VerificationState vs) { - if (vs.setResponseCodeAndCheckMax(PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS)) { - sendMessage(vs); - } - } - - private void sendFailureResponseIfNeeded(VerificationState vs) { - if (vs.setResponseCodeAndCheckMax(PackageManager.INTENT_FILTER_VERIFICATION_FAILURE)) { - sendMessage(vs); - } - } - - private void sendMessage(VerificationState vs) { - Message msg = mHandler.obtainMessage(VerificationHandler.MSG_VERIFY_INTENT_DONE); - msg.obj = vs; - mHandler.sendMessage(msg); - } - - private void processVerificationDone(Context context, VerificationState state) { - int verificationId = state.verificationId; - String hosts = state.hosts; - int responseCode = state.responseCode; - - final PackageManager pm = context.getPackageManager(); - - // Callback the PackageManager - pm.verifyIntentFilter(verificationId, responseCode, state.getFailedHosts()); - Log.d(TAG, "IntentFilter with verificationId: " + verificationId + " and hosts: " + - hosts + " got verification code: " + responseCode); - } - - /** - * We only connect to this service from the same process. - */ - public class LocalBinder extends Binder { - IntentVerificationService getService() { return IntentVerificationService.this; } - } - - private final IBinder mBinder = new LocalBinder(); - - @Override - public IBinder onBind(Intent intent) { - return mBinder; - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - Slog.i(TAG, "Received start id " + startId + ": " + intent); - - final Message msg = mHandler.obtainMessage(VerificationHandler.MSG_VERIFY_INTENT_START); - msg.obj = intent; - mHandler.sendMessage(msg); - - return START_STICKY; - } - - @Override - public void onCreate() { - super.onCreate(); - - Slog.d(TAG, "Starting up..."); - - final HandlerThread handlerThread = new HandlerThread("IntentVerificationService"); - handlerThread.start(); - mHandlerLooper = handlerThread.getLooper(); - - mHandler = new VerificationHandler(getApplicationContext(), mHandlerLooper); - - mRequestQueue = Volley.newRequestQueue(this); - mRequestQueue.start(); - - mConnectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); - } - - @Override - public void onDestroy() { - super.onDestroy(); - - Slog.d(TAG, "Shutting down..."); - - mHandlerLooper.quit(); - mRequestQueue.stop(); - } - - private boolean hasNetwork() { - NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); - return (info != null) && info.isConnected(); - } -} diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index a0ef5e2b6b9f..b0b78f2f374e 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -16,9 +16,6 @@ */ --> <resources> - <!-- Margin at the edge of the screen to ignore touch events for in the windowshade. --> - <dimen name="status_bar_edge_ignore">5dp</dimen> - <!-- Recent Applications parameters --> <!-- Upper width limit for application icon --> <dimen name="status_bar_recents_app_icon_max_width">48dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index f3a2912127d6..00b8df27ded8 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -400,6 +400,8 @@ <string name="accessibility_recents_all_items_dismissed">All recent applications dismissed.</string> <!-- Content description to tell the user an application has been launched from recents --> <string name="accessibility_recents_item_launched">Starting <xliff:g id="app" example="Calendar">%s</xliff:g>.</string> + <!-- Content description of individual recents task. --> + <string name="accessibility_recents_task_header"><xliff:g id="app" example="Chrome">%1$s</xliff:g> <xliff:g id="activity_label" example="www.google.com">%2$s</xliff:g></string> <!-- Content description to tell the user a notification has been removed from the notification shade --> <string name="accessibility_notification_dismissed">Notification dismissed.</string> diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistGestureManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistGestureManager.java index 36be3555e486..d9f2324b8e81 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistGestureManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistGestureManager.java @@ -196,7 +196,7 @@ public class AssistGestureManager { private boolean getVoiceInteractorSupportsAssistGesture() { try { - return mVoiceInteractionManagerService.activeServiceSupportsAssistGesture(); + return mVoiceInteractionManagerService.activeServiceSupportsAssist(); } catch (RemoteException e) { Log.w(TAG, "Failed to call activeServiceSupportsAssistGesture", e); return false; diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index d60df9ca30e2..e3fb16afe459 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -438,6 +438,33 @@ public class SystemServicesProxy { return info.loadLabel(mPm).toString(); } + /** Returns the application label */ + public String getApplicationLabel(Intent baseIntent, int userId) { + if (mPm == null) return null; + + // If we are mocking, then return a mock label + if (Constants.DebugFlags.App.EnableSystemServicesProxy) { + return "Recent Task"; + } + + ResolveInfo ri = mPm.resolveActivityAsUser(baseIntent, 0, userId); + CharSequence label = (ri != null) ? ri.loadLabel(mPm) : null; + return (label != null) ? label.toString() : null; + } + + /** Returns the content description for a given task */ + public String getContentDescription(Intent baseIntent, int userId, String activityLabel, + Resources res) { + String applicationLabel = getApplicationLabel(baseIntent, userId); + if (applicationLabel == null) { + return getBadgedLabel(activityLabel, userId); + } + String badgedApplicationLabel = getBadgedLabel(applicationLabel, userId); + return applicationLabel.equals(activityLabel) ? badgedApplicationLabel + : res.getString(R.string.accessibility_recents_task_header, + badgedApplicationLabel, activityLabel); + } + /** * Returns the activity icon for the ActivityInfo for a user, badging if * necessary. @@ -464,6 +491,16 @@ public class SystemServicesProxy { return icon; } + /** + * Returns the given label for a user, badging if necessary. + */ + public String getBadgedLabel(String label, int userId) { + if (userId != UserHandle.myUserId()) { + label = mPm.getUserBadgedLabel(label, new UserHandle(userId)).toString(); + } + return label; + } + /** Returns the package name of the home activity. */ public String getHomeActivityPackageName() { if (mPm == null) return null; diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java index 5d98dda71e6c..40cd211289de 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java @@ -130,6 +130,8 @@ public class RecentsTaskLoadPlan { // Load the label, icon, and color String activityLabel = loader.getAndUpdateActivityLabel(taskKey, t.taskDescription, mSystemServicesProxy, infoHandle); + String contentDescription = loader.getAndUpdateContentDescription(taskKey, + activityLabel, mSystemServicesProxy, res); Drawable activityIcon = loader.getAndUpdateActivityIcon(taskKey, t.taskDescription, mSystemServicesProxy, res, infoHandle, false); int activityColor = loader.getActivityPrimaryColor(t.taskDescription, mConfig); @@ -148,9 +150,9 @@ public class RecentsTaskLoadPlan { // Add the task to the stack Task task = new Task(taskKey, (t.id != RecentsTaskLoader.INVALID_TASK_ID), - t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel, activityIcon, - activityColor, (i == (taskCount - 1)), mConfig.lockToAppEnabled, icon, - iconFilename); + t.affiliatedTaskId, t.affiliatedTaskColor, activityLabel, contentDescription, + activityIcon, activityColor, (i == (taskCount - 1)), mConfig.lockToAppEnabled, + icon, iconFilename); task.thumbnail = loader.getAndUpdateThumbnail(taskKey, mSystemServicesProxy, false); if (DEBUG) Log.d(TAG, "\tthumbnail: " + taskKey + ", " + task.thumbnail); diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java index 3192fe61d1d7..b2aa2b67c574 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoader.java @@ -259,6 +259,7 @@ public class RecentsTaskLoader { DrawableLruCache mApplicationIconCache; BitmapLruCache mThumbnailCache; StringLruCache mActivityLabelCache; + StringLruCache mContentDescriptionCache; TaskResourceLoadQueue mLoadQueue; TaskResourceLoader mLoader; @@ -298,6 +299,7 @@ public class RecentsTaskLoader { mApplicationIconCache = new DrawableLruCache(iconCacheSize); mThumbnailCache = new BitmapLruCache(thumbnailCacheSize); mActivityLabelCache = new StringLruCache(100); + mContentDescriptionCache = new StringLruCache(100); mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache, mDefaultThumbnail, mDefaultApplicationIcon); } @@ -348,6 +350,24 @@ public class RecentsTaskLoader { return label; } + /** Returns the content description using as many cached values as we can. */ + public String getAndUpdateContentDescription(Task.TaskKey taskKey, String activityLabel, + SystemServicesProxy ssp, Resources res) { + // Return the cached content description if it exists + String label = mContentDescriptionCache.getAndInvalidateIfModified(taskKey); + if (label != null) { + return label; + } + label = ssp.getContentDescription(taskKey.baseIntent, taskKey.userId, activityLabel, res); + if (label != null) { + mContentDescriptionCache.put(taskKey, label); + } else { + Log.w(TAG, "Missing content description for " + taskKey.baseIntent.getComponent() + + " u=" + taskKey.userId); + } + return label; + } + /** Returns the activity icon using as many cached values as we can. */ public Drawable getAndUpdateActivityIcon(Task.TaskKey taskKey, ActivityManager.TaskDescription td, SystemServicesProxy ssp, @@ -541,6 +561,7 @@ public class RecentsTaskLoader { mApplicationIconCache.evictAll(); // The cache is small, only clear the label cache when we are critical mActivityLabelCache.evictAll(); + mContentDescriptionCache.evictAll(); break; default: break; diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java index 0cd55d70badb..c14adf454c11 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java +++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java @@ -124,6 +124,7 @@ public class Task { public boolean isLaunchTarget; public Drawable applicationIcon; public Drawable activityIcon; + public String contentDescription; public String activityLabel; public int colorPrimary; public boolean useLightOnPrimaryColor; @@ -140,8 +141,8 @@ public class Task { } public Task(TaskKey key, boolean isActive, int taskAffiliation, int taskAffiliationColor, - String activityTitle, Drawable activityIcon, int colorPrimary, - boolean lockToThisTask, boolean lockToTaskEnabled, Bitmap icon, + String activityTitle, String contentDescription, Drawable activityIcon, + int colorPrimary, boolean lockToThisTask, boolean lockToTaskEnabled, Bitmap icon, String iconFilename) { boolean isInAffiliationGroup = (taskAffiliation != key.id); boolean hasAffiliationGroupColor = isInAffiliationGroup && (taskAffiliationColor != 0); @@ -149,6 +150,7 @@ public class Task { this.taskAffiliation = taskAffiliation; this.taskAffiliationColor = taskAffiliationColor; this.activityLabel = activityTitle; + this.contentDescription = contentDescription; this.activityIcon = activityIcon; this.colorPrimary = hasAffiliationGroupColor ? taskAffiliationColor : colorPrimary; this.useLightOnPrimaryColor = Utilities.computeContrastBetweenColors(this.colorPrimary, @@ -166,6 +168,7 @@ public class Task { this.taskAffiliation = o.taskAffiliation; this.taskAffiliationColor = o.taskAffiliationColor; this.activityLabel = o.activityLabel; + this.contentDescription = o.contentDescription; this.activityIcon = o.activityIcon; this.colorPrimary = o.colorPrimary; this.useLightOnPrimaryColor = o.useLightOnPrimaryColor; diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java index 60a91bf9b393..f397bc395eb4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewHeader.java @@ -190,10 +190,12 @@ public class TaskViewHeader extends FrameLayout { } else if (t.applicationIcon != null) { mApplicationIcon.setImageDrawable(t.applicationIcon); } - mApplicationIcon.setContentDescription(t.activityLabel); + mApplicationIcon.setContentDescription(t.contentDescription); if (!mActivityDescription.getText().toString().equals(t.activityLabel)) { mActivityDescription.setText(t.activityLabel); } + mActivityDescription.setContentDescription(t.contentDescription); + // Try and apply the system ui tint int existingBgColor = getBackgroundColor(); if (existingBgColor != t.colorPrimary) { @@ -207,7 +209,7 @@ public class TaskViewHeader extends FrameLayout { mDismissButton.setImageDrawable(t.useLightOnPrimaryColor ? mLightDismissDrawable : mDarkDismissDrawable); mDismissButton.setContentDescription(String.format(mDismissContentDescription, - t.activityLabel)); + t.contentDescription)); mMoveTaskButton.setVisibility((mConfig.multiStackEnabled) ? View.VISIBLE : View.INVISIBLE); if (mConfig.multiStackEnabled) { updateResizeTaskBarIcon(t); 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 9a6a80e70c2c..1c46d42c4511 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -91,6 +91,7 @@ import android.view.animation.PathInterpolator; import android.widget.ImageView; import android.widget.TextView; +import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.StatusBarIcon; import com.android.keyguard.KeyguardHostView.OnDismissAction; import com.android.keyguard.ViewMediatorCallback; @@ -297,7 +298,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, int mTrackingPosition; // the position of the top of the tracking view. // Tracking finger for opening/closing. - int mEdgeBorder; // corresponds to R.dimen.status_bar_edge_ignore boolean mTracking; VelocityTracker mVelocityTracker; @@ -691,6 +691,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mDismissView.setOnButtonClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + MetricsLogger.action(mContext, MetricsLogger.ACTION_DISMISS_ALL_NOTES); clearAllNotifications(); } }); @@ -724,8 +725,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, R.id.keyguard_indication_text)); mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController); - mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); - // set the inital view visibility setAreThereNotifications(); @@ -2818,8 +2817,6 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, mNaturalBarHeight = res.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height); - mEdgeBorder = res.getDimensionPixelSize(R.dimen.status_bar_edge_ignore); - mRowMinHeight = res.getDimensionPixelSize(R.dimen.notification_min_height); mRowMaxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height); diff --git a/rs/java/android/renderscript/ScriptIntrinsicBLAS.java b/rs/java/android/renderscript/ScriptIntrinsicBLAS.java index 149c0beccb83..5e28d3f5a372 100644 --- a/rs/java/android/renderscript/ScriptIntrinsicBLAS.java +++ b/rs/java/android/renderscript/ScriptIntrinsicBLAS.java @@ -1288,7 +1288,7 @@ public final class ScriptIntrinsicBLAS extends ScriptIntrinsic { } else { K = A.getType().getX(); } - mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_ssyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha, A.getID(mRS), B.getID(mRS), beta, C.getID(mRS), 0, 0, 0, 0); + mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dsyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha, A.getID(mRS), B.getID(mRS), beta, C.getID(mRS), 0, 0, 0, 0); } public void CSYR2K(@Uplo int Uplo, @Transpose int Trans, Float2 alpha, Allocation A, Allocation B, Float2 beta, Allocation C) { validateUplo(Uplo); @@ -1299,7 +1299,7 @@ public final class ScriptIntrinsicBLAS extends ScriptIntrinsic { } else { K = A.getType().getX(); } - mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_ssyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), beta.x, beta.y, C.getID(mRS), 0, 0, 0, 0); + mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_csyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), beta.x, beta.y, C.getID(mRS), 0, 0, 0, 0); } public void ZSYR2K(@Uplo int Uplo, @Transpose int Trans, Double2 alpha, Allocation A, Allocation B, Double2 beta, Allocation C) { validateUplo(Uplo); @@ -1310,7 +1310,7 @@ public final class ScriptIntrinsicBLAS extends ScriptIntrinsic { } else { K = A.getType().getX(); } - mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_ssyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), beta.x, beta.y, C.getID(mRS), 0, 0, 0, 0); + mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_zsyr2k, Trans, 0, 0, Uplo, 0, 0, C.getType().getX(), K, alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), beta.x, beta.y, C.getID(mRS), 0, 0, 0, 0); } static void validateTRMM(Element e, @Side int Side, @Transpose int TransA, Allocation A, Allocation B) { @@ -1351,21 +1351,21 @@ public final class ScriptIntrinsicBLAS extends ScriptIntrinsic { validateUplo(Uplo); validateDiag(Diag); validateTRMM(Element.F64(mRS), Side, TransA, A, B); - mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_strmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0, - alpha, A.getID(mRS), B.getID(mRS), 0.f, 0, 0, 0, 0, 0); + mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dtrmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0, + alpha, A.getID(mRS), B.getID(mRS), 0, 0, 0, 0, 0, 0); } public void CTRMM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Float2 alpha, Allocation A, Allocation B) { validateUplo(Uplo); validateDiag(Diag); validateTRMM(Element.F32_2(mRS), Side, TransA, A, B); - mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_strmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0, + mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_ctrmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0, alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), 0, 0, 0, 0, 0, 0, 0); } public void ZTRMM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Double2 alpha, Allocation A, Allocation B) { validateUplo(Uplo); validateDiag(Diag); validateTRMM(Element.F64_2(mRS), Side, TransA, A, B); - mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_strmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0, + mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_ztrmm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0, alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), 0, 0, 0, 0, 0, 0, 0); } @@ -1409,21 +1409,21 @@ public final class ScriptIntrinsicBLAS extends ScriptIntrinsic { validateUplo(Uplo); validateDiag(Diag); validateTRSM(Element.F64(mRS), Side, TransA, A, B); - mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_strsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0, + mRS.nScriptIntrinsicBLAS_Double(getID(mRS), RsBlas_dtrsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0, alpha, A.getID(mRS), B.getID(mRS), 0, 0, 0, 0, 0, 0); } public void CTRSM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Float2 alpha, Allocation A, Allocation B) { validateUplo(Uplo); validateDiag(Diag); validateTRSM(Element.F32_2(mRS), Side, TransA, A, B); - mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_strsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0, + mRS.nScriptIntrinsicBLAS_Complex(getID(mRS), RsBlas_ctrsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0, alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), 0, 0, 0, 0, 0, 0, 0); } public void ZTRSM(@Side int Side, @Uplo int Uplo, @Transpose int TransA, @Diag int Diag, Double2 alpha, Allocation A, Allocation B) { validateUplo(Uplo); validateDiag(Diag); validateTRSM(Element.F64_2(mRS), Side, TransA, A, B); - mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_strsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0, + mRS.nScriptIntrinsicBLAS_Z(getID(mRS), RsBlas_ztrsm, TransA, 0, Side, Uplo, Diag, B.getType().getY(), B.getType().getX(), 0, alpha.x, alpha.y, A.getID(mRS), B.getID(mRS), 0, 0, 0, 0, 0, 0, 0); } diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 17c7e0cd4549..78cbac9c5373 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -63,6 +63,7 @@ import android.util.AtomicFile; import android.util.AttributeSet; import android.util.Pair; import android.util.Slog; +import android.util.SparseArray; import android.util.SparseIntArray; import android.util.TypedValue; import android.util.Xml; @@ -171,6 +172,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku private final SparseIntArray mLoadedUserIds = new SparseIntArray(); + private final SparseArray<ArraySet<String>> mWidgetPackages = new SparseArray<>(); + private final BackupRestoreController mBackupRestoreController; private final Context mContext; @@ -572,7 +575,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku widget.host = host; host.widgets.add(widget); - mWidgets.add(widget); + addWidgetLocked(widget); saveGroupStateAsync(userId); @@ -800,6 +803,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku widget.provider = provider; widget.options = (options != null) ? cloneIfLocalBinder(options) : new Bundle(); + onWidgetProviderAddedOrChangedLocked(widget); + // We need to provide a default value for the widget category if it is not specified if (!widget.options.containsKey(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY)) { widget.options.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, @@ -1410,7 +1415,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku host.widgets.remove(widget); pruneHostLocked(host); - mWidgets.remove(widget); + removeWidgetLocked(widget); Provider provider = widget.provider; if (provider != null) { @@ -1874,7 +1879,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku updateAppWidgetInstanceLocked(widget, null, false); // clear out references to this appWidgetId widget.host.widgets.remove(widget); - mWidgets.remove(widget); + removeWidgetLocked(widget); widget.provider = null; pruneHostLocked(widget.host); widget.host = null; @@ -2277,14 +2282,14 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku if (version >= 0) { // Hooke'm up... - bindLoadedWidgets(loadedWidgets); + bindLoadedWidgetsLocked(loadedWidgets); // upgrade the database if needed performUpgradeLocked(version); } else { // failed reading, clean up Slog.w(TAG, "Failed to read state, clearing widgets and hosts."); - mWidgets.clear(); + clearWidgetsLocked(); mHosts.clear(); final int N = mProviders.size(); for (int i = 0; i < N; i++) { @@ -2293,7 +2298,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku } } - private void bindLoadedWidgets(List<LoadedWidgetState> loadedWidgets) { + private void bindLoadedWidgetsLocked(List<LoadedWidgetState> loadedWidgets) { final int loadedWidgetCount = loadedWidgets.size(); for (int i = loadedWidgetCount - 1; i >= 0; i--) { LoadedWidgetState loadedWidget = loadedWidgets.remove(i); @@ -2314,7 +2319,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku widget.provider.widgets.add(widget); widget.host.widgets.add(widget); - mWidgets.add(widget); + addWidgetLocked(widget); } } @@ -2346,6 +2351,91 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku return null; } + /** + * Adds the widget to mWidgets and tracks the package name in mWidgetPackages. + */ + void addWidgetLocked(Widget widget) { + mWidgets.add(widget); + + onWidgetProviderAddedOrChangedLocked(widget); + } + + /** + * Checks if the provider is assigned and updates the mWidgetPackages to track packages + * that have bound widgets. + */ + void onWidgetProviderAddedOrChangedLocked(Widget widget) { + if (widget.provider == null) return; + + int userId = widget.provider.getUserId(); + ArraySet<String> packages = mWidgetPackages.get(userId); + if (packages == null) { + mWidgetPackages.put(userId, packages = new ArraySet<String>()); + } + packages.add(widget.provider.info.provider.getPackageName()); + } + + /** + * Removes a widget from mWidgets and updates the cache of bound widget provider packages. + * If there are other widgets with the same package, leaves it in the cache, otherwise it + * removes the associated package from the cache. + */ + void removeWidgetLocked(Widget widget) { + mWidgets.remove(widget); + + onWidgetRemovedLocked(widget); + } + + private void onWidgetRemovedLocked(Widget widget) { + if (widget.provider == null) return; + + final int userId = widget.provider.getUserId(); + final String packageName = widget.provider.info.provider.getPackageName(); + ArraySet<String> packages = mWidgetPackages.get(userId); + if (packages == null) { + return; + } + // Check if there is any other widget with the same package name. + // Remove packageName if none. + final int N = mWidgets.size(); + for (int i = 0; i < N; i++) { + Widget w = mWidgets.get(i); + if (w.provider == null) continue; + if (w.provider.getUserId() == userId + && packageName.equals(w.provider.info.provider.getPackageName())) { + return; + } + } + packages.remove(packageName); + } + + /** + * Clears all widgets and associated cache of packages with bound widgets. + */ + void clearWidgetsLocked() { + mWidgets.clear(); + + onWidgetsClearedLocked(); + } + + private void onWidgetsClearedLocked() { + mWidgetPackages.clear(); + } + + @Override + public boolean isBoundWidgetPackage(String packageName, int userId) { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Only the system process can call this"); + } + synchronized (mLock) { + final ArraySet<String> packages = mWidgetPackages.get(userId); + if (packages != null) { + return packages.contains(packageName); + } + } + return false; + } + private void saveStateLocked(int userId) { tagProvidersAndHosts(); @@ -2692,7 +2782,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku // as we do not want to make host callbacks and provider broadcasts // as the host and the provider will be killed. if (hostInUser && (!hasProvider || providerInUser)) { - mWidgets.remove(i); + removeWidgetLocked(widget); widget.host.widgets.remove(widget); widget.host = null; if (hasProvider) { @@ -3751,7 +3841,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku Slog.i(TAG, "New restored id " + restoredId + " now " + id); } - mWidgets.add(id); + addWidgetLocked(id); } if (id.provider.info != null) { stashProviderRestoreUpdateLocked(id.provider, @@ -4013,7 +4103,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku host.widgets.remove(widget); provider.widgets.remove(widget); unbindAppWidgetRemoteViewsServicesLocked(widget); - mWidgets.remove(i); + removeWidgetLocked(widget); } } mPrunedApps.add(pkg); diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index 17b49399356a..1366149a1c62 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -744,6 +744,11 @@ public class AppOpsService extends IAppOpsService.Stub { } } + @Override + public int permissionToOpCode(String permission) { + return AppOpsManager.permissionToOpCode(permission); + } + void finishOperationLocked(Op op) { if (op.nesting <= 1) { if (op.nesting == 1) { diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index ef82bb70cec4..1019faa6d9c9 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -48,6 +48,7 @@ import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; import android.util.Log; import java.io.FileDescriptor; @@ -443,6 +444,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { /** Internal death rec list */ Map<IBinder, ClientDeathRecipient> mBleApps = new HashMap<IBinder, ClientDeathRecipient>(); + @Override + public boolean isBleScanAlwaysAvailable() { + try { + return (Settings.Global.getInt(mContentResolver, + Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE)) != 0; + } catch (SettingNotFoundException e) { + } + return false; + } + public int updateBleAppCount(IBinder token, boolean enable) { if (enable) { ClientDeathRecipient r = mBleApps.get(token); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 069878e91a8c..eea6234cf3ca 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -47,6 +47,7 @@ import android.app.ProfilerInfo; import android.app.usage.UsageEvents; import android.app.usage.UsageStatsManagerInternal; import android.appwidget.AppWidgetManager; +import android.content.pm.PermissionInfo; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.Point; @@ -6619,6 +6620,18 @@ public final class ActivityManagerService extends ActivityManagerNative return mActivityManagerService.mContext.getPackageManager() .getPackagesForUid(uid); } + + @Override + public boolean isRuntimePermission(String permission) { + try { + PermissionInfo info = mActivityManagerService.mContext.getPackageManager() + .getPermissionInfo(permission, 0); + return info.protectionLevel == PermissionInfo.PROTECTION_DANGEROUS; + } catch (NameNotFoundException nnfe) { + Slog.e(TAG, "No such permission: "+ permission, nnfe); + } + return false; + } } class IntentFirewallInterface implements IntentFirewall.AMSInterface { diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 0f3b4e6bfa79..fb98d94235ce 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -264,9 +264,9 @@ public final class Installer extends SystemService { return mInstaller.execute(builder.toString()); } - public int moveUserDataDirs(String fromUuid, String toUuid, String packageName, int appId, - String seinfo) { - StringBuilder builder = new StringBuilder("mvuserdata"); + public int moveCompleteApp(String fromUuid, String toUuid, String packageName, + String dataAppName, int appId, String seinfo) { + StringBuilder builder = new StringBuilder("mvcompleteapp"); builder.append(' '); builder.append(escapeNull(fromUuid)); builder.append(' '); @@ -274,6 +274,8 @@ public final class Installer extends SystemService { builder.append(' '); builder.append(packageName); builder.append(' '); + builder.append(dataAppName); + builder.append(' '); builder.append(appId); builder.append(' '); builder.append(seinfo); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 11ab0423d4ee..66dd8f160b94 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -171,6 +171,7 @@ import android.util.EventLog; import android.util.ExceptionUtils; import android.util.Log; import android.util.LogPrinter; +import android.util.MathUtils; import android.util.PrintStreamPrinter; import android.util.Slog; import android.util.SparseArray; @@ -241,6 +242,8 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -2158,6 +2161,8 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.mFingerprint = Build.FINGERPRINT; } + primeDomainVerificationsLPw(false); + // All the changes are done during package scanning. mSettings.updateInternalDatabaseVersion(); @@ -2175,8 +2180,6 @@ public class PackageManagerService extends IPackageManager.Stub { mIntentFilterVerifier = new IntentVerifierProxy(mContext, mIntentFilterVerifierComponent); - primeDomainVerificationsLPw(false); - } // synchronized (mPackages) } // synchronized (mInstallLock) @@ -2274,9 +2277,9 @@ public class PackageManagerService extends IPackageManager.Stub { } private void primeDomainVerificationsLPw(boolean logging) { - Slog.d(TAG, "Start priming domain verification"); + Slog.d(TAG, "Start priming domain verifications"); boolean updated = false; - ArrayList<String> allHosts = new ArrayList<>(); + ArraySet<String> allHostsSet = new ArraySet<>(); for (PackageParser.Package pkg : mPackages.values()) { final String packageName = pkg.packageName; if (!hasDomainURLs(pkg)) { @@ -2296,18 +2299,20 @@ public class PackageManagerService extends IPackageManager.Stub { for (PackageParser.Activity a : pkg.activities) { for (ActivityIntentInfo filter : a.intents) { if (hasValidDomains(filter, false)) { - allHosts.addAll(filter.getHostsList()); + allHostsSet.addAll(filter.getHostsList()); } } } - if (allHosts.size() == 0) { - allHosts.add("*"); + if (allHostsSet.size() == 0) { + allHostsSet.add("*"); } + ArrayList<String> allHostsList = new ArrayList<>(allHostsSet); IntentFilterVerificationInfo ivi = - mSettings.createIntentFilterVerificationIfNeededLPw(packageName, allHosts); + mSettings.createIntentFilterVerificationIfNeededLPw(packageName, allHostsList); if (ivi != null) { // We will always log this - Slog.d(TAG, "Priming domain verifications for package: " + packageName); + Slog.d(TAG, "Priming domain verifications for package: " + packageName + + " with hosts:" + ivi.getDomainsString()); ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS); updated = true; } @@ -2316,12 +2321,14 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.d(TAG, "No priming domain verifications for package: " + packageName); } } - allHosts.clear(); + allHostsSet.clear(); } if (updated) { - scheduleWriteSettingsLocked(); + if (logging) { + Slog.d(TAG, "Will need to write primed domain verifications"); + } } - Slog.d(TAG, "End priming domain verification"); + Slog.d(TAG, "End priming domain verifications"); } @Override @@ -5049,8 +5056,7 @@ public class PackageManagerService extends IPackageManager.Stub { + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), - ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString, - getAppDexInstructionSets(ps)); + ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } @@ -5116,8 +5122,7 @@ public class PackageManagerService extends IPackageManager.Stub { + " reverting from " + ps.codePathString + ": new version " + pkg.mVersionCode + " better than installed " + ps.versionCode); InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), - ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString, - getAppDexInstructionSets(ps)); + ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps)); synchronized (mInstallLock) { args.cleanUpResourcesLI(); } @@ -8499,46 +8504,53 @@ public class PackageManagerService extends IPackageManager.Stub { } }; - static final void sendPackageBroadcast(String action, String pkg, - Bundle extras, String targetPkg, IIntentReceiver finishedReceiver, - int[] userIds) { - IActivityManager am = ActivityManagerNative.getDefault(); - if (am != null) { - try { - if (userIds == null) { - userIds = am.getRunningUserIds(); - } - for (int id : userIds) { - final Intent intent = new Intent(action, - pkg != null ? Uri.fromParts("package", pkg, null) : null); - if (extras != null) { - intent.putExtras(extras); - } - if (targetPkg != null) { - intent.setPackage(targetPkg); - } - // Modify the UID when posting to other users - int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); - if (uid > 0 && UserHandle.getUserId(uid) != id) { - uid = UserHandle.getUid(id, UserHandle.getAppId(uid)); - intent.putExtra(Intent.EXTRA_UID, uid); + final void sendPackageBroadcast(final String action, final String pkg, + final Bundle extras, final String targetPkg, final IIntentReceiver finishedReceiver, + final int[] userIds) { + mHandler.post(new Runnable() { + @Override + public void run() { + try { + final IActivityManager am = ActivityManagerNative.getDefault(); + if (am == null) return; + final int[] resolvedUserIds; + if (userIds == null) { + resolvedUserIds = am.getRunningUserIds(); + } else { + resolvedUserIds = userIds; } - intent.putExtra(Intent.EXTRA_USER_HANDLE, id); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - if (DEBUG_BROADCASTS) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.d(TAG, "Sending to user " + id + ": " - + intent.toShortString(false, true, false, false) - + " " + intent.getExtras(), here); + for (int id : resolvedUserIds) { + final Intent intent = new Intent(action, + pkg != null ? Uri.fromParts("package", pkg, null) : null); + if (extras != null) { + intent.putExtras(extras); + } + if (targetPkg != null) { + intent.setPackage(targetPkg); + } + // Modify the UID when posting to other users + int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); + if (uid > 0 && UserHandle.getUserId(uid) != id) { + uid = UserHandle.getUid(id, UserHandle.getAppId(uid)); + intent.putExtra(Intent.EXTRA_UID, uid); + } + intent.putExtra(Intent.EXTRA_USER_HANDLE, id); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + if (DEBUG_BROADCASTS) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.d(TAG, "Sending to user " + id + ": " + + intent.toShortString(false, true, false, false) + + " " + intent.getExtras(), here); + } + am.broadcastIntent(null, intent, null, finishedReceiver, + 0, null, null, null, android.app.AppOpsManager.OP_NONE, + finishedReceiver != null, false, id); } - am.broadcastIntent(null, intent, null, finishedReceiver, - 0, null, null, null, android.app.AppOpsManager.OP_NONE, - finishedReceiver != null, false, id); + } catch (RemoteException ex) { } - } catch (RemoteException ex) { } - } + }); } /** @@ -8665,8 +8677,8 @@ public class PackageManagerService extends IPackageManager.Stub { final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile); final Message msg = mHandler.obtainMessage(INIT_COPY); - msg.obj = new InstallParams(origin, observer, installFlags, - installerPackageName, null, verificationParams, user, packageAbiOverride); + msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName, + null, verificationParams, user, packageAbiOverride); mHandler.sendMessage(msg); } @@ -8684,7 +8696,7 @@ public class PackageManagerService extends IPackageManager.Stub { } final Message msg = mHandler.obtainMessage(INIT_COPY); - msg.obj = new InstallParams(origin, observer, params.installFlags, + msg.obj = new InstallParams(origin, null, observer, params.installFlags, installerPackageName, params.volumeUuid, verifParams, user, params.abiOverride); mHandler.sendMessage(msg); } @@ -9517,8 +9529,30 @@ public class PackageManagerService extends IPackageManager.Stub { } } + class MoveInfo { + final int moveId; + final String fromUuid; + final String toUuid; + final String packageName; + final String dataAppName; + final int appId; + final String seinfo; + + public MoveInfo(int moveId, String fromUuid, String toUuid, String packageName, + String dataAppName, int appId, String seinfo) { + this.moveId = moveId; + this.fromUuid = fromUuid; + this.toUuid = toUuid; + this.packageName = packageName; + this.dataAppName = dataAppName; + this.appId = appId; + this.seinfo = seinfo; + } + } + class InstallParams extends HandlerParams { final OriginInfo origin; + final MoveInfo move; final IPackageInstallObserver2 observer; int installFlags; final String installerPackageName; @@ -9528,11 +9562,12 @@ public class PackageManagerService extends IPackageManager.Stub { private int mRet; final String packageAbiOverride; - InstallParams(OriginInfo origin, IPackageInstallObserver2 observer, int installFlags, - String installerPackageName, String volumeUuid, + InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer, + int installFlags, String installerPackageName, String volumeUuid, VerificationParams verificationParams, UserHandle user, String packageAbiOverride) { super(user); this.origin = origin; + this.move = move; this.observer = observer; this.installFlags = installFlags; this.installerPackageName = installerPackageName; @@ -9911,7 +9946,9 @@ public class PackageManagerService extends IPackageManager.Stub { } private InstallArgs createInstallArgs(InstallParams params) { - if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) { + if (params.move != null) { + return new MoveInstallArgs(params); + } else if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) { return new AsecInstallArgs(params); } else { return new FileInstallArgs(params); @@ -9923,7 +9960,7 @@ public class PackageManagerService extends IPackageManager.Stub { * when cleaning up old installs, or used as a move source. */ private InstallArgs createInstallArgsForExisting(int installFlags, String codePath, - String resourcePath, String nativeLibraryRoot, String[] instructionSets) { + String resourcePath, String[] instructionSets) { final boolean isInAsec; if (installOnExternalAsec(installFlags)) { /* Apps on SD card are always in ASEC containers. */ @@ -9943,14 +9980,15 @@ public class PackageManagerService extends IPackageManager.Stub { return new AsecInstallArgs(codePath, instructionSets, installOnExternalAsec(installFlags), installForwardLocked(installFlags)); } else { - return new FileInstallArgs(codePath, resourcePath, nativeLibraryRoot, - instructionSets); + return new FileInstallArgs(codePath, resourcePath, instructionSets); } } static abstract class InstallArgs { /** @see InstallParams#origin */ final OriginInfo origin; + /** @see InstallParams#move */ + final MoveInfo move; final IPackageInstallObserver2 observer; // Always refers to PackageManager flags only @@ -9966,10 +10004,12 @@ public class PackageManagerService extends IPackageManager.Stub { // if we move dex files under the common app path. /* nullable */ String[] instructionSets; - InstallArgs(OriginInfo origin, IPackageInstallObserver2 observer, int installFlags, - String installerPackageName, String volumeUuid, ManifestDigest manifestDigest, - UserHandle user, String[] instructionSets, String abiOverride) { + InstallArgs(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer, + int installFlags, String installerPackageName, String volumeUuid, + ManifestDigest manifestDigest, UserHandle user, String[] instructionSets, + String abiOverride) { this.origin = origin; + this.move = move; this.installFlags = installFlags; this.observer = observer; this.installerPackageName = installerPackageName; @@ -9994,12 +10034,10 @@ public class PackageManagerService extends IPackageManager.Stub { abstract String getCodePath(); /** @see PackageSettingBase#resourcePathString */ abstract String getResourcePath(); - abstract String getLegacyNativeLibraryPath(); // Need installer lock especially for dex file removal. abstract void cleanUpResourcesLI(); abstract boolean doPostDeleteLI(boolean delete); - abstract boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException; /** * Called before the source arguments are copied. This is used mostly @@ -10060,7 +10098,6 @@ public class PackageManagerService extends IPackageManager.Stub { class FileInstallArgs extends InstallArgs { private File codeFile; private File resourceFile; - private File legacyNativeLibraryPath; // Example topology: // /data/app/com.example/base.apk @@ -10071,7 +10108,7 @@ public class PackageManagerService extends IPackageManager.Stub { /** New install */ FileInstallArgs(InstallParams params) { - super(params.origin, params.observer, params.installFlags, + super(params.origin, params.move, params.observer, params.installFlags, params.installerPackageName, params.volumeUuid, params.getManifestDigest(), params.getUser(), null /* instruction sets */, params.packageAbiOverride); if (isFwdLocked()) { @@ -10080,21 +10117,11 @@ public class PackageManagerService extends IPackageManager.Stub { } /** Existing install */ - FileInstallArgs(String codePath, String resourcePath, String legacyNativeLibraryPath, - String[] instructionSets) { - super(OriginInfo.fromNothing(), null, 0, null, null, null, null, instructionSets, null); + FileInstallArgs(String codePath, String resourcePath, String[] instructionSets) { + super(OriginInfo.fromNothing(), null, null, 0, null, null, null, null, instructionSets, + null); this.codeFile = (codePath != null) ? new File(codePath) : null; this.resourceFile = (resourcePath != null) ? new File(resourcePath) : null; - this.legacyNativeLibraryPath = (legacyNativeLibraryPath != null) ? - new File(legacyNativeLibraryPath) : null; - } - - boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { - final long sizeBytes = imcs.calculateInstalledSize(origin.file.getAbsolutePath(), - isFwdLocked(), abiOverride); - - final StorageManager storage = StorageManager.from(mContext); - return (sizeBytes <= storage.getStorageBytesUntilLow(Environment.getDataDirectory())); } int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { @@ -10166,46 +10193,46 @@ public class PackageManagerService extends IPackageManager.Stub { if (status != PackageManager.INSTALL_SUCCEEDED) { cleanUp(); return false; - } else { - final File targetDir = codeFile.getParentFile(); - final File beforeCodeFile = codeFile; - final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName); + } - Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile); - try { - Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath()); - } catch (ErrnoException e) { - Slog.d(TAG, "Failed to rename", e); - return false; - } + final File targetDir = codeFile.getParentFile(); + final File beforeCodeFile = codeFile; + final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName); - if (!SELinux.restoreconRecursive(afterCodeFile)) { - Slog.d(TAG, "Failed to restorecon"); - return false; - } + Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile); + try { + Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath()); + } catch (ErrnoException e) { + Slog.d(TAG, "Failed to rename", e); + return false; + } - // Reflect the rename internally - codeFile = afterCodeFile; - resourceFile = afterCodeFile; + if (!SELinux.restoreconRecursive(afterCodeFile)) { + Slog.d(TAG, "Failed to restorecon"); + return false; + } - // Reflect the rename in scanned details - pkg.codePath = afterCodeFile.getAbsolutePath(); - pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile, - pkg.baseCodePath); - pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile, - pkg.splitCodePaths); + // Reflect the rename internally + codeFile = afterCodeFile; + resourceFile = afterCodeFile; - // Reflect the rename in app info - pkg.applicationInfo.volumeUuid = pkg.volumeUuid; - pkg.applicationInfo.setCodePath(pkg.codePath); - pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath); - pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths); - pkg.applicationInfo.setResourcePath(pkg.codePath); - pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath); - pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths); + // Reflect the rename in scanned details + pkg.codePath = afterCodeFile.getAbsolutePath(); + pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile, + pkg.baseCodePath); + pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile, + pkg.splitCodePaths); - return true; - } + // Reflect the rename in app info + pkg.applicationInfo.volumeUuid = pkg.volumeUuid; + pkg.applicationInfo.setCodePath(pkg.codePath); + pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath); + pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths); + pkg.applicationInfo.setResourcePath(pkg.codePath); + pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath); + pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths); + + return true; } int doPostInstall(int status, int uid) { @@ -10225,11 +10252,6 @@ public class PackageManagerService extends IPackageManager.Stub { return (resourceFile != null) ? resourceFile.getAbsolutePath() : null; } - @Override - String getLegacyNativeLibraryPath() { - return (legacyNativeLibraryPath != null) ? legacyNativeLibraryPath.getAbsolutePath() : null; - } - private boolean cleanUp() { if (codeFile == null || !codeFile.exists()) { return false; @@ -10245,13 +10267,6 @@ public class PackageManagerService extends IPackageManager.Stub { resourceFile.delete(); } - if (legacyNativeLibraryPath != null && !FileUtils.contains(codeFile, legacyNativeLibraryPath)) { - if (!FileUtils.deleteContents(legacyNativeLibraryPath)) { - Slog.w(TAG, "Couldn't delete native library directory " + legacyNativeLibraryPath); - } - legacyNativeLibraryPath.delete(); - } - return true; } @@ -10315,11 +10330,10 @@ public class PackageManagerService extends IPackageManager.Stub { String cid; String packagePath; String resourcePath; - String legacyNativeLibraryDir; /** New install */ AsecInstallArgs(InstallParams params) { - super(params.origin, params.observer, params.installFlags, + super(params.origin, params.move, params.observer, params.installFlags, params.installerPackageName, params.volumeUuid, params.getManifestDigest(), params.getUser(), null /* instruction sets */, params.packageAbiOverride); } @@ -10327,7 +10341,7 @@ public class PackageManagerService extends IPackageManager.Stub { /** Existing install */ AsecInstallArgs(String fullCodePath, String[] instructionSets, boolean isExternal, boolean isForwardLocked) { - super(OriginInfo.fromNothing(), null, (isExternal ? INSTALL_EXTERNAL : 0) + super(OriginInfo.fromNothing(), null, null, (isExternal ? INSTALL_EXTERNAL : 0) | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null, instructionSets, null); // Hackily pretend we're still looking at a full code path @@ -10344,7 +10358,7 @@ public class PackageManagerService extends IPackageManager.Stub { } AsecInstallArgs(String cid, String[] instructionSets, boolean isForwardLocked) { - super(OriginInfo.fromNothing(), null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0) + super(OriginInfo.fromNothing(), null, null, (isAsecExternal(cid) ? INSTALL_EXTERNAL : 0) | (isForwardLocked ? INSTALL_FORWARD_LOCK : 0), null, null, null, null, instructionSets, null); this.cid = cid; @@ -10355,21 +10369,6 @@ public class PackageManagerService extends IPackageManager.Stub { cid = mInstallerService.allocateExternalStageCidLegacy(); } - boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { - final long sizeBytes = imcs.calculateInstalledSize(packagePath, isFwdLocked(), - abiOverride); - - final File target; - if (isExternalAsec()) { - target = new UserEnvironment(UserHandle.USER_OWNER).getExternalStorageDirectory(); - } else { - target = Environment.getDataDirectory(); - } - - final StorageManager storage = StorageManager.from(mContext); - return (sizeBytes <= storage.getStorageBytesUntilLow(target)); - } - int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { if (origin.staged) { Slog.d(TAG, origin.cid + " already staged; skipping copy"); @@ -10410,11 +10409,6 @@ public class PackageManagerService extends IPackageManager.Stub { return resourcePath; } - @Override - String getLegacyNativeLibraryPath() { - return legacyNativeLibraryDir; - } - int doPreInstall(int status) { if (status != PackageManager.INSTALL_SUCCEEDED) { // Destroy container @@ -10513,8 +10507,6 @@ public class PackageManagerService extends IPackageManager.Stub { packagePath = mountFile.getAbsolutePath(); resourcePath = packagePath; } - - legacyNativeLibraryDir = new File(mountFile, LIB_DIR_NAME).getAbsolutePath(); } int doPostInstall(int status, int uid) { @@ -10576,8 +10568,6 @@ public class PackageManagerService extends IPackageManager.Stub { removeDexFiles(allCodePaths, instructionSets); } - - String getPackageName() { return getAsecPackageName(cid); } @@ -10626,6 +10616,108 @@ public class PackageManagerService extends IPackageManager.Stub { } } + /** + * Logic to handle movement of existing installed applications. + */ + class MoveInstallArgs extends InstallArgs { + private File codeFile; + private File resourceFile; + + /** New install */ + MoveInstallArgs(InstallParams params) { + super(params.origin, params.move, params.observer, params.installFlags, + params.installerPackageName, params.volumeUuid, params.getManifestDigest(), + params.getUser(), null /* instruction sets */, params.packageAbiOverride); + } + + int copyApk(IMediaContainerService imcs, boolean temp) { + Slog.d(TAG, "Moving " + move.packageName + " from " + move.fromUuid + " to " + + move.toUuid); + synchronized (mInstaller) { + if (mInstaller.moveCompleteApp(move.fromUuid, move.toUuid, move.packageName, + move.dataAppName, move.appId, move.seinfo) != 0) { + return PackageManager.INSTALL_FAILED_INTERNAL_ERROR; + } + } + + codeFile = new File(Environment.getDataAppDirectory(move.toUuid), move.dataAppName); + resourceFile = codeFile; + Slog.d(TAG, "codeFile after move is " + codeFile); + + return PackageManager.INSTALL_SUCCEEDED; + } + + int doPreInstall(int status) { + if (status != PackageManager.INSTALL_SUCCEEDED) { + cleanUp(); + } + return status; + } + + boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) { + if (status != PackageManager.INSTALL_SUCCEEDED) { + cleanUp(); + return false; + } + + // Reflect the move in app info + pkg.applicationInfo.volumeUuid = pkg.volumeUuid; + pkg.applicationInfo.setCodePath(pkg.codePath); + pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath); + pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths); + pkg.applicationInfo.setResourcePath(pkg.codePath); + pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath); + pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths); + + return true; + } + + int doPostInstall(int status, int uid) { + if (status != PackageManager.INSTALL_SUCCEEDED) { + cleanUp(); + } + return status; + } + + @Override + String getCodePath() { + return (codeFile != null) ? codeFile.getAbsolutePath() : null; + } + + @Override + String getResourcePath() { + return (resourceFile != null) ? resourceFile.getAbsolutePath() : null; + } + + private boolean cleanUp() { + if (codeFile == null || !codeFile.exists()) { + return false; + } + + if (codeFile.isDirectory()) { + mInstaller.rmPackageDir(codeFile.getAbsolutePath()); + } else { + codeFile.delete(); + } + + if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) { + resourceFile.delete(); + } + + return true; + } + + void cleanUpResourcesLI() { + cleanUp(); + } + + boolean doPostDeleteLI(boolean delete) { + // XXX err, shouldn't we respect the delete flag? + cleanUpResourcesLI(); + return true; + } + } + static String getAsecPackageName(String packageCid) { int idx = packageCid.lastIndexOf("-"); if (idx == -1) { @@ -11012,7 +11104,6 @@ public class PackageManagerService extends IPackageManager.Stub { res.removedInfo.args = createInstallArgsForExisting(0, deletedPackage.applicationInfo.getCodePath(), deletedPackage.applicationInfo.getResourcePath(), - deletedPackage.applicationInfo.nativeLibraryRootDir, getAppDexInstructionSets(deletedPackage.applicationInfo)); } else { res.removedInfo.args = null; @@ -11320,8 +11411,10 @@ public class PackageManagerService extends IPackageManager.Stub { return; } - // If app directory is not writable, dexopt will be called after the rename - if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) { + if (args.move != null) { + // We did an in-place move, so dex is ready to roll + scanFlags |= SCAN_NO_DEX; + } else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) { // Enable SCAN_NO_DEX flag to skip dexopt at a later stage scanFlags |= SCAN_NO_DEX; // Run dexopt before old package gets removed, to minimize time when app is unavailable @@ -11704,7 +11797,7 @@ public class PackageManagerService extends IPackageManager.Stub { return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR; } - static class PackageRemovedInfo { + class PackageRemovedInfo { String removedPackage; int uid = -1; int removedAppId = -1; @@ -11949,8 +12042,7 @@ public class PackageManagerService extends IPackageManager.Stub { // Delete application code and resources if (deleteCodeAndResources && (outInfo != null)) { outInfo.args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps), - ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString, - getAppDexInstructionSets(ps)); + ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps)); if (DEBUG_SD_INSTALL) Slog.i(TAG, "args=" + outInfo.args); } return true; @@ -13119,8 +13211,8 @@ public class PackageManagerService extends IPackageManager.Stub { enforceCrossUserPermission(uid, userId, true, true, "stop package"); // writer synchronized (mPackages) { - if (mSettings.setPackageStoppedStateLPw(packageName, stopped, allowedByPermission, - uid, userId)) { + if (mSettings.setPackageStoppedStateLPw(this, packageName, stopped, + allowedByPermission, uid, userId)) { scheduleWritePackageRestrictionsLocked(userId); } } @@ -14180,17 +14272,16 @@ public class PackageManagerService extends IPackageManager.Stub { private void loadPrivatePackages(VolumeInfo vol) { final ArrayList<ApplicationInfo> loaded = new ArrayList<>(); final int parseFlags = mDefParseFlags | PackageParser.PARSE_EXTERNAL_STORAGE; + synchronized (mInstallLock) { synchronized (mPackages) { final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(vol.fsUuid); for (PackageSetting ps : packages) { - synchronized (mInstallLock) { - final PackageParser.Package pkg; - try { - pkg = scanPackageLI(ps.codePath, parseFlags, 0, 0, null); - loaded.add(pkg.applicationInfo); - } catch (PackageManagerException e) { - Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage()); - } + final PackageParser.Package pkg; + try { + pkg = scanPackageLI(ps.codePath, parseFlags, 0, 0, null); + loaded.add(pkg.applicationInfo); + } catch (PackageManagerException e) { + Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage()); } } @@ -14198,6 +14289,7 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.writeLPr(); } + } Slog.d(TAG, "Loaded packages " + loaded); sendResourcesChangedBroadcast(true, false, loaded, null); @@ -14205,24 +14297,25 @@ public class PackageManagerService extends IPackageManager.Stub { private void unloadPrivatePackages(VolumeInfo vol) { final ArrayList<ApplicationInfo> unloaded = new ArrayList<>(); + synchronized (mInstallLock) { synchronized (mPackages) { final List<PackageSetting> packages = mSettings.getVolumePackagesLPr(vol.fsUuid); for (PackageSetting ps : packages) { if (ps.pkg == null) continue; - synchronized (mInstallLock) { - final ApplicationInfo info = ps.pkg.applicationInfo; - final PackageRemovedInfo outInfo = new PackageRemovedInfo(); - if (deletePackageLI(ps.name, null, false, null, null, - PackageManager.DELETE_KEEP_DATA, outInfo, false)) { - unloaded.add(info); - } else { - Slog.w(TAG, "Failed to unload " + ps.codePath); - } + + final ApplicationInfo info = ps.pkg.applicationInfo; + final PackageRemovedInfo outInfo = new PackageRemovedInfo(); + if (deletePackageLI(ps.name, null, false, null, null, + PackageManager.DELETE_KEEP_DATA, outInfo, false)) { + unloaded.add(info); + } else { + Slog.w(TAG, "Failed to unload " + ps.codePath); } } mSettings.writeLPr(); } + } Slog.d(TAG, "Unloaded packages " + unloaded); sendResourcesChangedBroadcast(false, false, unloaded, null); @@ -14255,6 +14348,7 @@ public class PackageManagerService extends IPackageManager.Stub { private void movePackageInternal(final String packageName, final String volumeUuid, final int moveId) throws PackageManagerException { final UserHandle user = new UserHandle(UserHandle.getCallingUserId()); + final StorageManager storage = mContext.getSystemService(StorageManager.class); final PackageManager pm = mContext.getPackageManager(); final boolean currentAsec; @@ -14284,11 +14378,17 @@ public class PackageManagerService extends IPackageManager.Stub { "Package already moved to " + volumeUuid); } + final File probe = new File(pkg.codePath); + final File probeOat = new File(probe, "oat"); + if (!probe.isDirectory() || !probeOat.isDirectory()) { + throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR, + "Move only supported for modern cluster style installs"); + } + if (ps.frozen) { throw new PackageManagerException(MOVE_FAILED_OPERATION_PENDING, "Failed to move already frozen package"); } - ps.frozen = true; currentAsec = pkg.applicationInfo.isForwardLocked() @@ -14302,7 +14402,7 @@ public class PackageManagerService extends IPackageManager.Stub { label = String.valueOf(pm.getApplicationLabel(pkg.applicationInfo)); } - // Now that we're guarded by frozen state, kill app during upgrade + // Now that we're guarded by frozen state, kill app during move killApplication(packageName, appId, "move pkg"); final Bundle extras = new Bundle(); @@ -14311,16 +14411,18 @@ public class PackageManagerService extends IPackageManager.Stub { mMoveCallbacks.notifyCreated(moveId, extras); int installFlags; - final boolean moveData; + final boolean moveCompleteApp; + final File measurePath; if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { installFlags = INSTALL_INTERNAL; - moveData = !currentAsec; + moveCompleteApp = !currentAsec; + measurePath = Environment.getDataAppDirectory(volumeUuid); } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { installFlags = INSTALL_EXTERNAL; - moveData = false; + moveCompleteApp = false; + measurePath = storage.getPrimaryPhysicalVolume().getPath(); } else { - final StorageManager storage = mContext.getSystemService(StorageManager.class); final VolumeInfo volume = storage.findVolumeByUuid(volumeUuid); if (volume == null || volume.getType() != VolumeInfo.TYPE_PRIVATE || !volume.isMountedWritable()) { @@ -14332,26 +14434,38 @@ public class PackageManagerService extends IPackageManager.Stub { Preconditions.checkState(!currentAsec); installFlags = INSTALL_INTERNAL; - moveData = true; + moveCompleteApp = true; + measurePath = Environment.getDataAppDirectory(volumeUuid); } - Slog.d(TAG, "Moving " + packageName + " from " + currentVolumeUuid + " to " + volumeUuid); - mMoveCallbacks.notifyStatusChanged(moveId, 10); - - if (moveData) { - synchronized (mInstallLock) { - // TODO: split this into separate copy and delete operations - if (mInstaller.moveUserDataDirs(currentVolumeUuid, volumeUuid, packageName, appId, - seinfo) != 0) { - unfreezePackage(packageName); - throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR, - "Failed to move private data to " + volumeUuid); - } + final PackageStats stats = new PackageStats(null, -1); + synchronized (mInstaller) { + if (!getPackageSizeInfoLI(packageName, -1, stats)) { + unfreezePackage(packageName); + throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR, + "Failed to measure package size"); } } - mMoveCallbacks.notifyStatusChanged(moveId, 50); + Slog.d(TAG, "Measured code size " + stats.codeSize + ", data size " + stats.dataSize); + + final long startFreeBytes = measurePath.getFreeSpace(); + final long sizeBytes; + if (moveCompleteApp) { + sizeBytes = stats.codeSize + stats.dataSize; + } else { + sizeBytes = stats.codeSize; + } + + if (sizeBytes > storage.getStorageBytesUntilLow(measurePath)) { + unfreezePackage(packageName); + throw new PackageManagerException(MOVE_FAILED_INTERNAL_ERROR, + "Not enough free space to move"); + } + mMoveCallbacks.notifyStatusChanged(moveId, 10); + + final CountDownLatch installedLatch = new CountDownLatch(1); final IPackageInstallObserver2 installObserver = new IPackageInstallObserver2.Stub() { @Override public void onUserActionRequired(Intent intent) throws RemoteException { @@ -14364,6 +14478,8 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.d(TAG, "Install result for move: " + PackageManager.installStatusToString(returnCode, msg)); + installedLatch.countDown(); + // Regardless of success or failure of the move operation, // always unfreeze the package unfreezePackage(packageName); @@ -14386,13 +14502,40 @@ public class PackageManagerService extends IPackageManager.Stub { } }; - // Treat a move like reinstalling an existing app, which ensures that we - // process everythign uniformly, like unpacking native libraries. + final MoveInfo move; + if (moveCompleteApp) { + // Kick off a thread to report progress estimates + new Thread() { + @Override + public void run() { + while (true) { + try { + if (installedLatch.await(1, TimeUnit.SECONDS)) { + break; + } + } catch (InterruptedException ignored) { + } + + final long deltaFreeBytes = startFreeBytes - measurePath.getFreeSpace(); + final int progress = 10 + (int) MathUtils.constrain( + ((deltaFreeBytes * 80) / sizeBytes), 0, 80); + mMoveCallbacks.notifyStatusChanged(moveId, progress); + } + } + }.start(); + + final String dataAppName = codeFile.getName(); + move = new MoveInfo(moveId, currentVolumeUuid, volumeUuid, packageName, + dataAppName, appId, seinfo); + } else { + move = null; + } + installFlags |= PackageManager.INSTALL_REPLACE_EXISTING; final Message msg = mHandler.obtainMessage(INIT_COPY); final OriginInfo origin = OriginInfo.fromExistingFile(codeFile); - msg.obj = new InstallParams(origin, installObserver, installFlags, + msg.obj = new InstallParams(origin, move, installObserver, installFlags, installerPackageName, volumeUuid, null, user, packageAbiOverride); mHandler.sendMessage(msg); } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index f9c248de287a..d3bfdebe464f 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -985,6 +985,13 @@ final class Settings { if (ivi == null) { ivi = new IntentFilterVerificationInfo(packageName, domains); ps.setIntentFilterVerificationInfo(ivi); + Slog.d(PackageManagerService.TAG, + "Creating new IntentFilterVerificationInfo for packageName: " + packageName); + } else { + ivi.setDomains(domains); + Slog.d(PackageManagerService.TAG, + "Setting domains to existing IntentFilterVerificationInfo for packageName: " + + packageName + " and with domains: " + ivi.getDomainsString()); } return ivi; } @@ -1021,7 +1028,7 @@ final class Settings { // Then, if we set a ALWAYS status, then put NEVER status for Apps whose IntentFilter // domains overlap the domains of the current package - ArraySet<String> currentDomains = current.getIntentFilterVerificationInfo().getDomainsSet(); + ArraySet<String> currentDomains = current.getIntentFilterVerificationInfo().getDomains(); if (status == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) { for (PackageSetting ps : mPackages.values()) { if (ps == null || ps.pkg.packageName.equals(packageName)) continue; @@ -1029,7 +1036,7 @@ final class Settings { if (ivi == null) { continue; } - ArraySet<String> set = ivi.getDomainsSet(); + ArraySet<String> set = ivi.getDomains(); set.retainAll(currentDomains); if (set.size() > 0) { ps.setDomainVerificationStatusForUser( @@ -3603,8 +3610,8 @@ final class Settings { return pkg.getCurrentEnabledStateLPr(classNameStr, userId); } - boolean setPackageStoppedStateLPw(String packageName, boolean stopped, - boolean allowedByPermission, int uid, int userId) { + boolean setPackageStoppedStateLPw(PackageManagerService yucky, String packageName, + boolean stopped, boolean allowedByPermission, int uid, int userId) { int appId = UserHandle.getAppId(uid); final PackageSetting pkgSetting = mPackages.get(packageName); if (pkgSetting == null) { @@ -3628,7 +3635,7 @@ final class Settings { // pkgSetting.pkg.mSetStopped = stopped; if (pkgSetting.getNotLaunched(userId)) { if (pkgSetting.installerPackageName != null) { - PackageManagerService.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, + yucky.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH, pkgSetting.name, null, pkgSetting.installerPackageName, null, new int[] {userId}); } diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp index 3d8905d65a27..8f4fb51093d9 100644 --- a/services/core/jni/com_android_server_AssetAtlasService.cpp +++ b/services/core/jni/com_android_server_AssetAtlasService.cpp @@ -64,7 +64,8 @@ namespace android { static jboolean com_android_server_AssetAtlasService_upload(JNIEnv* env, jobject, jobject graphicBuffer, jobject bitmapHandle) { - SkBitmap& bitmap = *GraphicsJNI::getSkBitmapDeprecated(env, bitmapHandle); + SkBitmap bitmap; + GraphicsJNI::getSkBitmap(env, bitmapHandle, &bitmap); SkAutoLockPixels alp(bitmap); // The goal of this method is to copy the bitmap into the GraphicBuffer diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp index c50d63c58dd6..64514a9f72e8 100644 --- a/services/core/jni/com_android_server_SystemServer.cpp +++ b/services/core/jni/com_android_server_SystemServer.cpp @@ -25,7 +25,7 @@ namespace android { -static void android_server_SystemServer_nativeInit(JNIEnv* /* env */, jobject /* clazz */) { +static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jobject /* clazz */) { char propBuf[PROPERTY_VALUE_MAX]; property_get("system_init.startsensorservice", propBuf, "1"); if (strcmp(propBuf, "1") == 0) { @@ -39,7 +39,7 @@ static void android_server_SystemServer_nativeInit(JNIEnv* /* env */, jobject /* */ static JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ - { "nativeInit", "()V", (void*) android_server_SystemServer_nativeInit }, + { "startSensorService", "()V", (void*) android_server_SystemServer_startSensorService }, }; int register_android_server_SystemServer(JNIEnv* env) diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index f801d2d2fb2c..c886c4c6fe0d 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -4065,11 +4065,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); synchronized (this) { - if (mDeviceOwner != null) { - return mDeviceOwner.getDeviceOwnerName(); + if (mDeviceOwner == null || !mDeviceOwner.hasDeviceOwner()) { + return null; } + String deviceOwnerPackage = mDeviceOwner.getDeviceOwnerPackageName(); + return getApplicationLabel(deviceOwnerPackage, UserHandle.USER_OWNER); } - return null; } // Returns the active device owner or null if there is no device owner. @@ -4426,7 +4427,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (profileOwner == null) { return null; } - DevicePolicyData policy = getUserData(userHandle); final int n = policy.mAdminList.size(); for (int i = 0; i < n; i++) { @@ -4444,13 +4444,37 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return null; } mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null); + ComponentName profileOwner = getProfileOwner(userHandle); + if (profileOwner == null) { + return null; + } + return getApplicationLabel(profileOwner.getPackageName(), userHandle); + } - synchronized (this) { - if (mDeviceOwner != null) { - return mDeviceOwner.getProfileOwnerName(userHandle); + /** + * Canonical name for a given package. + */ + private String getApplicationLabel(String packageName, int userHandle) { + long token = Binder.clearCallingIdentity(); + try { + final Context userContext; + try { + UserHandle handle = new UserHandle(userHandle); + userContext = mContext.createPackageContextAsUser(packageName, 0, handle); + } catch (PackageManager.NameNotFoundException nnfe) { + Log.w(LOG_TAG, packageName + " is not installed for user " + userHandle, nnfe); + return null; + } + ApplicationInfo appInfo = userContext.getApplicationInfo(); + CharSequence result = null; + if (appInfo != null) { + PackageManager pm = userContext.getPackageManager(); + result = pm.getApplicationLabel(appInfo); } + return result != null ? result.toString() : null; + } finally { + Binder.restoreCallingIdentity(token); } - return null; } /** diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 29221305fc86..c1153b68304b 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -156,9 +156,9 @@ public final class SystemServer { private boolean mFirstBoot; /** - * Called to initialize native system services. + * Start the sensor service. */ - private static native void nativeInit(); + private static native void startSensorService(); /** * The main entry point from zygote. @@ -233,7 +233,6 @@ public final class SystemServer { // Initialize native services. System.loadLibrary("android_servers"); - nativeInit(); // Check whether we failed to shut down last time we tried. // This call may not return. @@ -359,6 +358,10 @@ public final class SystemServer { // Set up the Application instance for the system process and get started. mActivityManagerService.setSystemProcess(); + + // The sensor service needs access to package manager service, app ops + // service, and permissions service, therefore we start it after them. + startSensorService(); } /** diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index c2e61c691a6e..117cbe4b5bc2 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -28,6 +28,7 @@ import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStats; import android.app.usage.UsageStatsManagerInternal; import android.app.usage.UsageStatsManagerInternal.AppIdleStateChangeListener; +import android.appwidget.AppWidgetManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -56,6 +57,7 @@ import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.appwidget.IAppWidgetService; import com.android.internal.os.BackgroundThread; import com.android.internal.util.IndentingPrintWriter; import com.android.server.SystemConfig; @@ -96,6 +98,7 @@ public class UsageStatsService extends SystemService implements Handler mHandler; AppOpsManager mAppOps; UserManager mUserManager; + AppWidgetManager mAppWidgetManager; private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>(); private File mUsageStatsDir; @@ -158,6 +161,7 @@ public class UsageStatsService extends SystemService implements if (phase == PHASE_SYSTEM_SERVICES_READY) { // Observe changes to the threshold new SettingsObserver(mHandler).registerObserver(); + mAppWidgetManager = getContext().getSystemService(AppWidgetManager.class); } else if (phase == PHASE_BOOT_COMPLETED) { setAppIdleParoled(getContext().getSystemService(BatteryManager.class).isCharging()); } @@ -500,6 +504,11 @@ public class UsageStatsService extends SystemService implements return false; } + if (mAppWidgetManager != null + && mAppWidgetManager.isBoundWidgetPackage(packageName, userId)) { + return false; + } + final long lastUsed = getLastPackageAccessTime(packageName, userId); return hasPassedIdleDuration(lastUsed); } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 81c5e6afbea5..56b2fa53d37b 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -734,10 +734,10 @@ public class VoiceInteractionManagerService extends SystemService { } @Override - public boolean activeServiceSupportsAssistGesture() { + public boolean activeServiceSupportsAssist() { enforceCallingPermission(Manifest.permission.ACCESS_VOICE_INTERACTION_SERVICE); synchronized (this) { - return mImpl != null && mImpl.mInfo.getSupportsAssistGesture(); + return mImpl != null && mImpl.mInfo.getSupportsAssist(); } } diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 9273939b8cc4..fee64951e3e3 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -132,7 +132,7 @@ public final class Call { /** * @hide */ - public static final int CAPABILITY_UNUSED = 0x00000010; + public static final int CAPABILITY_UNUSED_1 = 0x00000010; /** Call supports responding via text option. */ public static final int CAPABILITY_RESPOND_VIA_TEXT = 0x00000020; @@ -189,46 +189,55 @@ public final class Call { public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000; /** - * Whether the call is a generic conference, where we do not know the precise state of - * participants in the conference (eg. on CDMA). + * Speed up audio setup for MT call. + * @hide */ - public static final int CAPABILITY_GENERIC_CONFERENCE = 0x00004000; + public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000; /** - * Call is using high definition audio. + * Call can be upgraded to a video call. + * @hide */ - public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00008000; + public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000; /** - * Call is using WIFI. + * For video calls, indicates whether the outgoing video for the call can be paused using + * the {@link android.telecom.VideoProfile.VideoState#PAUSED} VideoState. */ - public static final int CAPABILITY_WIFI = 0x00010000; + public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000; + + //****************************************************************************************** + // Next CAPABILITY value: 0x00004000 + //****************************************************************************************** /** - * Indicates that the current device callback number should be shown. + * Whether the call is currently a conference. */ - public static final int CAPABILITY_SHOW_CALLBACK_NUMBER = 0x00020000; + public static final int PROPERTY_CONFERENCE = 0x00000001; /** - * Speed up audio setup for MT call. - * @hide + * Whether the call is a generic conference, where we do not know the precise state of + * participants in the conference (eg. on CDMA). */ - public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000; + public static final int PROPERTY_GENERIC_CONFERENCE = 0x00000002; /** - * Call can be upgraded to a video call. - * @hide + * Whether the call is made while the device is in emergency callback mode. */ - public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000; + public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 0x00000004; /** - * For video calls, indicates whether the outgoing video for the call can be paused using - * the {@link android.telecom.VideoProfile.VideoState#PAUSED} VideoState. + * Connection is using WIFI. */ - public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000; + public static final int PROPERTY_WIFI = 0x00000008; + + /** + * Call is using high definition audio. + */ + public static final int PROPERTY_HIGH_DEF_AUDIO = 0x00000010; //****************************************************************************************** - // Next CAPABILITY value: 0x00200000 + // Next PROPERTY value: 0x00000020 //****************************************************************************************** private final Uri mHandle; @@ -314,18 +323,6 @@ public final class Call { if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) { builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL"); } - if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) { - builder.append(" CAPABILITY_HIGH_DEF_AUDIO"); - } - if (can(capabilities, CAPABILITY_WIFI)) { - builder.append(" CAPABILITY_WIFI"); - } - if (can(capabilities, CAPABILITY_GENERIC_CONFERENCE)) { - builder.append(" CAPABILITY_GENERIC_CONFERENCE"); - } - if (can(capabilities, CAPABILITY_SHOW_CALLBACK_NUMBER)) { - builder.append(" CAPABILITY_SHOW_CALLBACK_NUMBER"); - } if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) { builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO"); } @@ -340,6 +337,55 @@ public final class Call { } /** + * Whether the supplied properties includes the specified property. + * + * @param properties A bit field of properties. + * @param property The property to check properties for. + * @return Whether the specified property is supported. + */ + public static boolean hasProperty(int properties, int property) { + return (properties & property) != 0; + } + + /** + * Whether the properties of this {@code Details} includes the specified property. + * + * @param property The property to check properties for. + * @return Whether the specified property is supported. + */ + public boolean hasProperty(int property) { + return hasProperty(mCallProperties, property); + } + + /** + * Render a set of property bits ({@code PROPERTY_*}) as a human readable string. + * + * @param properties A property bit field. + * @return A human readable string representation. + */ + public static String propertiesToString(int properties) { + StringBuilder builder = new StringBuilder(); + builder.append("[Properties:"); + if (hasProperty(properties, PROPERTY_CONFERENCE)) { + builder.append(" PROPERTY_CONFERENCE"); + } + if (hasProperty(properties, PROPERTY_GENERIC_CONFERENCE)) { + builder.append(" PROPERTY_GENERIC_CONFERENCE"); + } + if (hasProperty(properties, PROPERTY_WIFI)) { + builder.append(" PROPERTY_WIFI"); + } + if (hasProperty(properties, PROPERTY_HIGH_DEF_AUDIO)) { + builder.append(" PROPERTY_HIGH_DEF_AUDIO"); + } + if (hasProperty(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) { + builder.append(" EMERGENCY_CALLBACK_MODE"); + } + builder.append("]"); + return builder.toString(); + } + + /** * @return The handle (e.g., phone number) to which the {@code Call} is currently * connected. */ @@ -387,8 +433,8 @@ public final class Call { } /** - * @return A bitmask of the properties of the {@code Call}, as defined in - * {@link CallProperties}. + * @return A bitmask of the properties of the {@code Call}, as defined by the various + * {@code PROPERTY_*} constants in this class. */ public int getCallProperties() { return mCallProperties; diff --git a/telecomm/java/android/telecom/CallProperties.java b/telecomm/java/android/telecom/CallProperties.java deleted file mode 100644 index 1721a392eff8..000000000000 --- a/telecomm/java/android/telecom/CallProperties.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package android.telecom; - -/** - * Defines properties of a phone call which may be affected by changes to the call. - */ -public class CallProperties { - /** Call is currently in a conference call. */ - public static final int CONFERENCE = 0x00000001; -} diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index cdecb3383379..303a4920530f 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -1104,6 +1104,58 @@ public class ServiceState implements Parcelable { || radioTechnology == RIL_RADIO_TECHNOLOGY_EHRPD; } + /** @hide */ + public static boolean hasCdma(int radioTechnologyBitmask) { + int cdmaBitmask = (RIL_RADIO_TECHNOLOGY_IS95A + | RIL_RADIO_TECHNOLOGY_IS95B + | RIL_RADIO_TECHNOLOGY_1xRTT + | RIL_RADIO_TECHNOLOGY_EVDO_0 + | RIL_RADIO_TECHNOLOGY_EVDO_A + | RIL_RADIO_TECHNOLOGY_EVDO_B + | RIL_RADIO_TECHNOLOGY_EHRPD); + + return ((radioTechnologyBitmask & cdmaBitmask) != 0); + } + + /** @hide */ + public static boolean bitmaskHasTech(int bearerBitmask, int radioTech) { + if (bearerBitmask == 0) { + return true; + } else if (radioTech >= 1) { + return ((bearerBitmask & (1 << (radioTech - 1))) != 0); + } + return false; + } + + /** @hide */ + public static int getBitmaskForTech(int radioTech) { + if (radioTech >= 1) { + return (1 << (radioTech - 1)); + } + return 0; + } + + /** @hide */ + public static int getBitmaskFromString(String bearerList) { + String[] bearers = bearerList.split("\\|"); + int bearerBitmask = 0; + for (String bearer : bearers) { + int bearerInt = 0; + try { + bearerInt = Integer.parseInt(bearer.trim()); + } catch (NumberFormatException nfe) { + return 0; + } + + if (bearerInt == 0) { + return 0; + } + + bearerBitmask |= getBitmaskForTech(bearerInt); + } + return bearerBitmask; + } + /** * Returns a merged ServiceState consisting of the base SS with voice settings from the * voice SS. The voice SS is only used if it is IN_SERVICE (otherwise the base SS is returned). diff --git a/tests/Assist/res/xml/interaction_service.xml b/tests/Assist/res/xml/interaction_service.xml index 2fd50aae105f..f56156e53fb2 100644 --- a/tests/Assist/res/xml/interaction_service.xml +++ b/tests/Assist/res/xml/interaction_service.xml @@ -18,4 +18,4 @@ <voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android" android:sessionService="com.android.test.assist.AssistInteractionSessionService" android:recognitionService="com.android.test.assist.AssistRecognitionService" - android:supportsAssistGesture="true"/> + android:supportsAssist="true"/> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionActivity.java index 5ba3ad96b3a9..be5d7f98783d 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ProjectionActivity.java @@ -46,9 +46,9 @@ public class ProjectionActivity extends Activity { } private void setProject(boolean value) { - RenderNode displayList = getDisplayList(); - if (displayList != null) { - displayList.setProjectBackwards(value); + RenderNode renderNode = updateDisplayListIfDirty(); + if (renderNode != null) { + renderNode.setProjectBackwards(value); } // NOTE: we can't invalidate ProjectedView for the redraw because: // 1) the view won't preserve displayList properties that it doesn't know about diff --git a/tests/VoiceInteraction/res/xml/interaction_service.xml b/tests/VoiceInteraction/res/xml/interaction_service.xml index 789493a7f22c..c015ad232fae 100644 --- a/tests/VoiceInteraction/res/xml/interaction_service.xml +++ b/tests/VoiceInteraction/res/xml/interaction_service.xml @@ -21,4 +21,4 @@ android:sessionService="com.android.test.voiceinteraction.MainInteractionSessionService" android:recognitionService="com.android.test.voiceinteraction.MainRecognitionService" android:settingsActivity="com.android.test.voiceinteraction.SettingsActivity" - android:supportsAssistGesture="true" /> + android:supportsAssist="true" /> diff --git a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java index 30d204f21179..4098b989a16b 100644 --- a/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java +++ b/tests/permission/src/com/android/framework/permission/tests/ServiceManagerPermissionTests.java @@ -57,6 +57,11 @@ public class ServiceManagerPermissionTests extends TestCase { public String[] getPackagesForUid(int uid) { return new String[0]; } + + @Override + public boolean isRuntimePermission(String permission) { + return false; + } }; ServiceManagerNative.asInterface(BinderInternal.getContextObject()) .setPermissionController(pc); diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java index 7f1e977f5b49..4072302be67e 100644 --- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java +++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java @@ -164,10 +164,6 @@ public class LayoutInflater_Delegate { com.android.internal.R.styleable.Include_id, View.NO_ID); final int visibility = a.getInt( com.android.internal.R.styleable.Include_visibility, -1); - final boolean hasWidth = a.hasValue( - com.android.internal.R.styleable.Include_layout_width); - final boolean hasHeight = a.hasValue( - com.android.internal.R.styleable.Include_layout_height); a.recycle(); // We try to load the layout params set in the <include /> tag. If @@ -179,19 +175,17 @@ public class LayoutInflater_Delegate { // successfully loaded layout params from the <include /> tag, // false means we need to rely on the included layout params. ViewGroup.LayoutParams params = null; - if (hasWidth && hasHeight) { - try { - // ---- START CHANGES - sIsInInclude = true; - // ---- END CHANGES - - params = group.generateLayoutParams(attrs); - - } finally { - // ---- START CHANGES - sIsInInclude = false; - // ---- END CHANGES - } + try { + // ---- START CHANGES + sIsInInclude = true; + // ---- END CHANGES + + params = group.generateLayoutParams(attrs); + + } finally { + // ---- START CHANGES + sIsInInclude = false; + // ---- END CHANGES } if (params == null) { params = group.generateLayoutParams(childAttrs); |