diff options
85 files changed, 2784 insertions, 362 deletions
@@ -199,6 +199,8 @@ LOCAL_SRC_FILES += \ core/java/android/service/dreams/IDozeHardware.aidl \ core/java/android/service/dreams/IDreamManager.aidl \ core/java/android/service/dreams/IDreamService.aidl \ + core/java/android/service/fingerprint/IFingerprintService.aidl \ + core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl \ core/java/android/service/trust/ITrustAgentService.aidl \ core/java/android/service/trust/ITrustAgentServiceCallback.aidl \ core/java/android/service/voice/IVoiceInteractionService.aidl \ diff --git a/api/current.txt b/api/current.txt index cca12be..1c9f871 100644 --- a/api/current.txt +++ b/api/current.txt @@ -314,6 +314,7 @@ package android { field public static final int autoCompleteTextViewStyle = 16842859; // 0x101006b field public static final int autoLink = 16842928; // 0x10100b0 field public static final int autoMirrored = 16843754; // 0x10103ea + field public static final int autoRemoveFromRecents = 16843859; // 0x1010453 field public static final int autoStart = 16843445; // 0x10102b5 field public static final deprecated int autoText = 16843114; // 0x101016a field public static final int autoUrlDetect = 16843404; // 0x101028c @@ -463,6 +464,7 @@ package android { field public static final int dividerHorizontal = 16843564; // 0x101032c field public static final int dividerPadding = 16843562; // 0x101032a field public static final int dividerVertical = 16843530; // 0x101030a + field public static final int documentLaunchMode = 16843858; // 0x1010452 field public static final int drawSelectorOnTop = 16843004; // 0x10100fc field public static final int drawable = 16843161; // 0x1010199 field public static final int drawableBottom = 16843118; // 0x101016e @@ -5013,6 +5015,8 @@ package android.app.admin { method public void clearForwardingIntentFilters(android.content.ComponentName); method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String); method public void clearUserRestriction(android.content.ComponentName, java.lang.String); + method public void enableSystemApp(android.content.ComponentName, java.lang.String); + method public int enableSystemApp(android.content.ComponentName, android.content.Intent); method public java.util.List<android.content.ComponentName> getActiveAdmins(); method public android.os.Bundle getApplicationRestrictions(android.content.ComponentName, java.lang.String); method public boolean getCameraDisabled(android.content.ComponentName); @@ -6614,6 +6618,7 @@ package android.content { field public static final java.lang.String DISPLAY_SERVICE = "display"; field public static final java.lang.String DOWNLOAD_SERVICE = "download"; field public static final java.lang.String DROPBOX_SERVICE = "dropbox"; + field public static final java.lang.String FINGERPRINT_SERVICE = "fingerprint"; field public static final java.lang.String HDMI_CEC_SERVICE = "hdmi_cec"; field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method"; field public static final java.lang.String INPUT_SERVICE = "input"; @@ -7149,6 +7154,7 @@ package android.content { field public static final int FILL_IN_PACKAGE = 16; // 0x10 field public static final int FILL_IN_SELECTOR = 64; // 0x40 field public static final int FILL_IN_SOURCE_BOUNDS = 32; // 0x20 + field public static final int FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000 field public static final int FLAG_ACTIVITY_BROUGHT_TO_FRONT = 4194304; // 0x400000 field public static final int FLAG_ACTIVITY_CLEAR_TASK = 32768; // 0x8000 field public static final int FLAG_ACTIVITY_CLEAR_TOP = 67108864; // 0x4000000 @@ -7667,8 +7673,12 @@ package android.content.pm { field public static final int CONFIG_TOUCHSCREEN = 8; // 0x8 field public static final int CONFIG_UI_MODE = 512; // 0x200 field public static final android.os.Parcelable.Creator CREATOR; + field public static final int DOCUMENT_LAUNCH_ALWAYS = 2; // 0x2 + field public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1; // 0x1 + field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0 field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40 field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8 + field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000 field public static final int FLAG_CLEAR_TASK_ON_LAUNCH = 4; // 0x4 field public static final int FLAG_EXCLUDE_FROM_RECENTS = 32; // 0x20 field public static final int FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS = 256; // 0x100 @@ -7677,6 +7687,7 @@ package android.content.pm { field public static final int FLAG_IMMERSIVE = 2048; // 0x800 field public static final int FLAG_MULTIPROCESS = 1; // 0x1 field public static final int FLAG_NO_HISTORY = 128; // 0x80 + field public static final int FLAG_PERSISTABLE = 4096; // 0x1000 field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000 field public static final int FLAG_STATE_NOT_NEEDED = 16; // 0x10 field public static final int LAUNCH_MULTIPLE = 0; // 0x0 @@ -7701,6 +7712,7 @@ package android.content.pm { field public static final int SCREEN_ORIENTATION_USER_PORTRAIT = 12; // 0xc field public static final int UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW = 1; // 0x1 field public int configChanges; + field public int documentLaunchMode; field public int flags; field public int launchMode; field public java.lang.String parentActivityName; @@ -10734,6 +10746,7 @@ package android.graphics { method public void releaseTexImage(); method public void setDefaultBufferSize(int, int); method public void setOnFrameAvailableListener(android.graphics.SurfaceTexture.OnFrameAvailableListener); + method public void setOnFrameAvailableListener(android.graphics.SurfaceTexture.OnFrameAvailableListener, android.os.Handler); method public void updateTexImage(); } @@ -12171,17 +12184,29 @@ package android.hardware.camera2 { method public int getSequenceId(); field public static final android.hardware.camera2.CameraMetadata.Key BLACK_LEVEL_LOCK; field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_GAINS; + field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_MODE; field public static final android.hardware.camera2.CameraMetadata.Key COLOR_CORRECTION_TRANSFORM; + field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_ANTIBANDING_MODE; + field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_EXPOSURE_COMPENSATION; + field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_LOCK; field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_MODE; + field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_PRECAPTURE_TRIGGER; field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_REGIONS; field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_STATE; + field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AE_TARGET_FPS_RANGE; field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_MODE; field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_REGIONS; field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_STATE; + field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AF_TRIGGER; + field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_LOCK; field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_MODE; field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_REGIONS; field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_AWB_STATE; + field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_CAPTURE_INTENT; + field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_EFFECT_MODE; field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_MODE; + field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_SCENE_MODE; + field public static final android.hardware.camera2.CameraMetadata.Key CONTROL_VIDEO_STABILIZATION_MODE; field public static final android.hardware.camera2.CameraMetadata.Key EDGE_MODE; field public static final android.hardware.camera2.CameraMetadata.Key FLASH_MODE; field public static final android.hardware.camera2.CameraMetadata.Key FLASH_STATE; @@ -12212,6 +12237,7 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_PROFILE_TONE_CURVE; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_SENSITIVITY; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEMPERATURE; + field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_DATA; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TEST_PATTERN_MODE; field public static final android.hardware.camera2.CameraMetadata.Key SENSOR_TIMESTAMP; field public static final android.hardware.camera2.CameraMetadata.Key SHADING_MODE; @@ -12220,6 +12246,7 @@ package android.hardware.camera2 { field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_HOT_PIXEL_MAP; field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_HOT_PIXEL_MAP_MODE; field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_LENS_SHADING_MAP; + field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_LENS_SHADING_MAP_MODE; field public static final android.hardware.camera2.CameraMetadata.Key STATISTICS_SCENE_FLICKER; field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_BLUE; field public static final android.hardware.camera2.CameraMetadata.Key TONEMAP_CURVE_GREEN; @@ -20493,9 +20520,17 @@ package android.os { method public void setUserRestriction(java.lang.String, boolean); method public void setUserRestrictions(android.os.Bundle); method public void setUserRestrictions(android.os.Bundle, android.os.UserHandle); + field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user"; + field public static final java.lang.String DISALLOW_CONFIG_APPS = "no_config_apps"; field public static final java.lang.String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth"; + field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts"; field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials"; + field public static final java.lang.String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks"; + field public static final java.lang.String DISALLOW_CONFIG_TETHERING = "no_config_tethering"; + field public static final java.lang.String DISALLOW_CONFIG_VPN = "no_config_vpn"; field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi"; + field public static final java.lang.String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features"; + field public static final java.lang.String DISALLOW_FACTORY_RESET = "no_factory_reset"; field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps"; field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources"; field public static final java.lang.String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts"; @@ -20503,6 +20538,7 @@ package android.os { field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location"; field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps"; field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer"; + field public static final java.lang.String ENSURE_VERIFY_APPS = "ensure_verify_apps"; } public abstract class Vibrator { @@ -25047,6 +25083,36 @@ package android.service.dreams { } +package android.service.fingerprint { + + public class FingerprintManager { + ctor public FingerprintManager(android.content.Context); + method public void enroll(long); + method public void remove(int); + method public void startListening(android.service.fingerprint.FingerprintManagerReceiver); + method public void stopListening(); + field protected static final boolean DEBUG = true; + field public static final int FINGERPRINT_ERROR = -1; // 0xffffffff + field public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2; // 0x2 + field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1 + field public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10; // 0xfffffff6 + field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4 + field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3 + field public static final int FINGERPRINT_SCANNED = 1; // 0x1 + field public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2; // 0x2 + field public static final int FINGERPRINT_TEMPLATE_REMOVED = 4; // 0x4 + } + + public class FingerprintManagerReceiver { + ctor public FingerprintManagerReceiver(); + method public void onEnrollResult(int, int); + method public void onError(int); + method public void onRemoved(int); + method public void onScanned(int, int); + } + +} + package android.service.notification { public abstract class NotificationListenerService extends android.app.Service { @@ -31584,26 +31650,17 @@ package android.view { method public abstract void onFocusLost(android.view.WindowId); } - public class WindowInsets { + public final class WindowInsets { ctor public WindowInsets(android.view.WindowInsets); - method public android.view.WindowInsets cloneWithSystemWindowInsets(int, int, int, int); - method public android.view.WindowInsets cloneWithSystemWindowInsetsConsumed(); - method public android.view.WindowInsets cloneWithSystemWindowInsetsConsumed(boolean, boolean, boolean, boolean); - method public android.view.WindowInsets cloneWithWindowDecorInsets(int, int, int, int); - method public android.view.WindowInsets cloneWithWindowDecorInsetsConsumed(); - method public android.view.WindowInsets cloneWithWindowDecorInsetsConsumed(boolean, boolean, boolean, boolean); + method public android.view.WindowInsets consumeSystemWindowInsets(); method public int getSystemWindowInsetBottom(); method public int getSystemWindowInsetLeft(); method public int getSystemWindowInsetRight(); method public int getSystemWindowInsetTop(); - method public int getWindowDecorInsetBottom(); - method public int getWindowDecorInsetLeft(); - method public int getWindowDecorInsetRight(); - method public int getWindowDecorInsetTop(); method public boolean hasInsets(); method public boolean hasSystemWindowInsets(); - method public boolean hasWindowDecorInsets(); method public boolean isRound(); + method public android.view.WindowInsets replaceSystemWindowInsets(int, int, int, int); } public abstract interface WindowManager implements android.view.ViewManager { @@ -32930,6 +32987,16 @@ package android.webkit { method public boolean hasMimeType(java.lang.String); } + public abstract interface PermissionRequest { + method public abstract void deny(); + method public abstract android.net.Uri getOrigin(); + method public abstract long getResources(); + method public abstract void grant(long); + field public static final long RESOURCE_AUDIO_CAPTURE = 4L; // 0x4L + field public static final long RESOURCE_GEOLOCATION = 1L; // 0x1L + field public static final long RESOURCE_VIDEO_CAPTURE = 2L; // 0x2L + } + public abstract interface PluginStub { method public abstract android.view.View getEmbeddedView(int, android.content.Context); method public abstract android.view.View getFullScreenView(int, android.content.Context); @@ -32989,6 +33056,8 @@ package android.webkit { method public boolean onJsConfirm(android.webkit.WebView, java.lang.String, java.lang.String, android.webkit.JsResult); method public boolean onJsPrompt(android.webkit.WebView, java.lang.String, java.lang.String, java.lang.String, android.webkit.JsPromptResult); method public deprecated boolean onJsTimeout(); + method public void onPermissionRequest(android.webkit.PermissionRequest); + method public void onPermissionRequestCanceled(android.webkit.PermissionRequest); method public void onProgressChanged(android.webkit.WebView, int); method public deprecated void onReachedMaxAppCacheSize(long, long, android.webkit.WebStorage.QuotaUpdater); method public void onReceivedIcon(android.webkit.WebView, android.graphics.Bitmap); @@ -33276,6 +33345,7 @@ package android.webkit { method public boolean pageUp(boolean); method public void pauseTimers(); method public void postUrl(java.lang.String, byte[]); + method public void preauthorizePermission(android.net.Uri, long); method public void reload(); method public void removeJavascriptInterface(java.lang.String); method public void requestFocusNodeHref(android.os.Message); @@ -49559,6 +49629,7 @@ package javax.net.ssl { method public abstract boolean getEnableSessionCreation(); method public abstract java.lang.String[] getEnabledCipherSuites(); method public abstract java.lang.String[] getEnabledProtocols(); + method public javax.net.ssl.SSLSession getHandshakeSession(); method public abstract javax.net.ssl.SSLEngineResult.HandshakeStatus getHandshakeStatus(); method public abstract boolean getNeedClientAuth(); method public java.lang.String getPeerHost(); @@ -49632,10 +49703,12 @@ package javax.net.ssl { ctor public SSLParameters(java.lang.String[]); ctor public SSLParameters(java.lang.String[], java.lang.String[]); method public java.lang.String[] getCipherSuites(); + method public java.lang.String getEndpointIdentificationAlgorithm(); method public boolean getNeedClientAuth(); method public java.lang.String[] getProtocols(); method public boolean getWantClientAuth(); method public void setCipherSuites(java.lang.String[]); + method public void setEndpointIdentificationAlgorithm(java.lang.String); method public void setNeedClientAuth(boolean); method public void setProtocols(java.lang.String[]); method public void setWantClientAuth(boolean); @@ -49736,6 +49809,7 @@ package javax.net.ssl { method public abstract boolean getEnableSessionCreation(); method public abstract java.lang.String[] getEnabledCipherSuites(); method public abstract java.lang.String[] getEnabledProtocols(); + method public javax.net.ssl.SSLSession getHandshakeSession(); method public abstract boolean getNeedClientAuth(); method public javax.net.ssl.SSLParameters getSSLParameters(); method public abstract javax.net.ssl.SSLSession getSession(); @@ -49791,6 +49865,14 @@ package javax.net.ssl { method public java.lang.String chooseEngineServerAlias(java.lang.String, java.security.Principal[], javax.net.ssl.SSLEngine); } + public abstract class X509ExtendedTrustManager implements javax.net.ssl.X509TrustManager { + ctor public X509ExtendedTrustManager(); + method public abstract void checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String, java.net.Socket) throws java.security.cert.CertificateException; + method public abstract void checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String, javax.net.ssl.SSLEngine) throws java.security.cert.CertificateException; + method public abstract void checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, java.net.Socket) throws java.security.cert.CertificateException; + method public abstract void checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String, javax.net.ssl.SSLEngine) throws java.security.cert.CertificateException; + } + public abstract interface X509KeyManager implements javax.net.ssl.KeyManager { method public abstract java.lang.String chooseClientAlias(java.lang.String[], java.security.Principal[], java.net.Socket); method public abstract java.lang.String chooseServerAlias(java.lang.String, java.security.Principal[], java.net.Socket); diff --git a/core/java/android/animation/TypeEvaluator.java b/core/java/android/animation/TypeEvaluator.java index 2640457..429c435 100644 --- a/core/java/android/animation/TypeEvaluator.java +++ b/core/java/android/animation/TypeEvaluator.java @@ -29,7 +29,7 @@ public interface TypeEvaluator<T> { /** * This function returns the result of linearly interpolating the start and end values, with * <code>fraction</code> representing the proportion between the start and end values. The - * calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>, + * calculation is a simple parametric calculation: <code>result = x0 + t * (x1 - x0)</code>, * where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>, * and <code>t</code> is <code>fraction</code>. * diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index fe532bf..c621696 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -106,6 +106,9 @@ import android.os.storage.IMountService; import android.os.storage.StorageManager; import android.print.IPrintManager; import android.print.PrintManager; +import android.service.fingerprint.FingerprintManager; +import android.service.fingerprint.FingerprintManagerReceiver; +import android.service.fingerprint.FingerprintService; import android.telephony.TelephonyManager; import android.tv.ITvInputManager; import android.tv.TvInputManager; @@ -451,6 +454,11 @@ class ContextImpl extends Context { return new KeyguardManager(); }}); + registerService(FINGERPRINT_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + return new FingerprintManager(ctx); + }}); + registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext()); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 929bf65..b24b932 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -22,6 +22,7 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; @@ -2041,4 +2042,43 @@ public class DevicePolicyManager { } } } + + /** + * Called by profile or device owner to re-enable a system app that was disabled by default + * when the managed profile was created. This should only be called from a profile or device + * owner running within a managed profile. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param packageName The package to be re-enabled in the current profile. + */ + public void enableSystemApp(ComponentName admin, String packageName) { + if (mService != null) { + try { + mService.enableSystemApp(admin, packageName); + } catch (RemoteException e) { + Log.w(TAG, "Failed to install package: " + packageName); + } + } + } + + /** + * Called by profile or device owner to re-enable system apps by intent that were disabled + * by default when the managed profile was created. This should only be called from a profile + * or device owner running within a managed profile. + * + * @param admin Which {@link DeviceAdminReceiver} this request is associated with. + * @param intent An intent matching the app(s) to be installed. All apps that resolve for this + * intent will be re-enabled in the current profile. + * @returns int The number of activities that matched the intent and were installed. + */ + public int enableSystemApp(ComponentName admin, Intent intent) { + if (mService != null) { + try { + return mService.enableSystemAppWithIntent(admin, intent); + } catch (RemoteException e) { + Log.w(TAG, "Failed to install packages matching filter: " + intent); + } + } + return 0; + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index e3090b6..b30f1b9 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -18,6 +18,7 @@ package android.app.admin; import android.content.ComponentName; +import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.RemoteCallback; @@ -122,4 +123,7 @@ interface IDevicePolicyManager { void setUserRestriction(in ComponentName who, in String key, boolean enable); void addForwardingIntentFilter(in ComponentName admin, in IntentFilter filter, int flags); void clearForwardingIntentFilters(in ComponentName admin); + + void enableSystemApp(in ComponentName admin, in String packageName); + int enableSystemAppWithIntent(in ComponentName admin, in Intent intent); } diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index a396a05..7f8d0ab 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -512,6 +512,25 @@ public final class BluetoothDevice implements Parcelable { */ public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID"; + /** + * No preferrence of physical transport for GATT connections to remote dual-mode devices + * @hide + */ + public static final int TRANSPORT_AUTO = 0; + + /** + * Prefer BR/EDR transport for GATT connections to remote dual-mode devices + * @hide + */ + public static final int TRANSPORT_BREDR = 1; + + /** + * Prefer LE transport for GATT connections to remote dual-mode devices + * @hide + */ + public static final int TRANSPORT_LE = 2; + + /** * Lazy initialization. Guaranteed final after first object constructed, or * getService() called. @@ -1216,6 +1235,27 @@ public final class BluetoothDevice implements Parcelable { */ public BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback) { + return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO)); + } + + /** + * Connect to GATT Server hosted by this device. Caller acts as GATT client. + * The callback is used to deliver results to Caller, such as connection status as well + * as any further GATT client operations. + * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct + * GATT client operations. + * @param callback GATT callback handler that will receive asynchronous callbacks. + * @param autoConnect Whether to directly connect to the remote device (false) + * or to automatically connect as soon as the remote + * device becomes available (true). + * @param transport preferred transport for GATT connections to remote dual-mode devices + * {@link BluetoothDevice#TRANSPORT_AUTO} or + * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @throws IllegalArgumentException if callback is null + * @hide + */ + public BluetoothGatt connectGatt(Context context, boolean autoConnect, + BluetoothGattCallback callback, int transport) { // TODO(Bluetooth) check whether platform support BLE // Do the check here or in GattServer? BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); @@ -1226,10 +1266,11 @@ public final class BluetoothDevice implements Parcelable { // BLE is not supported return null; } - BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this); + BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this, transport); gatt.connect(autoConnect, callback); return gatt; } catch (RemoteException e) {Log.e(TAG, "", e);} return null; } + } diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java index ff3af7c..601d9ee 100644 --- a/core/java/android/bluetooth/BluetoothGatt.java +++ b/core/java/android/bluetooth/BluetoothGatt.java @@ -51,6 +51,7 @@ public final class BluetoothGatt implements BluetoothProfile { private int mConnState; private final Object mStateLock = new Object(); private Boolean mDeviceBusy = false; + private int mTransport; private static final int CONN_STATE_IDLE = 0; private static final int CONN_STATE_CONNECTING = 1; @@ -135,7 +136,7 @@ public final class BluetoothGatt implements BluetoothProfile { } try { mService.clientConnect(mClientIf, mDevice.getAddress(), - !mAutoConnect); // autoConnect is inverse of "isDirect" + !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect" } catch (RemoteException e) { Log.e(TAG,"",e); } @@ -600,10 +601,12 @@ public final class BluetoothGatt implements BluetoothProfile { } }; - /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) { + /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device, + int transport) { mContext = context; mService = iGatt; mDevice = device; + mTransport = transport; mServices = new ArrayList<BluetoothGattService>(); mConnState = CONN_STATE_IDLE; @@ -759,7 +762,7 @@ public final class BluetoothGatt implements BluetoothProfile { public boolean connect() { try { mService.clientConnect(mClientIf, mDevice.getAddress(), - false); // autoConnect is inverse of "isDirect" + false, mTransport); // autoConnect is inverse of "isDirect" return true; } catch (RemoteException e) { Log.e(TAG,"",e); diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java index 0c00c06..34e8605 100644 --- a/core/java/android/bluetooth/BluetoothGattServer.java +++ b/core/java/android/bluetooth/BluetoothGattServer.java @@ -50,6 +50,7 @@ public final class BluetoothGattServer implements BluetoothProfile { private Object mServerIfLock = new Object(); private int mServerIf; + private int mTransport; private List<BluetoothGattService> mServices; private static final int CALLBACK_REG_TIMEOUT = 10000; @@ -269,12 +270,13 @@ public final class BluetoothGattServer implements BluetoothProfile { /** * Create a BluetoothGattServer proxy object. */ - /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt) { + /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt, int transport) { mContext = context; mService = iGatt; mAdapter = BluetoothAdapter.getDefaultAdapter(); mCallback = null; mServerIf = 0; + mTransport = transport; mServices = new ArrayList<BluetoothGattService>(); } @@ -401,7 +403,7 @@ public final class BluetoothGattServer implements BluetoothProfile { try { mService.serverConnect(mServerIf, device.getAddress(), - autoConnect ? false : true); // autoConnect is inverse of "isDirect" + autoConnect ? false : true,mTransport); // autoConnect is inverse of "isDirect" } catch (RemoteException e) { Log.e(TAG,"",e); return false; diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java index 172f3bc..b1618cf3 100644 --- a/core/java/android/bluetooth/BluetoothManager.java +++ b/core/java/android/bluetooth/BluetoothManager.java @@ -194,6 +194,26 @@ public final class BluetoothManager { */ public BluetoothGattServer openGattServer(Context context, BluetoothGattServerCallback callback) { + + return (openGattServer (context, callback, BluetoothDevice.TRANSPORT_AUTO)); + } + + /** + * Open a GATT Server + * The callback is used to deliver results to Caller, such as connection status as well + * as the results of any other GATT server operations. + * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer + * to conduct GATT server operations. + * @param context App context + * @param callback GATT server callback handler that will receive asynchronous callbacks. + * @param transport preferred transport for GATT connections to remote dual-mode devices + * {@link BluetoothDevice#TRANSPORT_AUTO} or + * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE} + * @return BluetoothGattServer instance + * @hide + */ + public BluetoothGattServer openGattServer(Context context, + BluetoothGattServerCallback callback,int transport) { if (context == null || callback == null) { throw new IllegalArgumentException("null parameter: " + context + " " + callback); } @@ -208,7 +228,7 @@ public final class BluetoothManager { Log.e(TAG, "Fail to get GATT Server connection"); return null; } - BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt); + BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt,transport); Boolean regStatus = mGattServer.registerCallback(callback); return regStatus? mGattServer : null; } catch (RemoteException e) { diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java index f532f7c..00fd7ce 100644 --- a/core/java/android/bluetooth/BluetoothSocket.java +++ b/core/java/android/bluetooth/BluetoothSocket.java @@ -325,6 +325,7 @@ public final class BluetoothSocket implements Closeable { } } catch (RemoteException e) { Log.e(TAG, Log.getStackTraceString(new Throwable())); + throw new IOException("unable to send RPC: " + e.getMessage()); } } diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java index 6dd551e..a0b603e 100644 --- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java +++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java @@ -53,6 +53,9 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { private static final boolean DBG = true; private static final boolean VDBG = true; + // Event sent to the mBtdtHandler when DHCP fails so we can tear down the network. + private static final int EVENT_NETWORK_FAILED = 1; + private AtomicBoolean mTeardownRequested = new AtomicBoolean(false); private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false); private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0); @@ -315,6 +318,7 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { } if (!success) { Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError()); + mBtdtHandler.obtainMessage(EVENT_NETWORK_FAILED).sendToTarget(); return; } mLinkProperties = dhcpResults.linkProperties; @@ -407,6 +411,10 @@ public class BluetoothTetheringDataTracker extends BaseNetworkStateTracker { if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties); mBtdt.stopReverseTether(); break; + case EVENT_NETWORK_FAILED: + if (VDBG) Log.d(TAG, "got EVENT_NETWORK_FAILED"); + mBtdt.teardown(); + break; } } } diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java index 4b28516..ab53fb0 100644 --- a/core/java/android/bluetooth/BluetoothUuid.java +++ b/core/java/android/bluetooth/BluetoothUuid.java @@ -18,6 +18,8 @@ package android.bluetooth; import android.os.ParcelUuid; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.Arrays; import java.util.HashSet; import java.util.UUID; @@ -76,6 +78,12 @@ public final class BluetoothUuid { public static final ParcelUuid BASE_UUID = ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB"); + /** Length of bytes for 16 bit UUID */ + public static final int UUID_BYTES_16_BIT = 2; + /** Length of bytes for 32 bit UUID */ + public static final int UUID_BYTES_32_BIT = 4; + /** Length of bytes for 128 bit UUID */ + public static final int UUID_BYTES_128_BIT = 16; public static final ParcelUuid[] RESERVED_UUIDS = { AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget, @@ -216,15 +224,60 @@ public final class BluetoothUuid { } /** + * Parse UUID from bytes. The {@code uuidBytes} can represent a 16-bit, 32-bit or 128-bit UUID, + * but the returned UUID is always in 128-bit format. + * Note UUID is little endian in Bluetooth. + * + * @param uuidBytes Byte representation of uuid. + * @return {@link ParcelUuid} parsed from bytes. + * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed. + */ + public static ParcelUuid parseUuidFrom(byte[] uuidBytes) { + if (uuidBytes == null) { + throw new IllegalArgumentException("uuidBytes cannot be null"); + } + int length = uuidBytes.length; + if (length != UUID_BYTES_16_BIT && length != UUID_BYTES_32_BIT && + length != UUID_BYTES_128_BIT) { + throw new IllegalArgumentException("uuidBytes length invalid - " + length); + } + + // Construct a 128 bit UUID. + if (length == UUID_BYTES_128_BIT) { + ByteBuffer buf = ByteBuffer.wrap(uuidBytes).order(ByteOrder.LITTLE_ENDIAN); + long msb = buf.getLong(8); + long lsb = buf.getLong(0); + return new ParcelUuid(new UUID(msb, lsb)); + } + + // For 16 bit and 32 bit UUID we need to convert them to 128 bit value. + // 128_bit_value = uuid * 2^96 + BASE_UUID + long shortUuid; + if (length == UUID_BYTES_16_BIT) { + shortUuid = uuidBytes[0] & 0xFF; + shortUuid += (uuidBytes[1] & 0xFF) << 8; + } else { + shortUuid = uuidBytes[0] & 0xFF ; + shortUuid += (uuidBytes[1] & 0xFF) << 8; + shortUuid += (uuidBytes[2] & 0xFF) << 16; + shortUuid += (uuidBytes[3] & 0xFF) << 24; + } + long msb = BASE_UUID.getUuid().getMostSignificantBits() + (shortUuid << 32); + long lsb = BASE_UUID.getUuid().getLeastSignificantBits(); + return new ParcelUuid(new UUID(msb, lsb)); + } + + /** * Check whether the given parcelUuid can be converted to 16 bit bluetooth uuid. + * * @param parcelUuid * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise. */ public static boolean isShortUuid(ParcelUuid parcelUuid) { - UUID uuid = parcelUuid.getUuid(); - if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { - return false; - } - return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L); + UUID uuid = parcelUuid.getUuid(); + if (uuid.getLeastSignificantBits() != BASE_UUID.getUuid().getLeastSignificantBits()) { + return false; + } + return ((uuid.getMostSignificantBits() & 0xFFFF0000FFFFFFFFL) == 0x1000L); } } diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl index c6b5c3d..49b156d 100644 --- a/core/java/android/bluetooth/IBluetoothGatt.aidl +++ b/core/java/android/bluetooth/IBluetoothGatt.aidl @@ -35,7 +35,7 @@ interface IBluetoothGatt { void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback); void unregisterClient(in int clientIf); - void clientConnect(in int clientIf, in String address, in boolean isDirect); + void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport); void clientDisconnect(in int clientIf, in String address); void startAdvertising(in int appIf); void stopAdvertising(); @@ -77,7 +77,7 @@ interface IBluetoothGatt { void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback); void unregisterServer(in int serverIf); - void serverConnect(in int servertIf, in String address, in boolean isDirect); + void serverConnect(in int servertIf, in String address, in boolean isDirect, in int transport); void serverDisconnect(in int serverIf, in String address); void beginServiceDeclaration(in int serverIf, in int srvcType, in int srvcInstanceId, in int minHandles, diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index de223a3..7c625bd 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2377,6 +2377,16 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a + * {@link android.service.fingerprint.FingerprintManager} for handling management + * of fingerprints. + * + * @see #getSystemService + * @see android.app.FingerprintManager + */ + public static final String FINGERPRINT_SERVICE = "fingerprint"; + + /** + * Use with {@link #getSystemService} to retrieve a * {@link android.media.MediaRouter} for controlling and managing * routing of media. * diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index ae5437b..3cfc56c 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -3736,7 +3736,8 @@ public class Intent implements Parcelable, Cloneable { * * <p>When set, the activity specified by this Intent will launch into a * separate task rooted at that activity. The activity launched must be - * defined with {@link android.R.attr#launchMode} "standard" or "singleTop". + * defined with {@link android.R.attr#launchMode} <code>standard</code> + * or <code>singleTop</code>. * * <p>If FLAG_ACTIVITY_NEW_DOCUMENT is used without * {@link #FLAG_ACTIVITY_MULTIPLE_TASK} then the activity manager will @@ -3751,6 +3752,8 @@ public class Intent implements Parcelable, Cloneable { * always create a new task. Thus the same document may be made to appear * more than one time in Recents. * + * <p>This is equivalent to the attribute {@link android.R.attr#documentLaunchMode}. + * * @see #FLAG_ACTIVITY_MULTIPLE_TASK */ public static final int FLAG_ACTIVITY_NEW_DOCUMENT = @@ -3815,6 +3818,15 @@ public class Intent implements Parcelable, Cloneable { */ public static final int FLAG_ACTIVITY_TASK_ON_HOME = 0X00004000; /** + * If set and the new activity is the root of a new task, then the task + * will remain in the list of recently launched tasks only until all of + * the activities in it are finished. + * + * <p>This is equivalent to the attribute + * {@link android.R.styleable#AndroidManifestActivity_autoRemoveFromRecents}. + */ + public static final int FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS = 0x00002000; + /** * If set, when sending a broadcast only registered receivers will be * called -- no BroadcastReceiver components will be launched. */ @@ -4019,7 +4031,7 @@ public class Intent implements Parcelable, Cloneable { /** * Create an intent for a specific component with a specified action and data. - * This is equivalent using {@link #Intent(String, android.net.Uri)} to + * This is equivalent to using {@link #Intent(String, android.net.Uri)} to * construct the Intent and then calling {@link #setClass} to set its * class. * diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index c53e545..c2fe3a2 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -67,7 +67,37 @@ public class ActivityInfo extends ComponentInfo * {@link #LAUNCH_SINGLE_INSTANCE}. */ public int launchMode; - + + /** + * Constant corresponding to <code>none</code> in + * the {@link android.R.attr#documentLaunchMode} attribute. + */ + public static final int DOCUMENT_LAUNCH_NONE = 0; + /** + * Constant corresponding to <code>intoExisting</code> in + * the {@link android.R.attr#documentLaunchMode} attribute. + */ + public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1; + /** + * Constant corresponding to <code>always</code> in + * the {@link android.R.attr#documentLaunchMode} attribute. + */ + public static final int DOCUMENT_LAUNCH_ALWAYS = 2; + /** + * The document launch mode style requested by the activity. From the + * {@link android.R.attr#documentLaunchMode} attribute, one of + * {@link #DOCUMENT_LAUNCH_NONE}, {@link #DOCUMENT_LAUNCH_INTO_EXISTING}, + * {@link #DOCUMENT_LAUNCH_ALWAYS}. + * + * <p>Modes DOCUMENT_LAUNCH_ALWAYS + * and DOCUMENT_LAUNCH_INTO_EXISTING are equivalent to {@link + * android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT + * Intent.FLAG_ACTIVITY_NEW_DOCUMENT} with and without {@link + * android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK + * Intent.FLAG_ACTIVITY_MULTIPLE_TASK} respectively. + */ + public int documentLaunchMode; + /** * Optional name of a permission required to be able to access this * Activity. From the "permission" attribute. @@ -192,10 +222,15 @@ public class ActivityInfo extends ComponentInfo * Bit in {@link #flags} indicating that this activity is to be persisted across * reboots for display in the Recents list. * {@link android.R.attr#persistable} - * @hide */ public static final int FLAG_PERSISTABLE = 0x1000; /** + * Bit in {@link #flags} indicating that tasks started with this activity are to be + * removed from the recent list of tasks when the last activity in the task is finished. + * {@link android.R.attr#autoRemoveFromRecents} + */ + public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 0x2000; + /** * @hide Bit in {@link #flags}: If set, this component will only be seen * by the primary user. Only works with broadcast receivers. Set from the * android.R.attr#primaryUserOnly attribute. diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 080b37b..d80ab7b 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2462,17 +2462,6 @@ public class PackageParser { a.info.flags |= ActivityInfo.FLAG_IMMERSIVE; } - if (sa.getBoolean( - com.android.internal.R.styleable.AndroidManifestActivity_persistable, false)) { - a.info.flags |= ActivityInfo.FLAG_PERSISTABLE; - } - - if (sa.getBoolean( - com.android.internal.R.styleable.AndroidManifestActivity_allowEmbedded, - false)) { - a.info.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED; - } - if (!receiver) { if (sa.getBoolean( com.android.internal.R.styleable.AndroidManifestActivity_hardwareAccelerated, @@ -2483,6 +2472,9 @@ public class PackageParser { a.info.launchMode = sa.getInt( com.android.internal.R.styleable.AndroidManifestActivity_launchMode, ActivityInfo.LAUNCH_MULTIPLE); + a.info.documentLaunchMode = sa.getInt( + com.android.internal.R.styleable.AndroidManifestActivity_documentLaunchMode, + ActivityInfo.DOCUMENT_LAUNCH_NONE); a.info.screenOrientation = sa.getInt( com.android.internal.R.styleable.AndroidManifestActivity_screenOrientation, ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); @@ -2492,6 +2484,23 @@ public class PackageParser { a.info.softInputMode = sa.getInt( com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode, 0); + + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestActivity_persistable, false)) { + a.info.flags |= ActivityInfo.FLAG_PERSISTABLE; + } + + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestActivity_allowEmbedded, + false)) { + a.info.flags |= ActivityInfo.FLAG_ALLOW_EMBEDDED; + } + + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestActivity_autoRemoveFromRecents, + false)) { + a.info.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS; + } } else { a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE; a.info.configChanges = 0; diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index d8981c8..1d2d0e9 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -124,6 +124,58 @@ public final class CaptureResult extends CameraMetadata { /** + * <p>The mode control selects how the image data is converted from the + * sensor's native color into linear sRGB color.</p> + * <p>When auto-white balance is enabled with {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, this + * control is overridden by the AWB routine. When AWB is disabled, the + * application controls how the color mapping is performed.</p> + * <p>We define the expected processing pipeline below. For consistency + * across devices, this is always the case with TRANSFORM_MATRIX.</p> + * <p>When either FULL or HIGH_QUALITY is used, the camera device may + * do additional processing but {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and + * {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} will still be provided by the + * camera device (in the results) and be roughly correct.</p> + * <p>Switching to TRANSFORM_MATRIX and using the data provided from + * FAST or HIGH_QUALITY will yield a picture with the same white point + * as what was produced by the camera device in the earlier frame.</p> + * <p>The expected processing pipeline is as follows:</p> + * <p><img alt="White balance processing pipeline" src="../../../../images/camera2/metadata/android.colorCorrection.mode/processing_pipeline.png" /></p> + * <p>The white balance is encoded by two values, a 4-channel white-balance + * gain vector (applied in the Bayer domain), and a 3x3 color transform + * matrix (applied after demosaic).</p> + * <p>The 4-channel white-balance gains are defined as:</p> + * <pre><code>{@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} = [ R G_even G_odd B ] + * </code></pre> + * <p>where <code>G_even</code> is the gain for green pixels on even rows of the + * output, and <code>G_odd</code> is the gain for green pixels on the odd rows. + * These may be identical for a given camera device implementation; if + * the camera device does not support a separate gain for even/odd green + * channels, it will use the <code>G_even</code> value, and write <code>G_odd</code> equal to + * <code>G_even</code> in the output result metadata.</p> + * <p>The matrices for color transforms are defined as a 9-entry vector:</p> + * <pre><code>{@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform} = [ I0 I1 I2 I3 I4 I5 I6 I7 I8 ] + * </code></pre> + * <p>which define a transform from input sensor colors, <code>P_in = [ r g b ]</code>, + * to output linear sRGB, <code>P_out = [ r' g' b' ]</code>,</p> + * <p>with colors as follows:</p> + * <pre><code>r' = I0r + I1g + I2b + * g' = I3r + I4g + I5b + * b' = I6r + I7g + I8b + * </code></pre> + * <p>Both the input and output value ranges must match. Overflow/underflow + * values are clipped to fit within the range.</p> + * + * @see CaptureRequest#COLOR_CORRECTION_GAINS + * @see CaptureRequest#COLOR_CORRECTION_TRANSFORM + * @see CaptureRequest#CONTROL_AWB_MODE + * @see #COLOR_CORRECTION_MODE_TRANSFORM_MATRIX + * @see #COLOR_CORRECTION_MODE_FAST + * @see #COLOR_CORRECTION_MODE_HIGH_QUALITY + */ + public static final Key<Integer> COLOR_CORRECTION_MODE = + new Key<Integer>("android.colorCorrection.mode", int.class); + + /** * <p>A color transform matrix to use to transform * from sensor RGB color space to output linear sRGB color space</p> * <p>This matrix is either set by the camera device when the request @@ -176,6 +228,82 @@ public final class CaptureResult extends CameraMetadata { new Key<Integer>("android.control.aePrecaptureId", int.class); /** + * <p>The desired setting for the camera device's auto-exposure + * algorithm's antibanding compensation.</p> + * <p>Some kinds of lighting fixtures, such as some fluorescent + * lights, flicker at the rate of the power supply frequency + * (60Hz or 50Hz, depending on country). While this is + * typically not noticeable to a person, it can be visible to + * a camera device. If a camera sets its exposure time to the + * wrong value, the flicker may become visible in the + * viewfinder as flicker or in a final captured image, as a + * set of variable-brightness bands across the image.</p> + * <p>Therefore, the auto-exposure routines of camera devices + * include antibanding routines that ensure that the chosen + * exposure value will not cause such banding. The choice of + * exposure time depends on the rate of flicker, which the + * camera device can detect automatically, or the expected + * rate can be selected by the application using this + * control.</p> + * <p>A given camera device may not support all of the possible + * options for the antibanding mode. The + * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_ANTIBANDING_MODES android.control.aeAvailableAntibandingModes} key contains + * the available modes for a given camera device.</p> + * <p>The default mode is AUTO, which must be supported by all + * camera devices.</p> + * <p>If manual exposure control is enabled (by setting + * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} to OFF), + * then this setting has no effect, and the application must + * ensure it selects exposure times that do not cause banding + * issues. The {@link CaptureResult#STATISTICS_SCENE_FLICKER android.statistics.sceneFlicker} key can assist + * the application in this.</p> + * + * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_ANTIBANDING_MODES + * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureRequest#CONTROL_MODE + * @see CaptureResult#STATISTICS_SCENE_FLICKER + * @see #CONTROL_AE_ANTIBANDING_MODE_OFF + * @see #CONTROL_AE_ANTIBANDING_MODE_50HZ + * @see #CONTROL_AE_ANTIBANDING_MODE_60HZ + * @see #CONTROL_AE_ANTIBANDING_MODE_AUTO + */ + public static final Key<Integer> CONTROL_AE_ANTIBANDING_MODE = + new Key<Integer>("android.control.aeAntibandingMode", int.class); + + /** + * <p>Adjustment to AE target image + * brightness</p> + * <p>For example, if EV step is 0.333, '6' will mean an + * exposure compensation of +2 EV; -3 will mean an exposure + * compensation of -1</p> + */ + public static final Key<Integer> CONTROL_AE_EXPOSURE_COMPENSATION = + new Key<Integer>("android.control.aeExposureCompensation", int.class); + + /** + * <p>Whether AE is currently locked to its latest + * calculated values.</p> + * <p>Note that even when AE is locked, the flash may be + * fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_AUTO_FLASH / ON_ALWAYS_FLASH / + * ON_AUTO_FLASH_REDEYE.</p> + * <p>If AE precapture is triggered (see {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}) + * when AE is already locked, the camera device will not change the exposure time + * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}) and sensitivity ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity}) + * parameters. The flash may be fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} + * is ON_AUTO_FLASH/ON_AUTO_FLASH_REDEYE and the scene is too dark. If the + * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_ALWAYS_FLASH, the scene may become overexposed.</p> + * <p>See {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE lock related state transition details.</p> + * + * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER + * @see CaptureResult#CONTROL_AE_STATE + * @see CaptureRequest#SENSOR_EXPOSURE_TIME + * @see CaptureRequest#SENSOR_SENSITIVITY + */ + public static final Key<Boolean> CONTROL_AE_LOCK = + new Key<Boolean>("android.control.aeLock", boolean.class); + + /** * <p>The desired mode for the camera device's * auto-exposure routine.</p> * <p>This control is only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} is @@ -237,6 +365,35 @@ public final class CaptureResult extends CameraMetadata { new Key<int[]>("android.control.aeRegions", int[].class); /** + * <p>Range over which fps can be adjusted to + * maintain exposure</p> + * <p>Only constrains AE algorithm, not manual control + * of {@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}</p> + * + * @see CaptureRequest#SENSOR_EXPOSURE_TIME + */ + public static final Key<int[]> CONTROL_AE_TARGET_FPS_RANGE = + new Key<int[]>("android.control.aeTargetFpsRange", int[].class); + + /** + * <p>Whether the camera device will trigger a precapture + * metering sequence when it processes this request.</p> + * <p>This entry is normally set to IDLE, or is not + * included at all in the request settings. When included and + * set to START, the camera device will trigger the autoexposure + * precapture metering sequence.</p> + * <p>The effect of AE precapture trigger depends on the current + * AE mode and state; see {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture + * state transition details.</p> + * + * @see CaptureResult#CONTROL_AE_STATE + * @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE + * @see #CONTROL_AE_PRECAPTURE_TRIGGER_START + */ + public static final Key<Integer> CONTROL_AE_PRECAPTURE_TRIGGER = + new Key<Integer>("android.control.aePrecaptureTrigger", int.class); + + /** * <p>Current state of AE algorithm</p> * <p>Switching between or enabling AE modes ({@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}) always * resets the AE state to INACTIVE. Similarly, switching between {@link CaptureRequest#CONTROL_MODE android.control.mode}, @@ -481,6 +638,24 @@ public final class CaptureResult extends CameraMetadata { new Key<int[]>("android.control.afRegions", int[].class); /** + * <p>Whether the camera device will trigger autofocus for this request.</p> + * <p>This entry is normally set to IDLE, or is not + * included at all in the request settings.</p> + * <p>When included and set to START, the camera device will trigger the + * autofocus algorithm. If autofocus is disabled, this trigger has no effect.</p> + * <p>When set to CANCEL, the camera device will cancel any active trigger, + * and return to its initial AF state.</p> + * <p>See {@link CaptureResult#CONTROL_AF_STATE android.control.afState} for what that means for each AF mode.</p> + * + * @see CaptureResult#CONTROL_AF_STATE + * @see #CONTROL_AF_TRIGGER_IDLE + * @see #CONTROL_AF_TRIGGER_START + * @see #CONTROL_AF_TRIGGER_CANCEL + */ + public static final Key<Integer> CONTROL_AF_TRIGGER = + new Key<Integer>("android.control.afTrigger", int.class); + + /** * <p>Current state of AF algorithm.</p> * <p>Switching between or enabling AF modes ({@link CaptureRequest#CONTROL_AF_MODE android.control.afMode}) always * resets the AF state to INACTIVE. Similarly, switching between {@link CaptureRequest#CONTROL_MODE android.control.mode}, @@ -889,6 +1064,16 @@ public final class CaptureResult extends CameraMetadata { new Key<Integer>("android.control.afTriggerId", int.class); /** + * <p>Whether AWB is currently locked to its + * latest calculated values.</p> + * <p>Note that AWB lock is only meaningful for AUTO + * mode; in other modes, AWB is already fixed to a specific + * setting.</p> + */ + public static final Key<Boolean> CONTROL_AWB_LOCK = + new Key<Boolean>("android.control.awbLock", boolean.class); + + /** * <p>Whether AWB is currently setting the color * transform fields, and what its illumination target * is.</p> @@ -948,6 +1133,30 @@ public final class CaptureResult extends CameraMetadata { new Key<int[]>("android.control.awbRegions", int[].class); /** + * <p>Information to the camera device 3A (auto-exposure, + * auto-focus, auto-white balance) routines about the purpose + * of this capture, to help the camera device to decide optimal 3A + * strategy.</p> + * <p>This control (except for MANUAL) is only effective if + * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p> + * <p>ZERO_SHUTTER_LAG must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} + * contains ZSL. MANUAL must be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} + * contains MANUAL_SENSOR.</p> + * + * @see CaptureRequest#CONTROL_MODE + * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES + * @see #CONTROL_CAPTURE_INTENT_CUSTOM + * @see #CONTROL_CAPTURE_INTENT_PREVIEW + * @see #CONTROL_CAPTURE_INTENT_STILL_CAPTURE + * @see #CONTROL_CAPTURE_INTENT_VIDEO_RECORD + * @see #CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT + * @see #CONTROL_CAPTURE_INTENT_ZERO_SHUTTER_LAG + * @see #CONTROL_CAPTURE_INTENT_MANUAL + */ + public static final Key<Integer> CONTROL_CAPTURE_INTENT = + new Key<Integer>("android.control.captureIntent", int.class); + + /** * <p>Current state of AWB algorithm</p> * <p>Switching between or enabling AWB modes ({@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}) always * resets the AWB state to INACTIVE. Similarly, switching between {@link CaptureRequest#CONTROL_MODE android.control.mode}, @@ -1078,6 +1287,31 @@ public final class CaptureResult extends CameraMetadata { new Key<Integer>("android.control.awbState", int.class); /** + * <p>A special color effect to apply.</p> + * <p>When this mode is set, a color effect will be applied + * to images produced by the camera device. The interpretation + * and implementation of these color effects is left to the + * implementor of the camera device, and should not be + * depended on to be consistent (or present) across all + * devices.</p> + * <p>A color effect will only be applied if + * {@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF.</p> + * + * @see CaptureRequest#CONTROL_MODE + * @see #CONTROL_EFFECT_MODE_OFF + * @see #CONTROL_EFFECT_MODE_MONO + * @see #CONTROL_EFFECT_MODE_NEGATIVE + * @see #CONTROL_EFFECT_MODE_SOLARIZE + * @see #CONTROL_EFFECT_MODE_SEPIA + * @see #CONTROL_EFFECT_MODE_POSTERIZE + * @see #CONTROL_EFFECT_MODE_WHITEBOARD + * @see #CONTROL_EFFECT_MODE_BLACKBOARD + * @see #CONTROL_EFFECT_MODE_AQUA + */ + public static final Key<Integer> CONTROL_EFFECT_MODE = + new Key<Integer>("android.control.effectMode", int.class); + + /** * <p>Overall mode of 3A control * routines.</p> * <p>High-level 3A control. When set to OFF, all 3A control @@ -1106,6 +1340,57 @@ public final class CaptureResult extends CameraMetadata { new Key<Integer>("android.control.mode", int.class); /** + * <p>A camera mode optimized for conditions typical in a particular + * capture setting.</p> + * <p>This is the mode that that is active when + * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} == USE_SCENE_MODE</code>. Aside from FACE_PRIORITY, + * these modes will disable {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}, + * {@link CaptureRequest#CONTROL_AWB_MODE android.control.awbMode}, and {@link CaptureRequest#CONTROL_AF_MODE android.control.afMode} while in use.</p> + * <p>The interpretation and implementation of these scene modes is left + * to the implementor of the camera device. Their behavior will not be + * consistent across all devices, and any given device may only implement + * a subset of these modes.</p> + * + * @see CaptureRequest#CONTROL_AE_MODE + * @see CaptureRequest#CONTROL_AF_MODE + * @see CaptureRequest#CONTROL_AWB_MODE + * @see CaptureRequest#CONTROL_MODE + * @see #CONTROL_SCENE_MODE_DISABLED + * @see #CONTROL_SCENE_MODE_FACE_PRIORITY + * @see #CONTROL_SCENE_MODE_ACTION + * @see #CONTROL_SCENE_MODE_PORTRAIT + * @see #CONTROL_SCENE_MODE_LANDSCAPE + * @see #CONTROL_SCENE_MODE_NIGHT + * @see #CONTROL_SCENE_MODE_NIGHT_PORTRAIT + * @see #CONTROL_SCENE_MODE_THEATRE + * @see #CONTROL_SCENE_MODE_BEACH + * @see #CONTROL_SCENE_MODE_SNOW + * @see #CONTROL_SCENE_MODE_SUNSET + * @see #CONTROL_SCENE_MODE_STEADYPHOTO + * @see #CONTROL_SCENE_MODE_FIREWORKS + * @see #CONTROL_SCENE_MODE_SPORTS + * @see #CONTROL_SCENE_MODE_PARTY + * @see #CONTROL_SCENE_MODE_CANDLELIGHT + * @see #CONTROL_SCENE_MODE_BARCODE + */ + public static final Key<Integer> CONTROL_SCENE_MODE = + new Key<Integer>("android.control.sceneMode", int.class); + + /** + * <p>Whether video stabilization is + * active</p> + * <p>If enabled, video stabilization can modify the + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} to keep the video stream + * stabilized</p> + * + * @see CaptureRequest#SCALER_CROP_REGION + * @see #CONTROL_VIDEO_STABILIZATION_MODE_OFF + * @see #CONTROL_VIDEO_STABILIZATION_MODE_ON + */ + public static final Key<Integer> CONTROL_VIDEO_STABILIZATION_MODE = + new Key<Integer>("android.control.videoStabilizationMode", int.class); + + /** * <p>Operation mode for edge * enhancement.</p> * <p>Edge/sharpness/detail enhancement. OFF means no @@ -1688,6 +1973,22 @@ public final class CaptureResult extends CameraMetadata { new Key<Float>("android.sensor.greenSplit", float.class); /** + * <p>A pixel <code>[R, G_even, G_odd, B]</code> that supplies the test pattern + * when {@link CaptureRequest#SENSOR_TEST_PATTERN_MODE android.sensor.testPatternMode} is SOLID_COLOR.</p> + * <p>Each color channel is treated as an unsigned 32-bit integer. + * The camera device then uses the most significant X bits + * that correspond to how many bits are in its Bayer raw sensor + * output.</p> + * <p>For example, a sensor with RAW10 Bayer output would use the + * 10 most significant bits from each color channel.</p> + * <p><b>Optional</b> - This value may be {@code null} on some devices.</p> + * + * @see CaptureRequest#SENSOR_TEST_PATTERN_MODE + */ + public static final Key<int[]> SENSOR_TEST_PATTERN_DATA = + new Key<int[]>("android.sensor.testPatternData", int[].class); + + /** * <p>When enabled, the sensor sends a test pattern instead of * doing a real exposure from the camera.</p> * <p>When a test pattern is enabled, all manual sensor controls specified @@ -1936,6 +2237,20 @@ public final class CaptureResult extends CameraMetadata { new Key<int[]>("android.statistics.hotPixelMap", int[].class); /** + * <p>Whether the camera device will output the lens + * shading map in output result metadata.</p> + * <p>When set to ON, + * {@link CaptureResult#STATISTICS_LENS_SHADING_MAP android.statistics.lensShadingMap} must be provided in + * the output result metadata.</p> + * + * @see CaptureResult#STATISTICS_LENS_SHADING_MAP + * @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF + * @see #STATISTICS_LENS_SHADING_MAP_MODE_ON + */ + public static final Key<Integer> STATISTICS_LENS_SHADING_MAP_MODE = + new Key<Integer>("android.statistics.lensShadingMapMode", int.class); + + /** * <p>Tonemapping / contrast / gamma curve for the blue * channel, to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is * CONTRAST_CURVE.</p> diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 1b2b798..84639eb 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -91,7 +91,6 @@ public class UserManager { * @see #setUserRestrictions(Bundle) * @see #getUserRestrictions() */ - public static final String DISALLOW_SHARE_LOCATION = "no_share_location"; /** @@ -145,6 +144,96 @@ public class UserManager { */ public static final String DISALLOW_REMOVE_USER = "no_remove_user"; + /** + * Key for user restrictions. Specifies if a user is disallowed from enabling or + * accessing debugging features. The default value is <code>false</code>. + * <p/> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_DEBUGGING_FEATURES = "no_debugging_features"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from configuring VPN. + * The default value is <code>false</code>. + * <p/> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_CONFIG_VPN = "no_config_vpn"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from configuring Tethering + * & portable hotspots. The default value is <code>false</code>. + * <p/> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_CONFIG_TETHERING = "no_config_tethering"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from factory resetting + * from Settings. + * The default value is <code>false</code>. + * <p> + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_FACTORY_RESET = "no_factory_reset"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from adding new users and + * profiles. The default value is <code>false</code>. + * <p> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_ADD_USER = "no_add_user"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from disabling application + * verification. The default value is <code>false</code>. + * <p> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String ENSURE_VERIFY_APPS = "ensure_verify_apps"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from configuring cell + * broadcasts. The default value is <code>false</code>. + * <p> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from configuring mobile + * networks. The default value is <code>false</code>. + * <p> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks"; + + /** + * Key for user restrictions. Specifies if a user is disallowed from configuring + * applications in Settings. The default value is <code>false</code>. + * <p> + * Type: Boolean + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_CONFIG_APPS = "no_config_apps"; + /** @hide */ public static final int PIN_VERIFICATION_FAILED_INCORRECT = -3; /** @hide */ diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 0eb994d..d5a3bcb 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -3480,6 +3480,12 @@ public final class Settings { "lock_screen_appwidget_ids"; /** + * List of enrolled fingerprint identifiers (comma-delimited). + * @hide + */ + public static final String USER_FINGERPRINT_IDS = "user_fingerprint_ids"; + + /** * Id of the appwidget shown on the lock screen when appwidgets are disabled. * @hide */ diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java new file mode 100644 index 0000000..0d14c59 --- /dev/null +++ b/core/java/android/service/fingerprint/FingerprintManager.java @@ -0,0 +1,200 @@ +/** + * 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.service.fingerprint; + +import android.app.ActivityManagerNative; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; + +/** + * A class that coordinates access to the fingerprint hardware. + */ + +public class FingerprintManager { + private static final String TAG = "FingerprintManager"; + protected static final boolean DEBUG = true; + private static final String FINGERPRINT_SERVICE_PACKAGE = "com.android.service.fingerprint"; + private static final String FINGERPRINT_SERVICE_CLASS = + "com.android.service.fingerprint.FingerprintService"; + private static final int MSG_ENROLL_RESULT = 100; + private static final int MSG_SCANNED = 101; + private static final int MSG_ERROR = 102; + private static final int MSG_REMOVED = 103; + + public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10; + public static final int FINGERPRINT_ERROR = -1; // One of the error messages below. + + // Progress messages. + public static final int FINGERPRINT_SCANNED = 1; + public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2; + public static final int FINGERPRINT_TEMPLATE_REMOVED = 4; + + // Error messages. Must agree with fingerprint HAL definitions. + public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; + public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2; + public static final int FINGERPRINT_ERROR_TIMEOUT = 3; + public static final int FINGERPRINT_ERROR_NO_SPACE = 4; + + private IFingerprintService mService; + private FingerprintManagerReceiver mClientReceiver; + + private Handler mHandler = new Handler() { + public void handleMessage(android.os.Message msg) { + if (mClientReceiver != null) { + switch(msg.what) { + case MSG_ENROLL_RESULT: + mClientReceiver.onEnrollResult(msg.arg1, msg.arg2); + break; + case MSG_SCANNED: + mClientReceiver.onScanned(msg.arg1, msg.arg2); + break; + case MSG_ERROR: + mClientReceiver.onError(msg.arg1); + break; + case MSG_REMOVED: + mClientReceiver.onRemoved(msg.arg1); + } + } + } + }; + + public FingerprintManager(Context context) { + // Connect to service... + Intent intent = new Intent(); + intent.setClassName(FINGERPRINT_SERVICE_PACKAGE, FINGERPRINT_SERVICE_CLASS); + if (!context.bindServiceAsUser(intent, mFingerprintConnection, + Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF)) { + if (DEBUG) Log.v(TAG, "Can't bind to " + FINGERPRINT_SERVICE_CLASS); + } + } + + private final ServiceConnection mFingerprintConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (DEBUG) Log.v(TAG, "Connected to FingerprintService"); + mService = IFingerprintService.Stub.asInterface(service); + try { + mService.startListening(mServiceReceiver, getCurrentUserId()); + } catch (RemoteException e) { + if (DEBUG) Log.v(TAG, "Failed to set callback", e); + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + if (DEBUG) Log.v(TAG, "Disconnected from FingerprintService"); + mService = null; + } + }; + + private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() { + + public void onEnrollResult(int fingerprintId, int remaining) { + mHandler.obtainMessage(MSG_ENROLL_RESULT, fingerprintId, remaining).sendToTarget(); + } + + public void onScanned(int fingerprintId, int confidence) { + mHandler.obtainMessage(MSG_SCANNED, fingerprintId, confidence) + .sendToTarget();; + } + + public void onError(int error) { + mHandler.obtainMessage(MSG_ERROR, error, 0).sendToTarget(); + } + + public void onRemoved(int fingerprintId) { + mHandler.obtainMessage(MSG_REMOVED, fingerprintId, 0).sendToTarget(); + } + }; + + /** + * Start the enrollment process. Timeout dictates how long to wait for the user to + * enroll a fingerprint. + * + * @param timeout + */ + public void enroll(long timeout) { + if (mServiceReceiver == null) { + throw new IllegalStateException("enroll: Call registerCallback() first"); + } + if (mService != null) try { + mService.enroll(timeout, getCurrentUserId()); + } catch (RemoteException e) { + Log.v(TAG, "Remote exception while enrolling: ", e); + } + } + + /** + * Remove the given fingerprintId from the system. FingerprintId of 0 has special meaning + * which is to delete all fingerprint data for the current user. Use with caution. + * @param fingerprintId + */ + public void remove(int fingerprintId) { + if (mService != null) try { + mService.remove(fingerprintId, getCurrentUserId()); + } catch (RemoteException e) { + Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e); + } + } + + /** + * Starts listening for fingerprint events. When a finger is scanned or recognized, the + * client will be notified via the callback. + */ + public void startListening(FingerprintManagerReceiver receiver) { + mClientReceiver = receiver; + if (mService != null) { + try { + mService.startListening(mServiceReceiver, getCurrentUserId()); + } catch (RemoteException e) { + Log.v(TAG, "Remote exception in startListening(): ", e); + } + } + } + + private int getCurrentUserId() { + try { + return ActivityManagerNative.getDefault().getCurrentUser().id; + } catch (RemoteException e) { + Log.w(TAG, "Failed to get current user id\n"); + return UserHandle.USER_NULL; + } + } + + /** + * Stops the client from listening to fingerprint events. + */ + public void stopListening() { + mClientReceiver = null; + if (mService != null) { + try { + mService.stopListening(getCurrentUserId()); + } catch (RemoteException e) { + Log.v(TAG, "Remote exception in stopListening(): ", e); + } + } else { + Log.w(TAG, "stopListening(): Service not connected!"); + } + } +}
\ No newline at end of file diff --git a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java new file mode 100644 index 0000000..34f1655 --- /dev/null +++ b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java @@ -0,0 +1,59 @@ +package android.service.fingerprint; +/** + * 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. + */ + +public class FingerprintManagerReceiver { + /** + * Fingerprint enrollment progress update. Enrollment is considered complete if + * remaining hits 0 without {@link #onError(int)} being called. + * + * @param fingerprintId the fingerprint we're currently enrolling + * @param remaining the number of samples required to complete enrollment. It's up to + * the hardware to define what each step in enrollment means. Some hardware + * requires multiple samples of the same part of the finger. Others require sampling of + * different parts of the finger. The enrollment flow can use remaining to + * mean "step x" of the process or "just need another sample." + */ + public void onEnrollResult(int fingerprintId, int remaining) { } + + /** + * Fingerprint scan detected. Most clients will use this function to detect a fingerprint + * + * @param fingerprintId is the finger the hardware has detected. + * @param confidence from 0 (no confidence) to 65535 (high confidence). Fingerprint 0 has + * special meaning - the finger wasn't recognized. + */ + public void onScanned(int fingerprintId, int confidence) { } + + /** + * An error was detected during scan or enrollment. One of + * {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE}, + * {@link FingerprintManager#FINGERPRINT_ERROR_BAD_CAPTURE} or + * {@link FingerprintManager#FINGERPRINT_ERROR_TIMEOUT} + * {@link FingerprintManager#FINGERPRINT_ERROR_NO_SPACE} + * + * @param error one of the above error codes + */ + public void onError(int error) { } + + /** + * The given fingerprint template was successfully removed by the driver. + * See {@link FingerprintManager#remove(int)} + * + * @param fingerprintId id of template to remove. + */ + public void onRemoved(int fingerprintId) { } +}
\ No newline at end of file diff --git a/core/java/android/service/fingerprint/FingerprintService.java b/core/java/android/service/fingerprint/FingerprintService.java new file mode 100644 index 0000000..c7fa7cd --- /dev/null +++ b/core/java/android/service/fingerprint/FingerprintService.java @@ -0,0 +1,219 @@ +/** + * 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.service.fingerprint; + +import android.app.Service; +import android.content.ContentResolver; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.provider.Settings; +import android.util.Slog; + +import java.io.PrintWriter; +import java.util.HashMap; + +/** + * A service to manage multiple clients that want to access the fingerprint HAL API. + * The service is responsible for maintaining a list of clients and dispatching all + * fingerprint -related events. + * + * @hide + */ +public class FingerprintService extends Service { + private final String TAG = FingerprintService.class.getSimpleName() + + "[" + getClass().getSimpleName() + "]"; + private static final boolean DEBUG = true; + HashMap<IFingerprintServiceReceiver, ClientData> mClients = + new HashMap<IFingerprintServiceReceiver, ClientData>(); + + private static final int MSG_NOTIFY = 10; + + Handler mHandler = new Handler() { + public void handleMessage(android.os.Message msg) { + switch (msg.what) { + case MSG_NOTIFY: + handleNotify(msg.arg1, msg.arg2, (Integer) msg.obj); + break; + + default: + Slog.w(TAG, "Unknown message:" + msg.what); + } + } + }; + + private static final int STATE_IDLE = 0; + private static final int STATE_LISTENING = 1; + private static final int STATE_ENROLLING = 2; + private static final int STATE_DELETING = 3; + private static final long MS_PER_SEC = 1000; + + private static final class ClientData { + public IFingerprintServiceReceiver receiver; + int state; + int userId; + } + + @Override + public final IBinder onBind(Intent intent) { + if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent); + return new FingerprintServiceWrapper(); + } + + // JNI methods to communicate from FingerprintManagerService to HAL + native int nativeEnroll(int timeout); + native int nativeRemove(int fingerprintId); + + // JNI methods for communicating from HAL to clients + void notify(int msg, int arg1, int arg2) { + mHandler.obtainMessage(MSG_NOTIFY, msg, arg1, arg2).sendToTarget(); + } + + void handleNotify(int msg, int arg1, int arg2) { + for (int i = 0; i < mClients.size(); i++) { + ClientData clientData = mClients.get(i); + switch (msg) { + case FingerprintManager.FINGERPRINT_ERROR: { + if (clientData.state != STATE_IDLE) { + // FINGERPRINT_ERROR_HW_UNAVAILABLE + // FINGERPRINT_ERROR_BAD_CAPTURE + // FINGERPRINT_ERROR_TIMEOUT + // FINGERPRINT_ERROR_NO_SPACE + final int error = arg1; + clientData.state = STATE_IDLE; + if (clientData.receiver != null) { + try { + clientData.receiver.onError(error); + } catch (RemoteException e) { + Slog.e(TAG, "can't send message to client. Did it die?", e); + } + } + } + } + break; + case FingerprintManager.FINGERPRINT_SCANNED: { + final int fingerId = arg1; + final int confidence = arg2; + if (clientData.state == STATE_LISTENING && clientData.receiver != null) { + try { + clientData.receiver.onScanned(fingerId, confidence); + } catch (RemoteException e) { + Slog.e(TAG, "can't send message to client. Did it die?", e); + } + } + break; + } + case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: { + if (clientData.state == STATE_ENROLLING) { + final int fingerId = arg1; + final int remaining = arg2; + if (remaining == 0) { + FingerprintUtils.addFingerprintIdForUser(fingerId, + getContentResolver(), clientData.userId); + clientData.state = STATE_IDLE; // Nothing left to do + } + if (clientData.receiver != null) { + try { + clientData.receiver.onEnrollResult(fingerId, remaining); + } catch (RemoteException e) { + Slog.e(TAG, "can't send message to client. Did it die?", e); + } + } + } + break; + } + case FingerprintManager.FINGERPRINT_TEMPLATE_REMOVED: { + int fingerId = arg1; + if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL"); + if (clientData.state == STATE_DELETING) { + FingerprintUtils.removeFingerprintIdForUser(fingerId, getContentResolver(), + clientData.userId); + if (clientData.receiver != null) { + try { + clientData.receiver.onRemoved(fingerId); + } catch (RemoteException e) { + Slog.e(TAG, "can't send message to client. Did it die?", e); + } + } + } + } + break; + } + } + } + + int enroll(IFingerprintServiceReceiver receiver, long timeout, int userId) { + ClientData clientData = mClients.get(receiver); + if (clientData != null) { + if (clientData.userId != userId) throw new IllegalStateException("Bad user"); + clientData.state = STATE_ENROLLING; + return nativeEnroll((int) (timeout / MS_PER_SEC)); + } + return -1; + } + + int remove(IFingerprintServiceReceiver receiver, int fingerId, int userId) { + ClientData clientData = mClients.get(receiver); + if (clientData != null) { + if (clientData.userId != userId) throw new IllegalStateException("Bad user"); + clientData.state = STATE_DELETING; + // The fingerprint id will be removed when we get confirmation from the HAL + return nativeRemove(fingerId); + } + return -1; + } + + void startListening(IFingerprintServiceReceiver receiver, int userId) { + ClientData clientData = new ClientData(); + clientData.state = STATE_LISTENING; + clientData.receiver = receiver; + clientData.userId = userId; + mClients.put(receiver, clientData); + } + + void stopListening(IFingerprintServiceReceiver receiver, int userId) { + ClientData clientData = mClients.get(receiver); + if (clientData != null) { + clientData.state = STATE_IDLE; + clientData.userId = -1; + clientData.receiver = null; + } + mClients.remove(receiver); + } + + private final class FingerprintServiceWrapper extends IFingerprintService.Stub { + IFingerprintServiceReceiver mReceiver; + public int enroll(long timeout, int userId) { + return mReceiver != null ? FingerprintService.this.enroll(mReceiver, timeout, userId) + : FingerprintManager.FINGERPRINT_ERROR_NO_RECEIVER; + } + + public int remove(int fingerprintId, int userId) { + return FingerprintService.this.remove(mReceiver, fingerprintId, userId); + } + + public void startListening(IFingerprintServiceReceiver receiver, int userId) { + mReceiver = receiver; + FingerprintService.this.startListening(receiver, userId); + } + + public void stopListening(int userId) { + FingerprintService.this.stopListening(mReceiver, userId); + } + } +} diff --git a/core/java/android/service/fingerprint/FingerprintUtils.java b/core/java/android/service/fingerprint/FingerprintUtils.java new file mode 100644 index 0000000..81a2aac --- /dev/null +++ b/core/java/android/service/fingerprint/FingerprintUtils.java @@ -0,0 +1,85 @@ +/** + * 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.service.fingerprint; + +import android.content.ContentResolver; +import android.provider.Settings; +import android.util.Log; + +import java.util.Arrays; + +class FingerprintUtils { + private static final boolean DEBUG = true; + private static final String TAG = "FingerprintUtils"; + + public static int[] getFingerprintIdsForUser(ContentResolver res, int userId) { + String fingerIdsRaw = Settings.Secure.getStringForUser(res, + Settings.Secure.USER_FINGERPRINT_IDS, userId); + + String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", "); + int result[] = new int[fingerStringIds.length]; + for (int i = 0; i < result.length; i++) { + try { + result[i] = Integer.decode(fingerStringIds[i]); + } catch (NumberFormatException e) { + if (DEBUG) Log.d(TAG, "Error when parsing finger id " + fingerStringIds[i]); + } + } + return result; + } + + public static void addFingerprintIdForUser(int fingerId, ContentResolver res, int userId) { + int[] fingerIds = getFingerprintIdsForUser(res, userId); + + // FingerId 0 has special meaning. + if (fingerId == 0) return; + + // Don't allow dups + for (int i = 0; i < fingerIds.length; i++) { + if (fingerIds[i] == fingerId) return; + } + int[] newList = Arrays.copyOf(fingerIds, fingerIds.length + 1); + newList[fingerIds.length] = fingerId; + Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS, + Arrays.toString(newList), userId); + } + + public static boolean removeFingerprintIdForUser(int fingerId, ContentResolver res, int userId) + { + // FingerId 0 has special meaning. The HAL layer is supposed to remove each finger one + // at a time and invoke notify() for each fingerId. If we get called with 0 here, it means + // something bad has happened. + if (fingerId == 0) throw new IllegalStateException("Bad fingerId"); + + int[] fingerIds = getFingerprintIdsForUser(res, userId); + int[] resultIds = Arrays.copyOf(fingerIds, fingerIds.length); + int resultCount = 0; + for (int i = 0; i < fingerIds.length; i++) { + if (fingerId != fingerIds[i]) { + resultIds[resultCount++] = fingerIds[i]; + } + } + if (resultCount > 0) { + Settings.Secure.putStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS, + Arrays.toString(Arrays.copyOf(resultIds, resultCount)), userId); + return true; + } + return false; + } + +}; + diff --git a/core/java/android/service/fingerprint/IFingerprintService.aidl b/core/java/android/service/fingerprint/IFingerprintService.aidl new file mode 100644 index 0000000..e92c20c --- /dev/null +++ b/core/java/android/service/fingerprint/IFingerprintService.aidl @@ -0,0 +1,38 @@ +/* + * 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.service.fingerprint; + +import android.os.Bundle; +import android.service.fingerprint.IFingerprintServiceReceiver; + +/** + * Communication channel from client to the fingerprint service. + * @hide + */ +interface IFingerprintService { + // Returns 0 if successfully started, -1 otherwise + int enroll(long timeout, int userId); + + // Returns 0 if fingerprintId's template can be removed, -1 otherwise + int remove(int fingerprintId, int userId); + + // Start listening for fingerprint events. This has the side effect of starting + // the hardware if not already started. + oneway void startListening(IFingerprintServiceReceiver receiver, int userId); + + // Stops listening for fingerprints + oneway void stopListening(int userId); +} diff --git a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl new file mode 100644 index 0000000..4826b59 --- /dev/null +++ b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl @@ -0,0 +1,30 @@ +/* + * 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.service.fingerprint; + +import android.os.Bundle; +import android.os.UserHandle; + +/** + * Communication channel from the FingerprintService back to FingerprintManager. + * @hide + */ +oneway interface IFingerprintServiceReceiver { + void onEnrollResult(int fingerprintId, int remaining); + void onScanned(int fingerprintId, int confidence); + void onError(int error); + void onRemoved(int fingerprintId); +} diff --git a/core/java/android/speech/tts/BlockingAudioTrack.java b/core/java/android/speech/tts/BlockingAudioTrack.java index 186cb49..92bb0ac 100644 --- a/core/java/android/speech/tts/BlockingAudioTrack.java +++ b/core/java/android/speech/tts/BlockingAudioTrack.java @@ -83,7 +83,7 @@ class BlockingAudioTrack { mVolume = volume; mPan = pan; - mBytesPerFrame = getBytesPerFrame(mAudioFormat) * mChannelCount; + mBytesPerFrame = AudioFormat.getBytesPerSample(mAudioFormat) * mChannelCount; mIsShortUtterance = false; mAudioBufferSize = 0; mBytesWritten = 0; @@ -229,17 +229,6 @@ class BlockingAudioTrack { return audioTrack; } - private static int getBytesPerFrame(int audioFormat) { - if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) { - return 1; - } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) { - return 2; - } - - return -1; - } - - private void blockUntilDone(AudioTrack audioTrack) { if (mBytesWritten <= 0) { return; diff --git a/core/java/android/speech/tts/FileSynthesisCallback.java b/core/java/android/speech/tts/FileSynthesisCallback.java index 717aeb6..d84f7f0 100644 --- a/core/java/android/speech/tts/FileSynthesisCallback.java +++ b/core/java/android/speech/tts/FileSynthesisCallback.java @@ -278,8 +278,7 @@ class FileSynthesisCallback extends AbstractSynthesisCallback { private ByteBuffer makeWavHeader(int sampleRateInHz, int audioFormat, int channelCount, int dataLength) { - // TODO: is AudioFormat.ENCODING_DEFAULT always the same as ENCODING_PCM_16BIT? - int sampleSizeInBytes = (audioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2); + int sampleSizeInBytes = AudioFormat.getBytesPerSample(audioFormat); int byteRate = sampleRateInHz * sampleSizeInBytes * channelCount; short blockAlign = (short) (sampleSizeInBytes * channelCount); short bitsPerSample = (short) (sampleSizeInBytes * 8); diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index 2d55a01..c15ce44 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -74,8 +74,10 @@ public class SurfaceControl { IBinder displayToken, int orientation, int l, int t, int r, int b, int L, int T, int R, int B); - private static native boolean nativeGetDisplayInfo( - IBinder displayToken, SurfaceControl.PhysicalDisplayInfo outInfo); + private static native SurfaceControl.PhysicalDisplayInfo[] nativeGetDisplayConfigs( + IBinder displayToken); + private static native int nativeGetActiveConfig(IBinder displayToken); + private static native boolean nativeSetActiveConfig(IBinder displayToken, int id); private static native void nativeBlankDisplay(IBinder displayToken); private static native void nativeUnblankDisplay(IBinder displayToken); @@ -499,14 +501,25 @@ public class SurfaceControl { nativeBlankDisplay(displayToken); } - public static boolean getDisplayInfo(IBinder displayToken, SurfaceControl.PhysicalDisplayInfo outInfo) { + public static SurfaceControl.PhysicalDisplayInfo[] getDisplayConfigs(IBinder displayToken) { if (displayToken == null) { throw new IllegalArgumentException("displayToken must not be null"); } - if (outInfo == null) { - throw new IllegalArgumentException("outInfo must not be null"); + return nativeGetDisplayConfigs(displayToken); + } + + public static int getActiveConfig(IBinder displayToken) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); + } + return nativeGetActiveConfig(displayToken); + } + + public static boolean setActiveConfig(IBinder displayToken, int id) { + if (displayToken == null) { + throw new IllegalArgumentException("displayToken must not be null"); } - return nativeGetDisplayInfo(displayToken, outInfo); + return nativeSetActiveConfig(displayToken, id); } public static void setDisplayProjection(IBinder displayToken, diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java index 3cfe5e9..1765c43 100644 --- a/core/java/android/view/TextureView.java +++ b/core/java/android/view/TextureView.java @@ -23,7 +23,6 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.SurfaceTexture; -import android.os.Looper; import android.util.AttributeSet; import android.util.Log; @@ -119,8 +118,6 @@ public class TextureView extends View { private boolean mUpdateLayer; private boolean mUpdateSurface; - private SurfaceTexture.OnFrameAvailableListener mUpdateListener; - private Canvas mCanvas; private int mSaveCount; @@ -370,21 +367,7 @@ public class TextureView extends View { mSurface.setDefaultBufferSize(getWidth(), getHeight()); nCreateNativeWindow(mSurface); - mUpdateListener = new SurfaceTexture.OnFrameAvailableListener() { - @Override - public void onFrameAvailable(SurfaceTexture surfaceTexture) { - // Per SurfaceTexture's documentation, the callback may be invoked - // from an arbitrary thread - updateLayer(); - - if (Looper.myLooper() == Looper.getMainLooper()) { - invalidate(); - } else { - postInvalidate(); - } - } - }; - mSurface.setOnFrameAvailableListener(mUpdateListener); + mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler); if (mListener != null && !mUpdateSurface) { mListener.onSurfaceTextureAvailable(mSurface, getWidth(), getHeight()); @@ -422,7 +405,7 @@ public class TextureView extends View { // To cancel updates, the easiest thing to do is simply to remove the // updates listener if (visibility == VISIBLE) { - mSurface.setOnFrameAvailableListener(mUpdateListener); + mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler); updateLayerAndInvalidate(); } else { mSurface.setOnFrameAvailableListener(null); @@ -767,6 +750,15 @@ public class TextureView extends View { mListener = listener; } + private final SurfaceTexture.OnFrameAvailableListener mUpdateListener = + new SurfaceTexture.OnFrameAvailableListener() { + @Override + public void onFrameAvailable(SurfaceTexture surfaceTexture) { + updateLayer(); + invalidate(); + } + }; + /** * This listener can be used to be notified when the surface texture * associated with this texture view is available. diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 3998c04..d8fcfc5 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -6147,12 +6147,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // call into it as a fallback in case we're in a class that overrides it // and has logic to perform. if (fitSystemWindows(insets.getSystemWindowInsets())) { - return insets.cloneWithSystemWindowInsetsConsumed(); + return insets.consumeSystemWindowInsets(); } } else { // We were called from within a direct call to fitSystemWindows. if (fitSystemWindowsInt(insets.getSystemWindowInsets())) { - return insets.cloneWithSystemWindowInsetsConsumed(); + return insets.consumeSystemWindowInsets(); } } return insets; @@ -9766,6 +9766,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -9809,6 +9810,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -9852,6 +9854,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -9887,6 +9890,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -9922,6 +9926,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -10083,6 +10088,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mPrivateFlags &= ~PFLAG_ALPHA_SET; invalidateViewProperty(true, false); mRenderNode.setAlpha(getFinalAlpha()); + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); } } } @@ -10513,6 +10520,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, invalidateViewProperty(false, true); invalidateParentIfNeededAndWasQuickRejected(); + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -10661,6 +10669,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { mRenderNode.setOutline(null); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -10815,6 +10824,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } invalidateParentIfNeeded(); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } @@ -10861,6 +10871,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } invalidateParentIfNeeded(); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 43bc0b6..4309366 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -4638,6 +4638,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (invalidate) { invalidateViewProperty(false, false); } + notifySubtreeAccessibilityStateChangedIfNeeded(); } /** diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 2160efe..294f472 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -29,7 +29,7 @@ import android.graphics.Rect; * @see View.OnApplyWindowInsetsListener * @see View#onApplyWindowInsets(WindowInsets) */ -public class WindowInsets { +public final class WindowInsets { private Rect mSystemWindowInsets; private Rect mWindowDecorInsets; private Rect mTempRect; @@ -151,6 +151,7 @@ public class WindowInsets { * This can include action bars, title bars, toolbars, etc.</p> * * @return The left window decor inset + * @hide pending API */ public int getWindowDecorInsetLeft() { return mWindowDecorInsets.left; @@ -164,6 +165,7 @@ public class WindowInsets { * This can include action bars, title bars, toolbars, etc.</p> * * @return The top window decor inset + * @hide pending API */ public int getWindowDecorInsetTop() { return mWindowDecorInsets.top; @@ -177,6 +179,7 @@ public class WindowInsets { * This can include action bars, title bars, toolbars, etc.</p> * * @return The right window decor inset + * @hide pending API */ public int getWindowDecorInsetRight() { return mWindowDecorInsets.right; @@ -190,6 +193,7 @@ public class WindowInsets { * This can include action bars, title bars, toolbars, etc.</p> * * @return The bottom window decor inset + * @hide pending API */ public int getWindowDecorInsetBottom() { return mWindowDecorInsets.bottom; @@ -217,6 +221,7 @@ public class WindowInsets { * This can include action bars, title bars, toolbars, etc.</p> * * @return true if any of the window decor inset values are nonzero + * @hide pending API */ public boolean hasWindowDecorInsets() { return mWindowDecorInsets.left != 0 || mWindowDecorInsets.top != 0 || @@ -246,13 +251,28 @@ public class WindowInsets { return mIsRound; } - public WindowInsets cloneWithSystemWindowInsetsConsumed() { + /** + * Returns a copy of this WindowInsets with the system window insets fully consumed. + * + * @return A modified copy of this WindowInsets + */ + public WindowInsets consumeSystemWindowInsets() { final WindowInsets result = new WindowInsets(this); result.mSystemWindowInsets = new Rect(0, 0, 0, 0); return result; } - public WindowInsets cloneWithSystemWindowInsetsConsumed(boolean left, boolean top, + /** + * Returns a copy of this WindowInsets with selected system window insets fully consumed. + * + * @param left true to consume the left system window inset + * @param top true to consume the top system window inset + * @param right true to consume the right system window inset + * @param bottom true to consume the bottom system window inset + * @return A modified copy of this WindowInsets + * @hide pending API + */ + public WindowInsets consumeSystemWindowInsets(boolean left, boolean top, boolean right, boolean bottom) { if (left || top || right || bottom) { final WindowInsets result = new WindowInsets(this); @@ -265,19 +285,36 @@ public class WindowInsets { return this; } - public WindowInsets cloneWithSystemWindowInsets(int left, int top, int right, int bottom) { + /** + * Returns a copy of this WindowInsets with selected system window insets replaced + * with new values. + * + * @param left New left inset in pixels + * @param top New top inset in pixels + * @param right New right inset in pixels + * @param bottom New bottom inset in pixels + * @return A modified copy of this WindowInsets + */ + public WindowInsets replaceSystemWindowInsets(int left, int top, + int right, int bottom) { final WindowInsets result = new WindowInsets(this); result.mSystemWindowInsets = new Rect(left, top, right, bottom); return result; } - public WindowInsets cloneWithWindowDecorInsetsConsumed() { + /** + * @hide + */ + public WindowInsets consumeWindowDecorInsets() { final WindowInsets result = new WindowInsets(this); result.mWindowDecorInsets.set(0, 0, 0, 0); return result; } - public WindowInsets cloneWithWindowDecorInsetsConsumed(boolean left, boolean top, + /** + * @hide + */ + public WindowInsets consumeWindowDecorInsets(boolean left, boolean top, boolean right, boolean bottom) { if (left || top || right || bottom) { final WindowInsets result = new WindowInsets(this); @@ -290,7 +327,10 @@ public class WindowInsets { return this; } - public WindowInsets cloneWithWindowDecorInsets(int left, int top, int right, int bottom) { + /** + * @hide + */ + public WindowInsets replaceWindowDecorInsets(int left, int top, int right, int bottom) { final WindowInsets result = new WindowInsets(this); result.mWindowDecorInsets = new Rect(left, top, right, bottom); return result; diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java index 2f8850b..3e33498 100644 --- a/core/java/android/webkit/PermissionRequest.java +++ b/core/java/android/webkit/PermissionRequest.java @@ -19,14 +19,11 @@ package android.webkit; import android.net.Uri; /** - * This class wraps a permission request, and is used to request permission for - * the web content to access the resources. + * This interface defines a permission request and is used when web content + * requests access to protected resources. * - * Either {@link #grant(long) grant()} or {@link #deny()} must be called to response the - * request, otherwise, {@link WebChromeClient#onPermissionRequest(PermissionRequest)} will - * not be invoked again if there is other permission request in this WebView. - * - * @hide + * Either {@link #grant(long) grant()} or {@link #deny()} must be called in UI + * thread to respond to the request. */ public interface PermissionRequest { /** @@ -62,8 +59,6 @@ public interface PermissionRequest { * must be equals or a subset of granted resources. * This parameter is designed to avoid granting permission by accident * especially when new resources are requested by web content. - * Calling grant(getResources()) has security issue, the new permission - * will be granted without being noticed. */ public void grant(long resources); diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 60cba86..d630a9a 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -304,7 +304,6 @@ public class WebChromeClient { * If this method isn't overridden, the permission is denied. * * @param request the PermissionRequest from current web content. - * @hide */ public void onPermissionRequest(PermissionRequest request) { request.deny(); @@ -314,8 +313,7 @@ public class WebChromeClient { * Notify the host application that the given permission request * has been canceled. Any related UI should therefore be hidden. * - * @param request the PermissionRequest need be canceled. - * @hide + * @param request the PermissionRequest that needs be canceled. */ public void onPermissionRequestCanceled(PermissionRequest request) {} diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 4b2b52c..91ca7b4 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -1682,13 +1682,15 @@ public class WebView extends AbsoluteLayout /** * Preauthorize the given origin to access resources. - * This authorization only valid for this WebView instance life cycle and + * The authorization only valid for this WebView instance's life cycle and * will not retained. * + * In the case that an origin has had resources preauthorized, calls to + * {@link WebChromeClient#onPermissionRequest(PermissionRequest)} will not be + * made for those resources from that origin. + * * @param origin the origin authorized to access resources * @param resources the resource authorized to be accessed by origin. - * - * @hide */ public void preauthorizePermission(Uri origin, long resources) { checkThread(); diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index d1d1a52..75feb5d 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -33,7 +33,8 @@ oneway interface IStatusBar void animateCollapsePanels(); void setSystemUiVisibility(int vis, int mask); void topAppWindowChanged(boolean menuVisible); - void setImeWindowStatus(in IBinder token, int vis, int backDisposition); + void setImeWindowStatus(in IBinder token, int vis, int backDisposition, + boolean showImeSwitcher); void setHardKeyboardStatus(boolean available, boolean enabled); void toggleRecentApps(); void preloadRecentApps(); diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index caa6b98..cf334c3 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -31,7 +31,8 @@ interface IStatusBarService void setIconVisibility(String slot, boolean visible); void removeIcon(String slot); void topAppWindowChanged(boolean menuVisible); - void setImeWindowStatus(in IBinder token, int vis, int backDisposition); + void setImeWindowStatus(in IBinder token, int vis, int backDisposition, + boolean showImeSwitcher); void expandSettingsPanel(); void setCurrentUser(int newUserId); diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 667bf6c..8bd2e4f 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -141,6 +141,7 @@ LOCAL_SRC_FILES:= \ android_util_FileObserver.cpp \ android/opengl/poly_clip.cpp.arm \ android/opengl/util.cpp.arm \ + android_server_FingerprintManager.cpp \ android_server_NetworkManagementSocketTagger.cpp \ android_server_Watchdog.cpp \ android_ddm_DdmHandleNativeHeap.cpp \ diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp index b78b131..621534e 100644 --- a/core/jni/android/graphics/SurfaceTexture.cpp +++ b/core/jni/android/graphics/SurfaceTexture.cpp @@ -221,7 +221,7 @@ static void SurfaceTexture_classInit(JNIEnv* env, jclass clazz) } fields.postEvent = env->GetStaticMethodID(clazz, "postEventFromNative", - "(Ljava/lang/Object;)V"); + "(Ljava/lang/ref/WeakReference;)V"); if (fields.postEvent == NULL) { ALOGE("can't find android/graphics/SurfaceTexture.postEventFromNative"); } @@ -338,7 +338,7 @@ static void SurfaceTexture_release(JNIEnv* env, jobject thiz) static JNINativeMethod gSurfaceTextureMethods[] = { {"nativeClassInit", "()V", (void*)SurfaceTexture_classInit }, - {"nativeInit", "(IZLjava/lang/Object;)V", (void*)SurfaceTexture_init }, + {"nativeInit", "(IZLjava/lang/ref/WeakReference;)V", (void*)SurfaceTexture_init }, {"nativeFinalize", "()V", (void*)SurfaceTexture_finalize }, {"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize }, {"nativeUpdateTexImage", "()V", (void*)SurfaceTexture_updateTexImage }, diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h index f4bab26..fedb1b2 100644 --- a/core/jni/android_media_AudioFormat.h +++ b/core/jni/android_media_AudioFormat.h @@ -22,6 +22,7 @@ // keep these values in sync with AudioFormat.java #define ENCODING_PCM_16BIT 2 #define ENCODING_PCM_8BIT 3 +#define ENCODING_PCM_FLOAT 4 static inline audio_format_t audioFormatToNative(int audioFormat) { @@ -30,6 +31,8 @@ static inline audio_format_t audioFormatToNative(int audioFormat) return AUDIO_FORMAT_PCM_16_BIT; case ENCODING_PCM_8BIT: return AUDIO_FORMAT_PCM_8_BIT; + case ENCODING_PCM_FLOAT: + return AUDIO_FORMAT_PCM_FLOAT; default: return AUDIO_FORMAT_INVALID; } diff --git a/core/jni/android_server_FingerprintManager.cpp b/core/jni/android_server_FingerprintManager.cpp new file mode 100644 index 0000000..f8a1fd9 --- /dev/null +++ b/core/jni/android_server_FingerprintManager.cpp @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#define LOG_TAG "Fingerprint-JNI" + +#include "JNIHelp.h" + +#include <android_runtime/AndroidRuntime.h> +#include <android_runtime/Log.h> +#include <utils/Log.h> + +namespace android { + +static struct { + jclass clazz; + jmethodID notify; +} gFingerprintManagerClassInfo; + +static jint nativeEnroll(JNIEnv* env, jobject clazz, jint timeout) { + return -1; // TODO +} + +static jint nativeRemove(JNIEnv* env, jobject clazz, jint fingerprintId) { + return -1; // TODO +} + +// ---------------------------------------------------------------------------- + +static const JNINativeMethod g_methods[] = { + { "nativeEnroll", "(I)I", (void*)nativeEnroll }, + { "nativeRemove", "(I)I", (void*)nativeRemove }, +}; + +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); \ + var = jclass(env->NewGlobalRef(var)); + +#define GET_STATIC_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find static method" methodName); + +#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method" methodName); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + +int register_android_server_FingerprintManager(JNIEnv* env) { + FIND_CLASS(gFingerprintManagerClassInfo.clazz, + "android/service/fingerprint/FingerprintManager"); + GET_METHOD_ID(gFingerprintManagerClassInfo.notify, gFingerprintManagerClassInfo.clazz, + "notify", "(III)V"); + return AndroidRuntime::registerNativeMethods( + env, "com/android/service/fingerprint/FingerprintManager", g_methods, NELEM(g_methods)); +} + +} // namespace android diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index c293c7a..5a935a9 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -50,6 +50,8 @@ static const char* const OutOfResourcesException = "android/view/Surface$OutOfResourcesException"; static struct { + jclass clazz; + jmethodID ctor; jfieldID width; jfieldID height; jfieldID refreshRate; @@ -346,24 +348,49 @@ static void nativeSetDisplayProjection(JNIEnv* env, jclass clazz, SurfaceComposerClient::setDisplayProjection(token, orientation, layerStackRect, displayRect); } -static jboolean nativeGetDisplayInfo(JNIEnv* env, jclass clazz, - jobject tokenObj, jobject infoObj) { +static jobjectArray nativeGetDisplayConfigs(JNIEnv* env, jclass clazz, + jobject tokenObj) { sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); - if (token == NULL) return JNI_FALSE; + if (token == NULL) return NULL; - DisplayInfo info; - if (SurfaceComposerClient::getDisplayInfo(token, &info)) { - return JNI_FALSE; + Vector<DisplayInfo> configs; + if (SurfaceComposerClient::getDisplayConfigs(token, &configs) != NO_ERROR || + configs.size() == 0) { + return NULL; } - env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.width, info.w); - env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.height, info.h); - env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.refreshRate, info.fps); - env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.density, info.density); - env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.xDpi, info.xdpi); - env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.yDpi, info.ydpi); - env->SetBooleanField(infoObj, gPhysicalDisplayInfoClassInfo.secure, info.secure); - return JNI_TRUE; + jobjectArray configArray = env->NewObjectArray(configs.size(), + gPhysicalDisplayInfoClassInfo.clazz, NULL); + + for (size_t c = 0; c < configs.size(); ++c) { + const DisplayInfo& info = configs[c]; + jobject infoObj = env->NewObject(gPhysicalDisplayInfoClassInfo.clazz, + gPhysicalDisplayInfoClassInfo.ctor); + env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.width, info.w); + env->SetIntField(infoObj, gPhysicalDisplayInfoClassInfo.height, info.h); + env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.refreshRate, info.fps); + env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.density, info.density); + env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.xDpi, info.xdpi); + env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.yDpi, info.ydpi); + env->SetBooleanField(infoObj, gPhysicalDisplayInfoClassInfo.secure, info.secure); + env->SetObjectArrayElement(configArray, static_cast<jsize>(c), infoObj); + env->DeleteLocalRef(infoObj); + } + + return configArray; +} + +static jint nativeGetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == NULL) return -1; + return static_cast<jint>(SurfaceComposerClient::getActiveConfig(token)); +} + +static jboolean nativeSetActiveConfig(JNIEnv* env, jclass clazz, jobject tokenObj, jint id) { + sp<IBinder> token(ibinderForJavaObject(env, tokenObj)); + if (token == NULL) return JNI_FALSE; + status_t err = SurfaceComposerClient::setActiveConfig(token, static_cast<int>(id)); + return err == NO_ERROR ? JNI_TRUE : JNI_FALSE; } static void nativeBlankDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) { @@ -576,8 +603,12 @@ static JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeSetDisplayLayerStack }, {"nativeSetDisplayProjection", "(Landroid/os/IBinder;IIIIIIIII)V", (void*)nativeSetDisplayProjection }, - {"nativeGetDisplayInfo", "(Landroid/os/IBinder;Landroid/view/SurfaceControl$PhysicalDisplayInfo;)Z", - (void*)nativeGetDisplayInfo }, + {"nativeGetDisplayConfigs", "(Landroid/os/IBinder;)[Landroid/view/SurfaceControl$PhysicalDisplayInfo;", + (void*)nativeGetDisplayConfigs }, + {"nativeGetActiveConfig", "(Landroid/os/IBinder;)I", + (void*)nativeGetActiveConfig }, + {"nativeSetActiveConfig", "(Landroid/os/IBinder;I)Z", + (void*)nativeSetActiveConfig }, {"nativeBlankDisplay", "(Landroid/os/IBinder;)V", (void*)nativeBlankDisplay }, {"nativeUnblankDisplay", "(Landroid/os/IBinder;)V", @@ -598,6 +629,9 @@ int register_android_view_SurfaceControl(JNIEnv* env) sSurfaceControlMethods, NELEM(sSurfaceControlMethods)); jclass clazz = env->FindClass("android/view/SurfaceControl$PhysicalDisplayInfo"); + gPhysicalDisplayInfoClassInfo.clazz = static_cast<jclass>(env->NewGlobalRef(clazz)); + gPhysicalDisplayInfoClassInfo.ctor = env->GetMethodID(gPhysicalDisplayInfoClassInfo.clazz, + "<init>", "()V"); gPhysicalDisplayInfoClassInfo.width = env->GetFieldID(clazz, "width", "I"); gPhysicalDisplayInfoClassInfo.height = env->GetFieldID(clazz, "height", "I"); gPhysicalDisplayInfoClassInfo.refreshRate = env->GetFieldID(clazz, "refreshRate", "F"); diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index cce4dbd..b1f256e 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -885,6 +885,47 @@ be passed a persistable Bundle in their Intent.extras. --> <attr name="persistable" format="boolean" /> + <!-- Specify whether this activity should always be launched in doc-centric mode. For + values other than <code>none</code> the activity must be defined with + {@link android.R.attr#launchMode} <code>standard</code> or <code>singleTop</code>. + This attribute can be overridden by {@link + android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}. + + <p>If this attribute is not specified, <code>none</code> will be used. + Note that this launch behavior can be changed in some ways at runtime + through the {@link android.content.Intent} flags + {@link android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT}. --> + <attr name="documentLaunchMode"> + <!-- The default mode, which will create a new task only when + {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK + Intent.FLAG_ACTIVITY_NEW_TASK} is set. --> + <enum name="none" value="0" /> + <!-- All tasks will be searched for a matching Intent. If one is found + That task will cleared and restarted with the root activity receiving a call + to {@link android.app.Activity#onNewIntent Activity.onNewIntent}. If no + such task is found a new task will be created. + This is the equivalent of with {@link + android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_DOCUMENT} + without {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK + Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. --> + <enum name="intoExisting" value="1" /> + <!-- A new task rooted at this activity will be created. + This is the equivalent of with {@link + android.content.Intent#FLAG_ACTIVITY_NEW_DOCUMENT Intent.FLAG_ACTIVITY_NEW_DOCUMENT} + paired with {@link android.content.Intent#FLAG_ACTIVITY_MULTIPLE_TASK + Intent.FLAG_ACTIVITY_MULTIPLE_TASK}. --> + <enum name="always" value="2" /> + </attr> + + <!-- Tasks launched by activities with this attribute will remain in the recent task + list until the last activity in the task is completed. When that happens the task + will be automatically removed from the recent task list. + + This attribute is the equivalent of {@link + android.content.Intent#FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS + Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS} --> + <attr name="autoRemoveFromRecents" format="boolean" /> + <!-- The <code>manifest</code> tag is the root of an <code>AndroidManifest.xml</code> file, describing the contents of an Android package (.apk) file. One @@ -1549,6 +1590,8 @@ <attr name="primaryUserOnly" format="boolean" /> <attr name="persistable" /> <attr name="allowEmbedded" /> + <attr name="documentLaunchMode" /> + <attr name="autoRemoveFromRecents" /> </declare-styleable> <!-- The <code>activity-alias</code> tag declares a new diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index e88a6ee..8874c30 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2169,6 +2169,8 @@ <public type="attr" name="excludeClass" /> <public type="attr" name="hideOnContentScroll" /> <public type="attr" name="actionOverflowMenuStyle" /> + <public type="attr" name="documentLaunchMode" /> + <public type="attr" name="autoRemoveFromRecents" /> <public-padding type="dimen" name="l_resource_pad" end="0x01050010" /> diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java new file mode 100644 index 0000000..0f3ea0e --- /dev/null +++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothUuidTest.java @@ -0,0 +1,50 @@ +/* + * 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.bluetooth; + +import android.os.ParcelUuid; +import android.test.suitebuilder.annotation.SmallTest; + +import junit.framework.TestCase; + +/** + * Unit test cases for {@link BluetoothUuid}. + * <p> + * To run this test, use adb shell am instrument -e class 'android.bluetooth.BluetoothUuidTest' -w + * 'com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner' + */ +public class BluetoothUuidTest extends TestCase { + + @SmallTest + public void testUuidParser() { + byte[] uuid16 = new byte[] { + 0x0B, 0x11 }; + assertEquals(ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB"), + BluetoothUuid.parseUuidFrom(uuid16)); + + byte[] uuid32 = new byte[] { + 0x0B, 0x11, 0x33, (byte) 0xFE }; + assertEquals(ParcelUuid.fromString("FE33110B-0000-1000-8000-00805F9B34FB"), + BluetoothUuid.parseUuidFrom(uuid32)); + + byte[] uuid128 = new byte[] { + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, (byte) 0xFF }; + assertEquals(ParcelUuid.fromString("FF0F0E0D-0C0B-0A09-0807-0060504030201"), + BluetoothUuid.parseUuidFrom(uuid128)); + } +} diff --git a/docs/html/guide/components/intents-common.jd b/docs/html/guide/components/intents-common.jd index a0f7ce1..b4813a5 100644 --- a/docs/html/guide/components/intents-common.jd +++ b/docs/html/guide/components/intents-common.jd @@ -737,7 +737,7 @@ so you can populate fields of the contact details. <pre> public void editContact(Uri contactUri, String email) { Intent intent = new Intent(Intent.ACTION_EDIT); - intent.setDataAndType(contactUri, Contacts.CONTENT_TYPE); + intent.setData(contactUri); intent.putExtra(Intents.Insert.EMAIL, email); if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java index d877502..3f8c45c 100644 --- a/graphics/java/android/graphics/SurfaceTexture.java +++ b/graphics/java/android/graphics/SurfaceTexture.java @@ -62,9 +62,8 @@ import android.view.Surface; * #updateTexImage} should not be called directly from the callback. */ public class SurfaceTexture { - - private EventHandler mEventHandler; - private OnFrameAvailableListener mOnFrameAvailableListener; + private final Looper mCreatorLooper; + private Handler mOnFrameAvailableHandler; /** * These fields are used by native code, do not access or modify. @@ -83,7 +82,8 @@ public class SurfaceTexture { /** * Exception thrown when a SurfaceTexture couldn't be created or resized. * - * @deprecated No longer thrown. {@link Surface.OutOfResourcesException} is used instead. + * @deprecated No longer thrown. {@link android.view.Surface.OutOfResourcesException} + * is used instead. */ @SuppressWarnings("serial") @Deprecated @@ -100,10 +100,10 @@ public class SurfaceTexture { * * @param texName the OpenGL texture object name (e.g. generated via glGenTextures) * - * @throws OutOfResourcesException If the SurfaceTexture cannot be created. + * @throws Surface.OutOfResourcesException If the SurfaceTexture cannot be created. */ public SurfaceTexture(int texName) { - init(texName, false); + this(texName, false); } /** @@ -121,20 +121,58 @@ public class SurfaceTexture { * @param texName the OpenGL texture object name (e.g. generated via glGenTextures) * @param singleBufferMode whether the SurfaceTexture will be in single buffered mode. * - * @throws throws OutOfResourcesException If the SurfaceTexture cannot be created. + * @throws Surface.OutOfResourcesException If the SurfaceTexture cannot be created. */ public SurfaceTexture(int texName, boolean singleBufferMode) { - init(texName, singleBufferMode); + mCreatorLooper = Looper.myLooper(); + nativeInit(texName, singleBufferMode, new WeakReference<SurfaceTexture>(this)); } /** * Register a callback to be invoked when a new image frame becomes available to the - * SurfaceTexture. Note that this callback may be called on an arbitrary thread, so it is not + * SurfaceTexture. + * <p> + * This callback may be called on an arbitrary thread, so it is not * safe to call {@link #updateTexImage} without first binding the OpenGL ES context to the * thread invoking the callback. + * </p> + * + * @param listener The listener to set. */ - public void setOnFrameAvailableListener(OnFrameAvailableListener l) { - mOnFrameAvailableListener = l; + public void setOnFrameAvailableListener(OnFrameAvailableListener listener) { + setOnFrameAvailableListener(listener, null); + } + + /** + * Register a callback to be invoked when a new image frame becomes available to the + * SurfaceTexture. + * <p> + * If no handler is specified, then this callback may be called on an arbitrary thread, + * so it is not safe to call {@link #updateTexImage} without first binding the OpenGL ES + * context to the thread invoking the callback. + * </p> + * + * @param listener The listener to set. + * @param handler The handler on which the listener should be invoked, or null + * to use an arbitrary thread. + */ + public void setOnFrameAvailableListener(final OnFrameAvailableListener listener, + Handler handler) { + if (listener != null) { + // Although we claim the thread is arbitrary, earlier implementation would + // prefer to send the callback on the creating looper or the main looper + // so we preserve this behavior here. + Looper looper = handler != null ? handler.getLooper() : + mCreatorLooper != null ? mCreatorLooper : Looper.getMainLooper(); + mOnFrameAvailableHandler = new Handler(looper, null, true /*async*/) { + @Override + public void handleMessage(Message msg) { + listener.onFrameAvailable(SurfaceTexture.this); + } + }; + } else { + mOnFrameAvailableHandler = null; + } } /** @@ -285,49 +323,22 @@ public class SurfaceTexture { } } - private class EventHandler extends Handler { - public EventHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - if (mOnFrameAvailableListener != null) { - mOnFrameAvailableListener.onFrameAvailable(SurfaceTexture.this); - } - } - } - /** * This method is invoked from native code only. */ @SuppressWarnings({"UnusedDeclaration"}) - private static void postEventFromNative(Object selfRef) { - WeakReference weakSelf = (WeakReference)selfRef; - SurfaceTexture st = (SurfaceTexture)weakSelf.get(); - if (st == null) { - return; - } - - if (st.mEventHandler != null) { - Message m = st.mEventHandler.obtainMessage(); - st.mEventHandler.sendMessage(m); - } - } - - private void init(int texName, boolean singleBufferMode) throws Surface.OutOfResourcesException { - Looper looper; - if ((looper = Looper.myLooper()) != null) { - mEventHandler = new EventHandler(looper); - } else if ((looper = Looper.getMainLooper()) != null) { - mEventHandler = new EventHandler(looper); - } else { - mEventHandler = null; + private static void postEventFromNative(WeakReference<SurfaceTexture> weakSelf) { + SurfaceTexture st = weakSelf.get(); + if (st != null) { + Handler handler = st.mOnFrameAvailableHandler; + if (handler != null) { + handler.sendEmptyMessage(0); + } } - nativeInit(texName, singleBufferMode, new WeakReference<SurfaceTexture>(this)); } - private native void nativeInit(int texName, boolean singleBufferMode, Object weakSelf) + private native void nativeInit(int texName, boolean singleBufferMode, + WeakReference<SurfaceTexture> weakSelf) throws Surface.OutOfResourcesException; private native void nativeFinalize(); private native void nativeGetTransformMatrix(float[] mtx); diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index ad0d459..6b2a247 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -32,11 +32,13 @@ public class AudioFormat { /** Default audio data format */ public static final int ENCODING_DEFAULT = 1; - // These two values must be kept in sync with core/jni/android_media_AudioFormat.h + // These values must be kept in sync with core/jni/android_media_AudioFormat.h /** Audio data format: PCM 16 bit per sample. Guaranteed to be supported by devices. */ public static final int ENCODING_PCM_16BIT = 2; /** Audio data format: PCM 8 bit per sample. Not guaranteed to be supported by devices. */ public static final int ENCODING_PCM_8BIT = 3; + /** @hide Candidate for public API */ + public static final int ENCODING_PCM_FLOAT = 4; /** Invalid audio channel configuration */ /** @deprecated use CHANNEL_INVALID instead */ @@ -139,4 +141,19 @@ public class AudioFormat { public static final int CHANNEL_IN_FRONT_BACK = CHANNEL_IN_FRONT | CHANNEL_IN_BACK; // CHANNEL_IN_ALL is not yet defined; if added then it should match AUDIO_CHANNEL_IN_ALL + /** @hide */ + public static int getBytesPerSample(int audioFormat) + { + switch (audioFormat) { + case ENCODING_PCM_8BIT: + return 1; + case ENCODING_PCM_16BIT: + case ENCODING_DEFAULT: + return 2; + case ENCODING_INVALID: + default: + throw new IllegalArgumentException("Bad audio format " + audioFormat); + } + } + } diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java index a4891f8..384e120 100644 --- a/media/java/android/media/AudioRecord.java +++ b/media/java/android/media/AudioRecord.java @@ -319,7 +319,7 @@ public class AudioRecord // NB: this section is only valid with PCM data. // To update when supporting compressed formats int frameSizeInBytes = mChannelCount - * (mAudioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2); + * (AudioFormat.getBytesPerSample(mAudioFormat)); if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) { throw new IllegalArgumentException("Invalid audio buffer size."); } diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index 17840f2..1899685 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -518,7 +518,7 @@ public class AudioTrack // NB: this section is only valid with PCM data. // To update when supporting compressed formats int frameSizeInBytes = mChannelCount - * (mAudioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2); + * (AudioFormat.getBytesPerSample(mAudioFormat)); if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) { throw new IllegalArgumentException("Invalid audio buffer size."); } diff --git a/media/java/android/media/JetPlayer.java b/media/java/android/media/JetPlayer.java index bd91fc5..7735e78 100644 --- a/media/java/android/media/JetPlayer.java +++ b/media/java/android/media/JetPlayer.java @@ -169,9 +169,11 @@ public class JetPlayer native_setup(new WeakReference<JetPlayer>(this), JetPlayer.getMaxTracks(), - // bytes to frame conversion: sample format is ENCODING_PCM_16BIT, 2 channels + // bytes to frame conversion: // 1200 == minimum buffer size in frames on generation 1 hardware - Math.max(1200, buffSizeInBytes / 4)); + Math.max(1200, buffSizeInBytes / + (AudioFormat.getBytesPerSample(AudioFormat.ENCODING_PCM_16BIT) * + 2 /*channels*/))); } } diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 28de6ac..d763bd6 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -227,6 +227,12 @@ <!-- The radius of the rounded corners on a task view. --> <dimen name="recents_task_view_rounded_corners_radius">2dp</dimen> + <!-- The min translation in the Z index for the last task. --> + <dimen name="recents_task_view_z_min">3dp</dimen> + + <!-- The translation in the Z index for each task above the last task. --> + <dimen name="recents_task_view_z_increment">5dp</dimen> + <!-- The amount of space a user has to scroll to dismiss any info panes. --> <dimen name="recents_task_stack_scroll_dismiss_info_pane_distance">50dp</dimen> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml new file mode 100644 index 0000000..e5168c4 --- /dev/null +++ b/packages/SystemUI/res/values/ids.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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 + --> + +<resources> + <item type="id" name="translation_y_animator_tag"/> + <item type="id" name="translation_z_animator_tag"/> + <item type="id" name="alpha_animator_tag"/> + <item type="id" name="top_inset_animator_tag"/> + <item type="id" name="height_animator_tag"/> + <item type="id" name="translation_y_animator_end_value_tag"/> + <item type="id" name="translation_z_animator_end_value_tag"/> + <item type="id" name="alpha_animator_end_value_tag"/> + <item type="id" name="top_inset_animator_end_value_tag"/> + <item type="id" name="height_animator_end_value_tag"/> +</resources> + diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java index d38d828..6387a92 100644 --- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java @@ -322,6 +322,7 @@ public class SwipeHelper implements Gefingerpoken { anim.addListener(new AnimatorListenerAdapter() { public void onAnimationEnd(Animator animator) { updateAlphaFromOffset(animView, canAnimViewBeDismissed); + mCallback.onChildSnappedBack(animView); } }); anim.start(); @@ -407,5 +408,7 @@ public class SwipeHelper implements Gefingerpoken { void onChildDismissed(View v); void onDragCancelled(View v); + + void onChildSnappedBack(View animView); } } diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java index 35c824b..0759b8e 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsHorizontalScrollView.java @@ -217,6 +217,10 @@ public class RecentsHorizontalScrollView extends HorizontalScrollView public void onDragCancelled(View v) { } + @Override + public void onChildSnappedBack(View animView) { + } + public View getChildAtPosition(MotionEvent ev) { final float x = ev.getX() + getScrollX(); final float y = ev.getY() + getScrollY(); diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java index 297fe0d..c2dde6a 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsVerticalScrollView.java @@ -225,6 +225,10 @@ public class RecentsVerticalScrollView extends ScrollView public void onDragCancelled(View v) { } + @Override + public void onChildSnappedBack(View animView) { + } + public View getChildAtPosition(MotionEvent ev) { final float x = ev.getX() + getScrollX(); final float y = ev.getY() + getScrollY(); diff --git a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java index f2e322d..396cb14 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java +++ b/packages/SystemUI/src/com/android/systemui/recents/AlternateRecentsComponent.java @@ -64,6 +64,17 @@ public class AlternateRecentsComponent { mSingleCountFirstTaskRect.offset(0, (int) statusBarHeight); mMultipleCountFirstTaskRect = replyData.getParcelable(KEY_MULTIPLE_TASK_STACK_RECT); mMultipleCountFirstTaskRect.offset(0, (int) statusBarHeight); + Console.log(Constants.DebugFlags.App.RecentsComponent, + "[RecentsComponent|RecentsMessageHandler|handleMessage]", + "singleTaskRect: " + mSingleCountFirstTaskRect + + " multipleTaskRect: " + mMultipleCountFirstTaskRect); + + // If we had the update the animation rects as a result of onServiceConnected, then + // we check for whether we need to toggle the recents here. + if (mToggleRecentsUponServiceBound) { + startAlternateRecentsActivity(); + mToggleRecentsUponServiceBound = false; + } } } } @@ -78,11 +89,16 @@ public class AlternateRecentsComponent { mService = new Messenger(service); mServiceIsBound = true; - // Toggle recents if this service connection was triggered by hitting the recents button - if (mToggleRecentsUponServiceBound) { - startAlternateRecentsActivity(); + if (hasValidTaskRects()) { + // Toggle recents if this new service connection was triggered by hitting recents + if (mToggleRecentsUponServiceBound) { + startAlternateRecentsActivity(); + mToggleRecentsUponServiceBound = false; + } + } else { + // Otherwise, update the animation rects before starting the recents if requested + updateAnimationRects(); } - mToggleRecentsUponServiceBound = false; } @Override @@ -191,6 +207,26 @@ public class AlternateRecentsComponent { } public void onConfigurationChanged(Configuration newConfig) { + updateAnimationRects(); + } + + /** Binds to the recents implementation */ + private void bindToRecentsService(boolean toggleRecentsUponConnection) { + mToggleRecentsUponServiceBound = toggleRecentsUponConnection; + Intent intent = new Intent(); + intent.setClassName(sRecentsPackage, sRecentsService); + mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); + } + + /** Returns whether we have valid task rects to animate to. */ + boolean hasValidTaskRects() { + return mSingleCountFirstTaskRect != null && mSingleCountFirstTaskRect.width() > 0 && + mSingleCountFirstTaskRect.height() > 0 && mMultipleCountFirstTaskRect != null && + mMultipleCountFirstTaskRect.width() > 0 && mMultipleCountFirstTaskRect.height() > 0; + } + + /** Updates each of the task animation rects. */ + void updateAnimationRects() { if (mServiceIsBound) { Resources res = mContext.getResources(); int statusBarHeight = res.getDimensionPixelSize( @@ -216,14 +252,6 @@ public class AlternateRecentsComponent { } } - /** Binds to the recents implementation */ - private void bindToRecentsService(boolean toggleRecentsUponConnection) { - mToggleRecentsUponServiceBound = toggleRecentsUponConnection; - Intent intent = new Intent(); - intent.setClassName(sRecentsPackage, sRecentsService); - mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE); - } - /** Loads the first task thumbnail */ Bitmap loadFirstTaskThumbnail() { SystemServicesProxy ssp = mSystemServicesProxy; @@ -300,6 +328,49 @@ public class AlternateRecentsComponent { return SurfaceControl.screenshot((int) dims[0], (int) dims[1]); } + /** Creates the activity options for a thumbnail transition. */ + ActivityOptions getThumbnailTransitionActivityOptions(Rect taskRect) { + // Loading from thumbnail + Bitmap thumbnail; + Bitmap firstThumbnail = loadFirstTaskThumbnail(); + if (firstThumbnail != null) { + // Create the thumbnail + thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(), + Bitmap.Config.ARGB_8888); + int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight()); + Canvas c = new Canvas(thumbnail); + c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size), + new Rect(0, 0, taskRect.width(), taskRect.height()), null); + c.setBitmap(null); + // Recycle the old thumbnail + firstThumbnail.recycle(); + } else { + // Load the thumbnail from the screenshot if can't get one from the system + WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); + Display display = wm.getDefaultDisplay(); + Bitmap screenshot = takeScreenshot(display); + if (screenshot != null) { + Resources res = mContext.getResources(); + int size = Math.min(screenshot.getWidth(), screenshot.getHeight()); + int statusBarHeight = res.getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_height); + thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(), + Bitmap.Config.ARGB_8888); + Canvas c = new Canvas(thumbnail); + c.drawBitmap(screenshot, new Rect(0, statusBarHeight, size, statusBarHeight + + size), new Rect(0, 0, taskRect.width(), taskRect.height()), null); + c.setBitmap(null); + // Recycle the temporary screenshot + screenshot.recycle(); + } else { + return null; + } + } + + return ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView, thumbnail, + taskRect.left, taskRect.top, null); + } + /** Starts the recents activity */ void startAlternateRecentsActivity() { // If the user has toggled it too quickly, then just eat up the event here (it's better than @@ -351,47 +422,28 @@ public class AlternateRecentsComponent { // number of items in the list. List<ActivityManager.RecentTaskInfo> recentTasks = ssp.getRecentTasks(4, UserHandle.CURRENT.getIdentifier()); - boolean hasMultipleTasks = hasMultipleRecentsTask(recentTasks); + Rect taskRect = hasMultipleRecentsTask(recentTasks) ? mMultipleCountFirstTaskRect : + mSingleCountFirstTaskRect; boolean isTaskExcludedFromRecents = isTopTaskExcludeFromRecents(recentTasks); - Rect taskRect = hasMultipleTasks ? mMultipleCountFirstTaskRect : mSingleCountFirstTaskRect; - if (!isTopTaskHome && !isTaskExcludedFromRecents && - (taskRect != null) && (taskRect.width() > 0) && (taskRect.height() > 0)) { - // Loading from thumbnail - Bitmap thumbnail; - Bitmap firstThumbnail = loadFirstTaskThumbnail(); - if (firstThumbnail != null) {// Create the thumbnail - thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(), - Bitmap.Config.ARGB_8888); - int size = Math.min(firstThumbnail.getWidth(), firstThumbnail.getHeight()); - Canvas c = new Canvas(thumbnail); - c.drawBitmap(firstThumbnail, new Rect(0, 0, size, size), - new Rect(0, 0, taskRect.width(), taskRect.height()), null); - c.setBitmap(null); - // Recycle the old thumbnail - firstThumbnail.recycle(); + boolean useThumbnailTransition = !isTopTaskHome && !isTaskExcludedFromRecents && + hasValidTaskRects(); + + if (useThumbnailTransition) { + // Try starting with a thumbnail transition + ActivityOptions opts = getThumbnailTransitionActivityOptions(taskRect); + if (opts != null) { + startAlternateRecentsActivity(opts, true); } else { - // Load the thumbnail from the screenshot if can't get one from the system - WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); - Display display = wm.getDefaultDisplay(); - Bitmap screenshot = takeScreenshot(display); - Resources res = mContext.getResources(); - int size = Math.min(screenshot.getWidth(), screenshot.getHeight()); - int statusBarHeight = res.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); - thumbnail = Bitmap.createBitmap(taskRect.width(), taskRect.height(), - Bitmap.Config.ARGB_8888); - Canvas c = new Canvas(thumbnail); - c.drawBitmap(screenshot, new Rect(0, statusBarHeight, size, statusBarHeight + size), - new Rect(0, 0, taskRect.width(), taskRect.height()), null); - c.setBitmap(null); - // Recycle the temporary screenshot - screenshot.recycle(); + // Fall through below to the non-thumbnail transition + useThumbnailTransition = false; } + } - ActivityOptions opts = ActivityOptions.makeThumbnailScaleDownAnimation(mStatusBarView, - thumbnail, taskRect.left, taskRect.top, null); - startAlternateRecentsActivity(opts, true); - } else { + // If there is no thumbnail transition, then just use a generic transition + // XXX: This should be different between home and from a recents-excluded app, perhaps the + // recents-excluded app should still show up in recents, when the app is in the + // foreground + if (!useThumbnailTransition) { ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, R.anim.recents_from_launcher_enter, R.anim.recents_from_launcher_exit); diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java index bc8ab45..90ea873 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java @@ -91,11 +91,8 @@ public class Constants { public static final int TaskStackOverscrollRange = 150; public static final int FilterStartDelay = 25; - // The amount to inverse scale the movement if we are overscrolling - public static final float TouchOverscrollScaleFactor = 3f; - // The padding will be applied to the smallest dimension, and then applied to all sides - public static final float StackPaddingPct = 0.15f; + public static final float StackPaddingPct = 0.1f; // The overlap height relative to the task height public static final float StackOverlapPct = 0.65f; // The height of the peek space relative to the stack height diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java index 23a0179..d1a3954 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java @@ -49,6 +49,8 @@ public class RecentsConfiguration { public int taskStackScrollDismissInfoPaneDistance; public int taskStackMaxDim; public int taskViewInfoPaneAnimDuration; + public int taskViewTranslationZMinPx; + public int taskViewTranslationZIncrementPx; public int taskViewRoundedCornerRadiusPx; public int searchBarSpaceHeightPx; @@ -108,6 +110,9 @@ public class RecentsConfiguration { res.getInteger(R.integer.recents_animate_task_view_info_pane_duration); taskViewRoundedCornerRadiusPx = res.getDimensionPixelSize(R.dimen.recents_task_view_rounded_corners_radius); + taskViewTranslationZMinPx = res.getDimensionPixelSize(R.dimen.recents_task_view_z_min); + taskViewTranslationZIncrementPx = + res.getDimensionPixelSize(R.dimen.recents_task_view_z_increment); searchBarSpaceHeightPx = res.getDimensionPixelSize(R.dimen.recents_search_bar_space_height); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java index 983ad49..c6c29a6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskInfoView.java @@ -20,10 +20,12 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; +import android.content.res.ColorStateList; import android.graphics.Canvas; import android.graphics.Path; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.drawable.TouchFeedbackDrawable; import android.util.AttributeSet; import android.widget.Button; import android.widget.FrameLayout; @@ -151,6 +153,15 @@ class TaskInfoView extends FrameLayout { RecentsConfiguration configuration = RecentsConfiguration.getInstance(); if (Constants.DebugFlags.App.EnableTaskBarThemeColors && t.colorPrimary != 0) { setBackgroundColor(t.colorPrimary); + // Workaround: The button currently doesn't support setting a custom background tint + // not defined in the theme. Just lower the alpha on the button to make it blend more + // into the background. + if (mAppInfoButton.getBackground() instanceof TouchFeedbackDrawable) { + TouchFeedbackDrawable d = (TouchFeedbackDrawable) mAppInfoButton.getBackground(); + if (d != null) { + d.setAlpha(96); + } + } } else { setBackgroundColor(configuration.taskBarViewDefaultBackgroundColor); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java index e273ecf..ce43b5a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java @@ -395,12 +395,20 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal return false; } - /** Returns whether the specified scroll is out of bounds */ - boolean isScrollOutOfBounds(int scroll) { - return (scroll < mMinScroll) || (scroll > mMaxScroll); + + /** Returns the amount that the scroll is out of bounds */ + int getScrollAmountOutOfBounds(int scroll) { + if (scroll < mMinScroll) { + return mMinScroll - scroll; + } else if (scroll > mMaxScroll) { + return scroll - mMaxScroll; + } + return 0; } + + /** Returns whether the specified scroll is out of bounds */ boolean isScrollOutOfBounds() { - return isScrollOutOfBounds(getStackScroll()); + return getScrollAmountOutOfBounds(getStackScroll()) != 0; } /** Updates the min and max virtual scroll bounds */ @@ -561,7 +569,7 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal int smallestDimension = Math.min(width, height); int padding = (int) (Constants.Values.TaskStackView.StackPaddingPct * smallestDimension / 2f); if (Constants.DebugFlags.App.EnableSearchButton) { - // Don't need to pad the top since we have some padding on the search bar already + mStackRect.top += padding; mStackRect.left += padding; mStackRect.right -= padding; mStackRect.bottom -= padding; @@ -1297,9 +1305,13 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { } if (mIsScrolling) { int curStackScroll = mSv.getStackScroll(); - if (mSv.isScrollOutOfBounds(curStackScroll + deltaY)) { - // Scale the touch if we are overscrolling - deltaY /= Constants.Values.TaskStackView.TouchOverscrollScaleFactor; + int overScrollAmount = mSv.getScrollAmountOutOfBounds(curStackScroll + deltaY); + if (overScrollAmount != 0) { + // Bound the overscroll to a fixed amount, and inversely scale the y-movement + // relative to how close we are to the max overscroll + float maxOverScroll = mSv.mTaskRect.height() / 3f; + deltaY = Math.round(deltaY * (1f - (Math.min(maxOverScroll, overScrollAmount) + / maxOverScroll))); } mSv.setStackScroll(curStackScroll + deltaY); if (mSv.isScrollOutOfBounds()) { @@ -1319,6 +1331,7 @@ class TaskStackViewTouchHandler implements SwipeHelper.Callback { if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) { // Enable HW layers on the stack mSv.addHwLayersRefCount("flingScroll"); + // XXX: Make this animation a function of the velocity AND distance int overscrollRange = (int) (Math.min(1f, Math.abs((float) velocity / mMaximumVelocity)) * Constants.Values.TaskStackView.TaskStackOverscrollRange); diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java index ecd0c45..801de24 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java @@ -20,6 +20,7 @@ import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; +import android.graphics.Outline; import android.graphics.Path; import android.graphics.Point; import android.graphics.Rect; @@ -33,7 +34,6 @@ import com.android.systemui.R; import com.android.systemui.recents.BakedBezierInterpolator; import com.android.systemui.recents.Constants; import com.android.systemui.recents.RecentsConfiguration; -import com.android.systemui.recents.Utilities; import com.android.systemui.recents.model.Task; @@ -108,6 +108,11 @@ public class TaskView extends FrameLayout implements View.OnClickListener, mRoundedRectClipPath.reset(); mRoundedRectClipPath.addRoundRect(new RectF(0, 0, getMeasuredWidth(), getMeasuredHeight()), radius, radius, Path.Direction.CW); + + // Update the outline + Outline o = new Outline(); + o.setRoundRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), radius); + setOutline(o); } @Override @@ -134,14 +139,20 @@ public class TaskView extends FrameLayout implements View.OnClickListener, /** Synchronizes this view's properties with the task's transform */ void updateViewPropertiesToTaskTransform(TaskViewTransform animateFromTransform, TaskViewTransform toTransform, int duration) { + RecentsConfiguration config = RecentsConfiguration.getInstance(); + int minZ = config.taskViewTranslationZMinPx; + int incZ = config.taskViewTranslationZIncrementPx; + if (duration > 0) { if (animateFromTransform != null) { setTranslationY(animateFromTransform.translationY); + setTranslationZ(Math.max(minZ, minZ + (animateFromTransform.t * incZ))); setScaleX(animateFromTransform.scale); setScaleY(animateFromTransform.scale); setAlpha(animateFromTransform.alpha); } animate().translationY(toTransform.translationY) + .translationZ(Math.max(minZ, minZ + (toTransform.t * incZ))) .scaleX(toTransform.scale) .scaleY(toTransform.scale) .alpha(toTransform.alpha) @@ -157,6 +168,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, .start(); } else { setTranslationY(toTransform.translationY); + setTranslationZ(Math.max(minZ, minZ + (toTransform.t * incZ))); setScaleX(toTransform.scale); setScaleY(toTransform.scale); setAlpha(toTransform.alpha); @@ -169,6 +181,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, void resetViewProperties() { setTranslationX(0f); setTranslationY(0f); + setTranslationZ(0f); setScaleX(1f); setScaleY(1f); setAlpha(1f); @@ -363,7 +376,7 @@ public class TaskView extends FrameLayout implements View.OnClickListener, @Override public void onClick(View v) { if (v == mInfoView) { - // Do nothing + hideInfoPane(); } else if (v == mBarView.mApplicationIcon) { mCb.onTaskIconClicked(this); } else if (v == mInfoView.mAppInfoButton) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index 2c7464a..d224975 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -302,7 +302,7 @@ public abstract class BaseStatusBar extends SystemUI implements ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>(); mCommandQueue = new CommandQueue(this, iconList); - int[] switches = new int[7]; + int[] switches = new int[8]; ArrayList<IBinder> binders = new ArrayList<IBinder>(); try { mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys, notifications, @@ -317,7 +317,7 @@ public abstract class BaseStatusBar extends SystemUI implements setSystemUiVisibility(switches[1], 0xffffffff); topAppWindowChanged(switches[2] != 0); // StatusBarManagerService has a back up of IME token and it's restored here. - setImeWindowStatus(binders.get(0), switches[3], switches[4]); + setImeWindowStatus(binders.get(0), switches[3], switches[4], switches[7] != 0); setHardKeyboardStatus(switches[5] != 0, switches[6] != 0); // Set up the initial icon state diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index bbbe8fa..5362af5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -65,6 +65,8 @@ public class CommandQueue extends IStatusBar.Stub { public static final int FLAG_EXCLUDE_INPUT_METHODS_PANEL = 1 << 3; public static final int FLAG_EXCLUDE_COMPAT_MODE_PANEL = 1 << 4; + private static final String SHOW_IME_SWITCHER_KEY = "showImeSwitcherKey"; + private StatusBarIconList mList; private Callbacks mCallbacks; private Handler mHandler = new H(); @@ -91,7 +93,8 @@ public class CommandQueue extends IStatusBar.Stub { public void animateExpandSettingsPanel(); public void setSystemUiVisibility(int vis, int mask); public void topAppWindowChanged(boolean visible); - public void setImeWindowStatus(IBinder token, int vis, int backDisposition); + public void setImeWindowStatus(IBinder token, int vis, int backDisposition, + boolean showImeSwitcher); public void setHardKeyboardStatus(boolean available, boolean enabled); public void toggleRecentApps(); public void preloadRecentApps(); @@ -190,11 +193,13 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { + public void setImeWindowStatus(IBinder token, int vis, int backDisposition, + boolean showImeSwitcher) { synchronized (mList) { mHandler.removeMessages(MSG_SHOW_IME_BUTTON); - mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, vis, backDisposition, token) - .sendToTarget(); + Message m = mHandler.obtainMessage(MSG_SHOW_IME_BUTTON, vis, backDisposition, token); + m.getData().putBoolean(SHOW_IME_SWITCHER_KEY, showImeSwitcher); + m.sendToTarget(); } } @@ -298,7 +303,8 @@ public class CommandQueue extends IStatusBar.Stub { mCallbacks.topAppWindowChanged(msg.arg1 != 0); break; case MSG_SHOW_IME_BUTTON: - mCallbacks.setImeWindowStatus((IBinder) msg.obj, msg.arg1, msg.arg2); + mCallbacks.setImeWindowStatus((IBinder) msg.obj, msg.arg1, msg.arg2, + msg.getData().getBoolean(SHOW_IME_SWITCHER_KEY, false)); break; case MSG_SET_HARD_KEYBOARD_STATUS: mCallbacks.setHardKeyboardStatus(msg.arg1 != 0, msg.arg2 != 0); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java index 33e9051..169521d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java @@ -17,11 +17,6 @@ package com.android.systemui.statusbar; import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Outline; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.InsetDrawable; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; @@ -112,6 +107,10 @@ public abstract class ExpandableView extends FrameLayout { mClipTopAmount = clipTopAmount; } + public int getClipTopAmount() { + return mClipTopAmount; + } + public void setOnHeightChangedListener(OnHeightChangedListener listener) { mOnHeightChangedListener = listener; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java index 1f15eaf..379bd05 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java @@ -94,10 +94,6 @@ public class NotificationContentView extends ExpandableView { updateClipping(); } - public int getClipTopAmount() { - return mClipTopAmount; - } - private void updateClipping() { mClipBounds.set(0, mClipTopAmount, getWidth(), mActualHeight); setClipBounds(mClipBounds); 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 f945c79..3856ba1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -2235,7 +2235,8 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } @Override - public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { + public void setImeWindowStatus(IBinder token, int vis, int backDisposition, + boolean showImeSwitcher) { boolean imeShown = (vis & InputMethodService.IME_VISIBLE) != 0; int flags = mNavigationIconHints; if ((backDisposition == InputMethodService.BACK_DISPOSITION_WILL_DISMISS) || imeShown) { @@ -2243,7 +2244,7 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, } else { flags &= ~NAVIGATION_HINT_BACK_ALT; } - if (imeShown) { + if (showImeSwitcher) { flags |= NAVIGATION_HINT_IME_SHOWN; } else { flags &= ~NAVIGATION_HINT_IME_SHOWN; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java index 72e22e9..81e2cb3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java @@ -237,6 +237,10 @@ public class HeadsUpNotificationView extends FrameLayout implements SwipeHelper. } @Override + public void onChildSnappedBack(View animView) { + } + + @Override public View getChildAtPosition(MotionEvent ev) { return mContentHolder; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index e4e5fb1..f8aab80 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -97,6 +97,8 @@ public class NotificationStackScrollLayout extends ViewGroup private StackScrollState mCurrentStackScrollState = new StackScrollState(this); private ArrayList<View> mChildrenToAddAnimated = new ArrayList<View>(); private ArrayList<View> mChildrenToRemoveAnimated = new ArrayList<View>(); + private ArrayList<View> mSnappedBackChildren = new ArrayList<View>(); + private ArrayList<View> mDragAnimPendingChildren = new ArrayList<View>(); private ArrayList<AnimationEvent> mAnimationEvents = new ArrayList<AnimationEvent>(); private ArrayList<View> mSwipedOutViews = new ArrayList<View>(); @@ -377,11 +379,34 @@ public class NotificationStackScrollLayout extends ViewGroup veto.performClick(); } setSwipingInProgress(false); + if (mDragAnimPendingChildren.contains(v)) { + // We start the swipe and finish it in the same frame, we don't want any animation + // for the drag + mDragAnimPendingChildren.remove(v); + } mSwipedOutViews.add(v); + mStackScrollAlgorithm.onDragFinished(v); + } + + @Override + public void onChildSnappedBack(View animView) { + mStackScrollAlgorithm.onDragFinished(animView); + if (!mDragAnimPendingChildren.contains(animView)) { + mSnappedBackChildren.add(animView); + requestChildrenUpdate(); + mNeedsAnimation = true; + } else { + // We start the swipe and snap back in the same frame, we don't want any animation + mDragAnimPendingChildren.remove(animView); + } } public void onBeginDrag(View v) { setSwipingInProgress(true); + mDragAnimPendingChildren.add(v); + mStackScrollAlgorithm.onBeginDrag(v); + requestChildrenUpdate(); + mNeedsAnimation = true; } public void onDragCancelled(View v) { @@ -934,12 +959,30 @@ public class NotificationStackScrollLayout extends ViewGroup private void generateChildHierarchyEvents() { generateChildAdditionEvents(); generateChildRemovalEvents(); + generateSnapBackEvents(); + generateDragEvents(); generateTopPaddingEvent(); mNeedsAnimation = false; } + private void generateSnapBackEvents() { + for (View child : mSnappedBackChildren) { + mAnimationEvents.add(new AnimationEvent(child, + AnimationEvent.ANIMATION_TYPE_SNAP_BACK)); + } + mSnappedBackChildren.clear(); + } + + private void generateDragEvents() { + for (View child : mDragAnimPendingChildren) { + mAnimationEvents.add(new AnimationEvent(child, + AnimationEvent.ANIMATION_TYPE_START_DRAG)); + } + mDragAnimPendingChildren.clear(); + } + private void generateChildRemovalEvents() { - for (View child : mChildrenToRemoveAnimated) { + for (View child : mChildrenToRemoveAnimated) { boolean childWasSwipedOut = mSwipedOutViews.contains(child); int animationType = childWasSwipedOut ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT @@ -951,7 +994,7 @@ public class NotificationStackScrollLayout extends ViewGroup } private void generateChildAdditionEvents() { - for (View child : mChildrenToAddAnimated) { + for (View child : mChildrenToAddAnimated) { mAnimationEvents.add(new AnimationEvent(child, AnimationEvent.ANIMATION_TYPE_ADD)); } @@ -1173,6 +1216,8 @@ public class NotificationStackScrollLayout extends ViewGroup static int ANIMATION_TYPE_REMOVE = 2; static int ANIMATION_TYPE_REMOVE_SWIPED_OUT = 3; static int ANIMATION_TYPE_TOP_PADDING_CHANGED = 4; + static int ANIMATION_TYPE_START_DRAG = 5; + static int ANIMATION_TYPE_SNAP_BACK = 6; final long eventStartTime; final View changingView; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 09d8d50..f7818c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -61,6 +61,7 @@ public class StackScrollAlgorithm { private ExpandableView mFirstChildWhileExpanding; private boolean mExpandedOnStart; private int mTopStackTotalSize; + private ArrayList<View> mDraggedViews = new ArrayList<View>(); public StackScrollAlgorithm(Context context) { initConstants(context); @@ -118,6 +119,34 @@ public class StackScrollAlgorithm { // Phase 3: updateZValuesForState(resultState, algorithmState); + + handleDraggedViews(resultState, algorithmState); + } + + /** + * Handle the special state when views are being dragged + */ + private void handleDraggedViews(StackScrollState resultState, + StackScrollAlgorithmState algorithmState) { + for (View draggedView : mDraggedViews) { + int childIndex = algorithmState.visibleChildren.indexOf(draggedView); + if (childIndex >= 0 && childIndex < algorithmState.visibleChildren.size() - 1) { + View nextChild = algorithmState.visibleChildren.get(childIndex + 1); + if (!mDraggedViews.contains(nextChild)) { + // only if the view is not dragged itself we modify its state to be fully + // visible + StackScrollState.ViewState viewState = resultState.getViewStateForView( + nextChild); + // The child below the dragged one must be fully visible + viewState.alpha = 1; + } + + // Lets set the alpha to the one it currently has, as its currently being dragged + StackScrollState.ViewState viewState = resultState.getViewStateForView(draggedView); + // The dragged child should keep the set alpha + viewState.alpha = draggedView.getAlpha(); + } + } } /** @@ -566,6 +595,14 @@ public class StackScrollAlgorithm { } } + public void onBeginDrag(View view) { + mDraggedViews.add(view); + } + + public void onDragFinished(View view) { + mDraggedViews.remove(view); + } + class StackScrollAlgorithmState { /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java index 26cef36..70126f5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java @@ -93,6 +93,7 @@ public class StackScrollState { int numChildren = mHostView.getChildCount(); float previousNotificationEnd = 0; float previousNotificationStart = 0; + boolean previousNotificationIsSwiped = false; for (int i = 0; i < numChildren; i++) { ExpandableView child = (ExpandableView) mHostView.getChildAt(i); ViewState state = mStateMap.get(child); @@ -153,12 +154,20 @@ public class StackScrollState { // apply clipping and shadow float newNotificationEnd = newYTranslation + newHeight; + + // When the previous notification is swiped, we don't clip the content to the + // bottom of it. + float clipHeight = previousNotificationIsSwiped + ? newHeight + : newNotificationEnd - (previousNotificationEnd); + updateChildClippingAndBackground(child, newHeight, - newNotificationEnd - (previousNotificationEnd), + clipHeight, (int) (newHeight - (previousNotificationStart - newYTranslation))); - previousNotificationStart = newYTranslation; + previousNotificationStart = newYTranslation + child.getClipTopAmount(); previousNotificationEnd = newNotificationEnd; + previousNotificationIsSwiped = child.getTranslationX() != 0; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java index 4dce288..2e700aa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java @@ -16,13 +16,21 @@ package com.android.systemui.statusbar.stack; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.view.View; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; + +import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableView; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; /** * An stack state animator which handles animations to new StackScrollStates @@ -30,130 +38,372 @@ import java.util.ArrayList; public class StackStateAnimator { private static final int ANIMATION_DURATION = 360; + private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag; + private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag; + private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag; + private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag; + private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag; + private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag; + private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag; + private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag; + private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag; + private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag; private final Interpolator mFastOutSlowInInterpolator; public NotificationStackScrollLayout mHostLayout; - private boolean mAnimationIsRunning; private ArrayList<NotificationStackScrollLayout.AnimationEvent> mHandledEvents = new ArrayList<>(); + private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents = + new ArrayList<>(); + private Set<Animator> mAnimatorSet = new HashSet<Animator>(); + private Stack<AnimatorListenerAdapter> mAnimationListenerPool + = new Stack<AnimatorListenerAdapter>(); public StackStateAnimator(NotificationStackScrollLayout hostLayout) { mHostLayout = hostLayout; mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(), - android.R.interpolator.fast_out_slow_in); + android.R.interpolator.fast_out_slow_in); } public boolean isRunning() { - return mAnimationIsRunning; + return !mAnimatorSet.isEmpty(); } public void startAnimationForEvents( ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents, StackScrollState finalState) { - int numEvents = mAnimationEvents.size(); - if (numEvents == 0) { - // No events, so we don't perform any animation - return; - } - long lastEventStartTime = mAnimationEvents.get(numEvents - 1).eventStartTime; - long eventEnd = lastEventStartTime + ANIMATION_DURATION; - long currentTime = AnimationUtils.currentAnimationTimeMillis(); - long newDuration = eventEnd - currentTime; - if (newDuration <= 0) { - // last event is long before this, so we don't do anything - return; - } - initializeAddedViewStates(mAnimationEvents, finalState); + + processAnimationEvents(mAnimationEvents, finalState); + + boolean hasNewEvents = !mNewEvents.isEmpty(); int childCount = mHostLayout.getChildCount(); - boolean isFirstAnimatingView = true; for (int i = 0; i < childCount; i++) { final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i); StackScrollState.ViewState viewState = finalState.getViewStateForView(child); if (viewState == null) { continue; } - int childVisibility = child.getVisibility(); - boolean wasVisible = childVisibility == View.VISIBLE; - final float alpha = viewState.alpha; - if (!wasVisible && alpha != 0 && !viewState.gone) { - child.setVisibility(View.VISIBLE); - } - startPropertyAnimation(newDuration, isFirstAnimatingView, child, viewState, alpha); + startAnimations(child, viewState, hasNewEvents); - // TODO: animate clipBounds child.setClipBounds(null); - int currentHeigth = child.getActualHeight(); - if (viewState.height != currentHeigth) { - startHeightAnimation(newDuration, child, viewState, currentHeigth); - } - isFirstAnimatingView = false; } - mAnimationIsRunning = true; + if (!isRunning()) { + // no child has preformed any animation, lets finish + onAnimationFinished(); + } } - private void startPropertyAnimation(long newDuration, final boolean hasFinishAction, - final ExpandableView child, StackScrollState.ViewState viewState, final float alpha) { - child.animate().setInterpolator(mFastOutSlowInInterpolator) - .translationY(viewState.yTranslation) - .translationZ(viewState.zTranslation) - .setDuration(newDuration) - .withEndAction(new Runnable() { - @Override - public void run() { - mAnimationIsRunning = false; - if (hasFinishAction) { - mHandledEvents.clear(); - mHostLayout.onChildAnimationFinished(); - } - if (alpha == 0) { - child.setVisibility(View.INVISIBLE); - } - } - }); + /** + * Start an animation to the given viewState + */ + private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState, + boolean hasNewEvents) { + int childVisibility = child.getVisibility(); + boolean wasVisible = childVisibility == View.VISIBLE; + final float alpha = viewState.alpha; + if (!wasVisible && alpha != 0 && !viewState.gone) { + child.setVisibility(View.VISIBLE); + } + // start translationY animation + if (child.getTranslationY() != viewState.yTranslation) { + startYTranslationAnimation(child, viewState, hasNewEvents); + } + // start translationZ animation + if (child.getTranslationZ() != viewState.zTranslation) { + startZTranslationAnimation(child, viewState, hasNewEvents); + } + // start alpha animation if (alpha != child.getAlpha()) { - child.animate().withLayer().alpha(alpha); + startAlphaAnimation(child, viewState, hasNewEvents); + } + // start height animation + if (viewState.height != child.getActualHeight()) { + startHeightAnimation(child, viewState, hasNewEvents); } } - private void startHeightAnimation(long newDuration, final ExpandableView child, - StackScrollState.ViewState viewState, int currentHeigth) { - ValueAnimator heightAnimator = ValueAnimator.ofInt(currentHeigth, viewState.height); - heightAnimator.setInterpolator(mFastOutSlowInInterpolator); - heightAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + private void startHeightAnimation(final ExpandableView child, + StackScrollState.ViewState viewState, boolean hasNewEvents) { + Integer previousEndValue = getChildTag(child,TAG_END_HEIGHT); + if (previousEndValue != null && previousEndValue == viewState.height) { + return; + } + ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT); + long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents); + if (newDuration <= 0) { + // no new animation needed, let's just apply the value + child.setActualHeight(viewState.height); + if (previousAnimator != null && !isRunning()) { + onAnimationFinished(); + } + return; + } + + ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), viewState.height); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { child.setActualHeight((int) animation.getAnimatedValue()); } }); - heightAnimator.setDuration(newDuration); - heightAnimator.start(); + animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setDuration(newDuration); + animator.addListener(getGlobalAnimationFinishedListener()); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_HEIGHT, null); + child.setTag(TAG_END_HEIGHT, null); + } + }); + startInstantly(animator); + child.setTag(TAG_ANIMATOR_HEIGHT, animator); + child.setTag(TAG_END_HEIGHT, viewState.height); + } + + private void startAlphaAnimation(final ExpandableView child, + final StackScrollState.ViewState viewState, boolean hasNewEvents) { + final float endAlpha = viewState.alpha; + Float previousEndValue = getChildTag(child,TAG_END_ALPHA); + if (previousEndValue != null && previousEndValue == endAlpha) { + return; + } + ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA); + long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents); + if (newDuration <= 0) { + // no new animation needed, let's just apply the value + child.setAlpha(endAlpha); + if (endAlpha == 0) { + child.setVisibility(View.INVISIBLE); + } + if (previousAnimator != null && !isRunning()) { + onAnimationFinished(); + } + return; + } + + ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA, + child.getAlpha(), endAlpha); + animator.setInterpolator(mFastOutSlowInInterpolator); + // Handle layer type + final int currentLayerType = child.getLayerType(); + child.setLayerType(View.LAYER_TYPE_HARDWARE, null); + animator.addListener(new AnimatorListenerAdapter() { + public boolean mWasCancelled; + + @Override + public void onAnimationEnd(Animator animation) { + child.setLayerType(currentLayerType, null); + if (endAlpha == 0 && !mWasCancelled) { + child.setVisibility(View.INVISIBLE); + } + child.setTag(TAG_ANIMATOR_ALPHA, null); + child.setTag(TAG_END_ALPHA, null); + } + + @Override + public void onAnimationCancel(Animator animation) { + mWasCancelled = true; + } + + @Override + public void onAnimationStart(Animator animation) { + mWasCancelled = false; + } + }); + animator.setDuration(newDuration); + animator.addListener(getGlobalAnimationFinishedListener()); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + + } + }); + startInstantly(animator); + child.setTag(TAG_ANIMATOR_ALPHA, animator); + child.setTag(TAG_END_ALPHA, endAlpha); + } + + private void startZTranslationAnimation(final ExpandableView child, + final StackScrollState.ViewState viewState, boolean hasNewEvents) { + Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z); + if (previousEndValue != null && previousEndValue == viewState.zTranslation) { + return; + } + ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z); + long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents); + if (newDuration <= 0) { + // no new animation needed, let's just apply the value + child.setTranslationZ(viewState.zTranslation); + + if (previousAnimator != null && !isRunning()) { + onAnimationFinished(); + } + return; + } + + ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z, + child.getTranslationZ(), viewState.zTranslation); + animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setDuration(newDuration); + animator.addListener(getGlobalAnimationFinishedListener()); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null); + child.setTag(TAG_END_TRANSLATION_Z, null); + } + }); + startInstantly(animator); + child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator); + child.setTag(TAG_END_TRANSLATION_Z, viewState.zTranslation); + } + + private void startYTranslationAnimation(final ExpandableView child, + StackScrollState.ViewState viewState, boolean hasNewEvents) { + Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y); + if (previousEndValue != null && previousEndValue == viewState.yTranslation) { + return; + } + ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y); + long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents); + if (newDuration <= 0) { + // no new animation needed, let's just apply the value + child.setTranslationY(viewState.yTranslation); + if (previousAnimator != null && !isRunning()) { + onAnimationFinished(); + } + return; + } + + ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, + child.getTranslationY(), viewState.yTranslation); + animator.setInterpolator(mFastOutSlowInInterpolator); + animator.setDuration(newDuration); + animator.addListener(getGlobalAnimationFinishedListener()); + // remove the tag when the animation is finished + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); + child.setTag(TAG_END_TRANSLATION_Y, null); + } + }); + startInstantly(animator); + child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator); + child.setTag(TAG_END_TRANSLATION_Y, viewState.yTranslation); + } + + /** + * Start an animator instantly instead of waiting on the next synchronization frame + */ + private void startInstantly(ValueAnimator animator) { + animator.start(); + animator.setCurrentPlayTime(0); + } + + /** + * @return an adapter which ensures that onAnimationFinished is called once no animation is + * running anymore + */ + private AnimatorListenerAdapter getGlobalAnimationFinishedListener() { + if (!mAnimationListenerPool.empty()) { + return mAnimationListenerPool.pop(); + } + + // We need to create a new one, no reusable ones found + return new AnimatorListenerAdapter() { + private boolean mWasCancelled; + + @Override + public void onAnimationEnd(Animator animation) { + mAnimatorSet.remove(animation); + if (mAnimatorSet.isEmpty() && !mWasCancelled) { + onAnimationFinished(); + } + mAnimationListenerPool.push(this); + } + + @Override + public void onAnimationCancel(Animator animation) { + mWasCancelled = true; + } + + @Override + public void onAnimationStart(Animator animation) { + mAnimatorSet.add(animation); + mWasCancelled = false; + } + }; + } + + private <T> T getChildTag(View child, int tag) { + return (T) child.getTag(tag); + } + + /** + * Cancel the previous animator and get the duration of the new animation. + * + * @param previousAnimator the animator which was running before + * @param hasNewEvents indicating whether new events came in in this animation + * @return the new duration + */ + private long cancelAnimatorAndGetNewDuration(ValueAnimator previousAnimator, + boolean hasNewEvents) { + long newDuration = ANIMATION_DURATION; + if (previousAnimator != null) { + if (!hasNewEvents) { + // This is only an update, no new event came in. lets just take the remaining + // duration as the new duration + newDuration = previousAnimator.getDuration() + - previousAnimator.getCurrentPlayTime(); + } + previousAnimator.cancel(); + } else if (!hasNewEvents){ + newDuration = 0; + } + return newDuration; + } + + private void onAnimationFinished() { + mHandledEvents.clear(); + mNewEvents.clear(); + mHostLayout.onChildAnimationFinished(); } /** - * Initialize the viewStates for the added children + * Process the animationEvents for a new animation * - * @param animationEvents the animation events who contain the added children + * @param animationEvents the animation events for the animation to perform * @param finalState the final state to animate to */ - private void initializeAddedViewStates( + private void processAnimationEvents( ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents, StackScrollState finalState) { + mNewEvents.clear(); for (NotificationStackScrollLayout.AnimationEvent event: animationEvents) { View changingView = event.changingView; - if (event.animationType == NotificationStackScrollLayout.AnimationEvent - .ANIMATION_TYPE_ADD && !mHandledEvents.contains(event)) { - - // This item is added, initialize it's properties. - StackScrollState.ViewState viewState = finalState.getViewStateForView(changingView); - if (viewState == null) { - // The position for this child was never generated, let's continue. - continue; + if (!mHandledEvents.contains(event)) { + if (event.animationType == NotificationStackScrollLayout.AnimationEvent + .ANIMATION_TYPE_ADD) { + + // This item is added, initialize it's properties. + StackScrollState.ViewState viewState = finalState + .getViewStateForView(changingView); + if (viewState == null) { + // The position for this child was never generated, let's continue. + continue; + } + changingView.setAlpha(0); + changingView.setTranslationY(viewState.yTranslation); + changingView.setTranslationZ(viewState.zTranslation); } - changingView.setAlpha(0); - changingView.setTranslationY(viewState.yTranslation); - changingView.setTranslationZ(viewState.zTranslation); mHandledEvents.add(event); + mNewEvents.add(event); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java index d615542..4b3d3b0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java @@ -78,7 +78,8 @@ public class TvStatusBar extends BaseStatusBar { } @Override - public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { + public void setImeWindowStatus(IBinder token, int vis, int backDisposition, + boolean showImeSwitcher) { } @Override diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 7ed1cc7..10315a7 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -1532,14 +1532,17 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } mImeWindowVis = vis; mBackDisposition = backDisposition; - if (mStatusBar != null) { - mStatusBar.setImeWindowStatus(token, vis, backDisposition); - } final boolean iconVisibility = ((vis & (InputMethodService.IME_ACTIVE)) != 0) && (mWindowManagerService.isHardKeyboardAvailable() || (vis & (InputMethodService.IME_VISIBLE)) != 0); + final boolean needsToShowImeSwitcher = iconVisibility + && needsToShowImeSwitchOngoingNotification(); + if (mStatusBar != null) { + mStatusBar.setImeWindowStatus(token, vis, backDisposition, + needsToShowImeSwitcher); + } final InputMethodInfo imi = mMethodMap.get(mCurMethodId); - if (imi != null && iconVisibility && needsToShowImeSwitchOngoingNotification()) { + if (imi != null && needsToShowImeSwitcher) { // Used to load label final CharSequence title = mRes.getText( com.android.internal.R.string.select_input_method); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index f908de2..cbb8377 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -7070,7 +7070,8 @@ public final class ActivityManagerService extends ActivityManagerNative final ArrayList<ActivityRecord> activities = tr.mActivities; int activityNdx; final int numActivities = activities.size(); - for (activityNdx = 0; activityNdx < numActivities; ++activityNdx) { + for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities; + ++activityNdx) { final ActivityRecord r = activities.get(activityNdx); if (r.intent != null && (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 91afec7..7f43e43 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -21,6 +21,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.SystemProperties; +import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.DisplayEventReceiver; @@ -47,8 +48,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { new SparseArray<LocalDisplayDevice>(); private HotplugDisplayEventReceiver mHotplugReceiver; - private final SurfaceControl.PhysicalDisplayInfo mTempPhys = new SurfaceControl.PhysicalDisplayInfo(); - // Called with SyncRoot lock held. public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener) { @@ -68,14 +67,31 @@ final class LocalDisplayAdapter extends DisplayAdapter { private void tryConnectDisplayLocked(int builtInDisplayId) { IBinder displayToken = SurfaceControl.getBuiltInDisplay(builtInDisplayId); - if (displayToken != null && SurfaceControl.getDisplayInfo(displayToken, mTempPhys)) { + if (displayToken != null) { + SurfaceControl.PhysicalDisplayInfo[] configs = + SurfaceControl.getDisplayConfigs(displayToken); + if (configs == null) { + // There are no valid configs for this device, so we can't use it + Slog.w(TAG, "No valid configs found for display device " + + builtInDisplayId); + return; + } + int activeConfig = SurfaceControl.getActiveConfig(displayToken); + if (activeConfig < 0) { + // There is no active config, and for now we don't have the + // policy to set one. + Slog.w(TAG, "No active config found for display device " + + builtInDisplayId); + return; + } LocalDisplayDevice device = mDevices.get(builtInDisplayId); if (device == null) { // Display was added. - device = new LocalDisplayDevice(displayToken, builtInDisplayId, mTempPhys); + device = new LocalDisplayDevice(displayToken, builtInDisplayId, + configs[activeConfig]); mDevices.put(builtInDisplayId, device); sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED); - } else if (device.updatePhysicalDisplayInfoLocked(mTempPhys)) { + } else if (device.updatePhysicalDisplayInfoLocked(configs[activeConfig])) { // Display properties changed. sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED); } @@ -228,4 +244,4 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } } -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java index fd2f8a1..8968da3 100644 --- a/services/core/java/com/android/server/dreams/DreamManagerService.java +++ b/services/core/java/com/android/server/dreams/DreamManagerService.java @@ -642,8 +642,9 @@ public final class DreamManagerService extends SystemService { try { synchronized (mMcuHal) { if (mReleased) { - throw new IllegalStateException("This operation cannot be performed " - + "because the dream has ended."); + Slog.w(TAG, "Ignoring message to MCU HAL because the dream " + + "has already ended: " + msg); + return null; } return mMcuHal.sendMessage(msg, arg); } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index c87fc99..b103a4d 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -40,7 +40,7 @@ import java.util.List; * * <p>Declared as package-private, accessed by {@link HdmiControlService} only. */ -class HdmiCecController { +final class HdmiCecController { private static final String TAG = "HdmiCecController"; private static final byte[] EMPTY_BODY = EmptyArray.BYTE; @@ -313,6 +313,63 @@ class HdmiCecController { return mDeviceInfos.get(logicalAddress); } + /** + * Add a new logical address to the device. Device's HW should be notified + * when a new logical address is assigned to a device, so that it can accept + * a command having available destinations. + * + * <p>Declared as package-private. accessed by {@link HdmiControlService} only. + * + * @param newLogicalAddress a logical address to be added + * @return 0 on success. Otherwise, returns negative value + */ + int addLogicalAddress(int newLogicalAddress) { + if (HdmiCec.isValidAddress(newLogicalAddress)) { + return nativeAddLogicalAddress(mNativePtr, newLogicalAddress); + } else { + return -1; + } + } + + /** + * Clear all logical addresses registered in the device. + * + * <p>Declared as package-private. accessed by {@link HdmiControlService} only. + */ + void clearLogicalAddress() { + nativeClearLogicalAddress(mNativePtr); + } + + /** + * Return the physical address of the device. + * + * <p>Declared as package-private. accessed by {@link HdmiControlService} only. + * + * @return CEC physical address of the device. The range of success address + * is between 0x0000 and 0xFFFF. If failed it returns -1 + */ + int getPhysicalAddress() { + return nativeGetPhysicalAddress(mNativePtr); + } + + /** + * Return CEC version of the device. + * + * <p>Declared as package-private. accessed by {@link HdmiControlService} only. + */ + int getVersion() { + return nativeGetVersion(mNativePtr); + } + + /** + * Return vendor id of the device. + * + * <p>Declared as package-private. accessed by {@link HdmiControlService} only. + */ + int getVendorId() { + return nativeGetVendorId(mNativePtr); + } + private void init(HdmiControlService service, long nativePtr) { mIoHandler = new IoHandler(service.getServiceLooper()); mControlHandler = new ControlHandler(service.getServiceLooper()); @@ -364,6 +421,11 @@ class HdmiCecController { } private static native long nativeInit(HdmiCecController handler); - private static native int nativeSendCecCommand(long contollerPtr, int srcAddress, + private static native int nativeSendCecCommand(long controllerPtr, int srcAddress, int dstAddress, byte[] body); + private static native int nativeAddLogicalAddress(long controllerPtr, int logicalAddress); + private static native void nativeClearLogicalAddress(long controllerPtr); + private static native int nativeGetPhysicalAddress(long controllerPtr); + private static native int nativeGetVersion(long controllerPtr); + private static native int nativeGetVendorId(long controllerPtr); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6a843a8..72fc295 100755 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -7479,11 +7479,7 @@ public class PackageManagerService extends IPackageManager.Stub { null); PackageSetting pkgSetting; final int uid = Binder.getCallingUid(); - if (UserHandle.getUserId(uid) != userId) { - mContext.enforceCallingPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "installExistingPackage for user " + userId); - } + enforceCrossUserPermission(uid, userId, true, "installExistingPackage for user " + userId); if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { return PackageManager.INSTALL_FAILED_USER_RESTRICTED; } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 3239b46..4f5326f 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -734,6 +734,15 @@ public class UserManagerService extends IUserManager.Stub { writeBoolean(serializer, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER); writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS); writeBoolean(serializer, restrictions, UserManager.DISALLOW_REMOVE_USER); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_DEBUGGING_FEATURES); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_VPN); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_TETHERING); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_FACTORY_RESET); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADD_USER); + writeBoolean(serializer, restrictions, UserManager.ENSURE_VERIFY_APPS); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); + writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_APPS); serializer.endTag(null, TAG_RESTRICTIONS); } serializer.endTag(null, TAG_USER); @@ -873,6 +882,15 @@ public class UserManagerService extends IUserManager.Stub { readBoolean(parser, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER); readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS); readBoolean(parser, restrictions, UserManager.DISALLOW_REMOVE_USER); + readBoolean(parser, restrictions, UserManager.DISALLOW_DEBUGGING_FEATURES); + readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_VPN); + readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_TETHERING); + readBoolean(parser, restrictions, UserManager.DISALLOW_FACTORY_RESET); + readBoolean(parser, restrictions, UserManager.DISALLOW_ADD_USER); + readBoolean(parser, restrictions, UserManager.ENSURE_VERIFY_APPS); + readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS); + readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS); + readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_APPS); } } } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 022bdae..738ad32 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -74,6 +74,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub private boolean mMenuVisible = false; private int mImeWindowVis = 0; private int mImeBackDisposition; + private boolean mShowImeSwitcher; private IBinder mImeToken = null; private int mCurrentUserId; @@ -346,7 +347,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub } @Override - public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition) { + public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition, + final boolean showImeSwitcher) { enforceStatusBar(); if (SPEW) { @@ -360,11 +362,12 @@ public class StatusBarManagerService extends IStatusBarService.Stub mImeWindowVis = vis; mImeBackDisposition = backDisposition; mImeToken = token; + mShowImeSwitcher = showImeSwitcher; mHandler.post(new Runnable() { public void run() { if (mBar != null) { try { - mBar.setImeWindowStatus(token, vis, backDisposition); + mBar.setImeWindowStatus(token, vis, backDisposition, showImeSwitcher); } catch (RemoteException ex) { } } @@ -512,6 +515,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub switches[2] = mMenuVisible ? 1 : 0; switches[3] = mImeWindowVis; switches[4] = mImeBackDisposition; + switches[7] = mShowImeSwitcher ? 1 : 0; binders.add(mImeToken); } switches[5] = mWindowManager.isHardKeyboardAvailable() ? 1 : 0; diff --git a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp index 527216d..27c8876 100644 --- a/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp +++ b/services/core/jni/com_android_server_hdmi_HdmiCecController.cpp @@ -43,6 +43,16 @@ public: // Send message to other device. Note that it runs in IO thread. int sendMessage(const cec_message_t& message); + // Add a logical address to device. + int addLogicalAddress(cec_logical_address_t address); + // Clear all logical address registered to the device. + void clearLogicaladdress(); + // Get physical address of device. + int getPhysicalAddress(); + // Get CEC version from driver. + int getVersion(); + // Get vendor id used for vendor command. + uint32_t getVendorId(); private: // Propagate the message up to Java layer. @@ -94,6 +104,34 @@ int HdmiCecController::sendMessage(const cec_message_t& message) { return mDevice->send_message(mDevice, &message); } +int HdmiCecController::addLogicalAddress(cec_logical_address_t address) { + return mDevice->add_logical_address(mDevice, address); +} + +void HdmiCecController::clearLogicaladdress() { + mDevice->clear_logical_address(mDevice); +} + +int HdmiCecController::getPhysicalAddress() { + uint16_t physicalAddress = 0xFFFF; + if (mDevice->get_physical_address(mDevice, &physicalAddress) == 0) { + return physicalAddress; + } + return -1; +} + +int HdmiCecController::getVersion() { + int version = 0; + mDevice->get_version(mDevice, &version); + return version; +} + +uint32_t HdmiCecController::getVendorId() { + uint32_t vendorId = 0; + mDevice->get_vendor_id(mDevice, &vendorId); + return vendorId; +} + // static void HdmiCecController::checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { @@ -180,12 +218,51 @@ static jint nativeSendCecCommand(JNIEnv* env, jclass clazz, jlong controllerPtr, return controller->sendMessage(message); } +static jint nativeAddLogicalAddress(JNIEnv* env, jclass clazz, + jlong controllerPtr, jint logicalAddress) { + HdmiCecController* controller = + reinterpret_cast<HdmiCecController*>(controllerPtr); + return controller->addLogicalAddress( + static_cast<cec_logical_address_t>(logicalAddress)); +} + +static void nativeClearLogicalAddress(JNIEnv* env, jclass clazz, + jlong controllerPtr) { + HdmiCecController* controller = + reinterpret_cast<HdmiCecController*>(controllerPtr); + controller->clearLogicaladdress(); +} + +static jint nativeGetPhysicalAddress(JNIEnv* env, jclass clazz, + jlong controllerPtr) { + HdmiCecController* controller = + reinterpret_cast<HdmiCecController*>(controllerPtr); + return controller->getPhysicalAddress(); +} + +static jint nativeGetVersion(JNIEnv* env, jclass clazz, + jlong controllerPtr) { + HdmiCecController* controller = + reinterpret_cast<HdmiCecController*>(controllerPtr); + return controller->getVersion(); +} + +static jint nativeGetVendorId(JNIEnv* env, jclass clazz, jlong controllerPtr) { + HdmiCecController* controller = + reinterpret_cast<HdmiCecController*>(controllerPtr); + return controller->getVendorId(); +} + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ { "nativeInit", "(Lcom/android/server/hdmi/HdmiCecController;)J", (void *) nativeInit }, - { "nativeSendCecCommand", "(JII[B)I", - (void *) nativeSendCecCommand }, + { "nativeSendCecCommand", "(JII[B)I", (void *) nativeSendCecCommand }, + { "nativeAddLogicalAddress", "(JI)I", (void *) nativeAddLogicalAddress }, + { "nativeClearLogicalAddress", "(J)V", (void *) nativeClearLogicalAddress }, + { "nativeGetPhysicalAddress", "(J)I", (void *) nativeGetPhysicalAddress }, + { "nativeGetVersion", "(J)I", (void *) nativeGetVersion }, + { "nativeGetVendorId", "(J)I", (void *) nativeGetVendorId }, }; #define CLASS_PATH "com/android/server/hdmi/HdmiCecController" diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 7a0d1c7..23b912f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -44,6 +44,7 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; @@ -3023,6 +3024,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + @Override public void addPersistentPreferredActivity(ComponentName who, IntentFilter filter, ComponentName activity) { synchronized (this) { @@ -3043,6 +3045,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + @Override public void clearPackagePersistentPreferredActivities(ComponentName who, String packageName) { synchronized (this) { if (who == null) { @@ -3168,4 +3171,110 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } } + + @Override + public void enableSystemApp(ComponentName who, String packageName) { + synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + + int userId = UserHandle.getCallingUserId(); + long id = Binder.clearCallingIdentity(); + + try { + UserManager um = UserManager.get(mContext); + if (!um.getUserInfo(userId).isManagedProfile()) { + throw new IllegalStateException( + "Only call this method from a managed profile."); + } + + // TODO: Use UserManager::getProfileParent when available. + UserInfo primaryUser = um.getUserInfo(UserHandle.USER_OWNER); + + if (DBG) { + Slog.v(LOG_TAG, "installing " + packageName + " for " + + userId); + } + + IPackageManager pm = AppGlobals.getPackageManager(); + if (!isSystemApp(pm, packageName, primaryUser.id)) { + throw new IllegalArgumentException("Only system apps can be enabled this way."); + } + + // Install the app. + pm.installExistingPackageAsUser(packageName, userId); + + } catch (RemoteException re) { + // shouldn't happen + Slog.wtf(LOG_TAG, "Failed to install " + packageName, re); + } finally { + restoreCallingIdentity(id); + } + } + } + + @Override + public int enableSystemAppWithIntent(ComponentName who, Intent intent) { + synchronized (this) { + if (who == null) { + throw new NullPointerException("ComponentName is null"); + } + + getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + + int userId = UserHandle.getCallingUserId(); + long id = Binder.clearCallingIdentity(); + + try { + UserManager um = UserManager.get(mContext); + if (!um.getUserInfo(userId).isManagedProfile()) { + throw new IllegalStateException( + "Only call this method from a managed profile."); + } + + // TODO: Use UserManager::getProfileParent when available. + UserInfo primaryUser = um.getUserInfo(UserHandle.USER_OWNER); + + IPackageManager pm = AppGlobals.getPackageManager(); + List<ResolveInfo> activitiesToEnable = pm.queryIntentActivities(intent, + intent.resolveTypeIfNeeded(mContext.getContentResolver()), + 0, // no flags + primaryUser.id); + + if (DBG) Slog.d(LOG_TAG, "Enabling system activities: " + activitiesToEnable); + int numberOfAppsInstalled = 0; + if (activitiesToEnable != null) { + for (ResolveInfo info : activitiesToEnable) { + if (info.activityInfo != null) { + + if (!isSystemApp(pm, info.activityInfo.packageName, primaryUser.id)) { + throw new IllegalArgumentException( + "Only system apps can be enabled this way."); + } + + + numberOfAppsInstalled++; + pm.installExistingPackageAsUser(info.activityInfo.packageName, userId); + } + } + } + return numberOfAppsInstalled; + } catch (RemoteException e) { + // shouldn't happen + Slog.wtf(LOG_TAG, "Failed to resolve intent for: " + intent); + return 0; + } finally { + restoreCallingIdentity(id); + } + } + } + + private boolean isSystemApp(IPackageManager pm, String packageName, int userId) + throws RemoteException { + ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0, userId); + return (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0; + } } |
