diff --git a/Android.mk b/Android.mk index 0054fd1821d..da22df32a18 100644 --- a/Android.mk +++ b/Android.mk @@ -358,6 +358,7 @@ LOCAL_SRC_FILES += \ media/java/android/media/IRingtonePlayer.aidl \ media/java/android/media/IVolumeController.aidl \ media/java/android/media/audiopolicy/IAudioPolicyCallback.aidl \ + media/java/android/media/midi/IBluetoothMidiService.aidl \ media/java/android/media/midi/IMidiDeviceListener.aidl \ media/java/android/media/midi/IMidiDeviceOpenCallback.aidl \ media/java/android/media/midi/IMidiDeviceServer.aidl \ diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 627c8547ceb..0ff7443fce8 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -694,6 +694,8 @@ public class Activity extends ContextThemeWrapper private static final String SAVED_DIALOGS_TAG = "android:savedDialogs"; private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_"; private static final String SAVED_DIALOG_ARGS_KEY_PREFIX = "android:dialog_args_"; + private static final String HAS_CURENT_PERMISSIONS_REQUEST_KEY = + "android:hasCurrentPermissionsRequest"; private static final String REQUEST_PERMISSIONS_WHO_PREFIX = "@android:requestPermissions:"; @@ -802,6 +804,8 @@ private static final class ManagedCursor { SharedElementCallback mEnterTransitionListener = SharedElementCallback.NULL_CALLBACK; SharedElementCallback mExitTransitionListener = SharedElementCallback.NULL_CALLBACK; + private boolean mHasCurrentPermissionsRequest; + /** Return the intent that started this activity. */ public Intent getIntent() { return mIntent; @@ -1303,6 +1307,7 @@ final void performSaveInstanceState(Bundle outState) { onSaveInstanceState(outState); saveManagedDialogs(outState); mActivityTransitionState.saveState(outState); + storeHasCurrentPermissionRequest(outState); if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState); } @@ -1318,6 +1323,7 @@ final void performSaveInstanceState(Bundle outState) { final void performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { onSaveInstanceState(outState, outPersistentState); saveManagedDialogs(outState); + storeHasCurrentPermissionRequest(outState); if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState + ", " + outPersistentState); } @@ -3845,8 +3851,15 @@ protected void onApplyThemeResource(Resources.Theme theme, @StyleRes int resid, * @see #shouldShowRequestPermissionRationale(String) */ public final void requestPermissions(@NonNull String[] permissions, int requestCode) { + if (mHasCurrentPermissionsRequest) { + Log.w(TAG, "Can reqeust only one set of permissions at a time"); + // Dispatch the callback with empty arrays which means a cancellation. + onRequestPermissionsResult(requestCode, new String[0], new int[0]); + return; + } Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions); startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null); + mHasCurrentPermissionsRequest = true; } /** @@ -6268,12 +6281,14 @@ final void performCreateCommon() { } final void performCreate(Bundle icicle) { + restoreHasCurrentPermissionRequest(icicle); onCreate(icicle); mActivityTransitionState.readState(icicle); performCreateCommon(); } final void performCreate(Bundle icicle, PersistableBundle persistentState) { + restoreHasCurrentPermissionRequest(icicle); onCreate(icicle, persistentState); mActivityTransitionState.readState(icicle); performCreateCommon(); @@ -6452,6 +6467,19 @@ public final boolean isResumed() { return mResumed; } + private void storeHasCurrentPermissionRequest(Bundle bundle) { + if (bundle != null && mHasCurrentPermissionsRequest) { + bundle.putBoolean(HAS_CURENT_PERMISSIONS_REQUEST_KEY, true); + } + } + + private void restoreHasCurrentPermissionRequest(Bundle bundle) { + if (bundle != null) { + mHasCurrentPermissionsRequest = bundle.getBoolean( + HAS_CURENT_PERMISSIONS_REQUEST_KEY, false); + } + } + void dispatchActivityResult(String who, int requestCode, int resultCode, Intent data) { if (false) Log.v( @@ -6579,6 +6607,7 @@ public interface TranslucentConversionListener { } private void dispatchRequestPermissionsResult(int requestCode, Intent data) { + mHasCurrentPermissionsRequest = false; // If the package installer crashed we may have not data - best effort. String[] permissions = (data != null) ? data.getStringArrayExtra( PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES) : new String[0]; diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 0adce5dc193..7cae745fcf2 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -1619,7 +1619,8 @@ private static boolean isPackageCandidateVolume(ApplicationInfo app, VolumeInfo // System apps and apps demanding internal storage can't be moved // anywhere else if (app.isSystemApp() - || app.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { + || app.installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY + || app.installLocation == PackageInfo.INSTALL_LOCATION_UNSPECIFIED) { return false; } diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index d50483e4a3f..29e8dd4614c 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -460,9 +460,6 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene // If set this fragment is being retained across the current config change. boolean mRetaining; - // If set this fragment's loaders are being retained across the current config change. - boolean mRetainLoader; - // If set this fragment has menu items to contribute. boolean mHasMenu; @@ -2404,7 +2401,7 @@ void performStop() { mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false); } if (mLoaderManager != null) { - if (mRetainLoader) { + if (mHost.getRetainLoaders()) { mLoaderManager.doRetain(); } else { mLoaderManager.doStop(); diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java index 1b45137fb7c..28dadfa78b3 100644 --- a/core/java/android/app/FragmentController.java +++ b/core/java/android/app/FragmentController.java @@ -341,7 +341,6 @@ public void doLoaderStart() { */ public void doLoaderStop(boolean retain) { mHost.doLoaderStop(retain); - mHost.mFragmentManager.setRetainLoader(retain); } /** diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java index 7b013072619..13517e600b0 100644 --- a/core/java/android/app/FragmentHostCallback.java +++ b/core/java/android/app/FragmentHostCallback.java @@ -42,9 +42,14 @@ public abstract class FragmentHostCallback extends FragmentContainer { private final Handler mHandler; final int mWindowAnimations; final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl(); + /** The loader managers for individual fragments [i.e. Fragment#getLoaderManager()] */ private ArrayMap mAllLoaderManagers; + /** Whether or not fragment loaders should retain their state */ + private boolean mRetainLoaders; + /** The loader manger for the fragment host [i.e. Activity#getLoaderManager()] */ private LoaderManagerImpl mLoaderManager; private boolean mCheckedForLoaderManager; + /** Whether or not the fragment host loader manager was started */ private boolean mLoadersStarted; public FragmentHostCallback(Context context, Handler handler, int windowAnimations) { @@ -166,6 +171,10 @@ public boolean onHasView() { return true; } + boolean getRetainLoaders() { + return mRetainLoaders; + } + Activity getActivity() { return mActivity; } @@ -217,6 +226,8 @@ void doLoaderStart() { } void doLoaderStop(boolean retain) { + mRetainLoaders = retain; + if (mLoaderManager == null) { return; } diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index 51d613247c8..696ccdb207c 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -869,17 +869,6 @@ public void performPendingDeferredStart(Fragment f) { } } - void setRetainLoader(boolean retain) { - if (mActive != null) { - for (int i=0; i= MIN_ASHMEM_BITMAP_SIZE) { mPicture = mPicture.createAshmemBitmap(); } if (mBigLargeIcon != null) { diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index 948ea1effae..498ff811cc7 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -71,10 +71,11 @@ public abstract class UsageStatsManagerInternal { * Could be hours, could be days, who knows? * * @param packageName + * @param uidForAppId The uid of the app, which will be used for its app id * @param userId * @return */ - public abstract boolean isAppIdle(String packageName, int userId); + public abstract boolean isAppIdle(String packageName, int uidForAppId, int userId); /** * Returns all of the uids for a given user where all packages associating with that uid diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index d9fc4abc3ea..5300e810134 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -643,20 +643,18 @@ public abstract FileInputStream openFileInput(String name) /** * Open a private file associated with this Context's application package - * for writing. Creates the file if it doesn't already exist. - * - *

No permissions are required to invoke this method, since it uses internal - * storage. + * for writing. Creates the file if it doesn't already exist. + *

+ * No additional permissions are required for the calling app to read or + * write the returned file. * * @param name The name of the file to open; can not contain path - * separators. - * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the - * default operation, {@link #MODE_APPEND} to append to an existing file, - * {@link #MODE_WORLD_READABLE} and {@link #MODE_WORLD_WRITEABLE} to control - * permissions. - * + * separators. + * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the + * default operation, {@link #MODE_APPEND} to append to an + * existing file, {@link #MODE_WORLD_READABLE} and + * {@link #MODE_WORLD_WRITEABLE} to control permissions. * @return The resulting {@link FileOutputStream}. - * * @see #MODE_APPEND * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE @@ -689,6 +687,9 @@ public abstract FileOutputStream openFileOutput(String name, int mode) /** * Returns the absolute path on the filesystem where a file created with * {@link #openFileOutput} is stored. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. * * @param name The name of the file for which you would like to get * its path. @@ -702,14 +703,16 @@ public abstract FileOutputStream openFileOutput(String name, int mode) public abstract File getFileStreamPath(String name); /** - * Returns the absolute path to the directory on the filesystem where - * files created with {@link #openFileOutput} are stored. - * - *

No permissions are required to read or write to the returned path, since this - * path is internal storage. + * Returns the absolute path to the directory on the filesystem where files + * created with {@link #openFileOutput} are stored. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

+ * No additional permissions are required for the calling app to read or + * write files under the returned path. * * @return The path of the directory holding application files. - * * @see #openFileOutput * @see #getFileStreamPath * @see #getDir @@ -718,17 +721,19 @@ public abstract FileOutputStream openFileOutput(String name, int mode) /** * Returns the absolute path to the directory on the filesystem similar to - * {@link #getFilesDir()}. The difference is that files placed under this - * directory will be excluded from automatic backup to remote storage. See + * {@link #getFilesDir()}. The difference is that files placed under this + * directory will be excluded from automatic backup to remote storage. See * {@link android.app.backup.BackupAgent BackupAgent} for a full discussion * of the automatic backup mechanism in Android. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

+ * No additional permissions are required for the calling app to read or + * write files under the returned path. * - *

No permissions are required to read or write to the returned path, since this - * path is internal storage. - * - * @return The path of the directory holding application files that will not be - * automatically backed up to remote storage. - * + * @return The path of the directory holding application files that will not + * be automatically backed up to remote storage. * @see #openFileOutput * @see #getFileStreamPath * @see #getDir @@ -737,200 +742,256 @@ public abstract FileOutputStream openFileOutput(String name, int mode) public abstract File getNoBackupFilesDir(); /** - * Returns the absolute path to the directory on the primary external filesystem - * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory() - * Environment.getExternalStorageDirectory()}) where the application can - * place persistent files it owns. These files are internal to the - * applications, and not typically visible to the user as media. - * - *

This is like {@link #getFilesDir()} in that these - * files will be deleted when the application is uninstalled, however there - * are some important differences: - * + * Returns the absolute path to the directory on the primary shared/external + * storage device where the application can place persistent files it owns. + * These files are internal to the applications, and not typically visible + * to the user as media. + *

+ * This is like {@link #getFilesDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: *

    - *
  • External files are not always available: they will disappear if the - * user mounts the external storage on a computer or removes it. See the - * APIs on {@link android.os.Environment} for information in the storage state. - *
  • There is no security enforced with these files. For example, any application - * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + *
  • Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
  • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to * these files. *
- * - *

Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions + *

+ * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), it's contents are + * backed by a private user data partition, which means there is little + * benefit to storing data here instead of the private directories returned + * by {@link #getFilesDir()}, etc. + *

+ * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions * are required to read or write to the returned path; it's always - * accessible to the calling app. This only applies to paths generated for - * package name of the calling application. To access paths belonging - * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} - * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. - * - *

On devices with multiple users (as described by {@link UserManager}), - * each user has their own isolated external storage. Applications only - * have access to the external storage for the user they're running as.

- * - *

Here is an example of typical code to manipulate a file in - * an application's private storage:

- * + * accessible to the calling app. This only applies to paths generated for + * package name of the calling application. To access paths belonging to + * other packages, + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. + *

+ * On devices with multiple users (as described by {@link UserManager}), + * each user has their own isolated shared storage. Applications only have + * access to the shared storage for the user they're running as. + *

+ * The returned path may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. + *

+ * Here is an example of typical code to manipulate a file in an + * application's shared storage: + *

* {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java * private_file} - * - *

If you supply a non-null type to this function, the returned - * file will be a path to a sub-directory of the given type. Though these files - * are not automatically scanned by the media scanner, you can explicitly - * add them to the media database with - * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[], - * android.media.MediaScannerConnection.OnScanCompletedListener) - * MediaScannerConnection.scanFile}. - * Note that this is not the same as + *

+ * If you supply a non-null type to this function, the returned + * file will be a path to a sub-directory of the given type. Though these + * files are not automatically scanned by the media scanner, you can + * explicitly add them to the media database with + * {@link android.media.MediaScannerConnection#scanFile(Context, String[], String[], android.media.MediaScannerConnection.OnScanCompletedListener) + * MediaScannerConnection.scanFile}. Note that this is not the same as * {@link android.os.Environment#getExternalStoragePublicDirectory * Environment.getExternalStoragePublicDirectory()}, which provides - * directories of media shared by all applications. The - * directories returned here are - * owned by the application, and their contents will be removed when the - * application is uninstalled. Unlike + * directories of media shared by all applications. The directories returned + * here are owned by the application, and their contents will be removed + * when the application is uninstalled. Unlike * {@link android.os.Environment#getExternalStoragePublicDirectory - * Environment.getExternalStoragePublicDirectory()}, the directory - * returned here will be automatically created for you. - * - *

Here is an example of typical code to manipulate a picture in - * an application's private storage and add it to the media database:

- * + * Environment.getExternalStoragePublicDirectory()}, the directory returned + * here will be automatically created for you. + *

+ * Here is an example of typical code to manipulate a picture in an + * application's shared storage and add it to the media database: + *

* {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java * private_picture} * - * @param type The type of files directory to return. May be null for - * the root of the files directory or one of - * the following Environment constants for a subdirectory: - * {@link android.os.Environment#DIRECTORY_MUSIC}, - * {@link android.os.Environment#DIRECTORY_PODCASTS}, - * {@link android.os.Environment#DIRECTORY_RINGTONES}, - * {@link android.os.Environment#DIRECTORY_ALARMS}, - * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, - * {@link android.os.Environment#DIRECTORY_PICTURES}, or - * {@link android.os.Environment#DIRECTORY_MOVIES}. - * - * @return The path of the directory holding application files - * on external storage. Returns null if external storage is not currently - * mounted so it could not ensure the path exists; you will need to call - * this method again when it is available. - * + * @param type The type of files directory to return. May be {@code null} + * for the root of the files directory or one of the following + * constants for a subdirectory: + * {@link android.os.Environment#DIRECTORY_MUSIC}, + * {@link android.os.Environment#DIRECTORY_PODCASTS}, + * {@link android.os.Environment#DIRECTORY_RINGTONES}, + * {@link android.os.Environment#DIRECTORY_ALARMS}, + * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, + * {@link android.os.Environment#DIRECTORY_PICTURES}, or + * {@link android.os.Environment#DIRECTORY_MOVIES}. + * @return the absolute path to application-specific directory. May return + * {@code null} if shared storage is not currently available. * @see #getFilesDir - * @see android.os.Environment#getExternalStoragePublicDirectory + * @see #getExternalFilesDirs(String) + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) */ @Nullable public abstract File getExternalFilesDir(@Nullable String type); /** * Returns absolute paths to application-specific directories on all - * external storage devices where the application can place persistent files - * it owns. These files are internal to the application, and not typically - * visible to the user as media. + * shared/external storage devices where the application can place + * persistent files it owns. These files are internal to the application, + * and not typically visible to the user as media. *

- * This is like {@link #getFilesDir()} in that these files will be deleted when - * the application is uninstalled, however there are some important differences: + * This is like {@link #getFilesDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: *

    - *
  • External files are not always available: they will disappear if the - * user mounts the external storage on a computer or removes it. - *
  • There is no security enforced with these files. + *
  • Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
  • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. *
*

- * External storage devices returned here are considered a permanent part of - * the device, including both emulated external storage and physical media - * slots, such as SD cards in a battery compartment. The returned paths do - * not include transient devices, such as USB flash drives. + * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), it's contents are + * backed by a private user data partition, which means there is little + * benefit to storing data here instead of the private directories returned + * by {@link #getFilesDir()}, etc. + *

+ * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The + * returned paths do not include transient devices, such as USB flash drives + * connected to handheld devices. *

- * An application may store data on any or all of the returned devices. For + * An application may store data on any or all of the returned devices. For * example, an app may choose to store large files on the device with the * most available space, as measured by {@link StatFs}. *

- * No permissions are required to read or write to the returned paths; they - * are always accessible to the calling app. Write access outside of these - * paths on secondary external storage devices is not available. + * No additional permissions are required for the calling app to read or + * write files under the returned path. Write access outside of these paths + * on secondary external storage devices is not available. *

- * The first path returned is the same as {@link #getExternalFilesDir(String)}. - * Returned paths may be {@code null} if a storage device is unavailable. - * + * The returned path may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. + * + * @param type The type of files directory to return. May be {@code null} + * for the root of the files directory or one of the following + * constants for a subdirectory: + * {@link android.os.Environment#DIRECTORY_MUSIC}, + * {@link android.os.Environment#DIRECTORY_PODCASTS}, + * {@link android.os.Environment#DIRECTORY_RINGTONES}, + * {@link android.os.Environment#DIRECTORY_ALARMS}, + * {@link android.os.Environment#DIRECTORY_NOTIFICATIONS}, + * {@link android.os.Environment#DIRECTORY_PICTURES}, or + * {@link android.os.Environment#DIRECTORY_MOVIES}. + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is + * not currently available. The first path returned is the same as + * {@link #getExternalFilesDir(String)}. * @see #getExternalFilesDir(String) * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) */ public abstract File[] getExternalFilesDirs(String type); /** - * Return the primary external storage directory where this application's OBB - * files (if there are any) can be found. Note if the application does not have - * any OBB files, this directory may not exist. + * Return the primary shared/external storage directory where this + * application's OBB files (if there are any) can be found. Note if the + * application does not have any OBB files, this directory may not exist. *

- * This is like {@link #getFilesDir()} in that these files will be deleted when - * the application is uninstalled, however there are some important differences: + * This is like {@link #getFilesDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: *

    - *
  • External files are not always available: they will disappear if the - * user mounts the external storage on a computer or removes it. - *
  • There is no security enforced with these files. For example, any application - * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + *
  • Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
  • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to * these files. *
*

* Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions * are required to read or write to the returned path; it's always - * accessible to the calling app. This only applies to paths generated for - * package name of the calling application. To access paths belonging - * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} - * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. + * accessible to the calling app. This only applies to paths generated for + * package name of the calling application. To access paths belonging to + * other packages, + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. *

* On devices with multiple users (as described by {@link UserManager}), * multiple users may share the same OBB storage location. Applications * should ensure that multiple instances running under different users don't * interfere with each other. + * + * @return the absolute path to application-specific directory. May return + * {@code null} if shared storage is not currently available. + * @see #getObbDirs() + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) */ public abstract File getObbDir(); /** * Returns absolute paths to application-specific directories on all - * external storage devices where the application's OBB files (if there are - * any) can be found. Note if the application does not have any OBB files, - * these directories may not exist. + * shared/external storage devices where the application's OBB files (if + * there are any) can be found. Note if the application does not have any + * OBB files, these directories may not exist. *

- * This is like {@link #getFilesDir()} in that these files will be deleted when - * the application is uninstalled, however there are some important differences: + * This is like {@link #getFilesDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: *

    - *
  • External files are not always available: they will disappear if the - * user mounts the external storage on a computer or removes it. - *
  • There is no security enforced with these files. + *
  • Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
  • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. *
*

- * External storage devices returned here are considered a permanent part of - * the device, including both emulated external storage and physical media - * slots, such as SD cards in a battery compartment. The returned paths do - * not include transient devices, such as USB flash drives. + * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The + * returned paths do not include transient devices, such as USB flash drives + * connected to handheld devices. *

- * An application may store data on any or all of the returned devices. For + * An application may store data on any or all of the returned devices. For * example, an app may choose to store large files on the device with the * most available space, as measured by {@link StatFs}. *

- * No permissions are required to read or write to the returned paths; they - * are always accessible to the calling app. Write access outside of these - * paths on secondary external storage devices is not available. - *

- * The first path returned is the same as {@link #getObbDir()}. - * Returned paths may be {@code null} if a storage device is unavailable. - * + * No additional permissions are required for the calling app to read or + * write files under the returned path. Write access outside of these paths + * on secondary external storage devices is not available. + * + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is + * not currently available. The first path returned is the same as + * {@link #getObbDir()} * @see #getObbDir() * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) */ public abstract File[] getObbDirs(); /** - * Returns the absolute path to the application specific cache directory - * on the filesystem. These files will be ones that get deleted first when the - * device runs low on storage. - * There is no guarantee when these files will be deleted. - * + * Returns the absolute path to the application specific cache directory on + * the filesystem. These files will be ones that get deleted first when the + * device runs low on storage. There is no guarantee when these files will + * be deleted. + *

* Note: you should not rely on the system deleting these * files for you; you should always have a reasonable maximum, such as 1 MB, * for the amount of space you consume with cache files, and prune those * files when exceeding that space. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

+ * Apps require no extra permissions to read or write to the returned path, + * since this path lives in their private storage. * * @return The path of the directory holding application cache files. - * * @see #openFileOutput * @see #getFileStreamPath * @see #getDir @@ -946,6 +1007,9 @@ public abstract FileOutputStream openFileOutput(String name, int mode) * This location is optimal for storing compiled or optimized code generated * by your application at runtime. *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

* Apps require no extra permissions to read or write to the returned path, * since this path lives in their private storage. * @@ -954,120 +1018,161 @@ public abstract FileOutputStream openFileOutput(String name, int mode) public abstract File getCodeCacheDir(); /** - * Returns the absolute path to the directory on the primary external filesystem - * (that is somewhere on {@link android.os.Environment#getExternalStorageDirectory() - * Environment.getExternalStorageDirectory()} where the application can - * place cache files it owns. These files are internal to the application, and - * not typically visible to the user as media. - * - *

This is like {@link #getCacheDir()} in that these - * files will be deleted when the application is uninstalled, however there - * are some important differences: - * + * Returns absolute path to application-specific directory on the primary + * shared/external storage device where the application can place cache + * files it owns. These files are internal to the application, and not + * typically visible to the user as media. + *

+ * This is like {@link #getCacheDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: *

    - *
  • The platform does not always monitor the space available in external - * storage, and thus may not automatically delete these files. Currently - * the only time files here will be deleted by the platform is when running - * on {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and - * {@link android.os.Environment#isExternalStorageEmulated() - * Environment.isExternalStorageEmulated()} returns true. Note that you should - * be managing the maximum space you will use for these anyway, just like - * with {@link #getCacheDir()}. - *
  • External files are not always available: they will disappear if the - * user mounts the external storage on a computer or removes it. See the - * APIs on {@link android.os.Environment} for information in the storage state. - *
  • There is no security enforced with these files. For example, any application - * holding {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + *
  • The platform does not always monitor the space available in shared + * storage, and thus may not automatically delete these files. Apps should + * always manage the maximum space used in this location. Currently the only + * time files here will be deleted by the platform is when running on + * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and + * {@link Environment#isExternalStorageEmulated(File)} returns true. + *
  • Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
  • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to * these files. *
- * - *

Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions + *

+ * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), it's contents are + * backed by a private user data partition, which means there is little + * benefit to storing data here instead of the private directory returned by + * {@link #getCacheDir()}. + *

+ * Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, no permissions * are required to read or write to the returned path; it's always - * accessible to the calling app. This only applies to paths generated for - * package name of the calling application. To access paths belonging - * to other packages, {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} - * and/or {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. - * - *

On devices with multiple users (as described by {@link UserManager}), - * each user has their own isolated external storage. Applications only - * have access to the external storage for the user they're running as.

- * - * @return The path of the directory holding application cache files - * on external storage. Returns null if external storage is not currently - * mounted so it could not ensure the path exists; you will need to call - * this method again when it is available. + * accessible to the calling app. This only applies to paths generated for + * package name of the calling application. To access paths belonging to + * other packages, + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} and/or + * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} are required. + *

+ * On devices with multiple users (as described by {@link UserManager}), + * each user has their own isolated shared storage. Applications only have + * access to the shared storage for the user they're running as. + *

+ * The returned path may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. * + * @return the absolute path to application-specific directory. May return + * {@code null} if shared storage is not currently available. * @see #getCacheDir + * @see #getExternalCacheDirs() + * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) */ @Nullable public abstract File getExternalCacheDir(); /** * Returns absolute paths to application-specific directories on all - * external storage devices where the application can place cache files it - * owns. These files are internal to the application, and not typically - * visible to the user as media. + * shared/external storage devices where the application can place cache + * files it owns. These files are internal to the application, and not + * typically visible to the user as media. *

- * This is like {@link #getCacheDir()} in that these files will be deleted when - * the application is uninstalled, however there are some important differences: + * This is like {@link #getCacheDir()} in that these files will be deleted + * when the application is uninstalled, however there are some important + * differences: *

    - *
  • External files are not always available: they will disappear if the - * user mounts the external storage on a computer or removes it. - *
  • There is no security enforced with these files. + *
  • The platform does not always monitor the space available in shared + * storage, and thus may not automatically delete these files. Apps should + * always manage the maximum space used in this location. Currently the only + * time files here will be deleted by the platform is when running on + * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1} or later and + * {@link Environment#isExternalStorageEmulated(File)} returns true. + *
  • Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
  • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. *
*

- * External storage devices returned here are considered a permanent part of - * the device, including both emulated external storage and physical media - * slots, such as SD cards in a battery compartment. The returned paths do - * not include transient devices, such as USB flash drives. + * If a shared storage device is emulated (as determined by + * {@link Environment#isExternalStorageEmulated(File)}), it's contents are + * backed by a private user data partition, which means there is little + * benefit to storing data here instead of the private directory returned by + * {@link #getCacheDir()}. + *

+ * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The + * returned paths do not include transient devices, such as USB flash drives + * connected to handheld devices. *

- * An application may store data on any or all of the returned devices. For + * An application may store data on any or all of the returned devices. For * example, an app may choose to store large files on the device with the * most available space, as measured by {@link StatFs}. *

- * No permissions are required to read or write to the returned paths; they - * are always accessible to the calling app. Write access outside of these - * paths on secondary external storage devices is not available. + * No additional permissions are required for the calling app to read or + * write files under the returned path. Write access outside of these paths + * on secondary external storage devices is not available. *

- * The first path returned is the same as {@link #getExternalCacheDir()}. - * Returned paths may be {@code null} if a storage device is unavailable. + * The returned paths may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. * + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is + * not currently available. The first path returned is the same as + * {@link #getExternalCacheDir()}. * @see #getExternalCacheDir() * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) */ public abstract File[] getExternalCacheDirs(); /** * Returns absolute paths to application-specific directories on all - * external storage devices where the application can place media files. - * These files are scanned and made available to other apps through + * shared/external storage devices where the application can place media + * files. These files are scanned and made available to other apps through * {@link MediaStore}. *

* This is like {@link #getExternalFilesDirs} in that these files will be * deleted when the application is uninstalled, however there are some * important differences: *

    - *
  • External files are not always available: they will disappear if the - * user mounts the external storage on a computer or removes it. - *
  • There is no security enforced with these files. + *
  • Shared storage may not always be available, since removable media can + * be ejected by the user. Media state can be checked using + * {@link Environment#getExternalStorageState(File)}. + *
  • There is no security enforced with these files. For example, any + * application holding + * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} can write to + * these files. *
*

- * External storage devices returned here are considered a permanent part of - * the device, including both emulated external storage and physical media - * slots, such as SD cards in a battery compartment. The returned paths do - * not include transient devices, such as USB flash drives. + * Shared storage devices returned here are considered a stable part of the + * device, including physical media slots under a protective cover. The + * returned paths do not include transient devices, such as USB flash drives + * connected to handheld devices. *

* An application may store data on any or all of the returned devices. For * example, an app may choose to store large files on the device with the * most available space, as measured by {@link StatFs}. *

- * No permissions are required to read or write to the returned paths; they - * are always accessible to the calling app. Write access outside of these - * paths on secondary external storage devices is not available. + * No additional permissions are required for the calling app to read or + * write files under the returned path. Write access outside of these paths + * on secondary external storage devices is not available. *

- * Returned paths may be {@code null} if a storage device is unavailable. + * The returned paths may change over time if different shared storage media + * is inserted, so only relative paths should be persisted. * + * @return the absolute paths to application-specific directories. Some + * individual paths may be {@code null} if that shared storage is + * not currently available. * @see Environment#getExternalStorageState(File) + * @see Environment#isExternalStorageEmulated(File) + * @see Environment#isExternalStorageRemovable(File) */ public abstract File[] getExternalMediaDirs(); @@ -1090,6 +1195,12 @@ public abstract FileOutputStream openFileOutput(String name, int mode) * created through a File object will only be accessible by your own * application; you can only set the mode of the entire directory, not * of individual files. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. + *

+ * Apps require no extra permissions to read or write to the returned path, + * since this path lives in their private storage. * * @param name Name of the directory to retrieve. This is a directory * that is created as part of your application data. @@ -1173,6 +1284,9 @@ public abstract SQLiteDatabase openOrCreateDatabase(String name, /** * Returns the absolute path on the filesystem where a database created with * {@link #openOrCreateDatabase} is stored. + *

+ * The returned path may change over time if the calling app is moved to an + * adopted storage device, so only relative paths should be persisted. * * @param name The name of the database for which you would like to get * its path. diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java index 3efd89aa38d..0441cccdd4f 100644 --- a/core/java/android/content/PeriodicSync.java +++ b/core/java/android/content/PeriodicSync.java @@ -21,6 +21,8 @@ import android.os.Parcel; import android.accounts.Account; +import java.util.Objects; + /** * Value type that contains information about a periodic sync. */ @@ -144,7 +146,9 @@ public static boolean syncExtrasEquals(Bundle b1, Bundle b2) { if (!b2.containsKey(key)) { return false; } - if (!b1.get(key).equals(b2.get(key))) { + // Null check. According to ContentResolver#validateSyncExtrasBundle null-valued keys + // are allowed in the bundle. + if (!Objects.equals(b1.get(key), b2.get(key))) { return false; } } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 43cc63b4a3c..e798eb8f7d2 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -33,12 +33,16 @@ */ public class ActivityInfo extends ComponentInfo implements Parcelable { + + // NOTE: When adding new data members be sure to update the copy-constructor, Parcel + // constructor, and writeToParcel. + /** * A style resource identifier (in the package's resources) of this * activity's theme. From the "theme" attribute or, if not set, 0. */ public int theme; - + /** * Constant corresponding to standard in * the {@link android.R.attr#launchMode} attribute. @@ -705,6 +709,7 @@ public ActivityInfo(ActivityInfo orig) { super(orig); theme = orig.theme; launchMode = orig.launchMode; + documentLaunchMode = orig.documentLaunchMode; permission = orig.permission; taskAffinity = orig.taskAffinity; targetActivity = orig.targetActivity; @@ -780,6 +785,7 @@ public void writeToParcel(Parcel dest, int parcelableFlags) { super.writeToParcel(dest, parcelableFlags); dest.writeInt(theme); dest.writeInt(launchMode); + dest.writeInt(documentLaunchMode); dest.writeString(permission); dest.writeString(taskAffinity); dest.writeString(targetActivity); @@ -809,6 +815,7 @@ private ActivityInfo(Parcel source) { super(source); theme = source.readInt(); launchMode = source.readInt(); + documentLaunchMode = source.readInt(); permission = source.readString(); taskAffinity = source.readString(); targetActivity = source.readString(); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index a5e9faf7b1b..fec2c449367 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -63,7 +63,9 @@ interface IPackageManager { boolean isPackageAvailable(String packageName, int userId); PackageInfo getPackageInfo(String packageName, int flags, int userId); int getPackageUid(String packageName, int userId); + int getPackageUidEtc(String packageName, int flags, int userId); int[] getPackageGids(String packageName, int userId); + int[] getPackageGidsEtc(String packageName, int flags, int userId); String[] currentToCanonicalPackageNames(in String[] names); String[] canonicalToCurrentPackageNames(in String[] names); diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 7032c9a861c..99bd3903535 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -894,6 +894,7 @@ private Package parseBaseApk(File apkFile, AssetManager assets, int flags) } pkg.volumeUuid = volumeUuid; + pkg.applicationInfo.volumeUuid = volumeUuid; pkg.baseCodePath = apkPath; pkg.mSignatures = null; diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 285134986f9..379e40f07cd 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -78,14 +78,14 @@ public SystemSensorManager(Context context, Looper mainLooper) { sSensorModuleInitialized = true; nativeClassInit(); } - } - // initialize the sensor list - for (int index = 0;;++index) { - Sensor sensor = new Sensor(); - if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break; - mFullSensorsList.add(sensor); - mHandleToSensor.append(sensor.getHandle(), sensor); + // initialize the sensor list + for (int index = 0;;++index) { + Sensor sensor = new Sensor(); + if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break; + mFullSensorsList.add(sensor); + mHandleToSensor.append(sensor.getHandle(), sensor); + } } } diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index c8b45c78c5b..3601b39fd37 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -61,6 +61,8 @@ interface IInputManager { // Registers an input devices changed listener. void registerInputDevicesChangedListener(IInputDevicesChangedListener listener); + // Queries whether the device is currently in tablet mode + int isInTabletMode(); // Registers a tablet mode change listener void registerTabletModeChangedListener(ITabletModeChangedListener listener); diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index a754d6b1e0c..618864f0e69 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -19,6 +19,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; +import android.annotation.IntDef; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.content.Context; @@ -39,6 +40,8 @@ import android.view.InputDevice; import android.view.InputEvent; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; @@ -179,6 +182,31 @@ public final class InputManager { */ public static final int INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH = 2; // see InputDispatcher.h + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({SWITCH_STATE_UNKNOWN, SWITCH_STATE_OFF, SWITCH_STATE_ON}) + public @interface SwitchState {} + + /** + * Switch State: Unknown. + * + * The system has yet to report a valid value for the switch. + * @hide + */ + public static final int SWITCH_STATE_UNKNOWN = -1; + + /** + * Switch State: Off. + * @hide + */ + public static final int SWITCH_STATE_OFF = 0; + + /** + * Switch State: On. + * @hide + */ + public static final int SWITCH_STATE_ON = 1; + private InputManager(IInputManager im) { mIm = im; } @@ -338,6 +366,23 @@ private int findInputDeviceListenerLocked(InputDeviceListener listener) { return -1; } + /** + * Queries whether the device is in tablet mode. + * + * @return The tablet switch state which is one of {@link #SWITCH_STATE_UNKNOWN}, + * {@link #SWITCH_STATE_OFF} or {@link #SWITCH_STATE_ON}. + * @hide + */ + @SwitchState + public int isInTabletMode() { + try { + return mIm.isInTabletMode(); + } catch (RemoteException ex) { + Log.w(TAG, "Could not get tablet mode state", ex); + return SWITCH_STATE_UNKNOWN; + } + } + /** * Register a tablet mode changed listener. * diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 11e0b487357..6bbd9c86363 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -906,8 +906,12 @@ public NetworkCapabilities getNetworkCapabilities(Network network) { * Tells the underlying networking system that the caller wants to * begin using the named feature. The interpretation of {@code feature} * is completely up to each networking implementation. - *

This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + *

This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.

+ * * @param networkType specifies which network the request pertains to * @param feature the name of the feature to be used * @return an integer value representing the outcome of the request. @@ -957,8 +961,12 @@ public int startUsingNetworkFeature(int networkType, String feature) { * Tells the underlying networking system that the caller is finished * using the named feature. The interpretation of {@code feature} * is completely up to each networking implementation. - *

This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + *

This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.

+ * * @param networkType specifies which network the request pertains to * @param feature the name of the feature that is no longer needed * @return an integer value representing the outcome of the request. @@ -1344,8 +1352,12 @@ public PacketKeepalive startNattKeepalive( * Ensure that a network route exists to deliver traffic to the specified * host via the specified network interface. An attempt to add a route that * already exists is ignored, but treated as successful. - *

This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + *

This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.

+ * * @param networkType the type of the network over which traffic to the specified * host is to be routed * @param hostAddress the IP address of the host to which the route is desired @@ -1365,8 +1377,12 @@ public boolean requestRouteToHost(int networkType, int hostAddress) { * Ensure that a network route exists to deliver traffic to the specified * host via the specified network interface. An attempt to add a route that * already exists is ignored, but treated as successful. - *

This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + *

This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.

+ * * @param networkType the type of the network over which traffic to the specified * host is to be routed * @param hostAddress the IP address of the host to which the route is desired @@ -1566,6 +1582,13 @@ public static ConnectivityManager from(Context context) { return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); } + /** {@hide} */ + public static final void enforceChangePermission(Context context) { + int uid = Binder.getCallingUid(); + Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings + .getPackageNameForUid(context, uid), true /* throwException */); + } + /** {@hide */ public static final void enforceTetherChangePermission(Context context) { if (context.getResources().getStringArray( @@ -1576,8 +1599,8 @@ public static final void enforceTetherChangePermission(Context context) { android.Manifest.permission.CONNECTIVITY_INTERNAL, "ConnectivityService"); } else { int uid = Binder.getCallingUid(); - Settings.checkAndNoteChangeNetworkStateOperation(context, uid, Settings - .getPackageNameForUid(context, uid), true); + Settings.checkAndNoteWriteSettingsOperation(context, uid, Settings + .getPackageNameForUid(context, uid), true /* throwException */); } } @@ -1682,8 +1705,11 @@ public String[] getTetheredDhcpRanges() { * allowed between the tethered devices and this device, though upstream net * access will of course fail until an upstream network interface becomes * active. - *

This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + *

This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.

* * @param iface the interface name to tether. * @return error a {@code TETHER_ERROR} value indicating success or failure type @@ -1700,8 +1726,11 @@ public int tether(String iface) { /** * Stop tethering the named interface. - *

This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + *

This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.

* * @param iface the interface name to untether. * @return error a {@code TETHER_ERROR} value indicating success or failure type @@ -1801,8 +1830,11 @@ public String[] getTetherableBluetoothRegexs() { * attempt to switch to Rndis and subsequently tether the resulting * interface on {@code true} or turn off tethering and switch off * Rndis on {@code false}. - *

This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + *

This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.

* * @param enable a boolean - {@code true} to enable tethering * @return error a {@code TETHER_ERROR} value indicating success or failure type @@ -2486,8 +2518,11 @@ private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, * network may never attain, and whether a network will attain these states * is unknown prior to bringing up the network so the framework does not * know how to go about satisfing a request with these capabilities. - *

This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + *

This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.

* * @param request {@link NetworkRequest} describing this request. * @param networkCallback The {@link NetworkCallback} to be utilized for this @@ -2509,8 +2544,12 @@ public void requestNetwork(NetworkRequest request, NetworkCallback networkCallba * network is not found within the given time (in milliseconds) the * {@link NetworkCallback#unavailable} callback is called. The request must * still be released normally by calling {@link releaseNetworkRequest}. - *

This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + *

This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.

+ * * @param request {@link NetworkRequest} describing this request. * @param networkCallback The callbacks to be utilized for this request. Note * the callbacks must not be shared - they uniquely specify @@ -2583,8 +2622,12 @@ public void requestNetwork(NetworkRequest request, NetworkCallback networkCallba * network may never attain, and whether a network will attain these states * is unknown prior to bringing up the network so the framework does not * know how to go about satisfing a request with these capabilities. - *

This method requires the caller to hold the permission - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}. + * + *

This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.

+ * * @param request {@link NetworkRequest} describing this request. * @param operation Action to perform when the network is available (corresponds * to the {@link NetworkCallback#onAvailable} call. Typically diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java index 87c063ff3eb..97bd5d284c7 100644 --- a/core/java/android/net/DhcpResults.java +++ b/core/java/android/net/DhcpResults.java @@ -21,7 +21,6 @@ import android.text.TextUtils; import android.util.Log; -import java.net.InetAddress; import java.net.Inet4Address; import java.util.Objects; @@ -34,7 +33,7 @@ public class DhcpResults extends StaticIpConfiguration { private static final String TAG = "DhcpResults"; - public InetAddress serverAddress; + public Inet4Address serverAddress; /** Vendor specific information (from RFC 2132). */ public String vendorInfo; @@ -142,7 +141,7 @@ public void writeToParcel(Parcel dest, int flags) { private static void readFromParcel(DhcpResults dhcpResults, Parcel in) { StaticIpConfiguration.readFromParcel(dhcpResults, in); dhcpResults.leaseDuration = in.readInt(); - dhcpResults.serverAddress = NetworkUtils.unparcelInetAddress(in); + dhcpResults.serverAddress = (Inet4Address) NetworkUtils.unparcelInetAddress(in); dhcpResults.vendorInfo = in.readString(); } @@ -183,8 +182,8 @@ public boolean addDns(String addrString) { public boolean setServerAddress(String addrString) { try { - serverAddress = NetworkUtils.numericToInetAddress(addrString); - } catch (IllegalArgumentException e) { + serverAddress = (Inet4Address) NetworkUtils.numericToInetAddress(addrString); + } catch (IllegalArgumentException|ClassCastException e) { Log.e(TAG, "setServerAddress failed with addrString " + addrString); return true; } diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 3f40484b259..a83e7224031 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -50,12 +50,13 @@ public class NetworkPolicyManager { public static final int POLICY_ALLOW_BACKGROUND_BATTERY_SAVE = 0x2; /* RULE_* are not masks and they must be exclusive */ + public static final int RULE_UNKNOWN = -1; /** All network traffic should be allowed. */ - public static final int RULE_ALLOW_ALL = 0x0; + public static final int RULE_ALLOW_ALL = 0; /** Reject traffic on metered networks. */ - public static final int RULE_REJECT_METERED = 0x1; + public static final int RULE_REJECT_METERED = 1; /** Reject traffic on all networks. */ - public static final int RULE_REJECT_ALL = 0x2; + public static final int RULE_REJECT_ALL = 2; public static final int FIREWALL_RULE_DEFAULT = 0; public static final int FIREWALL_RULE_ALLOW = 1; @@ -326,25 +327,4 @@ public static boolean isUidValidForPolicy(Context context, int uid) { // nothing found above; we can apply policy to UID return true; } - - /** {@hide} */ - public static void dumpPolicy(PrintWriter fout, int policy) { - fout.write("["); - if ((policy & POLICY_REJECT_METERED_BACKGROUND) != 0) { - fout.write("REJECT_METERED_BACKGROUND"); - } - fout.write("]"); - } - - /** {@hide} */ - public static void dumpRules(PrintWriter fout, int rules) { - fout.write("["); - if ((rules & RULE_REJECT_METERED) != 0) { - fout.write("REJECT_METERED"); - } else if ((rules & RULE_REJECT_ALL) != 0) { - fout.write("REJECT_ALL"); - } - fout.write("]"); - } - } diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index 57eef834311..b7a411e4b64 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -288,7 +288,8 @@ private boolean matchesMobile(NetworkIdentity ident) { } else { final boolean matchesType = (sForceAllNetworkTypes || contains(DATA_USAGE_NETWORK_TYPES, ident.mType)); - return matchesType && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId); + return matchesType && !ArrayUtils.isEmpty(mMatchSubscriberIds) + && ArrayUtils.contains(mMatchSubscriberIds, ident.mSubscriberId); } } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 23748993f21..862f4c447b3 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -577,7 +577,7 @@ public static class VERSION_CODES { public static final int KITKAT = 19; /** - * Android 4.4W: KitKat for watches, snacks on the run. + * June 2014: Android 4.4W. KitKat for watches, snacks on the run. * *

Applications targeting this or a later release will get these * new changes in behavior:

@@ -595,7 +595,7 @@ public static class VERSION_CODES { public static final int L = 21; /** - * Lollipop. A flat one with beautiful shadows. But still tasty. + * November 2014: Lollipop. A flat one with beautiful shadows. But still tasty. * *

Applications targeting this or a later release will get these * new changes in behavior:

@@ -626,12 +626,38 @@ public static class VERSION_CODES { public static final int LOLLIPOP = 21; /** - * Lollipop with an extra sugar coating on the outside! + * March 2015: Lollipop with an extra sugar coating on the outside! */ public static final int LOLLIPOP_MR1 = 22; /** - * M comes after L. + * M is for Marshmallow! + * + *

Applications targeting this or a later release will get these + * new changes in behavior:

+ *
    + *
  • Runtime permissions. Dangerous permissions are no longer granted at + * install time, but must be requested by the application at runtime through + * {@link android.app.Activity#requestPermissions}.
  • + *
  • Bluetooth and Wi-Fi scanning now requires holding the location permission.
  • + *
  • {@link android.app.AlarmManager#setTimeZone AlarmManager.setTimeZone} will fail if + * the given timezone is non-Olson.
  • + *
  • Activity transitions will only return shared + * elements mapped in the returned view hierarchy back to the calling activity.
  • + *
  • {@link android.view.View} allows a number of behaviors that may break + * existing apps: Canvas throws an exception if restore() is called too many times, + * widgets may return a hint size when returning UNSPECIFIED measure specs, and it + * will respect the attributes {@link android.R.attr#foreground}, + * {@link android.R.attr#foregroundGravity}, {@link android.R.attr#foregroundTint}, and + * {@link android.R.attr#foregroundTintMode}.
  • + *
  • {@link android.view.MotionEvent#getButtonState MotionEvent.getButtonState} + * will no longer report {@link android.view.MotionEvent#BUTTON_PRIMARY} + * and {@link android.view.MotionEvent#BUTTON_SECONDARY} as synonyms for + * {@link android.view.MotionEvent#BUTTON_STYLUS_PRIMARY} and + * {@link android.view.MotionEvent#BUTTON_STYLUS_SECONDARY}.
  • + *
  • {@link android.widget.ScrollView} now respects the layout param margins + * when measuring.
  • + *
*/ public static final int M = 23; } diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 97b85e2531d..fdd34f586e8 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -471,7 +471,7 @@ public static String getOtherLabel(int which) { * * */ - public String getMemoryStat(String statName) { + public String getMemoryStat(String statName) { switch(statName) { case "summary.java-heap": return Integer.toString(getSummaryJavaHeap()); @@ -1538,7 +1538,13 @@ public static Map getRuntimeStats() { /** * Retrieves information about this processes memory usages. This information is broken down by - * how much is in use by dalivk, the native heap, and everything else. + * how much is in use by dalvik, the native heap, and everything else. + * + *

Note: this method directly retrieves memory information for the give process + * from low-level data available to it. It may not be able to retrieve information about + * some protected allocations, such as graphics. If you want to be sure you can see + * all information about allocations by the process, use instead + * {@link android.app.ActivityManager#getProcessMemoryInfo(int[])}.

*/ public static native void getMemoryInfo(MemoryInfo memoryInfo); diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 64d6da57718..f346fe7a721 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -276,8 +276,8 @@ public static File getDataUserPackageDirectory(String volumeUuid, int userId, } /** - * Return the primary external storage directory. This directory may not - * currently be accessible if it has been mounted by the user on their + * Return the primary shared/external storage directory. This directory may + * not currently be accessible if it has been mounted by the user on their * computer, has been removed from the device, or some other problem has * happened. You can determine its current state with * {@link #getExternalStorageState()}. @@ -291,12 +291,15 @@ public static File getDataUserPackageDirectory(String volumeUuid, int userId, * filesystem on a computer. *

* On devices with multiple users (as described by {@link UserManager}), - * each user has their own isolated external storage. Applications only have - * access to the external storage for the user they're running as. + * each user has their own isolated shared storage. Applications only have + * access to the shared storage for the user they're running as. *

- * In devices with multiple "external" storage directories, this directory - * represents the "primary" external storage that the user will interact + * In devices with multiple shared/external storage directories, this + * directory represents the primary storage that the user will interact * with. Access to secondary storage is available through + * {@link Context#getExternalFilesDirs(String)}, + * {@link Context#getExternalCacheDirs()}, and + * {@link Context#getExternalMediaDirs()}. *

* Applications should not directly use this top-level directory, in order * to avoid polluting the user's root namespace. Any files that are private @@ -315,8 +318,9 @@ public static File getDataUserPackageDirectory(String volumeUuid, int userId, *

* Starting in {@link android.os.Build.VERSION_CODES#KITKAT}, if your * application only needs to store internal data, consider using - * {@link Context#getExternalFilesDir(String)} or - * {@link Context#getExternalCacheDir()}, which require no permissions to + * {@link Context#getExternalFilesDir(String)}, + * {@link Context#getExternalCacheDir()}, or + * {@link Context#getExternalMediaDirs()}, which require no permissions to * read or write. *

* This path may change between platform versions, so applications should @@ -325,8 +329,7 @@ public static File getDataUserPackageDirectory(String volumeUuid, int userId, * Here is an example of typical code to monitor the state of external * storage: *

- * {@sample - * development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java + * {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java * monitor_storage} * * @see #getExternalStorageState() @@ -446,32 +449,32 @@ public static File getLegacyExternalStorageObbDirectory() { public static String DIRECTORY_DOCUMENTS = "Documents"; /** - * Get a top-level public external storage directory for placing files of - * a particular type. This is where the user will typically place and - * manage their own files, so you should be careful about what you put here - * to ensure you don't erase their files or get in the way of their own + * Get a top-level shared/external storage directory for placing files of a + * particular type. This is where the user will typically place and manage + * their own files, so you should be careful about what you put here to + * ensure you don't erase their files or get in the way of their own * organization. - * - *

On devices with multiple users (as described by {@link UserManager}), - * each user has their own isolated external storage. Applications only - * have access to the external storage for the user they're running as.

- * - *

Here is an example of typical code to manipulate a picture on - * the public external storage:

- * + *

+ * On devices with multiple users (as described by {@link UserManager}), + * each user has their own isolated shared storage. Applications only have + * access to the shared storage for the user they're running as. + *

+ *

+ * Here is an example of typical code to manipulate a picture on the public + * shared storage: + *

* {@sample development/samples/ApiDemos/src/com/example/android/apis/content/ExternalStorage.java * public_picture} * - * @param type The type of storage directory to return. Should be one of - * {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS}, - * {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS}, - * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES}, - * {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or - * {@link #DIRECTORY_DCIM}. May not be null. - * - * @return Returns the File path for the directory. Note that this - * directory may not yet exist, so you must make sure it exists before - * using it such as with {@link File#mkdirs File.mkdirs()}. + * @param type The type of storage directory to return. Should be one of + * {@link #DIRECTORY_MUSIC}, {@link #DIRECTORY_PODCASTS}, + * {@link #DIRECTORY_RINGTONES}, {@link #DIRECTORY_ALARMS}, + * {@link #DIRECTORY_NOTIFICATIONS}, {@link #DIRECTORY_PICTURES}, + * {@link #DIRECTORY_MOVIES}, {@link #DIRECTORY_DOWNLOADS}, or + * {@link #DIRECTORY_DCIM}. May not be null. + * @return Returns the File path for the directory. Note that this directory + * may not yet exist, so you must make sure it exists before using + * it such as with {@link File#mkdirs File.mkdirs()}. */ public static File getExternalStoragePublicDirectory(String type) { throwIfUserRequired(); @@ -623,7 +626,7 @@ public static File getDownloadCacheDirectory() { public static final String MEDIA_EJECTING = "ejecting"; /** - * Returns the current state of the primary "external" storage device. + * Returns the current state of the primary shared/external storage media. * * @see #getExternalStorageDirectory() * @return one of {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED}, @@ -646,8 +649,8 @@ public static String getStorageState(File path) { } /** - * Returns the current state of the storage device that provides the given - * path. + * Returns the current state of the shared/external storage media at the + * given path. * * @return one of {@link #MEDIA_UNKNOWN}, {@link #MEDIA_REMOVED}, * {@link #MEDIA_UNMOUNTED}, {@link #MEDIA_CHECKING}, @@ -665,7 +668,8 @@ public static String getExternalStorageState(File path) { } /** - * Returns whether the primary "external" storage device is removable. + * Returns whether the primary shared/external storage media is physically + * removable. * * @return true if the storage device can be removed (such as an SD card), * or false if the storage device is built in and cannot be @@ -678,8 +682,8 @@ public static boolean isExternalStorageRemovable() { } /** - * Returns whether the storage device that provides the given path is - * removable. + * Returns whether the shared/external storage media at the given path is + * physically removable. * * @return true if the storage device can be removed (such as an SD card), * or false if the storage device is built in and cannot be @@ -697,9 +701,15 @@ public static boolean isExternalStorageRemovable(File path) { } /** - * Returns whether the primary "external" storage device is emulated. If - * true, data stored on this device will be stored on a portion of the - * internal storage system. + * Returns whether the primary shared/external storage media is emulated. + *

+ * The contents of emulated storage devices are backed by a private user + * data partition, which means there is little benefit to apps storing data + * here instead of the private directories returned by + * {@link Context#getFilesDir()}, etc. + *

+ * This returns true when emulated storage is backed by either internal + * storage or an adopted storage device. * * @see DevicePolicyManager#setStorageEncryption(android.content.ComponentName, * boolean) @@ -711,9 +721,16 @@ public static boolean isExternalStorageEmulated() { } /** - * Returns whether the storage device that provides the given path is - * emulated. If true, data stored on this device will be stored on a portion - * of the internal storage system. + * Returns whether the shared/external storage media at the given path is + * emulated. + *

+ * The contents of emulated storage devices are backed by a private user + * data partition, which means there is little benefit to apps storing data + * here instead of the private directories returned by + * {@link Context#getFilesDir()}, etc. + *

+ * This returns true when emulated storage is backed by either internal + * storage or an adopted storage device. * * @throws IllegalArgumentException if the path is not a valid storage * device. diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 1273772bc4e..5852f5fcd07 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.IntegerRes; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Log; @@ -42,6 +43,8 @@ import java.util.Map; import java.util.Set; +import dalvik.system.VMRuntime; + /** * Container for a message (data and object references) that can * be sent through an IBinder. A Parcel can contain both flattened data @@ -193,6 +196,7 @@ public final class Parcel { * indicating that we're responsible for its lifecycle. */ private boolean mOwnsNativeParcelObject; + private long mNativeSize; private RuntimeException mStack; @@ -244,7 +248,7 @@ public final class Parcel { private static native int nativeDataAvail(long nativePtr); private static native int nativeDataPosition(long nativePtr); private static native int nativeDataCapacity(long nativePtr); - private static native void nativeSetDataSize(long nativePtr, int size); + private static native long nativeSetDataSize(long nativePtr, int size); private static native void nativeSetDataPosition(long nativePtr, int pos); private static native void nativeSetDataCapacity(long nativePtr, int size); @@ -259,7 +263,7 @@ public final class Parcel { private static native void nativeWriteDouble(long nativePtr, double val); private static native void nativeWriteString(long nativePtr, String val); private static native void nativeWriteStrongBinder(long nativePtr, IBinder val); - private static native void nativeWriteFileDescriptor(long nativePtr, FileDescriptor val); + private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val); private static native byte[] nativeCreateByteArray(long nativePtr); private static native byte[] nativeReadBlob(long nativePtr); @@ -272,13 +276,13 @@ public final class Parcel { private static native FileDescriptor nativeReadFileDescriptor(long nativePtr); private static native long nativeCreate(); - private static native void nativeFreeBuffer(long nativePtr); + private static native long nativeFreeBuffer(long nativePtr); private static native void nativeDestroy(long nativePtr); private static native byte[] nativeMarshall(long nativePtr); - private static native void nativeUnmarshall( + private static native long nativeUnmarshall( long nativePtr, byte[] data, int offset, int length); - private static native void nativeAppendFrom( + private static native long nativeAppendFrom( long thisNativePtr, long otherNativePtr, int offset, int length); private static native boolean nativeHasFileDescriptors(long nativePtr); private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName); @@ -390,7 +394,7 @@ public final int dataCapacity() { * @param size The new number of bytes in the Parcel. */ public final void setDataSize(int size) { - nativeSetDataSize(mNativePtr, size); + updateNativeSize(nativeSetDataSize(mNativePtr, size)); } /** @@ -442,11 +446,11 @@ public final byte[] marshall() { * Set the bytes in data to be the raw bytes of this Parcel. */ public final void unmarshall(byte[] data, int offset, int length) { - nativeUnmarshall(mNativePtr, data, offset, length); + updateNativeSize(nativeUnmarshall(mNativePtr, data, offset, length)); } public final void appendFrom(Parcel parcel, int offset, int length) { - nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length); + updateNativeSize(nativeAppendFrom(mNativePtr, parcel.mNativePtr, offset, length)); } /** @@ -599,7 +603,24 @@ public final void writeStrongInterface(IInterface val) { * if {@link Parcelable#PARCELABLE_WRITE_RETURN_VALUE} is set.

*/ public final void writeFileDescriptor(FileDescriptor val) { - nativeWriteFileDescriptor(mNativePtr, val); + updateNativeSize(nativeWriteFileDescriptor(mNativePtr, val)); + } + + private void updateNativeSize(long newNativeSize) { + if (mOwnsNativeParcelObject) { + if (newNativeSize > Integer.MAX_VALUE) { + newNativeSize = Integer.MAX_VALUE; + } + if (newNativeSize != mNativeSize) { + int delta = (int) (newNativeSize - mNativeSize); + if (delta > 0) { + VMRuntime.getRuntime().registerNativeAllocation(delta); + } else { + VMRuntime.getRuntime().registerNativeFree(-delta); + } + mNativeSize = newNativeSize; + } + } } /** @@ -2545,7 +2566,7 @@ private void init(long nativePtr) { private void freeBuffer() { if (mOwnsNativeParcelObject) { - nativeFreeBuffer(mNativePtr); + updateNativeSize(nativeFreeBuffer(mNativePtr)); } } @@ -2553,6 +2574,7 @@ private void destroy() { if (mNativePtr != 0) { if (mOwnsNativeParcelObject) { nativeDestroy(mNativePtr); + updateNativeSize(0); } mNativePtr = 0; } diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java index 979c828c476..2445bc28abd 100644 --- a/core/java/android/preference/SeekBarVolumizer.java +++ b/core/java/android/preference/SeekBarVolumizer.java @@ -383,6 +383,7 @@ public void setListening(boolean listening) { final IntentFilter filter = new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION); filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION); filter.addAction(NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED); + filter.addAction(AudioManager.STREAM_DEVICES_CHANGED_ACTION); mContext.registerReceiver(this, filter); } else { mContext.unregisterReceiver(this); @@ -395,13 +396,7 @@ public void onReceive(Context context, Intent intent) { if (AudioManager.VOLUME_CHANGED_ACTION.equals(action)) { int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); int streamValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1); - final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType) - : (streamType == mStreamType); - if (mSeekBar != null && streamMatch && streamValue != -1) { - final boolean muted = mAudioManager.isStreamMute(mStreamType) - || streamValue == 0; - mUiHandler.postUpdateSlider(streamValue, mLastAudibleStreamVolume, muted); - } + updateVolumeSlider(streamType, streamValue); } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) { if (mNotificationOrRing) { mRingerMode = mAudioManager.getRingerModeInternal(); @@ -409,10 +404,24 @@ public void onReceive(Context context, Intent intent) { if (mAffectedByRingerMode) { updateSlider(); } + } else if (AudioManager.STREAM_DEVICES_CHANGED_ACTION.equals(action)) { + int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1); + int streamVolume = mAudioManager.getStreamVolume(streamType); + updateVolumeSlider(streamType, streamVolume); } else if (NotificationManager.ACTION_INTERRUPTION_FILTER_CHANGED.equals(action)) { mZenMode = mNotificationManager.getZenMode(); updateSlider(); } } + + private void updateVolumeSlider(int streamType, int streamValue) { + final boolean streamMatch = mNotificationOrRing ? isNotificationOrRing(streamType) + : (streamType == mStreamType); + if (mSeekBar != null && streamMatch && streamValue != -1) { + final boolean muted = mAudioManager.isStreamMute(mStreamType) + || streamValue == 0; + mUiHandler.postUpdateSlider(streamValue, mLastAudibleStreamVolume, muted); + } + } } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 7ebf829ab6e..6a9cc536eec 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1433,25 +1433,6 @@ public static boolean canDrawOverlays(Context context) { .getPackageNameForUid(context, uid), false); } - /** - * An app can use this method to check if it is currently allowed to change the network - * state. In order to be allowed to do so, an app must first declare either the - * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} or - * {@link android.Manifest.permission#WRITE_SETTINGS} permission in its manifest. If it - * is currently disallowed, it can prompt the user to grant it this capability through a - * management UI by sending an Intent with action - * {@link android.provider.Settings#ACTION_MANAGE_WRITE_SETTINGS}. - * - * @param context A context - * @return true if the calling app can change the state of network, false otherwise. - * @hide - */ - public static boolean canChangeNetworkState(Context context) { - int uid = Binder.getCallingUid(); - return Settings.isCallingPackageAllowedToChangeNetworkState(context, uid, Settings - .getPackageNameForUid(context, uid), false); - } - /** * System settings, containing miscellaneous system preferences. This * table holds simple name/value pairs. There are convenience @@ -8842,7 +8823,7 @@ public static boolean isCallingPackageAllowedToWriteSettings(Context context, in * write/modify system settings, as the condition differs for pre-M, M+, and * privileged/preinstalled apps. If the provided uid does not match the * callingPackage, a negative result will be returned. The caller is expected to have - * either WRITE_SETTINGS or CHANGE_NETWORK_STATE permission declared. + * the WRITE_SETTINGS permission declared. * * Note: if the check is successful, the operation of this app will be updated to the * current time. @@ -8858,31 +8839,22 @@ public static boolean checkAndNoteWriteSettingsOperation(Context context, int ui /** * Performs a strict and comprehensive check of whether a calling package is allowed to * change the state of network, as the condition differs for pre-M, M+, and - * privileged/preinstalled apps. If the provided uid does not match the - * callingPackage, a negative result will be returned. The caller is expected to have - * either of CHANGE_NETWORK_STATE or WRITE_SETTINGS permission declared. - * @hide - */ - public static boolean isCallingPackageAllowedToChangeNetworkState(Context context, int uid, - String callingPackage, boolean throwException) { - return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid, - callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS, - PM_CHANGE_NETWORK_STATE, false); - } - - /** - * Performs a strict and comprehensive check of whether a calling package is allowed to - * change the state of network, as the condition differs for pre-M, M+, and - * privileged/preinstalled apps. If the provided uid does not match the - * callingPackage, a negative result will be returned. The caller is expected to have - * either CHANGE_NETWORK_STATE or WRITE_SETTINGS permission declared. + * privileged/preinstalled apps. The caller is expected to have either the + * CHANGE_NETWORK_STATE or the WRITE_SETTINGS permission declared. Either of these + * permissions allow changing network state; WRITE_SETTINGS is a runtime permission and + * can be revoked, but (except in M, excluding M MRs), CHANGE_NETWORK_STATE is a normal + * permission and cannot be revoked. See http://b/23597341 * - * Note: if the check is successful, the operation of this app will be updated to the - * current time. + * Note: if the check succeeds because the application holds WRITE_SETTINGS, the operation + * of this app will be updated to the current time. * @hide */ public static boolean checkAndNoteChangeNetworkStateOperation(Context context, int uid, String callingPackage, boolean throwException) { + if (context.checkCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE) + == PackageManager.PERMISSION_GRANTED) { + return true; + } return isCallingPackageAllowedToPerformAppOpsProtectedOperation(context, uid, callingPackage, throwException, AppOpsManager.OP_WRITE_SETTINGS, PM_CHANGE_NETWORK_STATE, true); diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index db19f7a4ce2..8763496194d 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -41,6 +41,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.Locale; import java.util.Objects; import java.util.UUID; @@ -68,6 +69,7 @@ public class ZenModeConfig implements Parcelable { public static final int[] MINUTE_BUCKETS = generateMinuteBuckets(); private static final int SECONDS_MS = 1000; private static final int MINUTES_MS = 60 * SECONDS_MS; + private static final int DAY_MINUTES = 24 * 60; private static final int ZERO_VALUE_MS = 10 * SECONDS_MS; private static final boolean DEFAULT_ALLOW_CALLS = true; @@ -652,40 +654,68 @@ public static Condition toTimeCondition(Context context, int minutesFromNow, int boolean shortVersion) { final long now = System.currentTimeMillis(); final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS; - return toTimeCondition(context, now + millis, minutesFromNow, now, userHandle, - shortVersion); + return toTimeCondition(context, now + millis, minutesFromNow, userHandle, shortVersion); } - public static Condition toTimeCondition(Context context, long time, int minutes, long now, + public static Condition toTimeCondition(Context context, long time, int minutes, int userHandle, boolean shortVersion) { - final int num, summaryResId, line1ResId; + final int num; + String summary, line1, line2; + final CharSequence formattedTime = getFormattedTime(context, time, userHandle); + final Resources res = context.getResources(); if (minutes < 60) { // display as minutes num = minutes; - summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short + int summaryResId = shortVersion ? R.plurals.zen_mode_duration_minutes_summary_short : R.plurals.zen_mode_duration_minutes_summary; - line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short + summary = res.getQuantityString(summaryResId, num, num, formattedTime); + int line1ResId = shortVersion ? R.plurals.zen_mode_duration_minutes_short : R.plurals.zen_mode_duration_minutes; - } else { + line1 = res.getQuantityString(line1ResId, num, num, formattedTime); + line2 = res.getString(R.string.zen_mode_until, formattedTime); + } else if (minutes < DAY_MINUTES) { // display as hours num = Math.round(minutes / 60f); - summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short + int summaryResId = shortVersion ? R.plurals.zen_mode_duration_hours_summary_short : R.plurals.zen_mode_duration_hours_summary; - line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short + summary = res.getQuantityString(summaryResId, num, num, formattedTime); + int line1ResId = shortVersion ? R.plurals.zen_mode_duration_hours_short : R.plurals.zen_mode_duration_hours; + line1 = res.getQuantityString(line1ResId, num, num, formattedTime); + line2 = res.getString(R.string.zen_mode_until, formattedTime); + } else { + // display as day/time + summary = line1 = line2 = res.getString(R.string.zen_mode_until, formattedTime); } - final String skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma"; - final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); - final CharSequence formattedTime = DateFormat.format(pattern, time); - final Resources res = context.getResources(); - final String summary = res.getQuantityString(summaryResId, num, num, formattedTime); - final String line1 = res.getQuantityString(line1ResId, num, num, formattedTime); - final String line2 = res.getString(R.string.zen_mode_until, formattedTime); final Uri id = toCountdownConditionId(time); return new Condition(id, summary, line1, line2, 0, Condition.STATE_TRUE, Condition.FLAG_RELEVANT_NOW); } + public static Condition toNextAlarmCondition(Context context, long now, long alarm, + int userHandle) { + final CharSequence formattedTime = getFormattedTime(context, alarm, userHandle); + final Resources res = context.getResources(); + final String line1 = res.getString(R.string.zen_mode_alarm, formattedTime); + final Uri id = toCountdownConditionId(alarm); + return new Condition(id, "", line1, "", 0, Condition.STATE_TRUE, + Condition.FLAG_RELEVANT_NOW); + } + + private static CharSequence getFormattedTime(Context context, long time, int userHandle) { + String skeleton = "EEE " + (DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma"); + GregorianCalendar now = new GregorianCalendar(); + GregorianCalendar endTime = new GregorianCalendar(); + endTime.setTimeInMillis(time); + if (now.get(Calendar.YEAR) == endTime.get(Calendar.YEAR) + && now.get(Calendar.MONTH) == endTime.get(Calendar.MONTH) + && now.get(Calendar.DATE) == endTime.get(Calendar.DATE)) { + skeleton = DateFormat.is24HourFormat(context, userHandle) ? "Hm" : "hma"; + } + final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton); + return DateFormat.format(pattern, time); + } + // ==== Built-in system conditions ==== public static final String SYSTEM_AUTHORITY = "android"; @@ -883,11 +913,6 @@ public String newRuleId() { return UUID.randomUUID().toString().replace("-", ""); } - public static String getConditionLine1(Context context, ZenModeConfig config, - int userHandle, boolean shortVersion) { - return getConditionLine(context, config, userHandle, true /*useLine1*/, shortVersion); - } - public static String getConditionSummary(Context context, ZenModeConfig config, int userHandle, boolean shortVersion) { return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion); @@ -906,8 +931,8 @@ private static String getConditionLine(Context context, ZenModeConfig config, if (time > 0) { final long now = System.currentTimeMillis(); final long span = time - now; - c = toTimeCondition(context, - time, Math.round(span / (float) MINUTES_MS), now, userHandle, shortVersion); + c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS), + userHandle, shortVersion); } final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary; return TextUtils.isEmpty(rt) ? "" : rt; diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java index 10a994adde2..f2b6041457d 100644 --- a/core/java/android/text/Hyphenator.java +++ b/core/java/android/text/Hyphenator.java @@ -16,15 +16,17 @@ package android.text; -import com.android.internal.annotations.GuardedBy; - import android.annotation.Nullable; import android.util.Log; -import libcore.io.IoUtils; +import com.android.internal.annotations.GuardedBy; import java.io.File; import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.util.HashMap; import java.util.Locale; @@ -45,19 +47,29 @@ public class Hyphenator { @GuardedBy("sLock") final static HashMap sMap = new HashMap(); - final static Hyphenator sEmptyHyphenator = new Hyphenator(StaticLayout.nLoadHyphenator("")); + final static Hyphenator sEmptyHyphenator = + new Hyphenator(StaticLayout.nLoadHyphenator(null, 0), null); final private long mNativePtr; - private Hyphenator(long nativePtr) { + // We retain a reference to the buffer to keep the memory mapping valid + @SuppressWarnings("unused") + final private ByteBuffer mBuffer; + + private Hyphenator(long nativePtr, ByteBuffer b) { mNativePtr = nativePtr; + mBuffer = b; } - public static long get(@Nullable Locale locale) { + public long getNativePtr() { + return mNativePtr; + } + + public static Hyphenator get(@Nullable Locale locale) { synchronized (sLock) { Hyphenator result = sMap.get(locale); if (result != null) { - return result.mNativePtr; + return result; } // TODO: Convert this a proper locale-fallback system @@ -67,7 +79,7 @@ public static long get(@Nullable Locale locale) { result = sMap.get(languageOnlyLocale); if (result != null) { sMap.put(locale, result); - return result.mNativePtr; + return result; } // Fall back to script-only, if available @@ -80,22 +92,28 @@ public static long get(@Nullable Locale locale) { result = sMap.get(scriptOnlyLocale); if (result != null) { sMap.put(locale, result); - return result.mNativePtr; + return result; } } sMap.put(locale, sEmptyHyphenator); // To remember we found nothing. } - return sEmptyHyphenator.mNativePtr; + return sEmptyHyphenator; } private static Hyphenator loadHyphenator(String languageTag) { - String patternFilename = "hyph-"+languageTag.toLowerCase(Locale.US)+".pat.txt"; + String patternFilename = "hyph-" + languageTag.toLowerCase(Locale.US) + ".hyb"; File patternFile = new File(getSystemHyphenatorLocation(), patternFilename); try { - String patternData = IoUtils.readFileAsString(patternFile.getAbsolutePath()); - long nativePtr = StaticLayout.nLoadHyphenator(patternData); - return new Hyphenator(nativePtr); + RandomAccessFile f = new RandomAccessFile(patternFile, "r"); + try { + FileChannel fc = f.getChannel(); + MappedByteBuffer buf = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); + long nativePtr = StaticLayout.nLoadHyphenator(buf, 0); + return new Hyphenator(nativePtr, buf); + } finally { + f.close(); + } } catch (IOException e) { Log.e(TAG, "error loading hyphenation " + patternFile, e); return null; @@ -148,7 +166,7 @@ public static void init() { sMap.put(null, null); // TODO: replace this with a discovery-based method that looks into /system/usr/hyphen-data - String[] availableLanguages = {"en-US", "eu", "hu", "hy", "nb", "nn", "sa", "und-Ethi"}; + String[] availableLanguages = {"en-US", "eu", "hu", "hy", "nb", "nn", "und-Ethi"}; for (int i = 0; i < availableLanguages.length; i++) { String languageTag = availableLanguages[i]; Hyphenator h = loadHyphenator(languageTag); diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 3b0def29f00..fdc6cb1261d 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -29,6 +29,7 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; +import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Locale; @@ -341,7 +342,8 @@ public Builder setIndents(int[] leftIndents, int[] rightIndents) { private void setLocale(Locale locale) { if (!locale.equals(mLocale)) { - nSetLocale(mNativePtr, locale.toLanguageTag(), Hyphenator.get(locale)); + nSetLocale(mNativePtr, locale.toLanguageTag(), + Hyphenator.get(locale).getNativePtr()); mLocale = locale; } } @@ -625,7 +627,9 @@ private StaticLayout(Builder b) { chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class); - if (chooseHt.length != 0) { + if (chooseHt.length == 0) { + chooseHt = null; // So that out() would not assume it has any contents + } else { if (chooseHtv == null || chooseHtv.length < chooseHt.length) { chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length); @@ -808,7 +812,7 @@ private StaticLayout(Builder b) { v = out(source, here, endPos, fmAscent, fmDescent, fmTop, fmBottom, - v, spacingmult, spacingadd, chooseHt,chooseHtv, fm, flags[breakIndex], + v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, flags[breakIndex], needMultiply, chdirs, dir, easy, bufEnd, includepad, trackpad, chs, widths, paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint, moreChars); @@ -1243,7 +1247,7 @@ public int getEllipsizedWidth() { private static native void nFreeBuilder(long nativePtr); private static native void nFinishBuilder(long nativePtr); - /* package */ static native long nLoadHyphenator(String patternData); + /* package */ static native long nLoadHyphenator(ByteBuffer buf, int offset); private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator); diff --git a/core/java/android/transition/ChangeBounds.java b/core/java/android/transition/ChangeBounds.java index c82587b76fe..6d1d893f243 100644 --- a/core/java/android/transition/ChangeBounds.java +++ b/core/java/android/transition/ChangeBounds.java @@ -432,23 +432,24 @@ public void onTransitionResume(Transition transition) { return anim; } } else { - int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X); - int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y); - int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X); - int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y); + sceneRoot.getLocationInWindow(tempLocation); + int startX = (Integer) startValues.values.get(PROPNAME_WINDOW_X) - tempLocation[0]; + int startY = (Integer) startValues.values.get(PROPNAME_WINDOW_Y) - tempLocation[1]; + int endX = (Integer) endValues.values.get(PROPNAME_WINDOW_X) - tempLocation[0]; + int endY = (Integer) endValues.values.get(PROPNAME_WINDOW_Y) - tempLocation[1]; // TODO: also handle size changes: check bounds and animate size changes if (startX != endX || startY != endY) { - sceneRoot.getLocationInWindow(tempLocation); - Bitmap bitmap = Bitmap.createBitmap(view.getWidth(), view.getHeight(), - Bitmap.Config.ARGB_8888); + final int width = view.getWidth(); + final int height = view.getHeight(); + Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); view.draw(canvas); final BitmapDrawable drawable = new BitmapDrawable(bitmap); + drawable.setBounds(startX, startY, startX + width, startY + height); final float transitionAlpha = view.getTransitionAlpha(); view.setTransitionAlpha(0); sceneRoot.getOverlay().add(drawable); - Path topLeftPath = getPathMotion().getPath(startX - tempLocation[0], - startY - tempLocation[1], endX - tempLocation[0], endY - tempLocation[1]); + Path topLeftPath = getPathMotion().getPath(startX, startY, endX, endY); PropertyValuesHolder origin = PropertyValuesHolder.ofObject( DRAWABLE_ORIGIN_PROPERTY, null, topLeftPath); ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(drawable, origin); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 279e9d3b407..f69f9da272f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2355,7 +2355,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 1 PFLAG3_APPLYING_INSETS * 1 PFLAG3_FITTING_SYSTEM_WINDOWS * 1 PFLAG3_NESTED_SCROLLING_ENABLED - * 1 PFLAG3_ASSIST_BLOCKED + * 1 PFLAG3_SCROLL_INDICATOR_TOP + * 1 PFLAG3_SCROLL_INDICATOR_BOTTOM + * 1 PFLAG3_SCROLL_INDICATOR_LEFT + * 1 PFLAG3_SCROLL_INDICATOR_RIGHT + * 1 PFLAG3_SCROLL_INDICATOR_START + * 1 PFLAG3_SCROLL_INDICATOR_END + * 1 PFLAG3_ASSIST_BLOCKED * |-------|-------|-------|-------| */ @@ -2549,7 +2555,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, *

Indicates that we are allowing {@link ViewStructure} to traverse * into this view.

*/ - static final int PFLAG3_ASSIST_BLOCKED = 0x100; + static final int PFLAG3_ASSIST_BLOCKED = 0x4000; /** * Always allow a user to over-scroll this view, provided it is a diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 284e12cb4d5..0b261757572 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -578,7 +578,7 @@ public void setWindowManager(WindowManager wm, IBinder appToken, String appName, void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) { CharSequence curTitle = wp.getTitle(); if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW && - wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { + wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) { if (wp.token == null) { View decor = peekDecorView(); if (decor != null) { @@ -588,25 +588,38 @@ void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) { if (curTitle == null || curTitle.length() == 0) { String title; if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA) { - title="Media"; + title = "Media"; } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY) { - title="MediaOvr"; + title = "MediaOvr"; } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) { - title="Panel"; + title = "Panel"; } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL) { - title="SubPanel"; + title = "SubPanel"; } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL) { - title="AboveSubPanel"; + title = "AboveSubPanel"; } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG) { - title="AtchDlg"; + title = "AtchDlg"; } else { - title=Integer.toString(wp.type); + title = Integer.toString(wp.type); } if (mAppName != null) { title += ":" + mAppName; } wp.setTitle(title); } + } else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW && + wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) { + // We don't set the app token to this system window because the life cycles should be + // independent. If an app creates a system window and then the app goes to the stopped + // state, the system window should not be affected (can still show and receive input + // events). + if (curTitle == null || curTitle.length() == 0) { + String title = "Sys" + Integer.toString(wp.type); + if (mAppName != null) { + title += ":" + mAppName; + } + wp.setTitle(title); + } } else { if (wp.token == null) { wp.token = mContainer == null ? mAppToken : mContainer.mAppToken; diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 5b042c63b01..13c1937dd99 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -16,12 +16,6 @@ package android.widget; -import java.text.BreakIterator; -import java.util.Arrays; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; - import android.R; import android.annotation.Nullable; import android.app.PendingIntent; @@ -112,6 +106,12 @@ import com.android.internal.util.Preconditions; import com.android.internal.widget.EditableInputConnection; +import java.text.BreakIterator; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + /** * Helper class used by TextView to handle editable text views. @@ -1004,8 +1004,7 @@ public boolean performLongClick(boolean handled) { } if (!handled && mTextActionMode != null) { - // TODO: Fix dragging in extracted mode. - if (touchPositionIsInSelection() && !mTextView.isInExtractedMode()) { + if (touchPositionIsInSelection()) { // Start a drag final int start = mTextView.getSelectionStart(); final int end = mTextView.getSelectionEnd(); @@ -4254,10 +4253,14 @@ private void positionAndAdjustForCrossingHandles(int offset) { positionAtCursorOffset(offset, false); } + /** + * @param offset Cursor offset. Must be in [-1, length]. + * @param parentScrolled If the parent has been scrolled or not. + */ @Override protected void positionAtCursorOffset(int offset, boolean parentScrolled) { super.positionAtCursorOffset(offset, parentScrolled); - mInWord = !getWordIteratorWithText().isBoundary(offset); + mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset); } @Override @@ -4490,10 +4493,14 @@ private void positionAndAdjustForCrossingHandles(int offset) { positionAtCursorOffset(offset, false); } + /** + * @param offset Cursor offset. Must be in [-1, length]. + * @param parentScrolled If the parent has been scrolled or not. + */ @Override protected void positionAtCursorOffset(int offset, boolean parentScrolled) { super.positionAtCursorOffset(offset, parentScrolled); - mInWord = !getWordIteratorWithText().isBoundary(offset); + mInWord = (offset != -1) && !getWordIteratorWithText().isBoundary(offset); } @Override @@ -4860,9 +4867,8 @@ public void onTouchEvent(MotionEvent event) { mEndHandle.showAtLocation(endOffset); // No longer the first dragging motion, reset. - if (!(mTextView.isInExtractedMode())) { - startSelectionActionMode(); - } + startSelectionActionMode(); + mDragAcceleratorActive = false; mStartOffset = -1; mSwitchedLines = false; diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index ddbaa9db644..cc6ab657ed7 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -234,6 +234,8 @@ public void invalidateDrawable(Drawable dr) { if (w != mDrawableWidth || h != mDrawableHeight) { mDrawableWidth = w; mDrawableHeight = h; + // updates the matrix, which is dependent on the bounds + configureBounds(); } } /* we invalidate the whole view in this case because it's very diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index 4dfa7db793b..abcd614f3fc 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -704,7 +704,7 @@ private void measureChildHorizontal( } final int heightMode; - if (params.width == LayoutParams.MATCH_PARENT) { + if (params.height == LayoutParams.MATCH_PARENT) { heightMode = MeasureSpec.EXACTLY; } else { heightMode = MeasureSpec.AT_MOST; diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index a1462c4886a..0c4b60b0202 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -16,6 +16,8 @@ package android.widget; +import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; + import android.R; import android.annotation.ColorInt; import android.annotation.DrawableRes; @@ -115,14 +117,14 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; -import android.view.ViewParent; -import android.view.ViewStructure; import android.view.ViewConfiguration; import android.view.ViewDebug; import android.view.ViewGroup.LayoutParams; +import android.view.ViewHierarchyEncoder; +import android.view.ViewParent; import android.view.ViewRootImpl; +import android.view.ViewStructure; import android.view.ViewTreeObserver; -import android.view.ViewHierarchyEncoder; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; @@ -149,8 +151,6 @@ import java.util.ArrayList; import java.util.Locale; -import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; - /** * Displays text to the user and optionally allows them to edit it. A TextView * is a complete text editor, however the basic class is configured to not @@ -1474,7 +1474,12 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { } } } + } else if (mText instanceof Spannable) { + // Reset the selection. + stopTextActionMode(); + Selection.setSelection((Spannable) mText, getSelectionStart(), getSelectionEnd()); } + if (mEditor.hasSelectionController()) { mEditor.startSelectionActionMode(); } @@ -5243,14 +5248,6 @@ public boolean onPreDraw() { mEditor.mCreatedWithASelection = false; } - // Phone specific code (there is no ExtractEditText on tablets). - // ExtractEditText does not call onFocus when it is displayed, and mHasSelectionOnFocus can - // not be set. Do the test here instead. - if (isInExtractedMode() && hasSelection() && mEditor != null - && mEditor.mTextActionMode == null && isShown() && hasWindowFocus()) { - mEditor.startSelectionActionMode(); - } - unregisterForPreDraw(); return true; diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 2e0bf0652c4..7699673701f 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -16,6 +16,8 @@ package com.android.internal.app; +import android.animation.ObjectAnimator; +import android.annotation.NonNull; import android.app.Activity; import android.content.ComponentName; import android.content.Context; @@ -29,6 +31,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.database.DataSetObserver; +import android.graphics.Color; import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.os.Bundle; @@ -46,13 +49,18 @@ import android.service.chooser.IChooserTargetResult; import android.service.chooser.IChooserTargetService; import android.text.TextUtils; +import android.util.FloatProperty; import android.util.Log; import android.util.Slog; import android.view.LayoutInflater; import android.view.View; +import android.view.View.MeasureSpec; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; +import android.view.ViewGroup.LayoutParams; +import android.view.animation.AnimationUtils; +import android.view.animation.Interpolator; import android.widget.AbsListView; import android.widget.BaseAdapter; import android.widget.ListView; @@ -80,6 +88,7 @@ public class ChooserActivity extends ResolverActivity { private Intent mReferrerFillInIntent; private ChooserListAdapter mChooserListAdapter; + private ChooserRowAdapter mChooserRowAdapter; private final List mServiceConnections = new ArrayList<>(); @@ -253,7 +262,9 @@ void onPrepareAdapterView(AbsListView adapterView, ResolveListAdapter adapter, boolean alwaysUseOption) { final ListView listView = adapterView instanceof ListView ? (ListView) adapterView : null; mChooserListAdapter = (ChooserListAdapter) adapter; - adapterView.setAdapter(new ChooserRowAdapter(mChooserListAdapter)); + mChooserRowAdapter = new ChooserRowAdapter(mChooserListAdapter); + mChooserRowAdapter.registerDataSetObserver(new OffsetDataSetObserver(adapterView)); + adapterView.setAdapter(mChooserRowAdapter); if (listView != null) { listView.setItemsCanFocus(true); } @@ -362,6 +373,11 @@ void queryTargetServices(ChooserListAdapter adapter) { int targetsToQuery = 0; for (int i = 0, N = adapter.getDisplayResolveInfoCount(); i < N; i++) { final DisplayResolveInfo dri = adapter.getDisplayResolveInfo(i); + if (adapter.getScore(dri) == 0) { + // A score of 0 means the app hasn't been used in some time; + // don't query it as it's not likely to be relevant. + continue; + } final ActivityInfo ai = dri.getResolveInfo().activityInfo; final Bundle md = ai.metaData; final String serviceName = md != null ? convertServiceName(ai.packageName, @@ -909,7 +925,63 @@ static class BaseChooserTargetComparator implements Comparator { @Override public int compare(ChooserTarget lhs, ChooserTarget rhs) { // Descending order - return (int) Math.signum(lhs.getScore() - rhs.getScore()); + return (int) Math.signum(rhs.getScore() - lhs.getScore()); + } + } + + static class RowScale { + private static final int DURATION = 400; + + float mScale; + ChooserRowAdapter mAdapter; + private final ObjectAnimator mAnimator; + + public static final FloatProperty PROPERTY = + new FloatProperty("scale") { + @Override + public void setValue(RowScale object, float value) { + object.mScale = value; + object.mAdapter.notifyDataSetChanged(); + } + + @Override + public Float get(RowScale object) { + return object.mScale; + } + }; + + public RowScale(@NonNull ChooserRowAdapter adapter, float from, float to) { + mAdapter = adapter; + mScale = from; + if (from == to) { + mAnimator = null; + return; + } + + mAnimator = ObjectAnimator.ofFloat(this, PROPERTY, from, to).setDuration(DURATION); + } + + public RowScale setInterpolator(Interpolator interpolator) { + if (mAnimator != null) { + mAnimator.setInterpolator(interpolator); + } + return this; + } + + public float get() { + return mScale; + } + + public void startAnimation() { + if (mAnimator != null) { + mAnimator.start(); + } + } + + public void cancelAnimation() { + if (mAnimator != null) { + mAnimator.cancel(); + } } } @@ -917,15 +989,51 @@ class ChooserRowAdapter extends BaseAdapter { private ChooserListAdapter mChooserListAdapter; private final LayoutInflater mLayoutInflater; private final int mColumnCount = 4; + private RowScale[] mServiceTargetScale; + private final Interpolator mInterpolator; public ChooserRowAdapter(ChooserListAdapter wrappedAdapter) { mChooserListAdapter = wrappedAdapter; mLayoutInflater = LayoutInflater.from(ChooserActivity.this); + mInterpolator = AnimationUtils.loadInterpolator(ChooserActivity.this, + android.R.interpolator.decelerate_quint); + wrappedAdapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { super.onChanged(); + final int rcount = getServiceTargetRowCount(); + if (mServiceTargetScale == null + || mServiceTargetScale.length != rcount) { + RowScale[] old = mServiceTargetScale; + int oldRCount = old != null ? old.length : 0; + mServiceTargetScale = new RowScale[rcount]; + if (old != null && rcount > 0) { + System.arraycopy(old, 0, mServiceTargetScale, 0, + Math.min(old.length, rcount)); + } + + for (int i = rcount; i < oldRCount; i++) { + old[i].cancelAnimation(); + } + + for (int i = oldRCount; i < rcount; i++) { + final RowScale rs = new RowScale(ChooserRowAdapter.this, 0.f, 1.f) + .setInterpolator(mInterpolator); + mServiceTargetScale[i] = rs; + } + + // Start the animations in a separate loop. + // The process of starting animations will result in + // binding views to set up initial values, and we must + // have ALL of the new RowScale objects created above before + // we get started. + for (int i = oldRCount; i < rcount; i++) { + mServiceTargetScale[i].startAnimation(); + } + } + notifyDataSetChanged(); } @@ -933,19 +1041,43 @@ public void onChanged() { public void onInvalidated() { super.onInvalidated(); notifyDataSetInvalidated(); + if (mServiceTargetScale != null) { + for (RowScale rs : mServiceTargetScale) { + rs.cancelAnimation(); + } + } } }); } + private float getRowScale(int rowPosition) { + final int start = getCallerTargetRowCount(); + final int end = start + getServiceTargetRowCount(); + if (rowPosition >= start && rowPosition < end) { + return mServiceTargetScale[rowPosition - start].get(); + } + return 1.f; + } + @Override public int getCount() { return (int) ( - Math.ceil((float) mChooserListAdapter.getCallerTargetCount() / mColumnCount) - + Math.ceil((float) mChooserListAdapter.getServiceTargetCount() / mColumnCount) + getCallerTargetRowCount() + + getServiceTargetRowCount() + Math.ceil((float) mChooserListAdapter.getStandardTargetCount() / mColumnCount) ); } + public int getCallerTargetRowCount() { + return (int) Math.ceil( + (float) mChooserListAdapter.getCallerTargetCount() / mColumnCount); + } + + public int getServiceTargetRowCount() { + return (int) Math.ceil( + (float) mChooserListAdapter.getServiceTargetCount() / mColumnCount); + } + @Override public Object getItem(int position) { // We have nothing useful to return here. @@ -959,33 +1091,69 @@ public long getItemId(int position) { @Override public View getView(int position, View convertView, ViewGroup parent) { - final View[] holder; + final RowViewHolder holder; if (convertView == null) { holder = createViewHolder(parent); } else { - holder = (View[]) convertView.getTag(); + holder = (RowViewHolder) convertView.getTag(); } bindViewHolder(position, holder); - // We keep the actual list item view as the last item in the holder array - return holder[mColumnCount]; + return holder.row; } - View[] createViewHolder(ViewGroup parent) { - final View[] holder = new View[mColumnCount + 1]; - + RowViewHolder createViewHolder(ViewGroup parent) { final ViewGroup row = (ViewGroup) mLayoutInflater.inflate(R.layout.chooser_row, parent, false); + final RowViewHolder holder = new RowViewHolder(row, mColumnCount); + final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + for (int i = 0; i < mColumnCount; i++) { - holder[i] = mChooserListAdapter.createView(row); - row.addView(holder[i]); + final View v = mChooserListAdapter.createView(row); + final int column = i; + v.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + startSelected(holder.itemIndices[column], false, true); + } + }); + v.setOnLongClickListener(new OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + showAppDetails( + mChooserListAdapter.resolveInfoForPosition( + holder.itemIndices[column], true)); + return true; + } + }); + row.addView(v); + holder.cells[i] = v; + + // Force height to be a given so we don't have visual disruption during scaling. + LayoutParams lp = v.getLayoutParams(); + v.measure(spec, spec); + if (lp == null) { + lp = new LayoutParams(LayoutParams.MATCH_PARENT, v.getMeasuredHeight()); + row.setLayoutParams(lp); + } else { + lp.height = v.getMeasuredHeight(); + } + } + + // Pre-measure so we can scale later. + holder.measure(); + LayoutParams lp = row.getLayoutParams(); + if (lp == null) { + lp = new LayoutParams(LayoutParams.MATCH_PARENT, holder.measuredRowHeight); + row.setLayoutParams(lp); + } else { + lp.height = holder.measuredRowHeight; } row.setTag(holder); - holder[mColumnCount] = row; return holder; } - void bindViewHolder(int rowPosition, View[] holder) { + void bindViewHolder(int rowPosition, RowViewHolder holder) { final int start = getFirstRowPosition(rowPosition); final int startType = mChooserListAdapter.getPositionTargetType(start); @@ -994,34 +1162,26 @@ void bindViewHolder(int rowPosition, View[] holder) { end--; } - final ViewGroup row = (ViewGroup) holder[mColumnCount]; - if (startType == ChooserListAdapter.TARGET_SERVICE) { - row.setBackgroundColor(getColor(R.color.chooser_service_row_background_color)); + holder.row.setBackgroundColor( + getColor(R.color.chooser_service_row_background_color)); } else { - row.setBackground(null); + holder.row.setBackgroundColor(Color.TRANSPARENT); + } + + final int oldHeight = holder.row.getLayoutParams().height; + holder.row.getLayoutParams().height = Math.max(1, + (int) (holder.measuredRowHeight * getRowScale(rowPosition))); + if (holder.row.getLayoutParams().height != oldHeight) { + holder.row.requestLayout(); } for (int i = 0; i < mColumnCount; i++) { - final View v = holder[i]; + final View v = holder.cells[i]; if (start + i <= end) { v.setVisibility(View.VISIBLE); - final int itemIndex = start + i; - mChooserListAdapter.bindView(itemIndex, v); - v.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - startSelected(itemIndex, false, true); - } - }); - v.setOnLongClickListener(new OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - showAppDetails( - mChooserListAdapter.resolveInfoForPosition(itemIndex, true)); - return true; - } - }); + holder.itemIndices[i] = start + i; + mChooserListAdapter.bindView(holder.itemIndices[i], v); } else { v.setVisibility(View.GONE); } @@ -1048,6 +1208,25 @@ int getFirstRowPosition(int row) { } } + static class RowViewHolder { + final View[] cells; + final ViewGroup row; + int measuredRowHeight; + int[] itemIndices; + + public RowViewHolder(ViewGroup row, int cellCount) { + this.row = row; + this.cells = new View[cellCount]; + this.itemIndices = new int[cellCount]; + } + + public void measure() { + final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + row.measure(spec, spec); + measuredRowHeight = row.getMeasuredHeight(); + } + } + static class ChooserTargetServiceConnection implements ServiceConnection { private final DisplayResolveInfo mOriginalTarget; private ComponentName mConnectedComponent; @@ -1199,4 +1378,44 @@ public void destroy() { mSelectedTarget = null; } } + + class OffsetDataSetObserver extends DataSetObserver { + private final AbsListView mListView; + private int mCachedViewType = -1; + private View mCachedView; + + public OffsetDataSetObserver(AbsListView listView) { + mListView = listView; + } + + @Override + public void onChanged() { + if (mResolverDrawerLayout == null) { + return; + } + + final int chooserTargetRows = mChooserRowAdapter.getServiceTargetRowCount(); + int offset = 0; + for (int i = 0; i < chooserTargetRows; i++) { + final int pos = mChooserRowAdapter.getCallerTargetRowCount() + i; + final int vt = mChooserRowAdapter.getItemViewType(pos); + if (vt != mCachedViewType) { + mCachedView = null; + } + final View v = mChooserRowAdapter.getView(pos, mCachedView, mListView); + int height = ((RowViewHolder) (v.getTag())).measuredRowHeight; + + offset += (int) (height * mChooserRowAdapter.getRowScale(pos)); + + if (vt >= 0) { + mCachedViewType = vt; + mCachedView = v; + } else { + mCachedViewType = -1; + } + } + + mResolverDrawerLayout.setCollapsibleHeightReserved(offset); + } + } } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 17104894310..4ba678cfbee 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -26,6 +26,7 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Slog; +import android.view.View.OnAttachStateChangeListener; import android.widget.AbsListView; import com.android.internal.R; import com.android.internal.content.PackageMonitor; @@ -103,6 +104,8 @@ public class ResolverActivity extends Activity { private ResolverComparator mResolverComparator; private PickTargetOptionRequest mPickOptionRequest; + protected ResolverDrawerLayout mResolverDrawerLayout; + private boolean mRegistered; private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override public void onSomePackagesChanged() { @@ -253,6 +256,7 @@ public void onDismissed() { if (isVoiceInteraction()) { rdl.setCollapsed(false); } + mResolverDrawerLayout = rdl; } if (title == null) { @@ -328,6 +332,18 @@ public void onClick(View v) { if (isVoiceInteraction()) { onSetupVoiceInteraction(); } + + getWindow().getDecorView().addOnAttachStateChangeListener( + new OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + v.getViewRootImpl().setDrawDuringWindowsAnimating(true); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }); } /** @@ -1570,7 +1586,10 @@ public final void bindView(int position, View view) { private void onBindView(View view, TargetInfo info) { final ViewHolder holder = (ViewHolder) view.getTag(); - holder.text.setText(info.getDisplayLabel()); + final CharSequence label = info.getDisplayLabel(); + if (!TextUtils.equals(holder.text.getText(), label)) { + holder.text.setText(info.getDisplayLabel()); + } if (showsExtendedInfo(info)) { holder.text2.setVisibility(View.VISIBLE); holder.text2.setText(info.getExtendedInfo()); diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 54eea69af6b..fe0bc6565ec 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -3266,16 +3266,16 @@ private void updateColorViewInt(final ColorViewState state, int sysUiVis, int co int vis = show ? VISIBLE : INVISIBLE; visibilityChanged = state.targetVisibility != vis; state.targetVisibility = vis; + LayoutParams lp = (LayoutParams) view.getLayoutParams(); + if (lp.height != resolvedHeight || lp.width != resolvedWidth + || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) { + lp.height = resolvedHeight; + lp.width = resolvedWidth; + lp.gravity = resolvedGravity; + lp.rightMargin = rightMargin; + view.setLayoutParams(lp); + } if (show) { - LayoutParams lp = (LayoutParams) view.getLayoutParams(); - if (lp.height != resolvedHeight || lp.width != resolvedWidth - || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) { - lp.height = resolvedHeight; - lp.width = resolvedWidth; - lp.gravity = resolvedGravity; - lp.rightMargin = rightMargin; - view.setLayoutParams(lp); - } view.setBackgroundColor(color); } } diff --git a/core/java/com/android/internal/view/FloatingActionMode.java b/core/java/com/android/internal/view/FloatingActionMode.java index 44df0ce8913..b44baa2b842 100644 --- a/core/java/com/android/internal/view/FloatingActionMode.java +++ b/core/java/com/android/internal/view/FloatingActionMode.java @@ -35,7 +35,7 @@ public class FloatingActionMode extends ActionMode { private static final int MAX_HIDE_DURATION = 3000; - private static final int MOVING_HIDE_DELAY = 300; + private static final int MOVING_HIDE_DELAY = 50; private final Context mContext; private final ActionMode.Callback2 mCallback; @@ -181,7 +181,6 @@ private void repositionToolbar() { // Content rect is moving. mOriginatingView.removeCallbacks(mMovingOff); mFloatingToolbarVisibilityHelper.setMoving(true); - mFloatingToolbarVisibilityHelper.updateToolbarVisibility(); mOriginatingView.postDelayed(mMovingOff, MOVING_HIDE_DELAY); mFloatingToolbar.setContentRect(mContentRectOnScreen); @@ -189,9 +188,9 @@ private void repositionToolbar() { } } else { mFloatingToolbarVisibilityHelper.setOutOfBounds(true); - mFloatingToolbarVisibilityHelper.updateToolbarVisibility(); mContentRectOnScreen.setEmpty(); } + mFloatingToolbarVisibilityHelper.updateToolbarVisibility(); mPreviousContentRectOnScreen.set(mContentRectOnScreen); } diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java index ca6fe619f52..2a25db6b64c 100644 --- a/core/java/com/android/internal/widget/FloatingToolbar.java +++ b/core/java/com/android/internal/widget/FloatingToolbar.java @@ -1488,10 +1488,9 @@ private static PopupWindow createPopupWindow(View content) { private static AnimatorSet createEnterAnimation(View view) { AnimatorSet animation = new AnimatorSet(); animation.playTogether( - ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(200), + ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1).setDuration(150), // Make sure that view.x is always fixed throughout the duration of this animation. ObjectAnimator.ofFloat(view, View.X, view.getX(), view.getX())); - animation.setStartDelay(50); return animation; } @@ -1506,7 +1505,7 @@ private static AnimatorSet createExitAnimation( View view, int startDelay, Animator.AnimatorListener listener) { AnimatorSet animation = new AnimatorSet(); animation.playTogether( - ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(200)); + ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0).setDuration(100)); animation.setStartDelay(startDelay); animation.addListener(listener); return animation; diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 76796243f3f..c4347f832bd 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -69,6 +69,12 @@ public class ResolverDrawerLayout extends ViewGroup { private int mCollapsibleHeight; private int mUncollapsibleHeight; + /** + * The height in pixels of reserved space added to the top of the collapsed UI; + * e.g. chooser targets + */ + private int mCollapsibleHeightReserved; + private int mTopOffset; private boolean mIsDragging; @@ -153,12 +159,62 @@ public void setCollapsed(boolean collapsed) { } } + public void setCollapsibleHeightReserved(int heightPixels) { + final int oldReserved = mCollapsibleHeightReserved; + mCollapsibleHeightReserved = heightPixels; + + final int dReserved = mCollapsibleHeightReserved - oldReserved; + if (dReserved != 0 && mIsDragging) { + mLastTouchY -= dReserved; + } + + final int oldCollapsibleHeight = mCollapsibleHeight; + mCollapsibleHeight = Math.max(mCollapsibleHeight, getMaxCollapsedHeight()); + + if (updateCollapseOffset(oldCollapsibleHeight, !isDragging())) { + return; + } + + invalidate(); + } + private boolean isMoving() { return mIsDragging || !mScroller.isFinished(); } + private boolean isDragging() { + return mIsDragging || getNestedScrollAxes() == SCROLL_AXIS_VERTICAL; + } + + private boolean updateCollapseOffset(int oldCollapsibleHeight, boolean remainClosed) { + if (oldCollapsibleHeight == mCollapsibleHeight) { + return false; + } + + if (isLaidOut()) { + final boolean isCollapsedOld = mCollapseOffset != 0; + if (remainClosed && (oldCollapsibleHeight < mCollapsibleHeight + && mCollapseOffset == oldCollapsibleHeight)) { + // Stay closed even at the new height. + mCollapseOffset = mCollapsibleHeight; + } else { + mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); + } + final boolean isCollapsedNew = mCollapseOffset != 0; + if (isCollapsedOld != isCollapsedNew) { + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); + } + } else { + // Start out collapsed at first unless we restored state for otherwise + mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight; + } + return true; + } + private int getMaxCollapsedHeight() { - return isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight; + return (isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight) + + mCollapsibleHeightReserved; } public void setOnDismissedListener(OnDismissedListener listener) { @@ -676,7 +732,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp.alwaysShow && child.getVisibility() != GONE) { measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); - heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin; + heightUsed += getHeightUsed(child); } } @@ -688,7 +744,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (!lp.alwaysShow && child.getVisibility() != GONE) { measureChildWithMargins(child, widthSpec, widthPadding, heightSpec, heightUsed); - heightUsed += lp.topMargin + child.getMeasuredHeight() + lp.bottomMargin; + heightUsed += getHeightUsed(child); } } @@ -697,30 +753,43 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { heightUsed - alwaysShowHeight - getMaxCollapsedHeight()); mUncollapsibleHeight = heightUsed - mCollapsibleHeight; - if (isLaidOut()) { - final boolean isCollapsedOld = mCollapseOffset != 0; - if (oldCollapsibleHeight < mCollapsibleHeight - && mCollapseOffset == oldCollapsibleHeight) { - // Stay closed even at the new height. - mCollapseOffset = mCollapsibleHeight; - } else { - mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); - } - final boolean isCollapsedNew = mCollapseOffset != 0; - if (isCollapsedOld != isCollapsedNew) { - notifyViewAccessibilityStateChangedIfNeeded( - AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); - } - } else { - // Start out collapsed at first unless we restored state for otherwise - mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight; - } + updateCollapseOffset(oldCollapsibleHeight, !isDragging()); mTopOffset = Math.max(0, heightSize - heightUsed) + (int) mCollapseOffset; setMeasuredDimension(sourceWidth, heightSize); } + private int getHeightUsed(View child) { + // This method exists because we're taking a fast path at measuring ListViews that + // lets us get away with not doing the more expensive wrap_content measurement which + // imposes double child view measurement costs. If we're looking at a ListView, we can + // check against the lowest child view plus padding and margin instead of the actual + // measured height of the ListView. This lets the ListView hang off the edge when + // all of the content would fit on-screen. + + int heightUsed = child.getMeasuredHeight(); + if (child instanceof AbsListView) { + final AbsListView lv = (AbsListView) child; + final int lvPaddingBottom = lv.getPaddingBottom(); + + int lowest = 0; + for (int i = 0, N = lv.getChildCount(); i < N; i++) { + final int bottom = lv.getChildAt(i).getBottom() + lvPaddingBottom; + if (bottom > lowest) { + lowest = bottom; + } + } + + if (lowest < heightUsed) { + heightUsed = lowest; + } + } + + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + return lp.topMargin + heightUsed + lp.bottomMargin; + } + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { final int width = getWidth(); diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index fbe3ececc34..6cbdeaa9950 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -28,6 +28,7 @@ #include #define DEBUG_PARCEL 0 +#define ASHMEM_BITMAP_MIN_SIZE (128 * (1 << 10)) namespace android { @@ -993,7 +994,7 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { // Map the bitmap in place from the ashmem region if possible otherwise copy. Bitmap* nativeBitmap; - if (blob.fd() >= 0 && (blob.isMutable() || !isMutable)) { + if (blob.fd() >= 0 && (blob.isMutable() || !isMutable) && (size >= ASHMEM_BITMAP_MIN_SIZE)) { #if DEBUG_PARCEL ALOGD("Bitmap.createFromParcel: mapped contents of %s bitmap from %s blob " "(fds %s)", diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp index 8b69bbd084b..4faea182a1a 100644 --- a/core/jni/android_hardware_camera2_DngCreator.cpp +++ b/core/jni/android_hardware_camera2_DngCreator.cpp @@ -1660,10 +1660,11 @@ static sp DngCreator_setup(JNIEnv* env, jobject thiz, uint32_t image uint32_t width = static_cast(entry.data.i32[2]); uint32_t height = static_cast(entry.data.i32[3]); if (entry2.count > 0 && entry2.count == lsmWidth * lsmHeight * 4) { + // GainMap rectangle is relative to the active area origin. err = builder.addGainMapsForMetadata(lsmWidth, lsmHeight, - ymin, - xmin, + 0, + 0, height, width, opcodeCfaLayout, diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index e8acd978008..9b8243d9370 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -309,7 +309,9 @@ static void read_mapinfo(FILE *fp, stats_t* stats) // This is the regular Dalvik heap. whichHeap = HEAP_DALVIK; subHeap = HEAP_DALVIK_NORMAL; - } else if (strstr(name, "/dev/ashmem/dalvik-large object space") == name) { + } else if (strstr(name, "/dev/ashmem/dalvik-large object space") == name || + strstr(name, "/dev/ashmem/dalvik-free list large object space") + == name) { whichHeap = HEAP_DALVIK; subHeap = HEAP_DALVIK_LARGE; } else if (strstr(name, "/dev/ashmem/dalvik-non moving space") == name) { diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp index 0f5ba83fa19..07b7e60552b 100644 --- a/core/jni/android_os_Parcel.cpp +++ b/core/jni/android_os_Parcel.cpp @@ -114,7 +114,7 @@ static jint android_os_Parcel_dataCapacity(JNIEnv* env, jclass clazz, jlong nati return parcel ? parcel->dataCapacity() : 0; } -static void android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size) +static jlong android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativePtr, jint size) { Parcel* parcel = reinterpret_cast(nativePtr); if (parcel != NULL) { @@ -122,7 +122,9 @@ static void android_os_Parcel_setDataSize(JNIEnv* env, jclass clazz, jlong nativ if (err != NO_ERROR) { signalExceptionForError(env, clazz, err); } + return parcel->getOpenAshmemSize(); } + return 0; } static void android_os_Parcel_setDataPosition(JNIEnv* env, jclass clazz, jlong nativePtr, jint pos) @@ -304,7 +306,7 @@ static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong } } -static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object) +static jlong android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object) { Parcel* parcel = reinterpret_cast(nativePtr); if (parcel != NULL) { @@ -313,7 +315,9 @@ static void android_os_Parcel_writeFileDescriptor(JNIEnv* env, jclass clazz, jlo if (err != NO_ERROR) { signalExceptionForError(env, clazz, err); } + return parcel->getOpenAshmemSize(); } + return 0; } static jbyteArray android_os_Parcel_createByteArray(JNIEnv* env, jclass clazz, jlong nativePtr) @@ -550,12 +554,14 @@ static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz) return reinterpret_cast(parcel); } -static void android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr) +static jlong android_os_Parcel_freeBuffer(JNIEnv* env, jclass clazz, jlong nativePtr) { Parcel* parcel = reinterpret_cast(nativePtr); if (parcel != NULL) { parcel->freeData(); + return parcel->getOpenAshmemSize(); } + return 0; } static void android_os_Parcel_destroy(JNIEnv* env, jclass clazz, jlong nativePtr) @@ -593,12 +599,12 @@ static jbyteArray android_os_Parcel_marshall(JNIEnv* env, jclass clazz, jlong na return ret; } -static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr, - jbyteArray data, jint offset, jint length) +static jlong android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong nativePtr, + jbyteArray data, jint offset, jint length) { Parcel* parcel = reinterpret_cast(nativePtr); if (parcel == NULL || length < 0) { - return; + return 0; } jbyte* array = (jbyte*)env->GetPrimitiveArrayCritical(data, 0); @@ -612,24 +618,26 @@ static void android_os_Parcel_unmarshall(JNIEnv* env, jclass clazz, jlong native env->ReleasePrimitiveArrayCritical(data, array, 0); } + return parcel->getOpenAshmemSize(); } -static void android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr, - jlong otherNativePtr, jint offset, jint length) +static jlong android_os_Parcel_appendFrom(JNIEnv* env, jclass clazz, jlong thisNativePtr, + jlong otherNativePtr, jint offset, jint length) { Parcel* thisParcel = reinterpret_cast(thisNativePtr); if (thisParcel == NULL) { - return; + return 0; } Parcel* otherParcel = reinterpret_cast(otherNativePtr); if (otherParcel == NULL) { - return; + return thisParcel->getOpenAshmemSize(); } status_t err = thisParcel->appendFrom(otherParcel, offset, length); if (err != NO_ERROR) { signalExceptionForError(env, clazz, err); } + return thisParcel->getOpenAshmemSize(); } static jboolean android_os_Parcel_hasFileDescriptors(JNIEnv* env, jclass clazz, jlong nativePtr) @@ -722,7 +730,7 @@ static const JNINativeMethod gParcelMethods[] = { {"nativeDataAvail", "(J)I", (void*)android_os_Parcel_dataAvail}, {"nativeDataPosition", "(J)I", (void*)android_os_Parcel_dataPosition}, {"nativeDataCapacity", "(J)I", (void*)android_os_Parcel_dataCapacity}, - {"nativeSetDataSize", "(JI)V", (void*)android_os_Parcel_setDataSize}, + {"nativeSetDataSize", "(JI)J", (void*)android_os_Parcel_setDataSize}, {"nativeSetDataPosition", "(JI)V", (void*)android_os_Parcel_setDataPosition}, {"nativeSetDataCapacity", "(JI)V", (void*)android_os_Parcel_setDataCapacity}, @@ -737,7 +745,7 @@ static const JNINativeMethod gParcelMethods[] = { {"nativeWriteDouble", "(JD)V", (void*)android_os_Parcel_writeDouble}, {"nativeWriteString", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString}, {"nativeWriteStrongBinder", "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder}, - {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)V", (void*)android_os_Parcel_writeFileDescriptor}, + {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)J", (void*)android_os_Parcel_writeFileDescriptor}, {"nativeCreateByteArray", "(J)[B", (void*)android_os_Parcel_createByteArray}, {"nativeReadBlob", "(J)[B", (void*)android_os_Parcel_readBlob}, @@ -755,12 +763,12 @@ static const JNINativeMethod gParcelMethods[] = { {"clearFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_clearFileDescriptor}, {"nativeCreate", "()J", (void*)android_os_Parcel_create}, - {"nativeFreeBuffer", "(J)V", (void*)android_os_Parcel_freeBuffer}, + {"nativeFreeBuffer", "(J)J", (void*)android_os_Parcel_freeBuffer}, {"nativeDestroy", "(J)V", (void*)android_os_Parcel_destroy}, {"nativeMarshall", "(J)[B", (void*)android_os_Parcel_marshall}, - {"nativeUnmarshall", "(J[BII)V", (void*)android_os_Parcel_unmarshall}, - {"nativeAppendFrom", "(JJII)V", (void*)android_os_Parcel_appendFrom}, + {"nativeUnmarshall", "(J[BII)J", (void*)android_os_Parcel_unmarshall}, + {"nativeAppendFrom", "(JJII)J", (void*)android_os_Parcel_appendFrom}, {"nativeHasFileDescriptors", "(J)Z", (void*)android_os_Parcel_hasFileDescriptors}, {"nativeWriteInterfaceToken", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken}, {"nativeEnforceInterface", "(JLjava/lang/String;)V", (void*)android_os_Parcel_enforceInterface}, diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_StaticLayout.cpp index 90e4bb64133..a94ea8b06ed 100644 --- a/core/jni/android_text_StaticLayout.cpp +++ b/core/jni/android_text_StaticLayout.cpp @@ -117,9 +117,17 @@ static void nFinishBuilder(JNIEnv*, jclass, jlong nativePtr) { b->finish(); } -static jlong nLoadHyphenator(JNIEnv* env, jclass, jstring patternData) { - ScopedStringChars str(env, patternData); - Hyphenator* hyphenator = Hyphenator::load(str.get(), str.size()); +static jlong nLoadHyphenator(JNIEnv* env, jclass, jobject buffer, jint offset) { + const uint8_t* bytebuf = nullptr; + if (buffer != nullptr) { + void* rawbuf = env->GetDirectBufferAddress(buffer); + if (rawbuf != nullptr) { + bytebuf = reinterpret_cast(rawbuf) + offset; + } else { + ALOGE("failed to get direct buffer address"); + } + } + Hyphenator* hyphenator = Hyphenator::loadBinary(bytebuf); return reinterpret_cast(hyphenator); } @@ -177,7 +185,7 @@ static JNINativeMethod gMethods[] = { {"nNewBuilder", "()J", (void*) nNewBuilder}, {"nFreeBuilder", "(J)V", (void*) nFreeBuilder}, {"nFinishBuilder", "(J)V", (void*) nFinishBuilder}, - {"nLoadHyphenator", "(Ljava/lang/String;)J", (void*) nLoadHyphenator}, + {"nLoadHyphenator", "(Ljava/nio/ByteBuffer;I)J", (void*) nLoadHyphenator}, {"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale}, {"nSetupParagraph", "(J[CIFIF[IIII)V", (void*) nSetupParagraph}, {"nSetIndents", "(J[I)V", (void*) nSetIndents}, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 0daa611e37c..89828e70fdc 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1470,7 +1470,7 @@ + android:protectionLevel="signature|preinstalled|appop|pre23|development" /> @@ -1708,12 +1708,12 @@ android:protectionLevel="signature|privileged" /> + android:protectionLevel="normal" /> - + android:protectionLevel="signature|installer|verifier" /> - - [ApnSettingV3]SaskTel Tethering,inet.stm.sk.ca,,,,,,,,,204,04,,DUN,,,true,0,,,,,,,gid,5A - - true;BAE0000000000000 diff --git a/core/res/res/values-mcc302-mnc780/config.xml b/core/res/res/values-mcc302-mnc780/config.xml index 51abd36b365..a48f6957ba9 100644 --- a/core/res/res/values-mcc302-mnc780/config.xml +++ b/core/res/res/values-mcc302-mnc780/config.xml @@ -21,25 +21,6 @@ for different hardware and product builds. --> - - - - 1 - 4 - 7 - 9 - - - - - SaskTel Tethering,inet.stm.sk.ca,,,,,,,,,302,780,,DUN - - 302 diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml index e36dcf53d53..8dfdf1d3182 100644 --- a/core/res/res/values-mk-rMK/strings.xml +++ b/core/res/res/values-mk-rMK/strings.xml @@ -1490,6 +1490,7 @@ За %d ч. "До %1$s" + "До %1$s (следниот аларм)" "Додека не го исклучите" "Додека не го исклучите Не вознемирувај" "%1$s / %2$s" diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml index 1bee7e8a2f5..330c1e9a0b4 100644 --- a/core/res/res/values-ml-rIN/strings.xml +++ b/core/res/res/values-ml-rIN/strings.xml @@ -1488,6 +1488,7 @@ ഒരു മണിക്കൂറത്തേക്ക് "%1$s വരെ" + "%1$s വരെ (അടുത്ത അലാറം)" "നിങ്ങൾ ഇത് ഓ‌ഫാക്കും വരെ" "\'ശല്ല്യപ്പെടുത്തരുത്\' ഓഫാക്കുന്നതുവരെ" "%1$s / %2$s" diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml index bbae50695a9..2e6eb85f6fb 100644 --- a/core/res/res/values-mn-rMN/strings.xml +++ b/core/res/res/values-mn-rMN/strings.xml @@ -1486,6 +1486,7 @@ 1 цагийн турш: "%1$s хүртэл" + "%1$s хүртэл (дараагийн сэрүүлэг)" "Таныг унтраах хүртэл" "\"Бүү саад бол\"-ыг унтраах хүртэл" "%1$s / %2$s" diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml index 99262b0083f..74f36b9a61b 100644 --- a/core/res/res/values-mr-rIN/strings.xml +++ b/core/res/res/values-mr-rIN/strings.xml @@ -1488,6 +1488,7 @@ %d तासासाठी "%1$s पर्यंत" + "%1$s पर्यंत (पुढील अलार्म)" "आपण हे बंद करेपर्यंत" "आपण बंद करेपर्यंत व्यत्यय आणू नका" "%1$s / %2$s" diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml index 01f5c468dbc..5d666a7f5f5 100644 --- a/core/res/res/values-ms-rMY/strings.xml +++ b/core/res/res/values-ms-rMY/strings.xml @@ -1488,6 +1488,7 @@ Selama 1 jam "Sehingga %1$s" + "Sehingga %1$s (penggera akan datang)" "Sehingga anda matikan" "Hingga anda mematikan Jangan Ganggu" "%1$s / %2$s" diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml index b92e139a674..e65be4d8422 100644 --- a/core/res/res/values-my-rMM/strings.xml +++ b/core/res/res/values-my-rMM/strings.xml @@ -1488,6 +1488,7 @@ ၁ နာရီအတွက် "%1$sအထိ" + "%1$s အထိ (လာမည့် နှိုးစက်)" "သင်က ဒါကို ပိတ်မပစ်သည့် အထိ" "မနှောင့်ယှက်ရန် ကိုသင်ပိတ်သည်အထိ" "%1$s / %2$s" diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 5df9a82bbea..b538fd30418 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -1488,6 +1488,7 @@ I én time "Til %1$s" + "Til %1$s (neste alarm)" "Inntil du slår av funksjonen" "Inntil du slår av Ikke forstyrr" "%1$s/%2$s" diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml index 3e8e5b66d15..0d5460291b8 100644 --- a/core/res/res/values-ne-rNP/strings.xml +++ b/core/res/res/values-ne-rNP/strings.xml @@ -87,7 +87,7 @@ "कलर ID पूर्वनिर्धारितदेखि प्रतिबन्धित छैन। अर्को कल: प्रतिबन्धित छ" "कलर ID पूर्वनिर्धारितको लागि रोकावट छैन। अर्को कल: रोकावट छैन" "सेवाको व्यवस्था छैन।" - "तपाईं कलर ID सेटिङ परिवर्तन गर्न सक्नुहुन्न।" + "तपाईँ कलर ID सेटिङ परिवर्तन गर्न सक्नुहुन्न।" "प्रतिबन्धित पहुँच परिवर्तन भएको छ" "डेटा सेवा रोकिएको छ।" "आपतकालीन सेवा रोकिएको छ।" @@ -198,7 +198,7 @@ "तपाईंको TV बन्द हुनेछ।" "तपाईँको घडी बन्द गरिने छ।" "तपाईँको फोन बन्द हुने छ।" - "के तपाईं बन्द गर्न चाहनुहुन्छ?" + "के तपाईँ बन्द गर्न चाहनुहुन्छ?" "सुरक्षित मोडमा पुनःबुट गर्नुहोस्" "सुरक्षित मोडमा तपाईँ पुनःबुट गर्न चाहनु हुन्छ? तपाईँले स्थापना गरेका सबै तेस्रो पक्षका अनुप्रयोगहरूलाई असक्षम गराउने छ।" "नयाँ" @@ -786,7 +786,7 @@ "तपाईँको भ्वाइसमेल इनबक्समा सन्देश थप्नको लागि अनुप्रयोगलाई अनुमति दिन्छ।" "भूस्थान अनुमतिहरू ब्राउजर परिवर्तन गर्नुहोस्" "ब्राउजरको भू-स्थान अनुमतिहरू परिमार्जन गर्न अनुप्रयोगलाई अनुमति दिन्छ। खराब अनुप्रयोगहरूले स्थान सूचना मनपरी वेब साइटहरूमा पठाउने अनुमतिको लागि यसलाई प्रयोग गर्न सक्छन्।" - "के तपाईं ब्राउजरले यो पासवर्ड सम्झेको चाहनुहुन्छ?" + "के तपाईँ ब्राउजरले यो पासवर्ड सम्झेको चाहनुहुन्छ?" "अहिले होइन" "सम्झनुहोस्" "कहिल्यै पनि होइन" @@ -906,13 +906,13 @@ "दुर्भाग्यवश, प्रक्रिया %1$s बन्द भयो।" "%2$sले कार्य गरिरहेको छैन।\n\nके तपाईँ यसलाई बन्द गर्न चाहनु हुन्छ?" - "गतिविधि %1$s ले प्रतिक्रिया देखाइरहेको छैन।\n\nके तपाईं यसलाई बन्द गर्न चाहनु हुन्छ?" + "गतिविधि %1$s ले प्रतिक्रिया देखाइरहेको छैन।\n\nके तपाईँ यसलाई बन्द गर्न चाहनु हुन्छ?" "%1$s जवाफ दिइरहेको छैन। के तपाईँ यसलाई बन्द गर्न चाहनु हुन्छ?" "प्रक्रिया %1$sले कार्य गरिरहेको छैन।\n\nके तपाईँ यसलाई बन्द गर्न चाहनु हुन्छ?" "ठिक छ" "रिपोर्ट गर्नुहोस्" "प्रतीक्षा गर्नुहोस्" - "पृष्ठ गैर जिम्मेवारी भएको छ।\n\nके तपाईं यसलाई बन्द गर्न चाहनुहुन्छ?" + "पृष्ठ गैर जिम्मेवारी भएको छ।\n\nके तपाईँ यसलाई बन्द गर्न चाहनुहुन्छ?" "अनुप्रयोग पुनः निर्देशीत" "%1$s अहिले चलिरहेको छ।" "%1$s वास्तविक सुरुवात भएको थियो।" @@ -1025,8 +1025,8 @@ "सायद तपाईँलाई पैसा पर्न सक्छ।" "USB ठूलो भण्डारण" "USB जोडिएको छ" - "तपाईं आफ्नो कम्प्युटरमा USB मार्फत जडान हुनुभयो। तलको बटन टच गर्नुहोस् यदि तपाईं आफ्नो कम्प्युटर र एन्ड्रोइडको USB भण्डारण बीच फाइलहरू प्रतिलिपि गर्न चाहनुहुन्छ भने।" - "तपाईं आफ्नो कम्प्युटरमा USB मार्फत जडान हुनुभयो। तलको बटन टच गर्नुहोस् यदि तपाईं आफ्नो कम्प्युटर र एन्ड्रोइडको SD कार्ड बीच फाइलहरू प्रतिलिपि गर्न चाहनुहुन्छ भने।" + "तपाईँ आफ्नो कम्प्युटरमा USB मार्फत जडान हुनुभयो। तलको बटन टच गर्नुहोस् यदि तपाईँ आफ्नो कम्प्युटर र एन्ड्रोइडको USB भण्डारण बीच फाइलहरू प्रतिलिपि गर्न चाहनुहुन्छ भने।" + "तपाईँ आफ्नो कम्प्युटरमा USB मार्फत जडान हुनुभयो। तलको बटन टच गर्नुहोस् यदि तपाईँ आफ्नो कम्प्युटर र एन्ड्रोइडको SD कार्ड बीच फाइलहरू प्रतिलिपि गर्न चाहनुहुन्छ भने।" "USB भण्डारण चालु गर्नुहोस्" "USB आम भण्डारणको लागि तपाईँको USB भण्डारण प्रयोग गर्दा एउटा समस्या भयो।" "USB आम भण्डारणको लागि तपाईँको SD कार्ड प्रयोग गर्दा एउटा समस्या भयो।" @@ -1121,7 +1121,7 @@ "अस्वीकार गर्नुहोस्" "अनुरोध गरिएको अनुमति" \n"खाता %sको लागि अनुरोध गरिएको अनुमति।" - "तपाईं तपाईँको कार्य प्रोफाइल बाहिर यो अनुप्रयोग प्रयोग गरिरहनु भएको छ" + "तपाईँ तपाईँको कार्य प्रोफाइल बाहिर यो अनुप्रयोग प्रयोग गरिरहनु भएको छ" "तपाईँ आफ्नो कार्य प्रोफाइलमा यो अनुप्रयोग प्रयोग गरिरहनु भएको छ" "इनपुट विधि" "सिङ्क गर्नुहोस्" @@ -1169,7 +1169,7 @@ "हो" "होइन" "सीमा नाघेकाहरू मेट्नुहोस्" - "त्यहाँ %1$d मेटाइएका आइटमहरू छन् %2$sको लागि, खाता %3$s। तपाईं के गर्न चाहनु हुन्छ?" + "त्यहाँ %1$d मेटाइएका आइटमहरू छन् %2$sको लागि, खाता %3$s। तपाईँ के गर्न चाहनु हुन्छ?" "वस्तुहरू मेट्नुहोस्" "मेटिएकाहरू पूर्ववत बनाउनुहोस्।" "अहिलेको लागि केही नगर्नुहोस्" @@ -1494,6 +1494,7 @@ १ घन्टाको लागि "%1$s सम्म" + "%1$s (अर्को अलार्म) सम्म" "तपाईँले यसलाई बन्द नगरेसम्म" "तपाईँले बन्द नगरे सम्म बाधा नपुर्याउँनुहोस्" "%1$s / %2$s" diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index c8f87331faf..7eef3394caa 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -1488,6 +1488,7 @@ Gedurende 1 u "Tot %1$s" + "Tot %1$s (volgend alarm)" "Totdat u dit uitschakelt" "Totdat u \'Niet storen\' uitschakelt" "%1$s/%2$s" diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml index 5a9d2c1db72..1743570d07d 100644 --- a/core/res/res/values-pa-rIN/strings.xml +++ b/core/res/res/values-pa-rIN/strings.xml @@ -1488,6 +1488,7 @@ %d ਘੰਟਿਆਂ ਲਈ "%1$s ਤੱਕ" + "%1$s ਤੱਕ (ਅਗਲਾ ਅਲਾਰਮ)" "ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਇਸਨੂੰ ਬੰਦ ਨਹੀਂ ਕਰਦੇ ਹੋ" "ਜਦੋਂ ਤੱਕ ਤੁਸੀਂ ਪਰੇਸ਼ਾਨ ਨਾ ਕਰੋ ਨੂੰ ਬੰਦ ਨਹੀਂ ਕਰਦੇ ਹੋ" "%1$s / %2$s" diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 66bad327621..308bf89aa83 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -1522,6 +1522,7 @@ 1 godz. "Do %1$s" + "Do %1$s (następny alarm)" "Dopóki nie wyłączysz" "Do wyłączenia Nie przeszkadzać" "%1$s / %2$s" diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index d6f5760a637..7050cf82bba 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -1488,6 +1488,7 @@ Por %d horas "Até às %1$s" + "Até %1$s (próximo alarme)" "Até você desativar" "Até que você desative \"Não perturbe\"" "%1$s / %2$s" diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index ff41c14be62..48baf70db97 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -1488,6 +1488,7 @@ Durante 1 h "Até às %1$s" + "Até às %1$s (próximo alarme)" "Até que o utilizador desative" "Até desativar Não incomodar" "%1$s/%2$s" diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index d6f5760a637..7050cf82bba 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -1488,6 +1488,7 @@ Por %d horas "Até às %1$s" + "Até %1$s (próximo alarme)" "Até você desativar" "Até que você desative \"Não perturbe\"" "%1$s / %2$s" diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 0809984891d..1bbabfaa49e 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -45,7 +45,7 @@ "Mesaj vocal" "MSISDN1" "Problemă de conexiune sau cod MMI nevalid." - "Operaţia este limitată la numerele cu apelări restricţionate." + "Operația este limitată la numerele cu apelări restricţionate." "Serviciul a fost activat." "Serviciul a fost activat pentru:" "Serviciul a fost dezactivat." @@ -73,7 +73,7 @@ "ID-ul liniei conectate" "Restricționarea ID-ului liniei conectate" "Redirecționarea apelurilor" - "Apel în aşteptare" + "Apel în așteptare" "Blocarea apelurilor" "Modificare parolă" "Cod PIN modificat" @@ -270,7 +270,7 @@ "primeşte mesaje text (MMS)" "Permite aplicației să primească și să proceseze mesaje MMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta." "citeşte mesajele cu transmisie celulară" - "Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitivul dvs. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situațiile de urgenţă. Aplicaţiile rău intenţionate pot afecta performanţa sau funcționarea dispozitivului dvs. când este primită o transmisie celulară de urgenţă." + "Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitivul dvs. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situațiile de urgenţă. Aplicaţiile rău intenţionate pot afecta performanța sau funcționarea dispozitivului dvs. când este primită o transmisie celulară de urgenţă." "citire feeduri abonat" "Permite aplicației să obţină detalii despre feedurile sincronizate în prezent." "trimită și să vadă mesajele SMS" @@ -635,7 +635,7 @@ "Denumit după" "Rudă" "Soră" - "Soţ/Soţie" + "Soț/Soție" "Personalizată" "Ecran pornire" "Serviciu" @@ -677,7 +677,7 @@ "Opriți" "Derulaţi" "Derulaţi rapid înainte" - "Numai apeluri de urgenţă" + "Numai apeluri de urgență" "Rețea blocată" "Cardul SIM este blocat cu codul PUK." "Consultaţi Ghidul de utilizare sau contactaţi Serviciul de relaţii cu clienţii." @@ -791,7 +791,7 @@ "Nu acum" "Reţineţi" "Niciodată" - "Nu aveţi permisiunea de a deschide această pagină." + "Nu aveți permisiunea de a deschide această pagină." "Text copiat în clipboard." "Mai multe" "Meniu+" @@ -861,7 +861,7 @@ "Selectați-le pe toate" "Decupaţi" "Copiați" - "Inseraţi" + "Inserați" "Înlocuiţi..." "Ștergeți" "Copiați adresa URL" @@ -988,7 +988,7 @@ "Acceptaţi" "Refuzaţi" "Invitaţia a fost trimisă." - "Invitaţie pentru conectare" + "Invitație pentru conectare" "De la:" "Către:" "Introduceţi codul PIN necesar:" @@ -1174,7 +1174,7 @@ "Există %1$d (de) elemente şterse pentru %2$s, contul %3$s. Ce doriți să faceţi?" "Ștergeți elementele" "Anulați aceste ştergeri" - "Nu trebuie să luaţi nicio măsură deocamdată" + "Nu trebuie să luați nicio măsură deocamdată" "Alegeţi un cont" "Adăugaţi un cont" "Adăugaţi un cont" @@ -1505,6 +1505,7 @@ Pentru 1 h "Până la %1$s" + "Până la %1$s (următoarea alarmă)" "Până la dezactivare" "Până când dezactivați „Nu deranja”" "%1$s/%2$s" diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 9f42f034658..b857f275985 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -914,7 +914,7 @@ "Приложение \"%1$s\" не отвечает. Закрыть его?" "Приложение \"%1$s\" не отвечает.\n\nЗакрыть его?" "ОК" - "Создать отчет" + "Отправить отчет" "Подождать" "Страница не отвечает.\n\nЗакрыть ее?" "Приложение перенаправлено" @@ -1522,6 +1522,7 @@ На %d часа "До %1$s" + "Следующий сигнал в %1$s" "Пока я не отключу" "Пока вы не отключите режим \"Не беспокоить\"" "%1$s / %2$s" diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml index 4d7c7a012ca..76633336e6a 100644 --- a/core/res/res/values-si-rLK/strings.xml +++ b/core/res/res/values-si-rLK/strings.xml @@ -1490,6 +1490,7 @@ පැය %d ක් සඳහා "%1$s තෙක්" + "%1$s තෙක් (ඊළඟ එලාමය)" "ඔබ මෙය අක්‍රිය කරන තුරු" "බාධා නොකරන්න ඔබ අක්‍රිය කරන තුරු" "%1$s / %2$s" diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index cbff7d5d6b6..933c3fe5c65 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -1522,6 +1522,7 @@ Na 1 h "Do %1$s" + "Do %1$s (ďalší budík)" "Dokým túto funkciu nevypnete" "Dokým nevypnete stav Nerušiť" "%1$s / %2$s" diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index e686121ad5b..304fe52d876 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -1522,6 +1522,7 @@ %d h "Do %1$s" + "Do %1$s (naslednji alarm)" "Dokler tega ne izklopite" "Dokler ne izklopite načina »ne moti«" "%1$s/%2$s" diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml index 7da44e0e916..3d11d241006 100644 --- a/core/res/res/values-sq-rAL/strings.xml +++ b/core/res/res/values-sq-rAL/strings.xml @@ -1488,6 +1488,7 @@ Për 1 orë "Deri në %1$s" + "Deri në %1$s (alarmi tjetër)" "Deri sa ta çaktivizosh këtë" "Deri sa të çaktivizosh gjendjen \"Mos shqetëso\"" "%1$s / %2$s" diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 510a421ad43..d4f3ffbe767 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -1505,6 +1505,7 @@ За %d с "До %1$s" + "До %1$s (следећи аларм)" "Док не искључите" "Док не искључите режим Не узнемиравај" "%1$s/%2$s" diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index b54d7fa357f..e815fdbb779 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -163,7 +163,7 @@ "För många %s-borttagningar." "Pekdatorns lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme." "Klockans lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme." - "Lagringsutrymmet på tv:n är fullt. Ta bort några filer för att frigöra utrymme." + "Lagringsutrymmet på TV:n är fullt. Ta bort några filer för att frigöra utrymme." "Mobilens lagringsutrymme är fullt. Ta bort några filer för att frigöra utrymme." "Nätverket kan vara övervakat" "Av en okänd tredje part" @@ -177,7 +177,7 @@ "Administratörsappen saknar delar eller är skadad och kan inte användas. Enheten kommer nu att rensas. Kontakta administratören om du behöver hjälp." "Jag" "Alternativ för surfplattan" - "Tv-alternativ" + "TV-alternativ" "Telefonalternativ" "Tyst läge" "Aktivera trådlöst" @@ -195,7 +195,7 @@ "Startar om …" "Avslutar…" "Din surfplatta stängs av." - "Tv:n stängs av." + "TV:n stängs av." "Klockan stängs av." "Din telefon stängs av." "Vill du stänga av?" @@ -276,7 +276,7 @@ "Tillåter att appen skickar SMS. Detta kan leda till oväntade avgifter. Skadliga appar kan skicka meddelanden utan ditt godkännande vilket kan kosta pengar." "läsa dina textmeddelanden (SMS eller MMS)" "Tillåter att appen läser SMS som sparats på surfplattan eller på SIM-kortet. Med den här behörigheten tillåts appen att läsa alla SMS oavsett innehåll eller sekretess." - "Tillåter att appen läser sms som har sparats på tv:n eller SIM-kortet. På så sätt kan appen läsa alla sms oavsett innehåll eller sekretess." + "Tillåter att appen läser sms som har sparats på TV:n eller SIM-kortet. På så sätt kan appen läsa alla sms oavsett innehåll eller sekretess." "Tillåter att appen läser SMS som sparats på mobilen eller på SIM-kortet. Med den här behörigheten tillåts appen att läsa alla SMS oavsett innehåll eller sekretess." "ta emot textmeddelanden (WAP)" "Tillåter att appen tar emot och hanterar WAP-meddelanden. Med den här behörigheten kan appen övervaka eller ta bort meddelanden som skickats till dig utan att visa dem för dig." @@ -294,7 +294,7 @@ "Tillåter att appen att dras ovanpå andra appar eller delar av användargränssnittet. De kan störa din användning av gränssnittet i olika appar eller ändra vad du tror visas i andra appar." "se till att appen alltid körs" "Tillåter att delar av appen läggs beständigt i minnet. Detta kan innebära att det tillgängliga minnet för andra appar begränsas, vilket gör surfplattan långsam." - "Tillåter att en app gör vissa delar beständiga i minnet. Det kan begränsa mängden minne som är tillgänglig för andra appar och gör tv:n långsammare." + "Tillåter att en app gör vissa delar beständiga i minnet. Det kan begränsa mängden minne som är tillgänglig för andra appar och gör TV:n långsammare." "Tillåter att delar av appen läggs beständigt i minnet. Detta kan innebära att det tillgängliga minnet för andra appar begränsas, vilket gör mobilen långsam." "mäta appens lagringsplats" "Tillåter att appen hämtar kod, data och cachestorlekar" @@ -306,33 +306,33 @@ "Tillåter att appen startar automatiskt när systemet har startats om. Detta kan innebära att det tar längre tid att starta mobilen och att mobilen blir långsammare i och med att appen hela tiden körs i bakgrunden." "Skicka sticky broadcast" "Tillåter att appen skickar sticky broadcasts, som finns kvar när sändningen är slut. Vid intensiv användning kan mobilen bli långsam eller instabil eftersom minnet överbelastas." - "Tillåter att appen skickar sticky broadcasts som finns kvar när sändningen är slut. Överdriven användning kan göra tv:n seg eller instabil eftersom den använder för mycket minne." + "Tillåter att appen skickar sticky broadcasts som finns kvar när sändningen är slut. Överdriven användning kan göra TV:n seg eller instabil eftersom den använder för mycket minne." "Tillåter att appen skickar sticky broadcast, som finns kvar när sändningen är slut. Vid intensiv användning kan mobilen bli långsam eller instabil eftersom minnet överbelastas." "läsa dina kontakter" "Tillåter att appen läser kontaktuppgifter som sparats på surfplattan, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appen att spara kontaktuppgifter. Skadliga appar kan dela uppgifterna med andra utan din vetskap." - "Tillåter att appen läser data om dina kontakter som sparats på tv:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med enskilda individer. Med den här behörigheten kan appar spara dina kontaktuppgifter och skadliga appar kan dela kontaktuppgifter utan att du vet om det." + "Tillåter att appen läser data om dina kontakter som sparats på TV:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med enskilda individer. Med den här behörigheten kan appar spara dina kontaktuppgifter och skadliga appar kan dela kontaktuppgifter utan att du vet om det." "Tillåter att appen läser kontaktuppgifter som sparats på mobilen, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appen att spara kontaktuppgifter. Skadliga appar kan dela uppgifterna med andra utan din vetskap." "ändra kontakterna" "Tillåter att appen ändrar kontaktuppgifter som sparats på surfplattan, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appar att ta bort kontaktuppgifter." - "Tillåter att appen ändrar uppgifterna om dina kontakter som har sparats på tv:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med särskilda kontakter. Med den här behörigheten kan appar ta bort kontaktuppgifter." + "Tillåter att appen ändrar uppgifterna om dina kontakter som har sparats på TV:n, bland annat hur ofta du har ringt, skickat e-post eller kommunicerat på andra sätt med särskilda kontakter. Med den här behörigheten kan appar ta bort kontaktuppgifter." "Tillåter att appen ändrar kontaktuppgifter som sparats på mobilen, inklusive information om hur ofta du har ringt, skickat e-post till eller på andra sätt kommunicerat med specifika personer. Med den här behörigheten tillåts appar att ta bort kontaktuppgifter." "läs samtalslogg" "Tillåter att appen läser pekdatorns samtalslista, inklusive uppgifter om inkommande och utgående samtal. Med den här behörigheten tillåts appen att spara samtalshistoriken. Skadliga appar kan dela uppgifterna med andra utan din vetskap." - "Tillåter att appen läser tv:ns samtalslista, bland annat data om inkommande och utgående samtal. Med den här behörigheten kan appar spara data i dina samtalslistor och skadliga appar kan dela data i samtalslistor utan att du vet om det." + "Tillåter att appen läser TV:ns samtalslista, bland annat data om inkommande och utgående samtal. Med den här behörigheten kan appar spara data i dina samtalslistor och skadliga appar kan dela data i samtalslistor utan att du vet om det." "Tillåter att appen läser mobilens samtalslista, inklusive uppgifter om inkommande och utgående samtal. Med den här behörigheten tillåts appen att spara samtalshistoriken. Skadliga appar kan dela uppgifterna med andra utan din vetskap." "skriv samtalslogg" "Tillåter att appen gör ändringar i pekdatorns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga program kan använda detta för att radera eller ändra din samtalslista." - "Tillåter att appen gör ändringar i tv:ns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga appar kan använda detta för att rensa eller ändra din samtalslista." + "Tillåter att appen gör ändringar i TV:ns samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga appar kan använda detta för att rensa eller ändra din samtalslista." "Tillåter att appen gör ändringar i mobilens samtalslista, inklusive i uppgifter om inkommande och utgående samtal. Skadliga program kan använda detta för att radera eller ändra din samtalslista." "kroppssens. (för hjärtat m.m.)" "Ger appen åtkomst till information från sensorer om ditt fysiska tillstånd, till exempel din puls." "läsa kalenderuppgifter plus konfidentiell information" "Tillåter att appen läser alla kalenderuppgifter som sparats på surfplattan, inklusive dina vänners eller kollegors uppgifter. Med den här behörigheten kan appen tillåtas att dela eller spara kalenderuppgifter även om de är sekretessbelagda eller känsliga." - "Tillåter att appen läser alla kalenderhändelser som sparats på tv:n, bland annat de som tillhör vänner eller kollegor. På så sätt kan appen dela eller spara dina kalenderhändelser oavsett sekretess eller känslighet." + "Tillåter att appen läser alla kalenderhändelser som sparats på TV:n, bland annat de som tillhör vänner eller kollegor. På så sätt kan appen dela eller spara dina kalenderhändelser oavsett sekretess eller känslighet." "Tillåter att appen läser alla kalenderuppgifter som sparats på mobilen, inklusive dina vänners eller kollegors uppgifter. Med den här behörigheten kan appen tillåtas att dela eller spara kalenderuppgifter även om de är sekretessbelagda eller känsliga." "lägga till eller ändra kalenderuppgifter och skicka e-post till gäster utan ägarens vetskap" "Tillåter att appen lägger till, tar bort och ändrar sådana händelser som du kan ändra på surfplattan, inklusive dina vänners eller kollegors uppgifter. Detta kan innebära att appen tillåts skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra händelser utan ägarens vetskap." - "Tillåter att appen lägger till, tar bort och ändrar händelser som du kan ändra på tv:n, inklusive dina vänners eller kollegors uppgifter. Appen kan på så sätt skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra uppgifter utan ägarens vetskap." + "Tillåter att appen lägger till, tar bort och ändrar händelser som du kan ändra på TV:n, inklusive dina vänners eller kollegors uppgifter. Appen kan på så sätt skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra uppgifter utan ägarens vetskap." "Tillåter att appen lägger till, tar bort och ändrar sådana händelser som du kan ändra på mobilen, inklusive dina vänners eller kollegors uppgifter. Detta kan innebära att appen tillåts skicka meddelanden som ser ut att komma från kalenderns ägare eller ändra händelser utan ägarens vetskap." "få åtkomst till extra kommandon för platsleverantör" "Tillåter att appen får åtkomst till extra kommandon för platsleverantör. Detta kan innebära att appen tillåts störa funktionen för GPS eller andra platskällor." @@ -359,14 +359,14 @@ "läsa telefonens status och identitet" "Tillåter att appen kommer åt enhetens telefonfunktioner. Med den här behörigheten tillåts appen att identifiera mobilens telefonnummer och enhets-ID, om ett samtal pågår och vilket nummer samtalet är kopplat till." "förhindra att surfplattan går in i viloläge" - "förhindra att tv:n försätts i viloläge" + "förhindra att TV:n försätts i viloläge" "förhindra att telefonen sätts i viloläge" "Tillåter att appen förhindrar att surfplattan går in i viloläge." - "Tillåter att appen förhindrar att tv:n försätts i viloläge." + "Tillåter att appen förhindrar att TV:n försätts i viloläge." "Tillåter att appen förhindrar att mobilen går in i viloläge." "tillåt IR-sändning" "Tillåter att appen använder surfplattans IR-sändare." - "Tillåter att appen använder den infraröda sändaren på tv:n." + "Tillåter att appen använder den infraröda sändaren på TV:n." "Tillåter att appen använder mobilens IR-sändare." "ange bakgrund" "Tillåter att appen anger systemets bakgrund." @@ -374,11 +374,11 @@ "Tillåter att appen ger tips om systemets bakgrundsstorlek." "ange tidszon" "Tillåter att appen ändrar pekdatorns tidszon." - "Tillåter att appen ändrar tidszonen på tv:n." + "Tillåter att appen ändrar tidszonen på TV:n." "Tillåter att appen ändrar mobilens tidszon." "hitta konton på enheten" "Tillåter att appen hämtar en lista över alla kända konton på surfplattan. Detta kan inkludera konton som har skapats av appar som du har installerat." - "Tillåter att appen hämtar listan med konton som tv:n kan identifiera. Den kan innehålla konton som skapats av appar som du har installerat." + "Tillåter att appen hämtar listan med konton som TV:n kan identifiera. Den kan innehålla konton som skapats av appar som du har installerat." "Tillåter att appen hämtar en lista över alla kända konton på mobilen. Detta kan inkludera konton som har skapats av appar som du har installerat." "visa nätverksanslutningar" "Tillåter att appen kommer åt information om nätverksanslutningarna, till exempel vilka nätverk som finns och är anslutna." @@ -394,21 +394,21 @@ "Tillåter att appen ansluter till och kopplar från Wi-Fi-åtkomstpunkter samt gör ändringar i enhetens konfiguration för Wi-Fi-nätverk." "tillåt Wi-Fi multicast-mottagning" "Tillåter att appen tar emot paket som skickats med multicast-adress till alla enheter i ett Wi-Fi-nätverk och inte bara till den här surfplattan. Detta drar mer batteri än när multicastläget inte används." - "Tillåter att appen tar emot paket som skickats till alla enheter i ett Wi-Fi-nätverk med multicastadress, inte bara till tv:n. Detta drar mer batteri än när multicastläget inte används." + "Tillåter att appen tar emot paket som skickats till alla enheter i ett Wi-Fi-nätverk med multicastadress, inte bara till TV:n. Detta drar mer batteri än när multicastläget inte används." "Tillåter att appen tar emot paket som skickats med multicast-adress till alla enheter i ett Wi-Fi-nätverk och inte bara till den här mobilen. Detta drar mer batteri än när multicastläget inte används." "få åtkomst till Bluetooth-inställningar" "Tillåter att appen konfigurerar den lokala Bluetooth-surfplattan samt upptäcker och parkopplar den med fjärranslutna enheter." - "Tillåter att appen konfigurerar den lokala Bluetooth-tv:n och identifierar och kopplar den till fjärrenheter." + "Tillåter att appen konfigurerar den lokala Bluetooth-TV:n och identifierar och kopplar den till fjärrenheter." "Tillåter att appen konfigurerar den lokala Bluetooth-mobilen samt upptäcker och parkopplar den med fjärranslutna enheter." "ansluta till och koppla från WiMAX" "Tillåter att appen avgör om WiMAX är aktiverat och kommer åt information om eventuella anslutna WiMAX-nätverk." "ändra WiMAX-status" "Tillåter att appen ansluter surfplattan till eller kopplar från WiMAX-nätverk." - "Tillåter att appen ansluter tv:n till och kopplar från tv:n från WiMAX-nätverk." + "Tillåter att appen ansluter TV:n till och kopplar från TV:n från WiMAX-nätverk." "Tillåter att appen ansluter mobilen till eller kopplar från WiMAX-nätverk." "koppla till Bluetooth-enheter" "Tillåter att appen kommer åt pekdatorns Bluetooth-konfiguration och upprättar och godkänner anslutningar till parkopplade enheter." - "Tillåter att appen visar konfigurationen av Bluetooth på tv:n och godkänner alla anslutningar till kopplade enheter." + "Tillåter att appen visar konfigurationen av Bluetooth på TV:n och godkänner alla anslutningar till kopplade enheter." "Tillåter att appen kommer åt mobilens Bluetooth-konfiguration och upprättar och godkänner anslutningar till parkopplade enheter." "kontrollera närfältskommunikationen" "Tillåter att appen kommunicerar med etiketter, kort och läsare för närfältskommunikation (NFC)." @@ -499,10 +499,10 @@ "Styr tillåten längd och tillåtna tecken i lösenord och pinkoder för skärmlåset." "Övervaka försök att låsa upp skärmen" "Övervaka antalet felaktiga lösenord som angetts för skärmlåset och lås surfplattan eller ta bort alla data från surfplattan om för många felaktiga försök görs." - "Övervakar antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och låser tv:n eller rensar alla uppgifter på tv:n om för många felaktiga lösenord har skrivits in." + "Övervakar antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och låser TV:n eller rensar alla uppgifter på TV:n om för många felaktiga lösenord har skrivits in." "Övervaka antalet felaktiga lösenord som angivits för skärmlåset och lås mobilen eller ta bort alla data från mobilen om för många felaktiga försök görs." "Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås surfplattan eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in." - "Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås tv:n eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in." + "Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås TV:n eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in." "Övervaka antalet felaktiga lösenord som skrivits in vid upplåsning av skärmen och lås mobilen eller rensa alla uppgifter för den här användaren om för många felaktiga lösenord har skrivits in." "Ändra skärmlåset" "Ändra skärmlåset." @@ -510,11 +510,11 @@ "Kontrollera hur och när skärmlåset aktiveras." "Radera all data" "Ta bort data från surfplattan utan förvarning genom att återställa standardinställningarna." - "Rensar uppgifterna på tv:n utan föregående varning genom att återställa standardinställningarna." + "Rensar uppgifterna på TV:n utan föregående varning genom att återställa standardinställningarna." "Ta bort data från mobilen utan förvarning genom att återställa standardinställningarna." "Radera användaruppgifter" "Rensa användarens uppgifter på den här surfplattan utan förvarning." - "Rensa användarens uppgifter på den här tv:n utan förvarning." + "Rensa användarens uppgifter på den här TV:n utan förvarning." "Rensa användarens data på den här mobilen utan förvarning." "Ange global proxyserver" "Ange enhetens globala proxy som ska användas när policyn aktiveras. Det är bara enhetens ägare som kan ange global proxy." @@ -663,7 +663,7 @@ "Du har försökt låsa upp med Ansiktslås för många gånger" "Inget SIM-kort" "Inget SIM-kort i surfplattan." - "Det finns inget SIM-kort i tv:n." + "Det finns inget SIM-kort i TV:n." "Inget SIM-kort i telefonen." "Sätt i ett SIM-kort." "SIM-kort saknas eller kan inte läsas. Sätt i ett SIM-kort." @@ -686,13 +686,13 @@ "Du har angett fel lösenord %d gånger. \n\nFörsök igen om %d sekunder." "Du har angett fel lösenord %d gånger. \n\nFörsök igen om %d sekunder." "Du har ritat ditt grafiska lösenord fel %d gånger. Efter %d försök till ombeds du att låsa upp surfplattan med din Google-inloggning.\n\n Försök igen om %d sekunder." - "Du har ritat fel mönster för upplåsning %d gånger. Efter ytterligare %d misslyckade försök blir du ombedd att låsa upp tv:n genom att logga in på Google.\n\n Försök igen om %d sekunder." + "Du har ritat fel mönster för upplåsning %d gånger. Efter ytterligare %d misslyckade försök blir du ombedd att låsa upp TV:n genom att logga in på Google.\n\n Försök igen om %d sekunder." "Du har ritat ditt grafiska lösenord fel %d gånger. Efter %d försök till ombeds du att låsa upp mobilen med uppgifterna som du använder när du loggar in på Google.\n\n Försök igen om %d sekunder." "Du har försökt låsa upp surfplattan på fel sätt %d gånger. Efter %d misslyckade försök till kommer surfplattan att återställas till fabriksinställningarna. Du förlorar då alla användardata." - "Du har misslyckats med att låsa upp tv:n %d gånger. Efter ytterligare %d misslyckade försök kommer tv:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade." + "Du har misslyckats med att låsa upp TV:n %d gånger. Efter ytterligare %d misslyckade försök kommer TV:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade." "Du har försökt låsa upp mobilen på fel sätt %d gånger. Efter %d misslyckade försök till kommer mobilen att återställas till fabriksinställningarna. Du förlorar då alla användardata." "Du har försökt låsa upp surfplattan på fel sätt %d gånger. Surfplattan återställs nu till fabriksinställningarna." - "Du har misslyckats med att låsa upp tv:n %d gånger. Tv:n kommer nu att återställas till standardinställningarna." + "Du har misslyckats med att låsa upp TV:n %d gånger. TV:n kommer nu att återställas till standardinställningarna." "Du har försökt låsa upp mobilen på fel sätt %d gånger. Mobilen återställs nu till fabriksinställningarna." "Försök igen om %d sekunder." "Glömt ditt grafiska lösenord?" @@ -778,7 +778,7 @@ "Tillåter att appen läser historiken för besökta sidor och alla bokmärken i webbläsaren. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion." "skriva bokmärken och historik på webben" "Tillåter att appen ändrar historiken för besökta sidor i webbläsaren eller bokmärken som sparats på surfplattan. Det kan innebära att appen kan ta bort eller ändra webbläsarinformation. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion." - "Tillåter att appen ändrar webbläsarens historik eller bokmärken som har sparats på tv:n. Appen kan därmed rensa eller ändra uppgifter i webbläsaren. Obs! Den här behörigheten kanske inte gäller för webbläsare från tredje part eller andra appar med webbfunktioner." + "Tillåter att appen ändrar webbläsarens historik eller bokmärken som har sparats på TV:n. Appen kan därmed rensa eller ändra uppgifter i webbläsaren. Obs! Den här behörigheten kanske inte gäller för webbläsare från tredje part eller andra appar med webbfunktioner." "Tillåter att appen ändrar historiken för besökta sidor i webbläsaren eller bokmärken som sparats på telefonen. Det kan innebära att appen kan ta bort eller ändra webbläsarinformation. Observera att den här behörigheten kanske inte är tillämplig för webbläsare från tredje part eller andra appar med surffunktion." "ställa in ett alarm" "Tillåter att appen ställer in ett alarm i en befintlig alarmapp. Vissa alarmappar har inte den här funktionen." @@ -987,7 +987,7 @@ "Ange den obligatoriska PIN-koden:" "PIN-kod:" "Surfplattans Wi-Fi-anslutning kommer tillfälligt att avbrytas när den är ansluten till %1$s" - "Tv:n kopplas tillfälligt från Wi-Fi-nätverket när den är ansluten till %1$s" + "TV:n kopplas tillfälligt från Wi-Fi-nätverket när den är ansluten till %1$s" "Mobilen kommer tillfälligt att kopplas från Wi-Fi när den är ansluten till %1$s" "Infoga tecken" "Skickar SMS" @@ -1200,7 +1200,7 @@ "Dela med" "Dela med %s" "Skärmlåsfunktion. Tryck och dra." - "Lås upp genom att dra." + "Lås upp genom att svepa." "Anslut mikrofonlurar om du vill att lösenordet ska läsas upp." "Punkt." "Visa startsidan" @@ -1253,7 +1253,7 @@ "Bara en gång" "%1$s har inte stöd för arbetsprofil" "Surfplatta" - "Tv" + "TV" "Mobil" "Hörlurar" "Dockningsstationens högtalare" @@ -1307,13 +1307,13 @@ "Du har angett fel lösenord %d gånger. \n\nFörsök igen om %d sekunder." "Du har ritat ditt grafiska lösenord fel %d gånger. \n\nFörsök igen om %d sekunder." "Du har försökt låsa upp mobilen på fel sätt %d gånger. Efter ytterligare %d misslyckade försök återställs surfplattan till fabriksinställningarna. Du förlorar då alla användardata." - "Du har misslyckats med att låsa upp tv:n %d gånger. Efter ytterligare %d misslyckade försök kommer tv:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade." + "Du har misslyckats med att låsa upp TV:n %d gånger. Efter ytterligare %d misslyckade försök kommer TV:n att återställas till standardinställningarna och alla användaruppgifter kommer att gå förlorade." "Du har försökt låsa upp mobilen på fel sätt %d gånger. Efter ytterligare %d misslyckade försök återställs mobilen till fabriksinställningarna. Du förlorar då alla användardata." "Du har försökt låsa upp surfplattan på fel sätt %d gånger. Surfplattan återställs nu till fabriksinställningarna." - "Du har misslyckats med att låsa upp tv:n %d gånger. Tv:n kommer nu att återställas till standardinställningarna." + "Du har misslyckats med att låsa upp TV:n %d gånger. TV:n kommer nu att återställas till standardinställningarna." "Du har försökt låsa upp mobilen på fel sätt %d gånger. Mobilen återställs nu till fabriksinställningarna." "Du har ritat ditt grafiska lösenord fel %d gånger. Efter ytterligare %d försök ombeds du låsa upp surfplattan med ett e-postkonto.\n\n Försök igen om %d sekunder." - "Du har ritat fel mönster för upplåsning %d gånger. Efter ytterligare %d misslyckade försök blir du ombedd att låsa upp tv:n via ett e-postkonto.\n\n Försök igen om %d sekunder." + "Du har ritat fel mönster för upplåsning %d gånger. Efter ytterligare %d misslyckade försök blir du ombedd att låsa upp TV:n via ett e-postkonto.\n\n Försök igen om %d sekunder." "Du har ritat ditt grafiska lösenord fel %d gånger. Efter ytterligare %d försök ombeds du låsa upp mobilen med hjälp av ett e-postkonto.\n\n Försök igen om %d sekunder." " – " "Ta bort" @@ -1432,7 +1432,7 @@ "Försök igen senare" "Visar på fullskärm" - "Dra nedåt från skärmens överkant för att avsluta." + "Svep nedåt från skärmens överkant för att avsluta." "OK" "Klart" "Cirkelreglage för timmar" @@ -1488,6 +1488,7 @@ I en 1 tim "Till kl. %1$s" + "Till %1$s (nästa alarm)" "Tills du inaktiverar detta" "Tills du inaktiverar Stör ej" "%1$s/%2$s" diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 02bac089a96..da5d6a3c73b 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -589,7 +589,7 @@ "Teleksi" "TTY TDD" "Nambari ya Simu ya Mkononi ya Kazini" - "Kiunda ujumbe cha Kazini" + "Peja ya Kazini" "Msaidizi" "MMS" "Maalum" @@ -1490,6 +1490,7 @@ Kwa saa 1 "Hadi %1$s" + "Mpaka %1$s (kengele inayofuata)" "Hadi utakapozima hili" "Hadi utakapozima Usinisumbue" "%1$s / %2$s" diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml index ae9be638c0a..48b1e649386 100644 --- a/core/res/res/values-ta-rIN/strings.xml +++ b/core/res/res/values-ta-rIN/strings.xml @@ -1488,6 +1488,7 @@ 1 மணிநேரத்திற்கு "%1$s வரை" + "%1$s மணி (அடுத்த அலாரம்) வரை" "இதை முடக்கும்வரை" "தொந்தரவு செய்ய வேண்டாம் என்பதை முடக்கும் வரை" "%1$s / %2$s" diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml index 7eca8eee401..ff639a9de0a 100644 --- a/core/res/res/values-te-rIN/strings.xml +++ b/core/res/res/values-te-rIN/strings.xml @@ -1488,6 +1488,7 @@ 1 గం పాటు "%1$s వరకు" + "%1$s (తదుపరి అలారం) వరకు" "మీరు దీన్ని ఆఫ్ చేసే వరకు" "మీరు అంతరాయం కలిగించవద్దు ఎంపిక ఆఫ్ చేసే వరకు" "%1$s / %2$s" diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index dbf6319ad18..2db50ee51c7 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -1488,6 +1488,7 @@ เป็นเวลา 1 ชม. "จนถึงเวลา %1$s" + "จนถึงเวลา %1$s (การปลุกครั้งถัดไป)" "จนกว่าคุณจะปิดฟังก์ชันนี้" "จนกว่าคุณจะปิดห้ามรบกวน" "%1$s / %2$s" diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 004b3f807ad..27cd5bb74d4 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -1488,6 +1488,7 @@ Sa loob ng %d na oras "Hanggang %1$s" + "Hanggang %1$s (susunod na alarm)" "Hanggang sa i-off mo ito" "Hanggang sa i-off mo ang Huwag Istorbohin" "%1$s / %2$s" diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 6c58613d0c2..909061be89a 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -1005,14 +1005,14 @@ "Asla İzin Verme" "SIM kart çıkarıldı" "Hücresel ağ, geçerli bir SIM kart takıp cihazınızı yeniden başlatıncaya kadar kullanılamayacak." - "Tamamlandı" + "Bitti" "SIM kart eklendi" "Hücresel ağa erişmek için cihazınızı yeniden başlatın." "Yeniden başlat" "Saati ayarlayın" "Tarihi ayarlayın" "Ayarla" - "Tamamlandı" + "Bitti" "YENİ: " "Sağlayan: %1$s." "İzin gerektirmez" @@ -1488,6 +1488,7 @@ 1 saat için "Şu saate kadar: %1$s" + "%1$s (sonraki alarma) saatine kadar" "Siz bunu kapatana kadar" "Rahatsız Etmeyin ayarını kapatana kadar" "%1$s / %2$s" diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index ea3c9417160..aee730df0c0 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -1522,6 +1522,7 @@ Протягом %d год "До %1$s" + "До %1$s (наступний будильник)" "Доки ви не вимкнете" "Доки ввімкнено режим \"Не турбувати\"" "%1$s / %2$s" diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml index 6ce74a078b7..4e4d069b489 100644 --- a/core/res/res/values-ur-rPK/strings.xml +++ b/core/res/res/values-ur-rPK/strings.xml @@ -1488,6 +1488,7 @@ 1 گھنٹہ کیلئے "%1$s تک" + "%1$s تک (اگلا الارم)" "جب تک آپ اسے آف نہ کر دیں" "جب تک آپ ڈسڑب نہ کریں کو آف نہیں کر دیتے" "%1$s / %2$s" diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml index f1422d4e232..45ebbf363b6 100644 --- a/core/res/res/values-uz-rUZ/strings.xml +++ b/core/res/res/values-uz-rUZ/strings.xml @@ -1488,6 +1488,7 @@ 1 soat "Ushbu vaqtgacha: %1$s" + "%1$s gacha (keyingi signal)" "Men o‘chirmaguncha" "“Bezovta qilinmasin” rejimi o‘chirilmaguncha" "%1$s / %2$s" diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index e4c5ee775a3..b2a66082b38 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -530,8 +530,8 @@ "Nhà riêng" "Di Động" "Cơ quan" - "Số fax Cơ quan" - "Số fax Nhà riêng" + "Số fax cơ quan" + "Số fax nhà riêng" "Số máy nhắn tin" "Khác" "Tùy chỉnh" @@ -573,8 +573,8 @@ "Nhà riêng" "Di Động" "Cơ quan" - "Số fax Cơ quan" - "Số fax Nhà riêng" + "Số fax cơ quan" + "Số fax nhà riêng" "Số máy nhắn tin" "Khác" "Số gọi lại" @@ -586,8 +586,8 @@ "Radio" "Số telex" "TTY TDD" - "Số điện thoại di động tại Cơ quan" - "Số Máy nhắn tin tại Cơ quan" + "Di động tại cơ quan" + "Số máy nhắn tin cơ quan" "Số điện thoại Hỗ trợ" "MMS" "Tùy chỉnh" @@ -1488,6 +1488,7 @@ Trong 1 giờ "Cho đến %1$s" + "Cho tới %1$s (cảnh báo tiếp theo)" "Cho đến khi bạn tắt tính năng này" "Cho đến khi bạn tắt Đừng làm phiền" "%1$s / %2$s" diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index e2b0704b548..dea913f9af1 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -227,7 +227,7 @@ "立即锁定" "999+" "安全模式" - "Android系统" + "Android 系统" "个人" "工作" "通讯录" @@ -1495,6 +1495,7 @@ 1 小时 "到%1$s" + "直到%1$s(闹钟下次响铃时)" "直到您将其关闭" "直到您关闭“勿扰”模式" "%1$s / %2$s" diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 188723e1aeb..3576075c5f1 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -1488,6 +1488,7 @@ 需時 1 小時 "完成時間:%1$s" + "直至%1$s (下一次響鬧)" "直至您關閉這項設定" "直至您關閉「請勿騷擾」功能" "%1$s/%2$s" diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index f1413745a4c..175f7dc80aa 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -1488,6 +1488,7 @@ 1 小時 "結束時間:%1$s" + "到%1$s 為止 (下一個鬧鐘)" "手動關閉這項設定前一律啟用" "直到您關閉「零打擾」模式" "%1$s/%2$s" diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index f29e9f90e0e..5df33186471 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -1488,6 +1488,7 @@ Ngamahora angu-%d "Kuze kube ngu-%1$s" + "Kuze kube ngu-%1$s (i-alamu elandelayo)" "Uze uvale lokhu" "Uze uvale ungaphazamisi" "%1$s / %2$s" diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 1a45b3ac75b..cf08dea2233 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -2095,6 +2095,10 @@ + + + + diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 3fc6e55a4fa..53faad1e541 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2542,4 +2542,8 @@ true + + + diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 3e0617ea74b..6e64a45ecb5 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4046,6 +4046,9 @@ Until %1$s + + Until %1$s (next alarm) + Until you turn this off diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 60961d800f9..cb168b6716a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2106,6 +2106,7 @@ + @@ -2438,4 +2439,6 @@ + + diff --git a/data/keyboards/Vendor_18d1_Product_5018.kcm b/data/keyboards/Vendor_18d1_Product_5018.kcm new file mode 100644 index 00000000000..0ca85a20d7f --- /dev/null +++ b/data/keyboards/Vendor_18d1_Product_5018.kcm @@ -0,0 +1,321 @@ +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Key character map for Google Pixel C Keyboard +# + +type FULL + +### Basic QWERTY keys ### + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' + alt: '\u00e7' + shift+alt: '\u00c7' +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' + alt: '\u0301' +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' +} + +key I { + label: 'I' + base: 'i' + shift, capslock: 'I' + alt: '\u0302' +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' + alt: '\u0303' +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' + ralt: '[' + ralt+shift: '{' +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' + ralt: ']' + ralt+shift: '}' +} + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' + alt: '\u00df' +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' + alt: '\u0308' +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' +} + +key W { + label: 'W' + base: 'w' + shift, capslock: 'W' +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' +} + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' +} + +key 0 { + label: '0' + base: '0' + shift: ')' +} + +key 1 { + label: '1' + base: '1' + shift: '!' + ralt: replace ESCAPE +} + +key 2 { + label: '2' + base: '2' + shift: '@' + ralt: '`' + ralt+shift: '~' +} + +key 3 { + label: '3' + base: '3' + shift: '#' +} + +key 4 { + label: '4' + base: '4' + shift: '$' +} + +key 5 { + label: '5' + base: '5' + shift: '%' +} + +key 6 { + label: '6' + base: '6' + shift: '^' + alt+shift: '\u0302' +} + +key 7 { + label: '7' + base: '7' + shift: '&' +} + +key 8 { + label: '8' + base: '8' + shift: '*' +} + +key 9 { + label: '9' + base: '9' + shift: '(' +} + +key SPACE { + label: ' ' + base: ' ' + alt, meta: fallback SEARCH + ctrl: fallback LANGUAGE_SWITCH +} + +key ENTER { + label: '\n' + base: '\n' +} + +key TAB { + label: '\t' + base: '\t' +} + +key COMMA { + label: ',' + base: ',' + shift: '<' +} + +key PERIOD { + label: '.' + base: '.' + shift: '>' +} + +key SLASH { + label: '/' + base: '/' + shift: '?' +} + +key MINUS { + label: '-' + base: '-' + shift: '_' +} + +key EQUALS { + label: '=' + base: '=' + shift: '+' + ralt: '\\' + ralt+shift: '|' +} + +key SEMICOLON { + label: ';' + base: ';' + shift: ':' +} + +key APOSTROPHE { + label: '\'' + base: '\'' + shift: '"' +} + +### Non-printing keys ### + +key ESCAPE { + base: fallback BACK + alt, meta: fallback HOME + ctrl: fallback MENU +} diff --git a/data/keyboards/Vendor_18d1_Product_5018.kl b/data/keyboards/Vendor_18d1_Product_5018.kl new file mode 100644 index 00000000000..e95ccb5c472 --- /dev/null +++ b/data/keyboards/Vendor_18d1_Product_5018.kl @@ -0,0 +1,84 @@ +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Key layout for Google Pixel C Keyboard +# + +# Row 1 +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 +key 12 MINUS +key 14 DEL # Backspace + +# Row 2 +key 15 TAB +key 16 Q +key 17 W +key 18 E +key 19 R +key 20 T +key 21 Y +key 22 U +key 23 I +key 24 O +key 25 P +key 13 EQUALS +key 28 ENTER + +# Row 3 +key 125 META_LEFT # "Search key" +key 30 A +key 31 S +key 32 D +key 33 F +key 34 G +key 35 H +key 36 J +key 37 K +key 38 L +key 39 SEMICOLON +key 40 APOSTROPHE + +# Row 4 +key 42 SHIFT_LEFT +key 44 Z +key 45 X +key 46 C +key 47 V +key 48 B +key 49 N +key 50 M +key 51 COMMA +key 52 PERIOD +key 53 SLASH +key 54 SHIFT_RIGHT + +# Row 5 +key 29 CTRL_LEFT +key 56 ALT_LEFT +key 57 SPACE +key 100 ALT_RIGHT +key 103 DPAD_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 108 DPAD_DOWN diff --git a/docs/html/guide/topics/manifest/compatible-screens-element.jd b/docs/html/guide/topics/manifest/compatible-screens-element.jd index 3606b15cabc..de921d2e03c 100644 --- a/docs/html/guide/topics/manifest/compatible-screens-element.jd +++ b/docs/html/guide/topics/manifest/compatible-screens-element.jd @@ -9,7 +9,7 @@ parent.link=manifest-intro.html

 <compatible-screens>
     <screen android:screenSize=["small" | "normal" | "large" | "xlarge"]
-            android:screenDensity=["ldpi" | "mdpi" | "hdpi" | "xhdpi"] />
+            android:screenDensity=["ldpi" | "mdpi" | "hdpi" | "xhdpi" | "xxhdpi" | "xxxhdpi"] />
     ...
 </compatible-screens>
 
@@ -94,11 +94,9 @@ href="{@docRoot}guide/practices/screens_support.html#range">Supporting Multiple
  • {@code mdpi}
  • {@code hdpi}
  • {@code xhdpi}
  • +
  • {@code xxhdpi}
  • +
  • {@code xxxhdpi}
  • -

    Note: This attribute currently does not accept - {@code xxhdpi} as a valid value, but you can instead specify {@code 480} - as the value, which is the approximate threshold for xhdpi screens.

    -

    For information about the different screen densities, see Supporting Multiple Screens.

    @@ -110,8 +108,8 @@ href="{@docRoot}guide/practices/screens_support.html#range">Supporting Multiple
    example

    If your application is compatible with only small and normal screens, regardless -of screen density, then you must specify eight different {@code <screen>} elements, -because each screen size has four different density configurations. You must declare each one of +of screen density, then you must specify twelve different {@code <screen>} elements, +because each screen size has six different density configurations. You must declare each one of these; any combination of size and density that you do not specify is considered a screen configuration with which your application is not compatible. Here's what the manifest entry looks like if your application is compatible with only small and normal screens:

    @@ -125,11 +123,15 @@ entry looks like if your application is compatible with only small and normal sc <screen android:screenSize="small" android:screenDensity="mdpi" /> <screen android:screenSize="small" android:screenDensity="hdpi" /> <screen android:screenSize="small" android:screenDensity="xhdpi" /> + <screen android:screenSize="small" android:screenDensity="xxhdpi" /> + <screen android:screenSize="small" android:screenDensity="xxxhdpi" /> <!-- all normal size screens --> <screen android:screenSize="normal" android:screenDensity="ldpi" /> <screen android:screenSize="normal" android:screenDensity="mdpi" /> <screen android:screenSize="normal" android:screenDensity="hdpi" /> <screen android:screenSize="normal" android:screenDensity="xhdpi" /> + <screen android:screenSize="normal" android:screenDensity="xxhdpi" /> + <screen android:screenSize="normal" android:screenDensity="xxxhdpi" /> </compatible-screens> <application ... > ... diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java index 26232a9bc89..d800acbe805 100644 --- a/graphics/java/android/graphics/drawable/Icon.java +++ b/graphics/java/android/graphics/drawable/Icon.java @@ -377,12 +377,17 @@ public Drawable loadDrawableAsUser(Context context, int userId) { return loadDrawable(context); } + /** @hide */ + public static final int MIN_ASHMEM_ICON_SIZE = 128 * (1 << 10); + /** * Puts the memory used by this instance into Ashmem memory, if possible. * @hide */ public void convertToAshmem() { - if (mType == TYPE_BITMAP && getBitmap().isMutable()) { + if (mType == TYPE_BITMAP && + getBitmap().isMutable() && + getBitmap().getAllocationByteCount() >= MIN_ASHMEM_ICON_SIZE) { setBitmap(getBitmap().createAshmemBitmap()); } } diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 0951fc158ac..642ec25dfe8 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -666,7 +666,9 @@ template void RenderNode::issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler) { if (properties().getAlpha() <= 0.0f || properties().getOutline().getAlpha() <= 0.0f - || !properties().getOutline().getPath()) { + || !properties().getOutline().getPath() + || properties().getScaleX() == 0 + || properties().getScaleY() == 0) { // no shadow to draw return; } @@ -856,7 +858,10 @@ void RenderNode::issueOperations(OpenGLRenderer& renderer, T& handler) { const bool useViewProperties = (!mLayer || drawLayer); if (useViewProperties) { const Outline& outline = properties().getOutline(); - if (properties().getAlpha() <= 0 || (outline.getShouldClip() && outline.isEmpty())) { + if (properties().getAlpha() <= 0 + || (outline.getShouldClip() && outline.isEmpty()) + || properties().getScaleX() == 0 + || properties().getScaleY() == 0) { DISPLAY_LIST_LOGD("%*sRejected display list (%p, %s)", handler.level() * 2, "", this, getName()); return; diff --git a/libs/hwui/ShadowTessellator.cpp b/libs/hwui/ShadowTessellator.cpp index 09d125839a6..6c8665b50c5 100644 --- a/libs/hwui/ShadowTessellator.cpp +++ b/libs/hwui/ShadowTessellator.cpp @@ -77,9 +77,14 @@ void ShadowTessellator::tessellateSpotShadow(bool isCasterOpaque, } #if DEBUG_SHADOW - ALOGD("light center %f %f %f", - adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z); + ALOGD("light center %f %f %f %d", + adjustedLightCenter.x, adjustedLightCenter.y, adjustedLightCenter.z, lightRadius); #endif + if (isnan(adjustedLightCenter.x) + || isnan(adjustedLightCenter.y) + || isnan(adjustedLightCenter.z)) { + return; + } // light position (because it's in local space) needs to compensate for receiver transform // TODO: should apply to light orientation, not just position diff --git a/libs/hwui/SpotShadow.cpp b/libs/hwui/SpotShadow.cpp index b8c98041a6b..7a2b9af2ba5 100644 --- a/libs/hwui/SpotShadow.cpp +++ b/libs/hwui/SpotShadow.cpp @@ -1052,7 +1052,7 @@ void SpotShadow::dumpPolygon(const Vector2* poly, int polyLength, const char* po */ void SpotShadow::dumpPolygon(const Vector3* poly, int polyLength, const char* polyName) { for (int i = 0; i < polyLength; i++) { - ALOGD("polygon %s i %d x %f y %f", polyName, i, poly[i].x, poly[i].y); + ALOGD("polygon %s i %d x %f y %f z %f", polyName, i, poly[i].x, poly[i].y, poly[i].z); } } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 13b2878ab42..587d4942568 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -479,11 +479,6 @@ * {}

    * This method can be called in any state and calling it does not change * the object state.

    - * setPlaybackRate

    - * any

    - * {}

    - * This method can be called in any state and calling it does not change - * the object state.

    * setPlaybackParams

    * any

    * {}

    @@ -2243,10 +2238,14 @@ public void addSubtitleSource(InputStream is, MediaFormat format) final InputStream fIs = is; final MediaFormat fFormat = format; - // Ensure all input streams are closed. It is also a handy - // way to implement timeouts in the future. - synchronized(mOpenSubtitleSources) { - mOpenSubtitleSources.add(is); + if (is != null) { + // Ensure all input streams are closed. It is also a handy + // way to implement timeouts in the future. + synchronized(mOpenSubtitleSources) { + mOpenSubtitleSources.add(is); + } + } else { + Log.w(TAG, "addSubtitleSource called with null InputStream"); } // process each subtitle in its own thread diff --git a/media/java/android/media/midi/IBluetoothMidiService.aidl b/media/java/android/media/midi/IBluetoothMidiService.aidl new file mode 100644 index 00000000000..fe5566d6e66 --- /dev/null +++ b/media/java/android/media/midi/IBluetoothMidiService.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.midi; + +import android.bluetooth.BluetoothDevice; +import android.os.IBinder; + +/** @hide */ +interface IBluetoothMidiService +{ + IBinder addBluetoothDevice(in BluetoothDevice bluetoothDevice); +} diff --git a/media/java/android/media/midi/MidiManager.java b/media/java/android/media/midi/MidiManager.java index 7197dc09bbc..266b0d97cb5 100644 --- a/media/java/android/media/midi/MidiManager.java +++ b/media/java/android/media/midi/MidiManager.java @@ -169,6 +169,13 @@ public MidiManager(IMidiManager service) { /** * Registers a callback to receive notifications when MIDI devices are added and removed. * + * The {@link DeviceCallback#onDeviceStatusChanged} method will be called immediately + * for any devices that have open ports. This allows applications to know which input + * ports are already in use and, therefore, unavailable. + * + * Applications should call {@link #getDevices} before registering the callback + * to get a list of devices already added. + * * @param callback a {@link DeviceCallback} for MIDI device notifications * @param handler The {@link android.os.Handler Handler} that will be used for delivering the * device notifications. If handler is null, then the thread used for the @@ -288,7 +295,6 @@ public void onDeviceOpened(IMidiDeviceServer server, IBinder deviceToken) { // fetch MidiDeviceInfo from the server MidiDeviceInfo deviceInfo = server.getDeviceInfo(); device = new MidiDevice(deviceInfo, server, mService, mToken, deviceToken); - sendOpenDeviceResponse(device, listenerF, handlerF); } catch (RemoteException e) { Log.e(TAG, "remote exception in getDeviceInfo()"); } diff --git a/media/packages/BluetoothMidiService/Android.mk b/media/packages/BluetoothMidiService/Android.mk index 2c9c3c5615e..05659251f7a 100644 --- a/media/packages/BluetoothMidiService/Android.mk +++ b/media/packages/BluetoothMidiService/Android.mk @@ -3,7 +3,8 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional -LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_SRC_FILES += \ + $(call all-java-files-under,src) LOCAL_PACKAGE_NAME := BluetoothMidiService LOCAL_CERTIFICATE := platform diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml index b0b389a59aa..1cfd55d556a 100644 --- a/media/packages/BluetoothMidiService/AndroidManifest.xml +++ b/media/packages/BluetoothMidiService/AndroidManifest.xml @@ -8,7 +8,7 @@ - diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java index e6d59e4fb14..444705cfc79 100644 --- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java +++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiDevice.java @@ -147,14 +147,22 @@ public void onCharacteristicRead(BluetoothGatt gatt, // switch to receiving notifications after initial characteristic read mBluetoothGatt.setCharacteristicNotification(characteristic, true); + // Use writeType that requests acknowledgement. + // This improves compatibility with various BLE-MIDI devices. + int originalWriteType = characteristic.getWriteType(); + characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT); + BluetoothGattDescriptor descriptor = characteristic.getDescriptor( CLIENT_CHARACTERISTIC_CONFIG); if (descriptor != null) { descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); - mBluetoothGatt.writeDescriptor(descriptor); + boolean result = mBluetoothGatt.writeDescriptor(descriptor); + Log.d(TAG, "writeDescriptor returned " + result); } else { Log.e(TAG, "No CLIENT_CHARACTERISTIC_CONFIG for device " + mBluetoothDevice); } + + characteristic.setWriteType(originalWriteType); } @Override diff --git a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java index fbde2b4ccfb..5541f9f81f3 100644 --- a/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java +++ b/media/packages/BluetoothMidiService/src/com/android/bluetoothmidiservice/BluetoothMidiService.java @@ -19,6 +19,7 @@ import android.app.Service; import android.bluetooth.BluetoothDevice; import android.content.Intent; +import android.media.midi.IBluetoothMidiService; import android.media.midi.MidiManager; import android.os.IBinder; import android.util.Log; @@ -34,24 +35,31 @@ public class BluetoothMidiService extends Service { @Override public IBinder onBind(Intent intent) { - if (MidiManager.BLUETOOTH_MIDI_SERVICE_INTENT.equals(intent.getAction())) { - BluetoothDevice bluetoothDevice = (BluetoothDevice)intent.getParcelableExtra("device"); + // Return the interface + return mBinder; + } + + + private final IBluetoothMidiService.Stub mBinder = new IBluetoothMidiService.Stub() { + + public IBinder addBluetoothDevice(BluetoothDevice bluetoothDevice) { + BluetoothMidiDevice device; if (bluetoothDevice == null) { - Log.e(TAG, "no BluetoothDevice in onBind intent"); + Log.e(TAG, "no BluetoothDevice in addBluetoothDevice()"); return null; } - - BluetoothMidiDevice device; synchronized (mDeviceServerMap) { device = mDeviceServerMap.get(bluetoothDevice); if (device == null) { - device = new BluetoothMidiDevice(this, bluetoothDevice, this); + device = new BluetoothMidiDevice(BluetoothMidiService.this, + bluetoothDevice, BluetoothMidiService.this); + mDeviceServerMap.put(bluetoothDevice, device); } } return device.getBinder(); } - return null; - } + + }; void deviceClosed(BluetoothDevice device) { synchronized (mDeviceServerMap) { diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml index 382b2d09c88..ff14f94b471 100644 --- a/packages/DocumentsUI/AndroidManifest.xml +++ b/packages/DocumentsUI/AndroidManifest.xml @@ -3,6 +3,7 @@ + srcs = intent.getParcelableArrayListExtra(EXTRA_SRC_LIST); final DocumentStack stack = intent.getParcelableExtra(EXTRA_STACK); try { + wakeLock.acquire(); + // Acquire content providers. mSrcClient = DocumentsApplication.acquireUnstableProviderOrThrow(getContentResolver(), srcs.get(0).authority); @@ -151,6 +158,8 @@ protected void onHandleIntent(Intent intent) { ContentProviderClient.releaseQuietly(mSrcClient); ContentProviderClient.releaseQuietly(mDstClient); + wakeLock.release(); + // Dismiss the ongoing copy notification when the copy is done. mNotificationManager.cancel(mJobId, 0); @@ -179,7 +188,8 @@ protected void onHandleIntent(Intent intent) { @Override public void onCreate() { super.onCreate(); - mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + mPowerManager = getSystemService(PowerManager.class); + mNotificationManager = getSystemService(NotificationManager.class); } /** diff --git a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java index ffdf0a79fd3..9a86c8ef2f2 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java +++ b/packages/DocumentsUI/src/com/android/documentsui/DirectoryFragment.java @@ -19,7 +19,10 @@ import static com.android.documentsui.BaseActivity.State.ACTION_BROWSE; import static com.android.documentsui.BaseActivity.State.ACTION_BROWSE_ALL; import static com.android.documentsui.BaseActivity.State.ACTION_CREATE; +import static com.android.documentsui.BaseActivity.State.ACTION_GET_CONTENT; import static com.android.documentsui.BaseActivity.State.ACTION_MANAGE; +import static com.android.documentsui.BaseActivity.State.ACTION_OPEN; +import static com.android.documentsui.BaseActivity.State.ACTION_OPEN_TREE; import static com.android.documentsui.BaseActivity.State.MODE_GRID; import static com.android.documentsui.BaseActivity.State.MODE_LIST; import static com.android.documentsui.BaseActivity.State.MODE_UNKNOWN; @@ -580,11 +583,23 @@ public void onItemCheckedStateChanged( // Directories and footer items cannot be checked boolean valid = false; + final State state = getDisplayState(DirectoryFragment.this); final Cursor cursor = mAdapter.getItem(position); if (cursor != null) { final String docMimeType = getCursorString(cursor, Document.COLUMN_MIME_TYPE); final int docFlags = getCursorInt(cursor, Document.COLUMN_FLAGS); - valid = isDocumentEnabled(docMimeType, docFlags); + switch (state.action) { + case ACTION_OPEN: + case ACTION_CREATE: + case ACTION_GET_CONTENT: + case ACTION_OPEN_TREE: + valid = isDocumentEnabled(docMimeType, docFlags) + && !Document.MIME_TYPE_DIR.equals(docMimeType); + break; + default: + valid = isDocumentEnabled(docMimeType, docFlags); + break; + } } if (!valid) { diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java index f5908c55cfa..407838a99c7 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentLoader.java @@ -36,6 +36,7 @@ import com.android.documentsui.BaseActivity.State; import com.android.documentsui.model.RootInfo; +import com.android.internal.annotations.GuardedBy; import com.google.android.collect.Maps; import com.google.common.collect.Lists; import com.google.common.util.concurrent.AbstractFuture; @@ -81,6 +82,7 @@ public class RecentLoader extends AsyncTaskLoader { private final RootsCache mRoots; private final State mState; + @GuardedBy("mTasks") private final HashMap mTasks = Maps.newHashMap(); private final int mSortOrder = State.SORT_ORDER_LAST_MODIFIED; @@ -167,6 +169,12 @@ public RecentLoader(Context context, RootsCache roots, State state) { @Override public DirectoryResult loadInBackground() { + synchronized (mTasks) { + return loadInBackgroundLocked(); + } + } + + private DirectoryResult loadInBackgroundLocked() { if (mFirstPassLatch == null) { // First time through we kick off all the recent tasks, and wait // around to see if everyone finishes quickly. @@ -304,8 +312,10 @@ protected void onReset() { // Ensure the loader is stopped onStopLoading(); - for (RecentTask task : mTasks.values()) { - IoUtils.closeQuietly(task); + synchronized (mTasks) { + for (RecentTask task : mTasks.values()) { + IoUtils.closeQuietly(task); + } } IoUtils.closeQuietly(mResult); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java index 8300e0ea0d1..9c5abf3a00d 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java @@ -19,6 +19,7 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothProfile; +import android.bluetooth.le.BluetoothLeScanner; import android.content.Context; import android.os.ParcelUuid; import android.util.Log; @@ -106,6 +107,10 @@ public int getScanMode() { return mAdapter.getScanMode(); } + public BluetoothLeScanner getBluetoothLeScanner() { + return mAdapter.getBluetoothLeScanner(); + } + public int getState() { return mAdapter.getState(); } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index d0efbebcf48..12780852443 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -128,6 +128,9 @@ + + + diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index 8040ba7d929..f797e044363 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -428,4 +428,7 @@ "Geluk! Stelsel-UI-ontvanger is by Instellings gevoeg" "Verwyder uit Instellings" "Verwyder Stelsel-UI-ontvanger uit Instellings en staak die gebruik van al sy kenmerke?" + "Skakel Bluetooth aan?" + "Jy moet Bluetooth aanskakel om jou sleutelbord aan jou tablet te koppel." + "Skakel aan" diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 23410f03973..e4d04adb2ad 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -428,4 +428,7 @@ "እንኳን ደስ ያለዎት! የስርዓት በይነገጽ መቃኛ ወደ ቅንብሮች ታክሏል" "ከቅንብሮች አስወግድ" "ከቅንብሮች ላይ የስርዓት በይነገጽ መቃኛ ተወግዶ ሁሉም ባህሪዎቹን መጠቀም ይቁም?" + "ብሉቱዝ ይብራ?" + "የቁልፍ ሰሌዳዎን ከእርስዎ ጡባዊ ጋር ለማገናኘት በመጀመሪያ ብሉቱዝን ማብራት አለብዎት።" + "አብራ" diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 7c572833e4d..2a0d053f0ae 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -432,4 +432,7 @@ "تهانينا! تمت إضافة أداة ضبط واجهة مستخدم النظام إلى الإعدادات" "إزالة من الإعدادات" "هل تريد إزالة أداة ضبط واجهة مستخدم النظام من الإعدادات وإيقاف استخدام كل ميزاتها؟" + "تشغيل البلوتوث؟" + "لتوصيل لوحة المفاتيح بالجهاز اللوحي، يلزمك تشغيل بلوتوث أولاً." + "تشغيل" diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml index dfb61bc97d2..65c196b4f54 100644 --- a/packages/SystemUI/res/values-az-rAZ/strings.xml +++ b/packages/SystemUI/res/values-az-rAZ/strings.xml @@ -428,4 +428,7 @@ "Təbriklər! System UI Tuner Ayarlara əlavə edildi" "Ayarlardan Silin" "System UI Tuner Ayarlardan silinsin və onun bütün funksiyalarından istifadə dayandırılsın?" + "Bluetooth aktivləşsin?" + "Tabletinizlə klaviaturaya bağlanmaq üçün ilk olaraq Bluetooth\'u aktivləşdirməlisiniz." + "Aktivləşdirin" diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index de43d3d9758..c9dc33df9ae 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -428,4 +428,7 @@ "Поздравления! Тунерът на системния потребителски интерфейс е добавен към „Настройки“" "Премахване от „Настройки“" "Да се премахне ли от „Настройки“ тунерът на системния потребителски интерфейс и да се спре ли използването на всичките му функции?" + "Да се включи ли Bluetooth?" + "За да свържете клавиатурата с таблета си, първо трябва да включите Bluetooth." + "Включване" diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml index 877b290091f..c90065628e1 100644 --- a/packages/SystemUI/res/values-bn-rBD/strings.xml +++ b/packages/SystemUI/res/values-bn-rBD/strings.xml @@ -428,4 +428,7 @@ "অভিনন্দন! সেটিংস -এ সিস্টেম UI টিউনার যোগ করা হয়েছে" "সেটিংস থেকে সরান" "সেটিংস থেকে সিস্টেম UI টিউনার সরাতে এবং এটির সমস্ত বৈশিষ্ট্য ব্যবহার করা বন্ধ করতে চান?" + "Bluetooth চালু করবেন?" + "আপনার ট্যাবলেটের সাথে আপনার কীবোর্ড সংযুক্ত করতে, আপনাকে প্রথমে Bluetooth চালু করতে হবে।" + "চালু করুন" diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index e3ada5a08ea..318084f0bb9 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -170,7 +170,7 @@ "%1$s %2$s" "Notificació omesa." "Àrea de notificacions" - "Configuració ràpida." + "Configuració ràpida" "Pantalla de bloqueig" "Configuració" "Visió general" @@ -337,7 +337,7 @@ "Usuari nou" "Convidat" "Afegeix un convidat" - "Suprimeix l\'usuari" + "Suprimeix el convidat" "Vols suprimir el convidat?" "Totes les aplicacions i les dades d\'aquesta sessió se suprimiran." "Suprimeix" @@ -430,4 +430,7 @@ "Enhorabona! El Personalitzador d\'interfície d\'usuari s\'ha afegit a Configuració." "Treu de Configuració" "Vols suprimir el Personalitzador d\'interfície d\'usuari de Configuració i deixar d\'utilitzar-ne totes les funcions?" + "Vols activar el Bluetooth?" + "Per connectar el teclat amb la tauleta, primer has d\'activar el Bluetooth." + "Activa" diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index ad3f2eeb18e..8f347b1c7ef 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -432,4 +432,7 @@ "Úspěch! Do Nastavení byl přidán nástroj na ladění uživatelského rozhraní systému." "Odstranit z Nastavení" "Chcete nástroj na ladění uživatelského rozhraní systému odstranit z Nastavení a přestat používat všechny jeho funkce?" + "Zapnout Bluetooth?" + "Chcete-li klávesnici připojit k tabletu, nejdříve musíte zapnout Bluetooth." + "Zapnout" diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 32f90b1e53b..28e7a487a97 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -85,13 +85,13 @@ "Søg" "Kamera" "Telefon" - "Voice Assist" + "Taleassistent" "Lås op" "Knap til oplåsning. Venter på fingeraftryk" "Lås op uden at bruge dit fingeraftryk" "lås op" "åbn telefon" - "åbn voice assist" + "åbn taleassistent" "åbn kamera" "Vælg nyt opgavelayout" "Annuller" @@ -315,7 +315,7 @@ "Tryk igen for at åbne" "Stryg opad for at låse op" "Stryg fra telefonikonet" - "Stryg fra ikonet for voice assist" + "Stryg fra mikrofonikonet" "Stryg fra kameraikonet" "Helt lydløs. Denne handling slukker også skærmlæsere." "Total stilhed" @@ -428,4 +428,7 @@ "System UI Tuner blev føjet til Indstillinger" "Fjern fra Indstillinger" "Vil du fjerne System UI Tuner fra Indstillinger og stoppe med at bruge alle dens funktioner?" + "Vil du slå Bluetooth til?" + "Bluetooth skal være slået til, før du kan knytte dit tastatur til din tablet." + "Slå til" diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 8ef7c4e67d4..c70c1754561 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -430,4 +430,7 @@ "Herzlichen Glückwunsch! System UI Tuner wurde \"Einstellungen\" hinzugefügt." "Aus \"Einstellungen\" entfernen" "System UI Tuner aus \"Einstellungen\" entfernen und die Verwendung von allen zugehörigen Funktionen beenden?" + "Bluetooth aktivieren?" + "Zum Verbinden von Tastatur und Tablet muss Bluetooth aktiviert sein." + "Aktivieren" diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index e875639971b..4b68bdba633 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -430,4 +430,7 @@ "Συγχαρητήρια! Το System UI Tuner προστέθηκε στις Ρυθμίσεις" "Κατάργηση από τις Ρυθμίσεις" "Κατάργηση System UI Tuner από τις Ρυθμίσεις και διακοπή χρήσης όλων των λειτουργιών του;" + "Ενεργοποίηση Bluetooth;" + "Για να συνδέσετε το πληκτρολόγιο με το tablet σας, θα πρέπει πρώτα να ενεργοποιήσετε το Bluetooth." + "Ενεργοποίηση" diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 75d798a4805..5a9fc1685ec 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -428,4 +428,7 @@ "Congrats! System UI Tuner has been added to Settings" "Remove from settings" "Remove System UI Tuner from Settings and stop using all of its features?" + "Turn on Bluetooth?" + "To connect your keyboard with your tablet, you first have to turn on Bluetooth." + "Turn on" diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 75d798a4805..5a9fc1685ec 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -428,4 +428,7 @@ "Congrats! System UI Tuner has been added to Settings" "Remove from settings" "Remove System UI Tuner from Settings and stop using all of its features?" + "Turn on Bluetooth?" + "To connect your keyboard with your tablet, you first have to turn on Bluetooth." + "Turn on" diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 75d798a4805..5a9fc1685ec 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -428,4 +428,7 @@ "Congrats! System UI Tuner has been added to Settings" "Remove from settings" "Remove System UI Tuner from Settings and stop using all of its features?" + "Turn on Bluetooth?" + "To connect your keyboard with your tablet, you first have to turn on Bluetooth." + "Turn on" diff --git a/packages/SystemUI/res/values-en/donottranslate.xml b/packages/SystemUI/res/values-en/donottranslate.xml deleted file mode 100644 index 9f04e1f50b1..00000000000 --- a/packages/SystemUI/res/values-en/donottranslate.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - @string/keyguard_indication_charging_time_fast - - @string/keyguard_indication_charging_time_slowly - \ No newline at end of file diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 8b3c3a3e112..aaaa2dc38df 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -430,4 +430,7 @@ "Se agregó el sintonizador de IU del sistema a Configuración" "Quitar de Configuración" "¿Quieres quitar el sintonizador de IU del sistema de Configuración y dejar de utilizar todas sus funciones?" + "¿Activar Bluetooth?" + "Para conectar el teclado con la tablet, primero debes activar Bluetooth." + "Activar" diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 0409191f321..c8323ad4a19 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -428,4 +428,7 @@ "¡Enhorabuena! El configurador de IU del sistema se ha añadido a Ajustes" "Eliminar de Ajustes" "¿Eliminar el configurador de IU del sistema de Ajustes y dejar de utilizar sus funciones?" + "¿Activar Bluetooth?" + "Para poder conectar tu teclado a tu tablet, debes activar el Bluetooth." + "Activar" diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml index 1e79262b8c6..9877b8f3157 100644 --- a/packages/SystemUI/res/values-et-rEE/strings.xml +++ b/packages/SystemUI/res/values-et-rEE/strings.xml @@ -428,4 +428,7 @@ "Õnnitleme! Süsteemi kasutajaliidese tuuner lisati seadetesse" "Eemalda seadetest" "Kas eemaldada seadetest süsteemi kasutajaliidese tuuner ja lõpetada kõikide selle funktsioonide kasutamine?" + "Kas lülitada Bluetooth sisse?" + "Klaviatuuri ühendamiseks tahvelarvutiga peate esmalt Bluetoothi sisse lülitama." + "Lülita sisse" diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml index ccb3fb2f2f1..adeda3130a1 100644 --- a/packages/SystemUI/res/values-eu-rES/strings.xml +++ b/packages/SystemUI/res/values-eu-rES/strings.xml @@ -428,4 +428,7 @@ "Zorionak! Sistemako erabiltzaile-interfazearen konfiguratzailea Ezarpenak atalean gehitu da" "Kendu Ezarpenak ataletik" "Sistemako erabiltzaile-interfazearen konfiguratzailea ezarpenetatik kendu nahi duzu, eta haren eginbide guztiak erabiltzeari utzi nahi diozu?" + "Bluetooth eginbidea aktibatu nahi duzu?" + "Teklatua tabletara konektatzeko, Bluetooth eginbidea aktibatu behar duzu." + "Aktibatu" diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 46cd4685934..e308aabac55 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -67,12 +67,12 @@ "‏کاربری که درحال حاضر در این دستگاه وارد سیستم شده نمی‌تواند اشکال‌زدایی USB را روشن کند. برای استفاده از این ویژگی، به کاربر اصلی «%s» تغییر حالت دهید." "بزرگ‌نمایی برای پر کردن صفحه" "گسترده کردن برای پر کردن صفحه" - "در حال ذخیره تصویر صفحه..." - "در حال ذخیره تصویر صفحه..." - "تصویر صفحه ذخیره شد." - "تصویر صفحه گرفته شد." + "در حال ذخیره عکس صفحه‌نمایش..." + "در حال ذخیره عکس صفحه‌نمایش..." + "عکس صفحه‌نمایش ذخیره شد." + "عکس صفحه‌نمایش گرفته شد." "برای مشاهده عکس صفحه‌نمایشتان، لمس کنید." - "تصویر صفحه گرفته نشد." + "عکس صفحه‌نمایش گرفته نشد." "به دلیل فضای ذخیره‌سازی کم یا عدم اجازه برنامه یا سازمانتان، نمی‌توان از صفحه عکس گرفت." "‏گزینه‌های انتقال فایل USB" "‏نصب به‌عنوان دستگاه پخش رسانه (MTP)" @@ -219,7 +219,7 @@ "داده موقتاً متوقف شده است" "چون به محدودیت داده تنظیم شده رسیده‌اید، دستگاه مصرف داده را برای باقیمانده این دوره موقتاً متوقف کرده است.\n\nاگر ادامه دهید شاید موجب کسر هزینه از طرف شرکت مخابراتی شما شود." "از سر‌گیری" - "اتصال اینترنتی وجود ندارد" + "اتصال اینترنتی ندارید" "‏Wi-Fi متصل شد" "‏جستجو برای GPS" "‏مکان تنظیم شده توسط GPS" @@ -378,7 +378,7 @@ "دستگاه قفل باقی می‌ماند تا زمانی که قفل آن را به صورت دستی باز کنید" "دریافت سریع‌تر اعلان‌ها" "قبل از باز کردن قفل آنها را مشاهده کنید" - "خیر، سپاسگزارم" + "نه سپاسگزارم" "راه‌اندازی" "مشاهده همه" "پنهان کردن همه" @@ -390,7 +390,7 @@ "تا زمانی که پین را بردارید، در نما نگه‌داشته می‌شود. برای برداشتن پین، برگشت و نمای کلی را به صورت هم‌زمان لمس کنید و نگه‌دارید." "تا زمانی که پین را بردارید، در نما نگه‌داشته می‌شود. برای برداشتن پین، نمای کلی را لمس کنید و نگه‌دارید." "متوجه شدم" - "خیر متشکرم" + "نه سپاسگزارم" "%1$s مخفی شود؟" "دفعه بعد که آن را روشن کنید، در تنظیمات نشان داده می‌شود." "پنهان کردن" @@ -428,4 +428,7 @@ "تبریک می‌گوییم! «تنظیم‌کننده واسط کاربری سیستم» به «تنظیمات» اضافه شد" "حذف از تنظیمات" "«تنظیم‌کننده واسط کاربری سیستم» از تنظیمات حذف شود و همه ویژگی‌های آن متوقف شوند؟" + "بلوتوث روشن شود؟" + "برای مرتبط کردن صفحه‌کلید با رایانه لوحی، ابتدا باید بلوتوث را روشن کنید." + "روشن کردن" diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 5172f9cc86a..2dbd5be1e4a 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -428,4 +428,7 @@ "Hienoa! System UI Tuner on nyt lisätty Asetuksiin." "Poista Asetuksista" "Haluatko poistaa System UI Tunerin Asetuksista ja lopettaa sen ominaisuuksien käytön?" + "Otetaanko Bluetooth käyttöön?" + "Jotta voit yhdistää näppäimistön tablettiisi, sinun on ensin otettava Bluetooth käyttöön." + "Ota käyttöön" diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 0cb0a948fd2..d65c27797f0 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -430,4 +430,7 @@ "Félicitations! System UI Tuner a bien été ajouté aux paramètres." "Supprimer des paramètres" "Supprimer « System UI Tuner » des paramètres et arrêter d\'utiliser toutes ses fonctionnalités?" + "Activer Bluetooth?" + "Pour connecter votre clavier à votre tablette, vous devez d\'abord activer la connectivité Bluetooth." + "Activer" diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 86a45b6383d..ab9e64d1eed 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -327,8 +327,8 @@ "Priorité\nuniquement" "Alarmes\nuniquement" "Charge en cours… (chargé à 100 %% dans %s)" - "Charge rapide… (chargé à 100 % dans %s)" - "Charge lente… (chargé à 100 % dans %s)" + "Charge rapide… (chargé à 100 %% dans %s)" + "Charge lente… (chargé à 100 %% dans %s)" "Changer d\'utilisateur" "Changer d\'utilisateur (utilisateur actuel : %s)" "Utilisateur actuel : %s" @@ -430,4 +430,7 @@ "Félicitations ! System UI Tuner a bien été ajouté aux paramètres." "Supprimer l\'outil des paramètres" "Supprimer System UI Tuner des paramètres et arrêter d\'utiliser toutes ses fonctionnalités ?" + "Activer le Bluetooth ?" + "Pour connecter un clavier à votre tablette, vous devez avoir activé le Bluetooth." + "Activer" diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml index 35e47796b68..0200aa47a8f 100644 --- a/packages/SystemUI/res/values-gl-rES/strings.xml +++ b/packages/SystemUI/res/values-gl-rES/strings.xml @@ -430,4 +430,7 @@ "Parabéns! O configurador da IU do sistema engadiuse a Configuración" "Eliminar da Configuración" "Queres eliminar o configurador da IU do sistema da Configuración e deixar de usar todas as súas funcións?" + "Queres activar o Bluetooth?" + "Para conectar o teu teclado co tablet, primeiro tes que activar o Bluetooth." + "Activar" diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml index b40d6354e27..0c43a10a7be 100644 --- a/packages/SystemUI/res/values-gu-rIN/strings.xml +++ b/packages/SystemUI/res/values-gu-rIN/strings.xml @@ -428,4 +428,7 @@ "અભિનંદન! સિસ્ટમ UI ટ્યૂનરને સેટિંગ્સમાં ઉમેરવામાં આવ્યું છે" "સેટિંગ્સમાંથી દૂર કરો" "સેટિંગ્સમાંથી સિસ્ટમ UI ટ્યૂનર દૂર કરી અને તેની તમામ સુવિધાઓનો ઉપયોગ કરવાનું બંધ કરીએ?" + "Bluetooth ચાલુ કરવુ છે?" + "તમારા ટેબ્લેટ સાથે કીબોર્ડ કનેક્ટ કરવા માટે, તમારે પહેલાં Bluetooth ચાલુ કરવાની જરૂર પડશે." + "ચાલુ કરો" diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index df3f558a5be..2cebebbfc45 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -428,4 +428,7 @@ "बधाई हो! सिस्टम UI ट्यूनर को सेटिंग में जोड़ दिया गया है" "सेटिंग से निकालें" "सेटिंग से सिस्टम UI ट्यूनर निकालें और इसकी सभी सुविधाओं का उपयोग रोक दें?" + "ब्लूटूथ चालू करें?" + "अपने कीबोर्ड को अपने टैबलेट से कनेक्ट करने के लिए, आपको पहले ब्लूटूथ चालू करना होगा." + "चालू करें" diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 77b5826f168..f2283855730 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -429,4 +429,7 @@ "Čestitamo! Ugađanje korisničkog sučelja sustava dodano je u Postavke" "Ukloni iz Postavki" "Želite li ukloniti Ugađanje korisničkog sučelja sustava iz Postavki i prestati upotrebljavati njegove značajke?" + "Želite li uključiti Bluetooth?" + "Da biste povezali tipkovnicu s tabletom, morate uključiti Bluetooth." + "Uključi" diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index ea2c2854b8c..5f0ed210482 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -428,4 +428,7 @@ "Gratulálunk! A Kezelőfelület-hangolót hozzáadtuk a Beállításokhoz" "Eltávolítás a Beállítások közül" "Eltávolítja a Kezelőfelület-hangolót a Beállításokból, és nem használja tovább egyik funkcióját sem?" + "Engedélyezi a Bluetooth-kapcsolatot?" + "Ha a billentyűzetet csatlakoztatni szeretné táblagépéhez, először engedélyeznie kell a Bluetooth-kapcsolatot." + "Bekapcsolás" diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml index db17b04b0a5..d6052eafeb7 100644 --- a/packages/SystemUI/res/values-hy-rAM/strings.xml +++ b/packages/SystemUI/res/values-hy-rAM/strings.xml @@ -428,4 +428,7 @@ "Համակարգի ՕՄ-ի կարգավորիչը ավելացվել է կարգավորումներին" "Հեռացնել կարգավորումներից" "Հեռացնե՞լ Համակարգի ՕՄ-ի կարգավորիչը կարգավորումներից և չօգտվել այլևս նրա գործառույթներից:" + "Միացնե՞լ Bluetooth-ը:" + "Ստեղնաշարը ձեր պլանշետին միացնելու համար նախ անհրաժեշտ է միացնել Bluetooth-ը:" + "Միացնել" diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 116844c285b..a4a6c462393 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -428,4 +428,7 @@ "Selamat! Penyetel Antarmuka Pengguna Sistem telah ditambahkan ke Setelan" "Hapus dari Setelan" "Hapus Penyetel Antarmuka Pengguna Sistem dari Setelan dan berhenti menggunakan semua fiturnya?" + "Aktifkan Bluetooth?" + "Untuk menghubungkan keyboard dengan tablet, terlebih dahulu aktifkan Bluetooth." + "Aktifkan" diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml index bbd55c3b53b..3d93814cf46 100644 --- a/packages/SystemUI/res/values-is-rIS/strings.xml +++ b/packages/SystemUI/res/values-is-rIS/strings.xml @@ -428,4 +428,7 @@ "Til hamingju! Fínstillingum kerfisviðmóts hefur verið bætt við stillingar" "Fjarlægja úr stillingum" "Viltu fjarlægja fínstillingar kerfisviðmóts úr stillingum og hætta að nota eiginleika þeirra?" + "Kveikja á Bluetooth?" + "Til að geta tengt lyklaborðið við spjaldtölvuna þarftu fyrst að kveikja á Bluetooth." + "Kveikja" diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 5c3d7db9bce..d661cbf6501 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -430,4 +430,7 @@ "Complimenti! Il sintetizzatore interfaccia utente di sistema è stato aggiunto alle impostazioni." "Rimuovi dalle impostazioni" "Vuoi rimuovere il sintetizzatore interfaccia utente di sistema dalle impostazioni e smettere di utilizzare tutte le sue funzioni?" + "Attivare il Bluetooth?" + "Per connettere la tastiera al tablet, devi prima attivare il Bluetooth." + "Attiva" diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 4b96b2cb1d7..0a01c1682c1 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -430,4 +430,7 @@ "‏מזל טוב! System UI Tuner נוסף ל\'הגדרות\'" "הסר מההגדרות" "‏האם להסיר את System UI Tuner ולהפסיק להשתמש בכל התכונות שלו?" + "‏האם להפעיל את ה-Bluetooth?" + "‏כדי לחבר את המקלדת לטאבלט, תחילה עליך להפעיל את ה-Bluetooth." + "הפעל" diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 6ac3fe09fe4..7acc0ecade8 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -430,4 +430,7 @@ "システムUI調整ツールを設定に追加しました" "設定から削除" "設定からシステムUI調整ツールを削除して、全機能の使用を停止しますか?" + "BluetoothをONにしますか?" + "タブレットでキーボードに接続するには、最初にBluetoothをONにする必要があります。" + "ONにする" diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml index 326c83311a7..8e04d5305c2 100644 --- a/packages/SystemUI/res/values-ka-rGE/strings.xml +++ b/packages/SystemUI/res/values-ka-rGE/strings.xml @@ -428,4 +428,7 @@ "გილოცავთ! სისტემის UI ტუნერი დაემატა პარამეტრებს" "პარამეტრებიდან წაშლა" "გსურთ სისტემის UI ტუნერის პარამეტრებიდან წაშლა და მისი ყველა ფუნქციის გამოყენების შეწყვეტა?" + "გსურთ Bluetooth-ის ჩართვა?" + "კლავიატურის ტაბლეტთან დასაკავშირებლად, ჯერ უნდა ჩართოთ Bluetooth." + "ჩართვა" diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml index 6d6a77fb8b9..d3465ce782e 100644 --- a/packages/SystemUI/res/values-kk-rKZ/strings.xml +++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml @@ -428,4 +428,7 @@ "Құттықтаймыз! Жүйелік пайдаланушылық интерфейс тюнері \"Параметрлер\" тармағына қосылды" "Параметрлерден жою" "Жүйелік пайдаланушылық интерфейс тюнерін \"Параметрлер\" тармағынан жойып, оның барлық мүмкіндіктерін пайдалануды тоқтату керек пе?" + "Bluetooth функциясын қосу керек пе?" + "Пернетақтаны планшетке қосу үшін алдымен Bluetooth функциясын қосу керек." + "Қосу" diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml index 839197358ba..94b62bf9cb3 100644 --- a/packages/SystemUI/res/values-km-rKH/strings.xml +++ b/packages/SystemUI/res/values-km-rKH/strings.xml @@ -428,4 +428,7 @@ "សូមអបអរសាទរ! កម្មវិធីសម្រួល UI ប្រព័ន្ធត្រូវបានបន្ថែមទៅការកំណត់ហើយ" "យកចេញពីការកំណត់" "យកកម្មវិធីសម្រួល UI ប្រព័ន្ធចេញពីការកំណត់ ហើយឈប់ប្រើលក្ខណៈពិសេសរបស់វាទាំងអស់?" + "បើកប៊្លូធូសឬ?" + "ដើម្បីភ្ជាប់ក្តារចុចរបស់អ្នកជាមួយនឹងថេប្លេតរបស់អ្នក អ្នកត្រូវតែបើកប៊្លូធូសជាមុនសិន។" + "បើក" diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml index 75d5a2238ae..bf00716cb0e 100644 --- a/packages/SystemUI/res/values-kn-rIN/strings.xml +++ b/packages/SystemUI/res/values-kn-rIN/strings.xml @@ -428,4 +428,7 @@ "ಅಭಿನಂದನೆಗಳು! ಸಿಸ್ಟಮ್ UI ಟ್ಯೂನರ್ ಅನ್ನು ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಗೆ ಸೇರಿಸಲಾಗಿದೆ" "ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಂದ ತೆಗೆದುಹಾಕಿ" "ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಂದ ಸಿಸ್ಟಮ್ UI ಟ್ಯೂನರ್ ತೆಗೆದುಹಾಕುವುದೇ ಮತ್ತು ಅದರ ಎಲ್ಲಾ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಬಳಸುವುದನ್ನು ನಿಲ್ಲಿಸುವುದೇ?" + "ಬ್ಲೂಟೂತ್ ಆನ್ ಮಾಡುವುದೇ?" + "ನಿಮ್ಮ ಕೀಬೋರ್ಡ್ ಅನ್ನು ಟ್ಯಾಬ್ಲೆಟ್‌ಗೆ ಸಂಪರ್ಕಿಸಲು, ನೀವು ಮೊದಲು ಬ್ಲೂಟೂತ್ ಆನ್ ಮಾಡಬೇಕಾಗುತ್ತದೆ." + "ಆನ್ ಮಾಡು" diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index c4a50e593c6..5c49e510aac 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -428,4 +428,7 @@ "축하합니다. 시스템 UI 튜너가 설정에 추가되었습니다." "설정에서 삭제" "시스템 UI 튜너를 설정에서 삭제하고 모든 관련 기능의 사용을 중지하시겠습니까?" + "블루투스를 켜시겠습니까?" + "키보드를 태블릿에 연결하려면 먼저 블루투스를 켜야 합니다." + "사용" diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml index ce9845118f2..faad243a073 100644 --- a/packages/SystemUI/res/values-ky-rKG/strings.xml +++ b/packages/SystemUI/res/values-ky-rKG/strings.xml @@ -428,4 +428,7 @@ "Куттуктайбыз! Жөндөөлөргө System UI Tuner кошулду" "Жөндөөлөрдөн алып салуу" "System UI Tuner Жөндөөлөрдөн алынып салынып, анын бардык функциялары токтотулсунбу?" + "Bluetooth күйгүзүлсүнбү?" + "Баскычтобуңузду планшетиңизге туташтыруу үчүн, адегенде Bluetooth\'ту күйгүзүшүңүз керек." + "Күйгүзүү" diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml index f91374833e4..c75bbe1f3a3 100644 --- a/packages/SystemUI/res/values-lo-rLA/strings.xml +++ b/packages/SystemUI/res/values-lo-rLA/strings.xml @@ -428,4 +428,7 @@ "ຍິນດີດ້ວຍ! System UI Tuner ໄດ້ຖືກເພີ່ມໃສ່ການຕັ້ງຄ່າແລ້ວ" "ເອົາອອກ​ຈາກ​ການ​ຕັ້ງ​ຄ່າ" "ເອົາ System UI Tuner ອອກຈາກການຕັ້ງຄ່າ ແລະຢຸດການໃຊ້ທຸກຄຸນສົມບັດໃຊ້ງານຂອງມັນ?" + "ເປີດ​ໃຊ້ Bluetooth ບໍ່?" + "ເພື່ອ​ເຊື່ອມ​ຕໍ່​ແປ້ນ​ພິມ​ຂອງ​ທ່ານ​ກັບ​ແທັບ​ເລັດ​ຂອງ​ທ່ານ, ກ່ອນ​ອື່ນ​ໝົດ​ທ່ານ​ຕ້ອງ​ເປີດ Bluetooth." + "ເປີດ​" diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 3cc4082511e..96038c0924e 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -430,4 +430,7 @@ "Sveikiname! Sistemos naudotojo sąsajos derinimo priemonė pridėta prie nustatymų" "Pašalinti iš nustatymų" "Ar norite pašalinti sistemos naudotojo sąsajos derinimo priemonę iš nustatymų ir nebenaudoti jokių jos funkcijų?" + "Įjungti „Bluetooth“?" + "Kad galėtumėte prijungti klaviatūrą prie planšetinio kompiuterio, pirmiausia turite įjungti „Bluetooth“." + "Įjungti" diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 2ef6801bd80..f2d5eb1d50e 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -429,4 +429,7 @@ "Apsveicam! Sistēmas saskarnes regulators ir pievienots iestatījumiem." "Noņemt no iestatījumiem" "Vai noņemt sistēmas saskarnes regulatoru no iestatījumiem un pārtraukt izmantot visas tā funkcijas?" + "Vai ieslēgt Bluetooth savienojumu?" + "Lai pievienotu tastatūru planšetdatoram, vispirms ir jāieslēdz Bluetooth savienojums." + "Ieslēgt" diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml index a977782490e..9c464ad26f0 100644 --- a/packages/SystemUI/res/values-mk-rMK/strings.xml +++ b/packages/SystemUI/res/values-mk-rMK/strings.xml @@ -430,4 +430,7 @@ "Честито! Го додадовте Адаптерот на УИ на системот на Поставки" "Отстрани од поставки" "Да се отстрани Адаптерот на УИ на системот од Поставки и да престанат да се користат сите негови функции?" + "Да се вклучи Bluetooth?" + "За да ја поврзете тастатурата со таблетот, најпрво треба да вклучите Bluetooth." + "Вклучи" diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml index 89de6263773..eea27a55fbc 100644 --- a/packages/SystemUI/res/values-ml-rIN/strings.xml +++ b/packages/SystemUI/res/values-ml-rIN/strings.xml @@ -428,4 +428,7 @@ "അഭിനന്ദനങ്ങൾ! ക്രമീകരണത്തിലേക്ക് സിസ്റ്റം UI ട്യൂണർ ചേർത്തിരിക്കുന്നു" "ക്രമീകരണത്തിൽ നിന്ന് നീക്കംചെയ്യുക" "ക്രമീകരണത്തിൽ നിന്ന് സിസ്റ്റം UI ട്യൂണർ നീക്കംചെയ്യുകയും അതിന്റെ ഫീച്ചറുകളെല്ലാം ഉപയോഗിക്കുന്നത് നിർത്തുകയും ചെയ്യണോ?" + "Bluetooth ഓണാക്കണോ?" + "നിങ്ങളുടെ ടാബ്‌ലെറ്റുമായി കീബോർഡ് കണക്റ്റുചെയ്യുന്നതിന്, ആദ്യം Bluetooth ഓണാക്കേണ്ടതുണ്ട്." + "ഓണാക്കുക" diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml index 5bd986fe4c3..723cb267fd7 100644 --- a/packages/SystemUI/res/values-mn-rMN/strings.xml +++ b/packages/SystemUI/res/values-mn-rMN/strings.xml @@ -426,4 +426,7 @@ "Баяр хүргэе! Системийн UI Tохируулагчийг тохиргоонд нэмлээ" "Тохиргооноос устгах" "Системийн UI Тохируулагчийг тохиргооноос устгаж, үүнтэй холбоотой бүх тохиргоог ашиглахаа болих уу?" + "Bluetooth-г асаах уу?" + "Компьютерийн гараа таблетад холбохын тулд эхлээд Bluetooth-г асаана уу." + "Асаах" diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml index c86e475dc70..bbe80b40088 100644 --- a/packages/SystemUI/res/values-mr-rIN/strings.xml +++ b/packages/SystemUI/res/values-mr-rIN/strings.xml @@ -428,4 +428,7 @@ "अभिनंदन! सिस्टीम UI ट्यूनर सेटिंग्जमध्‍ये जोडले गेले आहे" "सेटिंग्ज मधून काढा" "सेटिंग्ज मधून सिस्टीम UI ट्यूनर काढून त्याची सर्व वैशिष्ट्‍ये वापरणे थांबवायचे?" + "ब्लूटुथ सुरू करायचे?" + "आपला कीबोर्ड आपल्या टॅब्लेटसह कनेक्ट करण्यासाठी, आपल्याला प्रथम ब्लूटुथ चालू करणे आवश्यक आहे." + "चालू करा" diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml index 30647e37c97..471bf681acf 100644 --- a/packages/SystemUI/res/values-ms-rMY/strings.xml +++ b/packages/SystemUI/res/values-ms-rMY/strings.xml @@ -428,4 +428,7 @@ "Tahniah! Penala UI Sistem telah ditambahkan pada Tetapan" "Alih keluar daripada Tetapan" "Alih keluar Penala UI Sistem daripada Tetapan dan berhenti menggunakan semua cirinya?" + "Hidupkan Bluetooth?" + "Untuk menyambungkan papan kekunci anda dengan tablet, anda perlu menghidupkan Bluetooth terlebih dahulu." + "Hidupkan" diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml index 7b80c1b08b8..1f7ff95b5c3 100644 --- a/packages/SystemUI/res/values-my-rMM/strings.xml +++ b/packages/SystemUI/res/values-my-rMM/strings.xml @@ -428,4 +428,7 @@ "ဂုဏ်ပြုပါရစေ! စနစ် UI ဖမ်းစက်ကို ဆက်တင်ထဲသို့ ထည့်ပြီးပြီ။" "ဆက်တင် အထဲမှ ဖယ်ရှားရန်" "ဆက်တင် အထဲမှ စနစ် UI ဖမ်းစက်ကို ဖယ်ရှားလျက် ၎င်း၏ အင်္ဂါရပ်များ အားလုံး အသုံးပြုမှု ရပ်တန့်ရမလား?" + "ဘလူးတုသ် ဖွင့်ရမလား။" + "ကီးဘုတ်ကို တပ်ဘလက်နှင့် ချိတ်ဆက်ရန်၊ ပမထဦးစွာ ဘလူးတုသ်ကို ဖွင့်ပါ။" + "ဖွင့်ပါ" diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index cf3e367b417..7e645ecfd9f 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -428,4 +428,7 @@ "Gratulerer! System UI Tuner er lagt til i Innstillinger" "Fjern fra Innstillinger" "Vil du fjerne System UI Tuner fra Innstillinger og slutte å bruke alle de tilknyttede funksjonene?" + "Vil du slå på Bluetooth?" + "For å koble tastaturet til nettbrettet ditt må du først slå på Bluetooth." + "Slå på" diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml index 4c22d09ce81..05a92051c0f 100644 --- a/packages/SystemUI/res/values-ne-rNP/strings.xml +++ b/packages/SystemUI/res/values-ne-rNP/strings.xml @@ -374,7 +374,7 @@ "तपाईँ %1$s सँग जडित हुनुहुन्छ जसले इ-मेल, अनुप्रयोगहरू र वेबसाइट लगायतका तपाईँको निजी नेटवर्क गतिविधिका अनुगमन गर्न सक्छ।" "तपाईँको कार्य प्रोफाइल %1$s द्वारा व्यवस्थापन गरिन्छ। यो %2$s सँग जोडिएको छ जसले इमेल, अनुप्रयोगहरू, र वेबसाइटहरू लगायतका तपाईँका नेटवर्क गतिविधि अनुगमन गर्न सक्छ।\n\nथप जानकारीको लागि, आफ्नो प्रशासकलाई सम्पर्क गर्नुहोस्।" "तपाईँको कार्य प्रोफाइल %1$s द्वारा व्यवस्थापन गरिन्छ। यो %2$s सँग जोडिएको छ जसले इमेल, अनुप्रयोगहरू, र वेबसाइटहरू लगायतका तपाईँका नेटवर्क गतिविधि अनुगमन गर्न सक्छ।\n\nतपाईँ %3$s सँग पनि जडित हुनुहुन्छ, जसले तपाईँको व्यक्तिगत नेटवर्क गतिविधि अनुगमन गर्न सक्छ।" - "तपाईंको उपकरण %1$s द्वारा व्यवस्थित गरिन्छ।\n\nतपाईंको प्रशासकले तपाईँको यन्त्र र त्यसको स्थान जानकारीमार्फत सेटिङहरू, कर्पोरेट पहुँच, अनुप्रयोगहरू, तपाईँको यन्त्रसँग सम्बद्ध डेटा र तपाईँको यन्त्रको स्थान जानकारीको अनुगमन र व्यवस्थापन गर्न सक्छ।\n\nतपाईं %2$s सँग जडित हुनुहुन्छ जसले इमेल, अनुप्रयोगहरू, र वेबसाइटहरू लगायतका तपाईँका नेटवर्क गतिविधिका अनुगमन गर्न सक्छ।\n\nथप जानकारीको लागि तपाईको प्रशासकलाई सम्पर्क गर्नुहोस्।" + "तपाईँको उपकरण %1$s द्वारा व्यवस्थित गरिन्छ।\n\nतपाईँको प्रशासकले तपाईँको यन्त्र र त्यसको स्थान जानकारीमार्फत सेटिङहरू, कर्पोरेट पहुँच, अनुप्रयोगहरू, तपाईँको यन्त्रसँग सम्बद्ध डेटा र तपाईँको यन्त्रको स्थान जानकारीको अनुगमन र व्यवस्थापन गर्न सक्छ।\n\nतपाईँ %2$s सँग जडित हुनुहुन्छ जसले इमेल, अनुप्रयोगहरू, र वेबसाइटहरू लगायतका तपाईँका नेटवर्क गतिविधिका अनुगमन गर्न सक्छ।\n\nथप जानकारीको लागि तपाईको प्रशासकलाई सम्पर्क गर्नुहोस्।" "तपाईँले नखोले सम्म उपकरण बन्द रहनेछ" "छिटो सूचनाहरू प्राप्त गर्नुहोस्" "तपाईँले अनलक गर्नअघि तिनीहरूलाई हेर्नुहोस्" @@ -387,12 +387,12 @@ "विस्तार गर्नुहोस्" "संक्षिप्त पार्नुहोस्" "पर्दा राखेका छ" - "तपाईं अनपिन सम्म यो दृश्य मा राख्छ। छुनुहोस् र अनपिन फिर्ता र सिंहावलोकन नै समय मा पकड।" - "तपाईं अनपिन सम्म यो दृश्य मा राख्छ। छुनुहोस् र अनपिन गर्न सिंहावलोकन पकड।" + "तपाईँ अनपिन सम्म यो दृश्य मा राख्छ। छुनुहोस् र अनपिन फिर्ता र सिंहावलोकन नै समय मा पकड।" + "तपाईँ अनपिन सम्म यो दृश्य मा राख्छ। छुनुहोस् र अनपिन गर्न सिंहावलोकन पकड।" "बुझेँ" "धन्यवाद पर्दैन" "लुकाउनुहुन्छ %1$s?" - "यो तपाईं सेटिङ् मा यो बारी अर्को समय देखापर्नेछ।" + "यो तपाईँ सेटिङ् मा यो बारी अर्को समय देखापर्नेछ।" "लुकाउनुहोस्" "%1$s भोल्यूम संवाद बन्न चाहन्छ।" "अनुमति दिनुहोस्" @@ -428,4 +428,7 @@ "बधाईँ छ! सेटिङहरूमा प्रणाली UI ट्युनर थप गरिएको छ" "सेटिङहरूबाट हटाउनुहोस्" "प्रणाली UI ट्युनर सेटिङहरूबाट हटाउने र यसका सबै सुविधाहरू प्रयोग गर्न रोक्ने हो?" + "ब्लुटुथ सक्रिय पार्ने हो?" + "आफ्नो ट्याब्लेटसँग किबोर्ड जोड्न, पहिले तपाईँले ब्लुटुथ सक्रिय गर्नुपर्छ।" + "सक्रिय पार्नुहोस्" diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index a4c0b13007c..bcf5213240e 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -428,4 +428,7 @@ "Systeem-UI-tuner is toegevoegd aan Instellingen" "Verwijderen uit Instellingen" "Systeem-UI-tuner uit Instellingen verwijderen en het gebruik van alle functies daarvan stopzetten?" + "Bluetooth inschakelen?" + "Als je je toetsenbord wilt verbinden met je tablet, moet je eerst Bluetooth inschakelen." + "Inschakelen" diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml index e2fb410f831..323292e127e 100644 --- a/packages/SystemUI/res/values-pa-rIN/strings.xml +++ b/packages/SystemUI/res/values-pa-rIN/strings.xml @@ -428,4 +428,7 @@ "ਵਧਾਈਆਂ! ਸਿਸਟਮ UI ਟਿਊਨਰ ਨੂੰ ਸੈਟਿੰਗਜ਼ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ ਹੈ" "ਸੈਟਿੰਗਜ਼ ਤੋਂ ਹਟਾਓ" "ਕੀ ਸੈਟਿੰਗਜ਼ ਤੋਂ ਸਿਸਟਮ UI ਟਿਊਨਰ ਨੂੰ ਹਟਾਉਣਾ ਹੈ ਅਤੇ ਇਸਦੀਆਂ ਸਾਰੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਨੂੰ ਉਪਯੋਗ ਕਰਨ ਤੋਂ ਰੋਕਣਾ ਹੈ?" + "Bluetooth ਚਾਲੂ ਕਰੋ?" + "ਆਪਣੇ ਟੈਬਲੇਟ ਨਾਲ ਆਪਣਾ ਕੀ-ਬੋਰਡ ਕਨੈਕਟ ਕਰਨ ਲਈ, ਤੁਹਾਨੂੰ ਪਹਿਲਾਂ Bluetooth ਚਾਲੂ ਕਰਨ ਦੀ ਜ਼ਰੂਰਤ ਹੈ।" + "ਚਾਲੂ ਕਰੋ" diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index b62001a088d..77ed1b81af3 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -430,4 +430,7 @@ "Gratulujemy, Kalibrator System UI został dodany do Ustawień" "Usuń z Ustawień" "Usunąć Kalibrator System UI z Ustawień i przestać używać wszystkich jego funkcji?" + "Włączyć Bluetooth?" + "Aby połączyć klawiaturę z tabletem, musisz najpierw włączyć Bluetooth." + "Włącz" diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 4d5b2407a86..8f6aebaf589 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -430,4 +430,7 @@ "Parabéns! O sintonizador System UI foi adicionado às configurações" "Remover das configurações" "Remover sintonizador System UI das configurações e parar de usar todos os seus recursos?" + "Ativar o Bluetooth?" + "Para conectar o teclado ao tablet, é preciso primeiro ativar o Bluetooth." + "Ativar" diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index aa2a5842dd4..05858defdd0 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -428,4 +428,7 @@ "Parabéns! O Sintonizador da interface do sistema foi adicionado às Definições" "Remover das Definições" "Pretende remover o Sintonizador da interface do sistema das Definições e deixar de utilizar todas as respetivas funcionalidades?" + "Pretende ativar o Bluetooth?" + "Para ligar o teclado ao tablet, tem de ativar primeiro o Bluetooth." + "Ativar" diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 4d5b2407a86..8f6aebaf589 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -430,4 +430,7 @@ "Parabéns! O sintonizador System UI foi adicionado às configurações" "Remover das configurações" "Remover sintonizador System UI das configurações e parar de usar todos os seus recursos?" + "Ativar o Bluetooth?" + "Para conectar o teclado ao tablet, é preciso primeiro ativar o Bluetooth." + "Ativar" diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index a78e571f467..d68188d30e1 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -159,7 +159,7 @@ "Se obţine GPS." "TeleTypewriter activat." "Vibrare sonerie." - "Sonerie silenţioasă." + "Sonerie silențioasă." "Închideți %s." @@ -255,7 +255,7 @@ "Localizarea este dezactivată" "Dispozitiv media" "RSSI" - "Numai apeluri de urgenţă" + "Numai apeluri de urgență" "Setări" "Ora" "Eu" @@ -429,4 +429,7 @@ "Felicitări! System UI Tuner a fost adăugat în Setări" "Eliminați din Setări" "Eliminați System UI Tuner din Setări și încetați utilizarea tuturor funcțiilor sale?" + "Activați Bluetooth?" + "Pentru a conecta tastatura la tabletă, mai întâi trebuie să activați Bluetooth." + "Activați" diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 0e51db4d47a..31886362f38 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -432,4 +432,7 @@ "Функция System UI Tuner добавлена в меню настроек" "Удалить из настроек" "Убрать функцию System UI Tuner из меню настроек и прекратить ее работу?" + "Подключение по Bluetooth" + "Чтобы подключить клавиатуру к планшету, включите Bluetooth." + "Включить" diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml index b6681066da7..a6873797e65 100644 --- a/packages/SystemUI/res/values-si-rLK/strings.xml +++ b/packages/SystemUI/res/values-si-rLK/strings.xml @@ -428,4 +428,7 @@ "සුබ පැතුම්! පද්ධති UI සුසරකය සැකසීම් වෙත එක් කර ඇත" "සැකසීම් වෙතින් ඉවත් කරන්න" "සැකසීම් වෙතින් පද්ධති UI සුසරකය ඉවත් කර සහ එහි සියලු අංග භාවිතය නවත් වන්නද?" + "බ්ලූටූත් ක්‍රියාත්මක කරන්නද?" + "ඔබේ යතුරු පුවරුව ඔබේ ටැබ්ලට් පරිගණකයට සම්බන්ධ කිරීමට, ඔබ පළමුව බ්ලූටූත් ක්‍රියාත්මක කළ යුතුය." + "ක්‍රියාත්මක කරන්න" diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 068d39ceec2..041a9498340 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -432,4 +432,7 @@ "Výborne, tuner používateľského rozhrania systému bol pridaný do Nastavení" "Odstrániť z Nastavení" "Chcete odstrániť tuner používateľského rozhrania systému z Nastavení a prestať používať všetky jeho funkcie?" + "Zapnúť Bluetooth?" + "Ak chcete klávesnicu pripojiť k tabletu, najprv musíte zapnúť Bluetooth." + "Zapnúť" diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index bea9e9aa3ac..2273758ec54 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -430,4 +430,7 @@ "Čestitke! Uglaševalnik uporabniškega vmesnika sistema je bil dodan v nastavitve." "Odstrani iz nastavitev" "Ali želite odstraniti Uglaševalnik uporabniškega vmesnika sistema iz nastavitev in prenehati uporabljati vse njegove funkcije?" + "Želite vklopiti Bluetooth?" + "Če želite povezati tipkovnico in tablični računalnik, vklopite Bluetooth." + "Vklop" diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml index a376b377c6d..8d6962eb74c 100644 --- a/packages/SystemUI/res/values-sq-rAL/strings.xml +++ b/packages/SystemUI/res/values-sq-rAL/strings.xml @@ -428,4 +428,7 @@ "Urime! Sintonizuesi i Sistemit të Ndërfaqes së Përdoruesit u shtua te Cilësimet" "Hiqe nga Cilësimet" "Të hiqet Sintonizuesi i Sistemit të Ndërfaqes së Përdoruesit nga Cilësimet dhe të ndërpritet përdorimi i të gjitha funksioneve të tij?" + "Të aktivizohet \"bluetooth-i\"?" + "Për të lidhur tastierën me tabletin, në fillim duhet të aktivizosh \"bluetooth-in\"." + "Aktivizo" diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 52d04e22b4b..3c8aeb551dd 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -429,4 +429,7 @@ "Честитамо! Тјунер за кориснички интерфејс система је додат у Подешавања" "Уклони из Подешавања" "Желите ли да уклоните Тјунер за кориснички интерфејс система из Подешавања и да престанете да користите све његове функције?" + "Желите ли да укључите Bluetooth?" + "Да бисте повезали тастатуру са таблетом, прво морате да укључите Bluetooth." + "Укључи" diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index a7caa30bc3a..76ebef2e9d5 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -313,10 +313,10 @@ "+%d" "Mindre brådskande aviseringar nedan" "Tryck igen för att öppna" - "Dra uppåt om du vill låsa upp" - "Dra från ikonen och öppna telefonen" - "Dra från ikonen och öppna röstassistenten" - "Dra från ikonen och öppna kameran" + "Svep uppåt om du vill låsa upp" + "Svep från ikonen och öppna telefonen" + "Svep från ikonen och öppna röstassistenten" + "Svep från ikonen och öppna kameran" "Total tystnad. Även skärmläsningsprogram tystas." "Helt tyst" "Bara prioriterade" @@ -428,4 +428,7 @@ "Grattis! Inställningar för systemgränssnitt har lagts till i inställningarna." "Ta bort från inställningarna" "Vill du ta bort inställningar för systemgränssnitt från inställningarna och sluta använda alla tillhörande funktioner?" + "Vill du aktivera Bluetooth?" + "Om du vill ansluta tangentbordet till surfplattan måste du först aktivera Bluetooth." + "Aktivera" diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 0fc04b69722..d63e1359f71 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -428,4 +428,7 @@ "Hongera! Kipokea Ishara cha System UI kimeongezwa kwenye Mipangilio" "Ondoa kwenye Mipangilio" "Je, ungependa kuondoa Kipokea ishara cha SystemUI kwenye Mipangilio na uache kutumia vipengele vyake vyote?" + "Je, ungependa kuwasha Bluetooth?" + "Ili uunganishe Kibodi yako kwenye kompyuta yako kibao, lazima kwanza uwashe Bluetooth." + "Washa" diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml index e398bf1b4ea..3e1df8cd7fe 100644 --- a/packages/SystemUI/res/values-ta-rIN/strings.xml +++ b/packages/SystemUI/res/values-ta-rIN/strings.xml @@ -428,4 +428,7 @@ "வாழ்த்துகள்! அமைப்புகளில் System UI Tuner சேர்க்கப்பட்டது" "அமைப்புகளிலிருந்து அகற்று" "அமைப்புகளிலிருந்து System UI Tunerஐ அகற்றிவிட்டு, அதன் எல்லா அம்சங்களையும் பயன்படுத்துவதை நிறுத்தவா?" + "புளூடூத்தை இயக்கவா?" + "உங்கள் டேப்லெட்டுடன் விசைப்பலகையை இணைக்க, முதலில் புளூடூத்தை இயக்க வேண்டும்." + "இயக்கு" diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml index 0923085163a..ca3011af05f 100644 --- a/packages/SystemUI/res/values-te-rIN/strings.xml +++ b/packages/SystemUI/res/values-te-rIN/strings.xml @@ -428,4 +428,7 @@ "అభినందనలు! సెట్టింగ్‌లకు సిస్టమ్ UI ట్యూనర్ జోడించబడింది" "సెట్టింగ్‌ల నుండి తీసివేయి" "సిస్టమ్ UI ట్యూనర్‌ను సెట్టింగ్‌ల నుండి తీసివేసి, దాని అన్ని లక్షణాలను ఉపయోగించడం ఆపివేయాలా?" + "బ్లూటూత్ ఆన్ చేయాలా?" + "మీ కీబోర్డ్‌ను మీ టాబ్లెట్‌తో కనెక్ట్ చేయడానికి, మీరు ముందుగా బ్లూటూత్ ఆన్ చేయాలి." + "ఆన్ చేయి" diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 23197acd1fa..2b56af687d4 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -428,4 +428,7 @@ "ยินดีด้วย! เพิ่มตัวรับสัญญาณ UI ระบบไปยังการตั้งค่าแล้ว" "นำออกจากการตั้งค่า" "นำตัวรับสัญญาณ UI ระบบออกจากการตั้งค่าและหยุดใช้คุณลักษณะทั้งหมดของตัวรับสัญญาณใช่ไหม" + "เปิดบลูทูธไหม" + "หากต้องการเชื่อมต่อแป้นพิมพ์กับแท็บเล็ต คุณต้องเปิดบลูทูธก่อน" + "เปิด" diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 65f3f8701cc..ebcfda727f5 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -428,4 +428,7 @@ "Binabati kita! Naidagdag na ang Tuner ng System UI sa Mga Setting" "Alisin sa Mga Setting" "Alisin ang Tuner ng System UI sa Mga Setting at ihinto ang paggamit ng lahat ng feature nito?" + "I-on ang Bluetooth?" + "Upang ikonekta ang iyong keyboard sa iyong tablet, kailangan mo munang i-on ang Bluetooth." + "I-on" diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index eb07cc96fb8..f213b503712 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -428,4 +428,7 @@ "Tebrikler! Sistem Kullanıcı Arayüzü Ayarlayıcı Ayarlar\'a eklendi" "Ayarlar\'dan kaldır" "Sistem Kullanıcı Arayüzü Ayarlayıcısı Ayarlar\'dan kaldırılsın ve tüm özelliklerinin kullanılması durdurulsun mu?" + "Bluetooth açılsın mı?" + "Klavyenizi tabletinize bağlamak için önce Bluetooth\'u açmanız gerekir." + "Aç" diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index a9288dac3ee..b91267875b0 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -430,4 +430,7 @@ "Вітаємо! System UI Tuner установлено в додатку Налаштування" "Видалити з додатка Налаштування" "Видалити інструмент System UI Tuner із додатка Налаштування та припинити користуватися всіма його функціями?" + "Увімкнути Bluetooth?" + "Щоб під’єднати клавіатуру до планшета, спершу потрібно ввімкнути Bluetooth." + "Увімкнути" diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml index b4d52a429f8..2c34cfd2166 100644 --- a/packages/SystemUI/res/values-ur-rPK/strings.xml +++ b/packages/SystemUI/res/values-ur-rPK/strings.xml @@ -428,4 +428,7 @@ "‏مبارک ہو! سسٹم UI ٹیونر کو ترتیبات میں شامل کر دیا گیا ہے" "ترتیبات سے ہٹائیں" "‏ترتیبات سے سسٹم UI ٹیونر کو ہٹائیں اور اس کی سبھی خصوصیات کا استعمال بند کریں؟" + "بلوٹوتھ آن کریں؟" + "اپنے کی بورڈ کو اپنے ٹیبلٹ کے ساتھ منسلک کرنے کیلئے پہلے آپ کو اپنا بلو ٹوتھ آن کرنا ہو گا۔" + "آن کریں" diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml index 27214768167..54cb37dc450 100644 --- a/packages/SystemUI/res/values-uz-rUZ/strings.xml +++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml @@ -428,4 +428,7 @@ "Tabriklaymiz! System UI Tuner Sozlamalarga qo‘shildi." "Sozlamalardan olib tashlash" "System UI Tuner Sozlamalardan olib tashlanib, uning barcha funksiyalaridan foydalanish to‘xtatilsinmi?" + "Bluetooth yoqilsinmi?" + "Klaviaturani planshetingizga ulash uchun Bluetooth xizmatini yoqishingiz kerak." + "Yoqish" diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 32d6d3fc6a7..547925edb1e 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -428,4 +428,7 @@ "Xin chúc mừng! Bộ điều hướng giao diện người dùng hệ thống đã được thêm vào Cài đặt" "Xóa khỏi Cài đặt" "Xóa Bộ điều hướng giao diện người dùng hệ thống khỏi Cài đặt và ngừng sử dụng tất cả tính năng của ứng dụng này?" + "Bật Bluetooth?" + "Để kết nối bàn phím với máy tính bảng, trước tiên, bạn phải bật Bluetooth." + "Bật" diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index a2b98525731..4aa71bb7e01 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -430,4 +430,7 @@ "恭喜!系统界面调谐器已添加到“设置”中" "从“设置”中移除" "要将系统界面调谐器从“设置”中移除,并停止使用所有相关功能吗?" + "要开启蓝牙吗?" + "要将您的键盘连接到平板电脑,您必须先开启蓝牙。" + "开启" diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 599fecefe0f..ef5cd399ca6 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -430,4 +430,7 @@ "恭喜您!系統使用者介面調諧器已新增至「設定」中" "從「設定」移除" "要從「設定」移除系統使用者介面調諧器,並停止其所有功能嗎?" + "要開啟藍牙嗎?" + "如要將鍵盤連接至平板電腦,請先開啟藍牙。" + "開啟" diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index e2a33c21eb4..7d704d3aca5 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -430,4 +430,7 @@ "恭喜!系統使用者介面調整精靈已新增到設定中" "從設定中移除" "要將系統使用者介面調整精靈從設定中移除,並停止使用所有相關功能嗎?" + "要開啟藍牙功能嗎?" + "如要將鍵盤連線到平板電腦,您必須先開啟藍牙。" + "開啟" diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 66fbd5d223f..67a49d41353 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -428,4 +428,7 @@ "Siyakuhalalisela! Isishuni se-UI sesistimu singeziwe kuzilungiselelo" "Susa kusuka kuzilungiselelo" "Susa isishuni se-UI yesistimu kusuka kuzilungiselelo futhi uyeke ukusebenzisa zonke izici zakhona?" + "Vula i-Bluetooth?" + "Ukuze uxhume ikhibhodi yakho nethebhulethi yakho, kufanele uqale ngokuvula i-Bluetooth." + "Vula" diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml index 527248cef80..49f7bdb5c4c 100644 --- a/packages/SystemUI/res/values/attrs.xml +++ b/packages/SystemUI/res/values/attrs.xml @@ -20,6 +20,7 @@ + diff --git a/packages/SystemUI/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml index 30ff7043f68..351a1fdbda0 100644 --- a/packages/SystemUI/res/values/donottranslate.xml +++ b/packages/SystemUI/res/values/donottranslate.xml @@ -20,10 +20,4 @@ @*android:string/system_ui_date_pattern - - @string/keyguard_indication_charging_time - - - @string/keyguard_indication_charging_time - diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index a2faf905706..5d5522e66c3 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1129,4 +1129,14 @@ Remove System UI Tuner from Settings and stop using all of its features?" + + Turn on Bluetooth? + + + To connect your keyboard with your tablet, you first have to turn on Bluetooth. + + + Turn on + diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 33bd726c8d4..0b066afe26d 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -48,6 +48,7 @@ public class SystemUIApplication extends Application { com.android.systemui.usb.StorageNotification.class, com.android.systemui.power.PowerUI.class, com.android.systemui.media.RingtonePlayer.class, + com.android.systemui.keyboard.KeyboardUI.class, }; /** diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java b/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java new file mode 100644 index 00000000000..64f3e13f3b4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/BluetoothDialog.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyboard; + +import android.app.AlertDialog; +import android.content.Context; +import android.view.WindowManager; + +import com.android.systemui.R; +import com.android.systemui.statusbar.phone.SystemUIDialog; + +public class BluetoothDialog extends SystemUIDialog { + + public BluetoothDialog(Context context) { + super(context); + + getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); + setShowForAllUsers(true); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java new file mode 100644 index 00000000000..96ee3975651 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyboard; + +import android.app.AlertDialog; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; +import android.bluetooth.le.BluetoothLeScanner; +import android.bluetooth.le.ScanCallback; +import android.bluetooth.le.ScanFilter; +import android.bluetooth.le.ScanRecord; +import android.bluetooth.le.ScanResult; +import android.bluetooth.le.ScanSettings; +import android.content.ContentResolver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.res.Configuration; +import android.hardware.input.InputManager; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.Process; +import android.os.SystemClock; +import android.os.UserHandle; +import android.provider.Settings.Secure; +import android.text.TextUtils; +import android.util.Slog; +import android.view.WindowManager; + +import com.android.settingslib.bluetooth.BluetoothCallback; +import com.android.settingslib.bluetooth.CachedBluetoothDevice; +import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; +import com.android.settingslib.bluetooth.LocalBluetoothAdapter; +import com.android.settingslib.bluetooth.LocalBluetoothManager; +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; +import com.android.systemui.R; +import com.android.systemui.SystemUI; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class KeyboardUI extends SystemUI implements InputManager.OnTabletModeChangedListener { + private static final String TAG = "KeyboardUI"; + private static final boolean DEBUG = false; + + // Give BT some time to start after SyUI comes up. This avoids flashing a dialog in the user's + // face because BT starts a little bit later in the boot process than SysUI and it takes some + // time for us to receive the signal that it's starting. + private static final long BLUETOOTH_START_DELAY_MILLIS = 10 * 1000; + + private static final int STATE_NOT_ENABLED = -1; + private static final int STATE_UNKNOWN = 0; + private static final int STATE_WAITING_FOR_BOOT_COMPLETED = 1; + private static final int STATE_WAITING_FOR_TABLET_MODE_EXIT = 2; + private static final int STATE_WAITING_FOR_DEVICE_DISCOVERY = 3; + private static final int STATE_WAITING_FOR_BLUETOOTH = 4; + private static final int STATE_WAITING_FOR_STATE_PAIRED = 5; + private static final int STATE_PAIRING = 6; + private static final int STATE_PAIRED = 7; + private static final int STATE_USER_CANCELLED = 8; + private static final int STATE_DEVICE_NOT_FOUND = 9; + + private static final int MSG_INIT = 0; + private static final int MSG_ON_BOOT_COMPLETED = 1; + private static final int MSG_PROCESS_KEYBOARD_STATE = 2; + private static final int MSG_ENABLE_BLUETOOTH = 3; + private static final int MSG_ON_BLUETOOTH_STATE_CHANGED = 4; + private static final int MSG_ON_DEVICE_BOND_STATE_CHANGED = 5; + private static final int MSG_ON_BLUETOOTH_DEVICE_ADDED = 6; + private static final int MSG_ON_BLE_SCAN_FAILED = 7; + private static final int MSG_SHOW_BLUETOOTH_DIALOG = 8; + private static final int MSG_DISMISS_BLUETOOTH_DIALOG = 9; + + private volatile KeyboardHandler mHandler; + private volatile KeyboardUIHandler mUIHandler; + + protected volatile Context mContext; + + private boolean mEnabled; + private String mKeyboardName; + private CachedBluetoothDeviceManager mCachedDeviceManager; + private LocalBluetoothAdapter mLocalBluetoothAdapter; + private LocalBluetoothProfileManager mProfileManager; + private boolean mBootCompleted; + private long mBootCompletedTime; + + private int mInTabletMode = InputManager.SWITCH_STATE_UNKNOWN; + private ScanCallback mScanCallback; + private BluetoothDialog mDialog; + + private int mState; + + @Override + public void start() { + mContext = super.mContext; + HandlerThread thread = new HandlerThread("Keyboard", Process.THREAD_PRIORITY_BACKGROUND); + thread.start(); + mHandler = new KeyboardHandler(thread.getLooper()); + mHandler.sendEmptyMessage(MSG_INIT); + } + + @Override + protected void onConfigurationChanged(Configuration newConfig) { + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("KeyboardUI:"); + pw.println(" mEnabled=" + mEnabled); + pw.println(" mBootCompleted=" + mEnabled); + pw.println(" mBootCompletedTime=" + mBootCompletedTime); + pw.println(" mKeyboardName=" + mKeyboardName); + pw.println(" mInTabletMode=" + mInTabletMode); + pw.println(" mState=" + stateToString(mState)); + } + + @Override + protected void onBootCompleted() { + mHandler.sendEmptyMessage(MSG_ON_BOOT_COMPLETED); + } + + @Override + public void onTabletModeChanged(long whenNanos, boolean inTabletMode) { + if (DEBUG) { + Slog.d(TAG, "onTabletModeChanged(" + whenNanos + ", " + inTabletMode + ")"); + } + + if (inTabletMode && mInTabletMode != InputManager.SWITCH_STATE_ON + || !inTabletMode && mInTabletMode != InputManager.SWITCH_STATE_OFF) { + mInTabletMode = inTabletMode ? + InputManager.SWITCH_STATE_ON : InputManager.SWITCH_STATE_OFF; + processKeyboardState(); + } + } + + // Shoud only be called on the handler thread + private void init() { + Context context = mContext; + mKeyboardName = + context.getString(com.android.internal.R.string.config_packagedKeyboardName); + if (TextUtils.isEmpty(mKeyboardName)) { + if (DEBUG) { + Slog.d(TAG, "No packaged keyboard name given."); + } + return; + } + + LocalBluetoothManager bluetoothManager = LocalBluetoothManager.getInstance(context, null); + if (bluetoothManager == null) { + if (DEBUG) { + Slog.e(TAG, "Failed to retrieve LocalBluetoothManager instance"); + } + return; + } + mEnabled = true; + mCachedDeviceManager = bluetoothManager.getCachedDeviceManager(); + mLocalBluetoothAdapter = bluetoothManager.getBluetoothAdapter(); + mProfileManager = bluetoothManager.getProfileManager(); + bluetoothManager.getEventManager().registerCallback(new BluetoothCallbackHandler()); + + InputManager im = (InputManager) context.getSystemService(Context.INPUT_SERVICE); + im.registerOnTabletModeChangedListener(this, mHandler); + mInTabletMode = im.isInTabletMode(); + + processKeyboardState(); + mUIHandler = new KeyboardUIHandler(); + } + + // Should only be called on the handler thread + private void processKeyboardState() { + mHandler.removeMessages(MSG_PROCESS_KEYBOARD_STATE); + + if (!mEnabled) { + mState = STATE_NOT_ENABLED; + return; + } + + if (!mBootCompleted) { + mState = STATE_WAITING_FOR_BOOT_COMPLETED; + return; + } + + if (mInTabletMode != InputManager.SWITCH_STATE_OFF) { + if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY) { + stopScanning(); + } + mState = STATE_WAITING_FOR_TABLET_MODE_EXIT; + return; + } + + final int btState = mLocalBluetoothAdapter.getState(); + if (btState == BluetoothAdapter.STATE_TURNING_ON || btState == BluetoothAdapter.STATE_ON + && mState == STATE_WAITING_FOR_BLUETOOTH) { + // If we're waiting for bluetooth but it has come on in the meantime, or is coming + // on, just dismiss the dialog. This frequently happens during device startup. + mUIHandler.sendEmptyMessage(MSG_DISMISS_BLUETOOTH_DIALOG); + } + + if (btState == BluetoothAdapter.STATE_TURNING_ON) { + mState = STATE_WAITING_FOR_BLUETOOTH; + // Wait for bluetooth to fully come on. + return; + } + + if (btState != BluetoothAdapter.STATE_ON) { + mState = STATE_WAITING_FOR_BLUETOOTH; + showBluetoothDialog(); + return; + } + + CachedBluetoothDevice device = getPairedKeyboard(); + if (mState == STATE_WAITING_FOR_TABLET_MODE_EXIT || mState == STATE_WAITING_FOR_BLUETOOTH) { + if (device != null) { + // If we're just coming out of tablet mode or BT just turned on, + // then we want to go ahead and automatically connect to the + // keyboard. We want to avoid this in other cases because we might + // be spuriously called after the user has manually disconnected + // the keyboard, meaning we shouldn't try to automtically connect + // it again. + mState = STATE_PAIRED; + device.connect(false); + return; + } + mCachedDeviceManager.clearNonBondedDevices(); + } + + device = getDiscoveredKeyboard(); + if (device != null) { + mState = STATE_PAIRING; + device.startPairing(); + } else { + mState = STATE_WAITING_FOR_DEVICE_DISCOVERY; + startScanning(); + } + } + + // Should only be called on the handler thread + public void onBootCompletedInternal() { + mBootCompleted = true; + mBootCompletedTime = SystemClock.uptimeMillis(); + if (mState == STATE_WAITING_FOR_BOOT_COMPLETED) { + processKeyboardState(); + } + } + + // Should only be called on the handler thread + private void showBluetoothDialog() { + if (isUserSetupComplete()) { + long now = SystemClock.uptimeMillis(); + long earliestDialogTime = mBootCompletedTime + BLUETOOTH_START_DELAY_MILLIS; + if (earliestDialogTime < now) { + mUIHandler.sendEmptyMessage(MSG_SHOW_BLUETOOTH_DIALOG); + } else { + mHandler.sendEmptyMessageAtTime(MSG_PROCESS_KEYBOARD_STATE, earliestDialogTime); + } + } else { + // If we're in setup wizard and the keyboard is docked, just automatically enable BT. + mLocalBluetoothAdapter.enable(); + } + } + + private boolean isUserSetupComplete() { + ContentResolver resolver = mContext.getContentResolver(); + return Secure.getIntForUser( + resolver, Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; + } + + private CachedBluetoothDevice getPairedKeyboard() { + Set devices = mLocalBluetoothAdapter.getBondedDevices(); + for (BluetoothDevice d : devices) { + if (mKeyboardName.equals(d.getName())) { + return getCachedBluetoothDevice(d); + } + } + return null; + } + + private CachedBluetoothDevice getDiscoveredKeyboard() { + Collection devices = mCachedDeviceManager.getCachedDevicesCopy(); + for (CachedBluetoothDevice d : devices) { + if (d.getName().equals(mKeyboardName)) { + return d; + } + } + return null; + } + + + private CachedBluetoothDevice getCachedBluetoothDevice(BluetoothDevice d) { + CachedBluetoothDevice cachedDevice = mCachedDeviceManager.findDevice(d); + if (cachedDevice == null) { + cachedDevice = mCachedDeviceManager.addDevice( + mLocalBluetoothAdapter, mProfileManager, d); + } + return cachedDevice; + } + + private void startScanning() { + BluetoothLeScanner scanner = mLocalBluetoothAdapter.getBluetoothLeScanner(); + ScanFilter filter = (new ScanFilter.Builder()).setDeviceName(mKeyboardName).build(); + ScanSettings settings = (new ScanSettings.Builder()) + .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) + .setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT) + .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) + .setReportDelay(0) + .build(); + mScanCallback = new KeyboardScanCallback(); + scanner.startScan(Arrays.asList(filter), settings, mScanCallback); + } + + private void stopScanning() { + if (mScanCallback != null) { + mLocalBluetoothAdapter.getBluetoothLeScanner().stopScan(mScanCallback); + mScanCallback = null; + } + } + + // Should only be called on the handler thread + private void onDeviceAddedInternal(CachedBluetoothDevice d) { + if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY && d.getName().equals(mKeyboardName)) { + stopScanning(); + d.startPairing(); + mState = STATE_PAIRING; + } + } + + // Should only be called on the handler thread + private void onBluetoothStateChangedInternal(int bluetoothState) { + if (bluetoothState == BluetoothAdapter.STATE_ON && mState == STATE_WAITING_FOR_BLUETOOTH) { + processKeyboardState(); + } + } + + // Should only be called on the handler thread + private void onDeviceBondStateChangedInternal(CachedBluetoothDevice d, int bondState) { + if (d.getName().equals(mKeyboardName) && bondState == BluetoothDevice.BOND_BONDED) { + // We don't need to manually connect to the device here because it will automatically + // try to connect after it has been paired. + mState = STATE_PAIRED; + } + } + + // Should only be called on the handler thread + private void onBleScanFailedInternal() { + mScanCallback = null; + if (mState == STATE_WAITING_FOR_DEVICE_DISCOVERY) { + mState = STATE_DEVICE_NOT_FOUND; + } + } + + private final class KeyboardUIHandler extends Handler { + public KeyboardUIHandler() { + super(Looper.getMainLooper(), null, true /*async*/); + } + @Override + public void handleMessage(Message msg) { + switch(msg.what) { + case MSG_SHOW_BLUETOOTH_DIALOG: { + DialogInterface.OnClickListener listener = new BluetoothDialogClickListener(); + mDialog = new BluetoothDialog(mContext); + mDialog.setTitle(R.string.enable_bluetooth_title); + mDialog.setMessage(R.string.enable_bluetooth_message); + mDialog.setPositiveButton(R.string.enable_bluetooth_confirmation_ok, listener); + mDialog.setNegativeButton(android.R.string.cancel, listener); + mDialog.show(); + break; + } + case MSG_DISMISS_BLUETOOTH_DIALOG: { + if (mDialog != null) { + mDialog.dismiss(); + mDialog = null; + } + break; + } + } + } + } + + private final class KeyboardHandler extends Handler { + public KeyboardHandler(Looper looper) { + super(looper, null, true /*async*/); + } + + @Override + public void handleMessage(Message msg) { + switch(msg.what) { + case MSG_INIT: { + init(); + break; + } + case MSG_ON_BOOT_COMPLETED: { + onBootCompletedInternal(); + break; + } + case MSG_PROCESS_KEYBOARD_STATE: { + processKeyboardState(); + break; + } + case MSG_ENABLE_BLUETOOTH: { + boolean enable = msg.arg1 == 1; + if (enable) { + mLocalBluetoothAdapter.enable(); + } else { + mState = STATE_USER_CANCELLED; + } + } + case MSG_ON_BLUETOOTH_STATE_CHANGED: { + int bluetoothState = msg.arg1; + onBluetoothStateChangedInternal(bluetoothState); + break; + } + case MSG_ON_DEVICE_BOND_STATE_CHANGED: { + CachedBluetoothDevice d = (CachedBluetoothDevice)msg.obj; + int bondState = msg.arg1; + onDeviceBondStateChangedInternal(d, bondState); + break; + } + case MSG_ON_BLUETOOTH_DEVICE_ADDED: { + BluetoothDevice d = (BluetoothDevice)msg.obj; + CachedBluetoothDevice cachedDevice = getCachedBluetoothDevice(d); + onDeviceAddedInternal(cachedDevice); + break; + + } + case MSG_ON_BLE_SCAN_FAILED: { + onBleScanFailedInternal(); + break; + } + } + } + } + + private final class BluetoothDialogClickListener implements DialogInterface.OnClickListener { + @Override + public void onClick(DialogInterface dialog, int which) { + int enable = DialogInterface.BUTTON_POSITIVE == which ? 1 : 0; + mHandler.obtainMessage(MSG_ENABLE_BLUETOOTH, enable, 0).sendToTarget(); + mDialog = null; + } + } + + private final class KeyboardScanCallback extends ScanCallback { + + private boolean isDeviceDiscoverable(ScanResult result) { + final ScanRecord scanRecord = result.getScanRecord(); + final int flags = scanRecord.getAdvertiseFlags(); + final int BT_DISCOVERABLE_MASK = 0x03; + + return (flags & BT_DISCOVERABLE_MASK) != 0; + } + + @Override + public void onBatchScanResults(List results) { + if (DEBUG) { + Slog.d(TAG, "onBatchScanResults(" + results.size() + ")"); + } + + BluetoothDevice bestDevice = null; + int bestRssi = Integer.MIN_VALUE; + + for (ScanResult result : results) { + if (DEBUG) { + Slog.d(TAG, "onBatchScanResults: considering " + result); + } + + if (isDeviceDiscoverable(result) && result.getRssi() > bestRssi) { + bestDevice = result.getDevice(); + bestRssi = result.getRssi(); + } + } + + if (bestDevice != null) { + mHandler.obtainMessage(MSG_ON_BLUETOOTH_DEVICE_ADDED, bestDevice).sendToTarget(); + } + } + + @Override + public void onScanFailed(int errorCode) { + if (DEBUG) { + Slog.d(TAG, "onScanFailed(" + errorCode + ")"); + } + mHandler.obtainMessage(MSG_ON_BLE_SCAN_FAILED).sendToTarget(); + } + + @Override + public void onScanResult(int callbackType, ScanResult result) { + if (DEBUG) { + Slog.d(TAG, "onScanResult(" + callbackType + ", " + result + ")"); + } + + if (isDeviceDiscoverable(result)) { + mHandler.obtainMessage(MSG_ON_BLUETOOTH_DEVICE_ADDED, + result.getDevice()).sendToTarget(); + } else if (DEBUG) { + Slog.d(TAG, "onScanResult: device " + result.getDevice() + + " is not discoverable, ignoring"); + } + } + } + + private final class BluetoothCallbackHandler implements BluetoothCallback { + @Override + public void onBluetoothStateChanged(int bluetoothState) { + mHandler.obtainMessage(MSG_ON_BLUETOOTH_STATE_CHANGED, + bluetoothState, 0).sendToTarget(); + } + + @Override + public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) { + mHandler.obtainMessage(MSG_ON_DEVICE_BOND_STATE_CHANGED, + bondState, 0, cachedDevice).sendToTarget(); + } + + @Override + public void onDeviceAdded(CachedBluetoothDevice cachedDevice) { } + @Override + public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) { } + @Override + public void onScanningStateChanged(boolean started) { } + @Override + public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) { } + } + + private static String stateToString(int state) { + switch (state) { + case STATE_NOT_ENABLED: + return "STATE_NOT_ENABLED"; + case STATE_WAITING_FOR_BOOT_COMPLETED: + return "STATE_WAITING_FOR_BOOT_COMPLETED"; + case STATE_WAITING_FOR_TABLET_MODE_EXIT: + return "STATE_WAITING_FOR_TABLET_MODE_EXIT"; + case STATE_WAITING_FOR_DEVICE_DISCOVERY: + return "STATE_WAITING_FOR_DEVICE_DISCOVERY"; + case STATE_WAITING_FOR_BLUETOOTH: + return "STATE_WAITING_FOR_BLUETOOTH"; + case STATE_WAITING_FOR_STATE_PAIRED: + return "STATE_WAITING_FOR_STATE_PAIRED"; + case STATE_PAIRING: + return "STATE_PAIRING"; + case STATE_PAIRED: + return "STATE_PAIRED"; + case STATE_USER_CANCELLED: + return "STATE_USER_CANCELLED"; + case STATE_DEVICE_NOT_FOUND: + return "STATE_DEVICE_NOT_FOUND"; + case STATE_UNKNOWN: + default: + return "STATE_UNKNOWN"; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java index 803a01410b9..728d558c03b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java +++ b/packages/SystemUI/src/com/android/systemui/media/NotificationPlayer.java @@ -21,6 +21,7 @@ import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; +import android.media.MediaPlayer.OnErrorListener; import android.net.Uri; import android.os.Looper; import android.os.PowerManager; @@ -36,7 +37,7 @@ * - whenever audio is played, audio focus is requested, * - whenever audio playback is stopped or the playback completed, audio focus is abandoned. */ -public class NotificationPlayer implements OnCompletionListener { +public class NotificationPlayer implements OnCompletionListener, OnErrorListener { private static final int PLAY = 1; private static final int STOP = 2; private static final boolean mDebug = false; @@ -112,6 +113,7 @@ public void run() { // done playing. This class should be modified to use a single thread, on which // command are issued, and on which it receives the completion callbacks. player.setOnCompletionListener(NotificationPlayer.this); + player.setOnErrorListener(NotificationPlayer.this); player.start(); if (mPlayer != null) { mPlayer.release(); @@ -245,6 +247,13 @@ public void onCompletion(MediaPlayer mp) { } } + public boolean onError(MediaPlayer mp, int what, int extra) { + Log.e(mTag, "error " + what + " (extra=" + extra + ") playing notification"); + // error happened, handle it just like a completion + onCompletion(mp); + return true; + } + private String mTag; private CmdThread mThread; private CreationAndCompletionThread mCompletionThread; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java index b330582089c..e4a37fbf1b2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -16,6 +16,7 @@ package com.android.systemui.qs; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.graphics.drawable.Animatable; @@ -320,6 +321,7 @@ public interface Callback { public interface Host { void startActivityDismissingKeyguard(Intent intent); + void startActivityDismissingKeyguard(PendingIntent intent); void warn(String message, Throwable t); void collapsePanels(); Looper getLooper(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java index 3d0dc7b5c1a..c7f2284c07a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/IntentTile.java @@ -99,7 +99,7 @@ private void sendIntent(String type, PendingIntent pi, String uri) { try { if (pi != null) { if (pi.isActivity()) { - getHost().startActivityDismissingKeyguard(pi.getIntent()); + getHost().startActivityDismissingKeyguard(pi); } else { pi.send(); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java index 2e0b80a9a51..be618e25fa6 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java @@ -162,6 +162,14 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { mVelocityTracker.addMovement(createMotionEventForStackScroll(ev)); break; } + case MotionEvent.ACTION_POINTER_DOWN: { + final int index = ev.getActionIndex(); + mActivePointerId = ev.getPointerId(index); + mLastMotionX = (int) ev.getX(index); + mLastMotionY = (int) ev.getY(index); + mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY); + break; + } case MotionEvent.ACTION_MOVE: { if (mActivePointerId == INACTIVE_POINTER_ID) break; @@ -187,6 +195,20 @@ public boolean onInterceptTouchEvent(MotionEvent ev) { mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY); break; } + case MotionEvent.ACTION_POINTER_UP: { + int pointerIndex = ev.getActionIndex(); + int pointerId = ev.getPointerId(pointerIndex); + if (pointerId == mActivePointerId) { + // Select a new active pointer id and reset the motion state + final int newPointerIndex = (pointerIndex == 0) ? 1 : 0; + mActivePointerId = ev.getPointerId(newPointerIndex); + mLastMotionX = (int) ev.getX(newPointerIndex); + mLastMotionY = (int) ev.getY(newPointerIndex); + mLastP = mSv.mLayoutAlgorithm.screenYToCurveProgress(mLastMotionY); + mVelocityTracker.clear(); + } + break; + } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: { // Animate the scroll back if we've cancelled diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java index eecac63b08d..9ff86ebf88a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java @@ -1490,6 +1490,59 @@ protected boolean inflateViews(Entry entry, ViewGroup parent) { return true; } + public void startPendingIntentDismissingKeyguard(final PendingIntent intent) { + if (!isDeviceProvisioned()) return; + + final boolean keyguardShowing = mStatusBarKeyguardViewManager.isShowing(); + final boolean afterKeyguardGone = intent.isActivity() + && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(), + mCurrentUserId); + dismissKeyguardThenExecute(new OnDismissAction() { + public boolean onDismiss() { + new Thread() { + @Override + public void run() { + try { + if (keyguardShowing && !afterKeyguardGone) { + ActivityManagerNative.getDefault() + .keyguardWaitingForActivityDrawn(); + } + + // The intent we are sending is for the application, which + // won't have permission to immediately start an activity after + // the user switches to home. We know it is safe to do at this + // point, so make sure new activity switches are now allowed. + ActivityManagerNative.getDefault().resumeAppSwitches(); + } catch (RemoteException e) { + } + + try { + intent.send(); + } catch (PendingIntent.CanceledException e) { + // the stack trace isn't very helpful here. + // Just log the exception message. + Log.w(TAG, "Sending intent failed: " + e); + + // TODO: Dismiss Keyguard. + } + if (intent.isActivity()) { + mAssistManager.hideAssist(); + overrideActivityPendingAppTransition(keyguardShowing + && !afterKeyguardGone); + } + } + }.start(); + + // close the shade if it was open + animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, + true /* force */, true /* delayed */); + visibilityChanged(false); + + return true; + } + }, afterKeyguardGone); + } + private final class NotificationClicker implements View.OnClickListener { public void onClick(final View v) { if (!(v instanceof ExpandableNotificationRow)) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 5f01306f577..56e9af53864 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -32,6 +32,7 @@ import android.view.ViewStub; import android.view.accessibility.AccessibilityEvent; import android.view.animation.LinearInterpolator; +import android.widget.Chronometer; import android.widget.ImageView; import com.android.systemui.R; @@ -88,6 +89,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView { private NotificationGuts mGuts; private StatusBarNotification mStatusBarNotification; private boolean mIsHeadsUp; + private boolean mLastChronometerRunning = true; private View mExpandButton; private View mExpandButtonDivider; private ViewStub mExpandButtonStub; @@ -294,6 +296,7 @@ protected int calculateContentHeightFromActualHeight(int actualHeight) { */ public void setPinned(boolean pinned) { mIsPinned = pinned; + setChronometerRunning(mLastChronometerRunning); } public boolean isPinned() { @@ -319,6 +322,41 @@ public boolean wasJustClicked() { return mJustClicked; } + public void setChronometerRunning(boolean running) { + mLastChronometerRunning = running; + setChronometerRunning(running, mPrivateLayout); + setChronometerRunning(running, mPublicLayout); + if (mChildrenContainer != null) { + List notificationChildren = + mChildrenContainer.getNotificationChildren(); + for (int i = 0; i < notificationChildren.size(); i++) { + ExpandableNotificationRow child = notificationChildren.get(i); + child.setChronometerRunning(running); + } + } + } + + private void setChronometerRunning(boolean running, NotificationContentView layout) { + if (layout != null) { + running = running || isPinned(); + View contractedChild = layout.getContractedChild(); + View expandedChild = layout.getExpandedChild(); + View headsUpChild = layout.getHeadsUpChild(); + setChronometerRunningForChild(running, contractedChild); + setChronometerRunningForChild(running, expandedChild); + setChronometerRunningForChild(running, headsUpChild); + } + } + + private void setChronometerRunningForChild(boolean running, View child) { + if (child != null) { + View chronometer = child.findViewById(com.android.internal.R.id.chronometer); + if (chronometer instanceof Chronometer) { + ((Chronometer) chronometer).setStarted(running); + } + } + } + public interface ExpansionLogger { public void logNotificationExpansion(String key, boolean userAction, boolean expanded); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 50d274d4fcc..fd84345e9f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -200,12 +200,12 @@ private String computePowerIndication() { switch (mChargingSpeed) { case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST: chargingId = hasChargingTime - ? R.string.keyguard_indication_charging_time_fast_if_translated + ? R.string.keyguard_indication_charging_time_fast : R.string.keyguard_plugged_in_charging_fast; break; case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY: chargingId = hasChargingTime - ? R.string.keyguard_indication_charging_time_slowly_if_translated + ? R.string.keyguard_indication_charging_time_slowly : R.string.keyguard_plugged_in_charging_slowly; break; default: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java index 9ef320bc3fd..8f689c6c00b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarter.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import android.app.PendingIntent; import android.content.Intent; /** @@ -24,6 +25,7 @@ * Keyguard. */ public interface ActivityStarter { + void startPendingIntentDismissingKeyguard(PendingIntent intent); void startActivity(Intent intent, boolean dismissShade); void startActivity(Intent intent, boolean dismissShade, Callback callback); void preventNextAnimation(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index c6743e772be..e03bcfb9008 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -123,7 +123,6 @@ public void run() { }; protected void onExpandingFinished() { - endClosing(); mBar.onExpandingFinished(); } @@ -138,6 +137,7 @@ private void notifyExpandingStarted() { } protected final void notifyExpandingFinished() { + endClosing(); if (mExpanding) { mExpanding = false; onExpandingFinished(); 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 779f6274d10..33a20ff2b51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -3305,6 +3305,15 @@ public boolean shouldDisableNavbarGestures() { return !isDeviceProvisioned() || (mDisabled1 & StatusBarManager.DISABLE_SEARCH) != 0; } + public void postStartActivityDismissingKeyguard(final PendingIntent intent) { + mHandler.post(new Runnable() { + @Override + public void run() { + startPendingIntentDismissingKeyguard(intent); + } + }); + } + public void postStartActivityDismissingKeyguard(final Intent intent, int delay) { mHandler.postDelayed(new Runnable() { @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 540b9d003a1..fa9c4bbcb48 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -109,6 +109,14 @@ else if (action.equals(TelecomManager.ACTION_CURRENT_TTY_MODE_CHANGED)) { } }; + private Runnable mRemoveCastIconRunnable = new Runnable() { + @Override + public void run() { + if (DEBUG) Log.v(TAG, "updateCast: hiding icon NOW"); + mService.setIconVisibility(SLOT_CAST, false); + } + }; + public PhoneStatusBarPolicy(Context context, CastController cast, HotspotController hotspot, UserInfoController userInfoController, BluetoothController bluetooth) { mContext = context; @@ -328,11 +336,17 @@ private void updateCast() { } } if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting); + mHandler.removeCallbacks(mRemoveCastIconRunnable); if (isCasting) { mService.setIcon(SLOT_CAST, R.drawable.stat_sys_cast, 0, mContext.getString(R.string.accessibility_casting)); + mService.setIconVisibility(SLOT_CAST, true); + } else { + // don't turn off the screen-record icon for a few seconds, just to make sure the user + // has seen it + if (DEBUG) Log.v(TAG, "updateCast: hiding icon in 3 sec..."); + mHandler.postDelayed(mRemoveCastIconRunnable, 3000); } - mService.setIconVisibility(SLOT_CAST, isCasting); } private void profileChanged(int userId) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java index 12434ac2af5..e66c63b5c0e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.phone; +import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.res.Resources; @@ -133,6 +134,11 @@ public void startActivityDismissingKeyguard(final Intent intent) { mStatusBar.postStartActivityDismissingKeyguard(intent, 0); } + @Override + public void startActivityDismissingKeyguard(PendingIntent intent) { + mStatusBar.postStartActivityDismissingKeyguard(intent); + } + @Override public void warn(String message, Throwable t) { // already logged diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java index e9c4e49d7b1..971978d7ee0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java @@ -527,8 +527,8 @@ public void run() { startBatteryActivity(); } else if (v == mAlarmStatus && mNextAlarm != null) { PendingIntent showIntent = mNextAlarm.getShowIntent(); - if (showIntent != null && showIntent.isActivity()) { - mActivityStarter.startActivity(showIntent.getIntent(), true /* dismissShade */); + if (showIntent != null) { + mActivityStarter.startPendingIntentDismissingKeyguard(showIntent); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java index ed9b123f743..4a95d3a4d52 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java @@ -169,7 +169,6 @@ public PhoneStatusBar getBar() { */ public void showNotification(NotificationData.Entry headsUp) { if (DEBUG) Log.v(TAG, "showNotification"); - MetricsLogger.count(mContext, "note_peek", 1); addHeadsUpEntry(headsUp); updateNotification(headsUp, true); headsUp.setInterruption(); @@ -246,6 +245,9 @@ private void updatePinnedMode() { return; } mHasPinnedNotification = hasPinnedNotification; + if (mHasPinnedNotification) { + MetricsLogger.count(mContext, "note_peek", 1); + } updateTouchableRegionListener(); for (OnHeadsUpChangedListener listener : mListeners) { listener.onHeadsUpPinnedModeChanged(hasPinnedNotification); @@ -580,6 +582,13 @@ public void updateEntry() { @Override public int compareTo(HeadsUpEntry o) { + boolean selfFullscreen = hasFullScreenIntent(entry); + boolean otherFullscreen = hasFullScreenIntent(o.entry); + if (selfFullscreen && !otherFullscreen) { + return -1; + } else if (!selfFullscreen && otherFullscreen) { + return 1; + } return postTime < o.postTime ? 1 : postTime == o.postTime ? entry.key.compareTo(o.entry.key) : -1; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java index 4c99792265f..4d268ceda4d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java @@ -18,12 +18,14 @@ import android.app.ActivityManager; import android.content.Context; +import android.content.res.Configuration; import android.content.res.TypedArray; import android.hardware.input.InputManager; import android.media.AudioManager; import android.os.Bundle; import android.os.SystemClock; import android.util.AttributeSet; +import android.util.TypedValue; import android.view.HapticFeedbackConstants; import android.view.InputDevice; import android.view.KeyCharacterMap; @@ -43,6 +45,7 @@ public class KeyButtonView extends ImageView { + private int mContentDescriptionRes; private long mDownTime; private int mCode; private int mTouchSlop; @@ -79,14 +82,29 @@ public KeyButtonView(Context context, AttributeSet attrs, int defStyle) { mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true); + TypedValue value = new TypedValue(); + if (a.getValue(R.styleable.KeyButtonView_android_contentDescription, value)) { + mContentDescriptionRes = value.resourceId; + } + a.recycle(); + setClickable(true); mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); setBackground(new KeyButtonRipple(context, this)); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + + if (mContentDescriptionRes != 0) { + setContentDescription(mContext.getString(mContentDescriptionRes)); + } + } + @Override public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(info); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java index b2df40a069e..0e91b0b4a9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeController.java @@ -27,7 +27,6 @@ public interface ZenModeController { void removeCallback(Callback callback); void setZen(int zen, Uri conditionId, String reason); int getZen(); - void requestConditions(boolean request); ZenRule getManualRule(); ZenModeConfig getConfig(); long getNextAlarm(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java index c07f1a86415..96efea11602 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -120,15 +120,6 @@ public boolean isZenAvailable() { return mSetupObserver.isDeviceProvisioned() && mSetupObserver.isUserSetup(); } - @Override - public void requestConditions(boolean request) { - mRequesting = request; - mNoMan.requestZenModeConditions(mListener, request ? Condition.FLAG_RELEVANT_NOW : 0); - if (!mRequesting) { - mConditions.clear(); - } - } - @Override public ZenRule getManualRule() { return mConfig == null ? null : mConfig.manualRule; 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 a7fe71ea45e..af38f5fb3ca 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -1778,6 +1778,7 @@ private void onViewAddedInternal(View child) { ((ExpandableView) child).setOnHeightChangedListener(this); generateAddAnimation(child, false /* fromMoreCard */); updateAnimationState(child); + updateChronometerForChild(child); if (canChildBeDismissed(child)) { // Make sure the dismissButton is visible and not in the animated state. // We need to do this to avoid a race where a clearable notification is added after the @@ -2287,6 +2288,21 @@ private void setIsExpanded(boolean isExpanded) { mStackScrollAlgorithm.setIsExpanded(isExpanded); if (changed) { updateNotificationAnimationStates(); + updateChronometers(); + } + } + + private void updateChronometers() { + int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + updateChronometerForChild(getChildAt(i)); + } + } + + private void updateChronometerForChild(View child) { + if (child instanceof ExpandableNotificationRow) { + ExpandableNotificationRow row = (ExpandableNotificationRow) child; + row.setChronometerRunning(mIsExpanded); } } @@ -2309,6 +2325,7 @@ public void onReset(ExpandableView view) { } mStackScrollAlgorithm.onReset(view); updateAnimationState(view); + updateChronometerForChild(view); } private void updateScrollPositionOnExpandInBottom(ExpandableView view) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java index 78364117065..e9f1095c0a6 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialog.java @@ -104,6 +104,7 @@ public class VolumeDialog { private final SpTexts mSpTexts; private final SparseBooleanArray mDynamic = new SparseBooleanArray(); private final KeyguardManager mKeyguard; + private final AudioManager mAudioManager; private final int mExpandButtonAnimationDuration; private final ZenFooter mZenFooter; private final LayoutTransition mLayoutTransition; @@ -135,6 +136,7 @@ public VolumeDialog(Context context, int windowType, VolumeDialogController cont mCallback = callback; mSpTexts = new SpTexts(mContext); mKeyguard = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE); + mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); mDialog = new CustomDialog(mContext); @@ -636,7 +638,8 @@ private void onStateChangedH(State state) { private void updateFooterH() { if (D.BUG) Log.d(TAG, "updateFooterH"); final boolean wasVisible = mZenFooter.getVisibility() == View.VISIBLE; - final boolean visible = mState.zenMode != Global.ZEN_MODE_OFF; + final boolean visible = mState.zenMode != Global.ZEN_MODE_OFF + && mAudioManager.isStreamAffectedByRingerMode(mActiveStream); if (wasVisible != visible && !visible) { prepareForCollapse(); } diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java index 3c9a7fc7cb6..33377149aed 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java +++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java @@ -1,4 +1,4 @@ -/* +/** * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -58,6 +58,8 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Arrays; +import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.Locale; import java.util.Objects; @@ -76,6 +78,8 @@ public class ZenModePanel extends LinearLayout { private static final int DEFAULT_BUCKET_INDEX = Arrays.binarySearch(MINUTE_BUCKETS, 60); private static final int FOREVER_CONDITION_INDEX = 0; private static final int COUNTDOWN_CONDITION_INDEX = 1; + private static final int COUNTDOWN_ALARM_CONDITION_INDEX = 2; + private static final int COUNTDOWN_CONDITION_COUNT = 2; public static final Intent ZEN_SETTINGS = new Intent(Settings.ACTION_ZEN_MODE_SETTINGS); @@ -86,7 +90,6 @@ public class ZenModePanel extends LinearLayout { private final LayoutInflater mInflater; private final H mHandler = new H(); private final ZenPrefs mPrefs; - private final IconPulser mIconPulser; private final TransitionHelper mTransitionHelper = new TransitionHelper(); private final Uri mForeverId; private final SpTexts mSpTexts; @@ -104,9 +107,6 @@ public class ZenModePanel extends LinearLayout { private Callback mCallback; private ZenModeController mController; private boolean mCountdownConditionSupported; - private int mMaxConditions; - private int mMaxOptionalConditions; - private int mFirstConditionIndex; private boolean mRequestingConditions; private Condition mExitCondition; private int mBucketIndex = -1; @@ -125,7 +125,6 @@ public ZenModePanel(Context context, AttributeSet attrs) { mContext = context; mPrefs = new ZenPrefs(); mInflater = LayoutInflater.from(mContext.getApplicationContext()); - mIconPulser = new IconPulser(mContext); mForeverId = Condition.newId(mContext).appendPath("forever").build(); mSpTexts = new SpTexts(mContext); mVoiceCapable = Util.isVoiceCapable(mContext); @@ -135,7 +134,6 @@ public ZenModePanel(Context context, AttributeSet attrs) { public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("ZenModePanel state:"); pw.print(" mCountdownConditionSupported="); pw.println(mCountdownConditionSupported); - pw.print(" mMaxConditions="); pw.println(mMaxConditions); pw.print(" mRequestingConditions="); pw.println(mRequestingConditions); pw.print(" mAttached="); pw.println(mAttached); pw.print(" mHidden="); pw.println(mHidden); @@ -286,14 +284,6 @@ private void setRequestingConditions(final boolean requesting) { if (mRequestingConditions == requesting) return; if (DEBUG) Log.d(mTag, "setRequestingConditions " + requesting); mRequestingConditions = requesting; - if (mController != null) { - AsyncTask.execute(new Runnable() { - @Override - public void run() { - mController.requestConditions(requesting); - } - }); - } if (mRequestingConditions) { mTimeCondition = parseExistingTimeCondition(mContext, mExitCondition); if (mTimeCondition != null) { @@ -304,6 +294,7 @@ public void run() { MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser()); } if (DEBUG) Log.d(mTag, "Initial bucket index: " + mBucketIndex); + mConditions = null; // reset conditions handleUpdateConditions(); } else { @@ -314,13 +305,9 @@ public void run() { public void init(ZenModeController controller) { mController = controller; mCountdownConditionSupported = mController.isCountdownConditionSupported(); - final int countdownDelta = mCountdownConditionSupported ? 1 : 0; - mFirstConditionIndex = COUNTDOWN_CONDITION_INDEX + countdownDelta; + final int countdownDelta = mCountdownConditionSupported ? COUNTDOWN_CONDITION_COUNT : 0; final int minConditions = 1 /*forever*/ + countdownDelta; - mMaxConditions = MathUtils.constrain(mContext.getResources() - .getInteger(R.integer.zen_mode_max_conditions), minConditions, 100); - mMaxOptionalConditions = mMaxConditions - minConditions; - for (int i = 0; i < mMaxConditions; i++) { + for (int i = 0; i < minConditions; i++) { mZenConditions.addView(mInflater.inflate(R.layout.zen_mode_condition, this, false)); } mSessionZen = getSelectedZen(-1); @@ -357,28 +344,10 @@ private static Condition copy(Condition condition) { return condition == null ? null : condition.copy(); } - public static String getExitConditionText(Context context, Condition exitCondition) { - if (exitCondition == null) { - return foreverSummary(context); - } else if (isCountdown(exitCondition)) { - final Condition condition = parseExistingTimeCondition(context, exitCondition); - return condition != null ? condition.summary : foreverSummary(context); - } else { - return exitCondition.summary; - } - } - public void setCallback(Callback callback) { mCallback = callback; } - public void showSilentHint() { - if (DEBUG) Log.d(mTag, "showSilentHint"); - if (mZenButtons == null || mZenButtons.getChildCount() == 0) return; - final View noneButton = mZenButtons.getChildAt(0); - mIconPulser.start(noneButton); - } - private void handleUpdateManualRule(ZenRule rule) { final int zen = rule != null ? rule.zenMode : Global.ZEN_MODE_OFF; handleUpdateZen(zen); @@ -410,7 +379,7 @@ private void handleExitConditionChanged(Condition exitCondition) { final ConditionTag tag = getConditionTagAt(i); if (tag != null) { if (sameConditionId(tag.condition, mExitCondition)) { - bind(exitCondition, mZenConditions.getChildAt(i)); + bind(exitCondition, mZenConditions.getChildAt(i), i); } } } @@ -495,64 +464,32 @@ private static Condition parseExistingTimeCondition(Context context, Condition c final long span = time - now; if (span <= 0 || span > MAX_BUCKET_MINUTES * MINUTES_MS) return null; return ZenModeConfig.toTimeCondition(context, - time, Math.round(span / (float) MINUTES_MS), now, ActivityManager.getCurrentUser(), + time, Math.round(span / (float) MINUTES_MS), ActivityManager.getCurrentUser(), false /*shortVersion*/); } - private void handleUpdateConditions(Condition[] conditions) { - conditions = trimConditions(conditions); - if (Arrays.equals(conditions, mConditions)) { - final int count = mConditions == null ? 0 : mConditions.length; - if (DEBUG) Log.d(mTag, "handleUpdateConditions unchanged conditionCount=" + count); - return; - } - mConditions = conditions; - handleUpdateConditions(); - } - - private Condition[] trimConditions(Condition[] conditions) { - if (conditions == null || conditions.length <= mMaxOptionalConditions) { - // no need to trim - return conditions; - } - // look for current exit condition, ensure it is included if found - int found = -1; - for (int i = 0; i < conditions.length; i++) { - final Condition c = conditions[i]; - if (mSessionExitCondition != null && sameConditionId(mSessionExitCondition, c)) { - found = i; - break; - } - } - final Condition[] rt = Arrays.copyOf(conditions, mMaxOptionalConditions); - if (found >= mMaxOptionalConditions) { - // found after the first N, promote to the end of the first N - rt[mMaxOptionalConditions - 1] = conditions[found]; - } - return rt; - } - private void handleUpdateConditions() { if (mTransitionHelper.isTransitioning()) { - mTransitionHelper.pendingUpdateConditions(); return; } final int conditionCount = mConditions == null ? 0 : mConditions.length; if (DEBUG) Log.d(mTag, "handleUpdateConditions conditionCount=" + conditionCount); // forever - bind(forever(), mZenConditions.getChildAt(FOREVER_CONDITION_INDEX)); + bind(forever(), mZenConditions.getChildAt(FOREVER_CONDITION_INDEX), + FOREVER_CONDITION_INDEX); // countdown if (mCountdownConditionSupported && mTimeCondition != null) { - bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX)); - } - // provider conditions - for (int i = 0; i < conditionCount; i++) { - bind(mConditions[i], mZenConditions.getChildAt(mFirstConditionIndex + i)); - } - // hide the rest - for (int i = mZenConditions.getChildCount() - 1; i > mFirstConditionIndex + conditionCount; - i--) { - mZenConditions.getChildAt(i).setVisibility(GONE); + bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX), + COUNTDOWN_CONDITION_INDEX); + } + // countdown until alarm + if (mCountdownConditionSupported) { + Condition nextAlarmCondition = getTimeUntilNextAlarmCondition(); + if (nextAlarmCondition != null) { + bind(nextAlarmCondition, + mZenConditions.getChildAt(COUNTDOWN_ALARM_CONDITION_INDEX), + COUNTDOWN_ALARM_CONDITION_INDEX); + } } // ensure something is selected if (mExpanded && isShown()) { @@ -569,6 +506,33 @@ private static String foreverSummary(Context context) { return context.getString(com.android.internal.R.string.zen_mode_forever); } + // Returns a time condition if the next alarm is within the next week. + private Condition getTimeUntilNextAlarmCondition() { + GregorianCalendar weekRange = new GregorianCalendar(); + final long now = weekRange.getTimeInMillis(); + setToMidnight(weekRange); + weekRange.roll(Calendar.DATE, 6); + final long nextAlarmMs = mController.getNextAlarm(); + if (nextAlarmMs > 0) { + GregorianCalendar nextAlarm = new GregorianCalendar(); + nextAlarm.setTimeInMillis(nextAlarmMs); + setToMidnight(nextAlarm); + + if (weekRange.compareTo(nextAlarm) >= 0) { + return ZenModeConfig.toNextAlarmCondition(mContext, now, nextAlarmMs, + ActivityManager.getCurrentUser()); + } + } + return null; + } + + private void setToMidnight(Calendar calendar) { + calendar.set(Calendar.HOUR_OF_DAY, 0); + calendar.set(Calendar.MINUTE, 0); + calendar.set(Calendar.SECOND, 0); + calendar.set(Calendar.MILLISECOND, 0); + } + private ConditionTag getConditionTagAt(int index) { return (ConditionTag) mZenConditions.getChildAt(index).getTag(); } @@ -610,7 +574,8 @@ private void ensureSelection() { mTimeCondition = ZenModeConfig.toTimeCondition(mContext, MINUTE_BUCKETS[favoriteIndex], ActivityManager.getCurrentUser()); mBucketIndex = favoriteIndex; - bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX)); + bind(mTimeCondition, mZenConditions.getChildAt(COUNTDOWN_CONDITION_INDEX), + COUNTDOWN_CONDITION_INDEX); getConditionTagAt(COUNTDOWN_CONDITION_INDEX).rb.setChecked(true); } } @@ -623,7 +588,7 @@ private boolean isForever(Condition c) { return c != null && mForeverId.equals(c.id); } - private void bind(final Condition condition, final View row) { + private void bind(final Condition condition, final View row, final int rowId) { if (condition == null) throw new IllegalArgumentException("condition must not be null"); final boolean enabled = condition.state == Condition.STATE_TRUE; final ConditionTag tag = @@ -640,8 +605,7 @@ private void bind(final Condition condition, final View row) { tag.rb.setEnabled(enabled); final boolean checked = (mSessionExitCondition != null || mAttachedZen != Global.ZEN_MODE_OFF) - && (sameConditionId(mSessionExitCondition, tag.condition) - || isCountdown(mSessionExitCondition) && isCountdown(tag.condition)); + && (sameConditionId(mSessionExitCondition, tag.condition)); if (checked != tag.rb.isChecked()) { if (DEBUG) Log.d(mTag, "bind checked=" + checked + " condition=" + conditionId); tag.rb.setChecked(checked); @@ -692,7 +656,7 @@ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { button1.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - onClickTimeButton(row, tag, false /*down*/); + onClickTimeButton(row, tag, false /*down*/, rowId); } }); @@ -700,7 +664,7 @@ public void onClick(View v) { button2.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - onClickTimeButton(row, tag, true /*up*/); + onClickTimeButton(row, tag, true /*up*/, rowId); } }); tag.lines.setOnClickListener(new OnClickListener() { @@ -711,7 +675,7 @@ public void onClick(View v) { }); final long time = ZenModeConfig.tryParseCountdownConditionId(conditionId); - if (time > 0) { + if (rowId != COUNTDOWN_ALARM_CONDITION_INDEX && time > 0) { button1.setVisibility(VISIBLE); button2.setVisibility(VISIBLE); if (mBucketIndex > -1) { @@ -761,7 +725,7 @@ private void announceConditionSelection(ConditionTag tag) { tag.line1.getText())); } - private void onClickTimeButton(View row, ConditionTag tag, boolean up) { + private void onClickTimeButton(View row, ConditionTag tag, boolean up, int rowId) { MetricsLogger.action(mContext, MetricsLogger.QS_DND_TIME, up); Condition newCondition = null; final int N = MINUTE_BUCKETS.length; @@ -777,7 +741,7 @@ private void onClickTimeButton(View row, ConditionTag tag, boolean up) { if (up && bucketTime > time || !up && bucketTime < time) { mBucketIndex = j; newCondition = ZenModeConfig.toTimeCondition(mContext, - bucketTime, bucketMinutes, now, ActivityManager.getCurrentUser(), + bucketTime, bucketMinutes, ActivityManager.getCurrentUser(), false /*shortVersion*/); break; } @@ -794,7 +758,7 @@ private void onClickTimeButton(View row, ConditionTag tag, boolean up) { MINUTE_BUCKETS[mBucketIndex], ActivityManager.getCurrentUser()); } mTimeCondition = newCondition; - bind(mTimeCondition, row); + bind(mTimeCondition, row, rowId); tag.rb.setChecked(true); select(mTimeCondition); announceConditionSelection(tag); @@ -837,11 +801,6 @@ private void fireExpanded() { } private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() { - @Override - public void onConditionsChanged(Condition[] conditions) { - mHandler.obtainMessage(H.UPDATE_CONDITIONS, conditions).sendToTarget(); - } - @Override public void onManualRuleChanged(ZenRule rule) { mHandler.obtainMessage(H.MANUAL_RULE_CHANGED, rule).sendToTarget(); @@ -849,7 +808,6 @@ public void onManualRuleChanged(ZenRule rule) { }; private final class H extends Handler { - private static final int UPDATE_CONDITIONS = 1; private static final int MANUAL_RULE_CHANGED = 2; private static final int UPDATE_WIDGETS = 3; @@ -860,7 +818,6 @@ private H() { @Override public void handleMessage(Message msg) { switch (msg.what) { - case UPDATE_CONDITIONS: handleUpdateConditions((Condition[]) msg.obj); break; case MANUAL_RULE_CHANGED: handleUpdateManualRule((ZenRule) msg.obj); break; case UPDATE_WIDGETS: updateWidgets(); break; } @@ -1005,26 +962,20 @@ private final class TransitionHelper implements TransitionListener, Runnable { private final ArraySet mTransitioningViews = new ArraySet(); private boolean mTransitioning; - private boolean mPendingUpdateConditions; private boolean mPendingUpdateWidgets; public void clear() { mTransitioningViews.clear(); - mPendingUpdateConditions = mPendingUpdateWidgets = false; + mPendingUpdateWidgets = false; } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println(" TransitionHelper state:"); - pw.print(" mPendingUpdateConditions="); pw.println(mPendingUpdateConditions); pw.print(" mPendingUpdateWidgets="); pw.println(mPendingUpdateWidgets); pw.print(" mTransitioning="); pw.println(mTransitioning); pw.print(" mTransitioningViews="); pw.println(mTransitioningViews); } - public void pendingUpdateConditions() { - mPendingUpdateConditions = true; - } - public void pendingUpdateWidgets() { mPendingUpdateWidgets = true; } @@ -1050,15 +1001,11 @@ public void endTransition(LayoutTransition transition, @Override public void run() { if (DEBUG) Log.d(mTag, "TransitionHelper run" - + " mPendingUpdateWidgets=" + mPendingUpdateWidgets - + " mPendingUpdateConditions=" + mPendingUpdateConditions); + + " mPendingUpdateWidgets=" + mPendingUpdateWidgets); if (mPendingUpdateWidgets) { updateWidgets(); } - if (mPendingUpdateConditions) { - handleUpdateConditions(); - } - mPendingUpdateWidgets = mPendingUpdateConditions = false; + mPendingUpdateWidgets = false; } private void updateTransitioning() { @@ -1067,10 +1014,10 @@ private void updateTransitioning() { mTransitioning = transitioning; if (DEBUG) Log.d(mTag, "TransitionHelper mTransitioning=" + mTransitioning); if (!mTransitioning) { - if (mPendingUpdateConditions || mPendingUpdateWidgets) { + if (mPendingUpdateWidgets) { mHandler.post(this); } else { - mPendingUpdateConditions = mPendingUpdateWidgets = false; + mPendingUpdateWidgets = false; } } } diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java index 8b1a0324956..4440417333f 100644 --- a/rs/java/android/renderscript/RenderScript.java +++ b/rs/java/android/renderscript/RenderScript.java @@ -1557,9 +1557,21 @@ private void helpDestroy() { nContextDeinitToClient(mContext); mMessageThread.mRun = false; - try { - mMessageThread.join(); - } catch(InterruptedException e) { + + // Wait for mMessageThread to join. Try in a loop, in case this thread gets interrupted + // during the wait. If interrupted, set the "interrupted" status of the current thread. + boolean hasJoined = false, interrupted = false; + while (!hasJoined) { + try { + mMessageThread.join(); + hasJoined = true; + } catch (InterruptedException e) { + interrupted = true; + } + } + if (interrupted) { + Log.v(LOG_TAG, "Interrupted during wait for MessageThread to join"); + Thread.currentThread().interrupt(); } nContextDestroy(); diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index ffc4fd82ab5..2bc48e43e0d 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -611,7 +611,7 @@ nScriptIntrinsicBLAS_Double(JNIEnv *_env, jobject _this, jlong con, jlong id, ji in_allocs[2] = (RsAllocation)C; rsScriptForEachMulti((RsContext)con, (RsScript)id, 0, - in_allocs, sizeof(in_allocs), nullptr, + in_allocs, NELEM(in_allocs), nullptr, &call, sizeof(call), nullptr, 0); } @@ -646,7 +646,7 @@ nScriptIntrinsicBLAS_Complex(JNIEnv *_env, jobject _this, jlong con, jlong id, j in_allocs[2] = (RsAllocation)C; rsScriptForEachMulti((RsContext)con, (RsScript)id, 0, - in_allocs, sizeof(in_allocs), nullptr, + in_allocs, NELEM(in_allocs), nullptr, &call, sizeof(call), nullptr, 0); } @@ -681,7 +681,7 @@ nScriptIntrinsicBLAS_Z(JNIEnv *_env, jobject _this, jlong con, jlong id, jint fu in_allocs[2] = (RsAllocation)C; rsScriptForEachMulti((RsContext)con, (RsScript)id, 0, - in_allocs, sizeof(in_allocs), nullptr, + in_allocs, NELEM(in_allocs), nullptr, &call, sizeof(call), nullptr, 0); } @@ -707,7 +707,7 @@ nScriptIntrinsicBLAS_BNNM(JNIEnv *_env, jobject _this, jlong con, jlong id, jint in_allocs[2] = (RsAllocation)C; rsScriptForEachMulti((RsContext)con, (RsScript)id, 0, - in_allocs, sizeof(in_allocs), nullptr, + in_allocs, NELEM(in_allocs), nullptr, &call, sizeof(call), nullptr, 0); } diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index edbb2893d7f..972822861af 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -185,7 +185,7 @@ private final class Constants extends ContentObserver { private static final long DEFAULT_MIN_FUTURITY = 5 * 1000; private static final long DEFAULT_MIN_INTERVAL = 60 * 1000; private static final long DEFAULT_ALLOW_WHILE_IDLE_SHORT_TIME = DEFAULT_MIN_FUTURITY; - private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 15*60*1000; + private static final long DEFAULT_ALLOW_WHILE_IDLE_LONG_TIME = 9*60*1000; private static final long DEFAULT_ALLOW_WHILE_IDLE_WHITELIST_DURATION = 10*1000; // Minimum futurity of a new alarm @@ -1000,8 +1000,8 @@ private void setImplLocked(Alarm a, boolean rebatching, boolean doValidate) { // This is a special alarm that will put the system into idle until it goes off. // The caller has given the time they want this to happen at, however we need // to pull that earlier if there are existing alarms that have requested to - // bring us out of idle. - if (mNextWakeFromIdle != null) { + // bring us out of idle at an earlier time. + if (mNextWakeFromIdle != null && a.whenElapsed > mNextWakeFromIdle.whenElapsed) { a.when = a.whenElapsed = a.maxWhenElapsed = mNextWakeFromIdle.whenElapsed; } // Add fuzz to make the alarm go off some time before the actual desired time. @@ -1318,7 +1318,7 @@ void dumpImpl(PrintWriter pw) { pw.print(" Idling until: "); if (mPendingIdleUntil != null) { pw.println(mPendingIdleUntil); - mPendingIdleUntil.dump(pw, " ", nowRTC, nowELAPSED, sdf); + mPendingIdleUntil.dump(pw, " ", nowRTC, nowELAPSED, sdf); } else { pw.println("null"); } diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index ff7d3982c56..05284479d81 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -43,7 +43,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.media.AudioAttributes; import android.os.AsyncTask; import android.os.Binder; @@ -292,12 +291,12 @@ public void systemReady() { Iterator it = pkgs.values().iterator(); while (it.hasNext()) { Ops ops = it.next(); - int curUid; + int curUid = -1; try { - curUid = mContext.getPackageManager().getPackageUid(ops.packageName, + curUid = AppGlobals.getPackageManager().getPackageUidEtc(ops.packageName, + PackageManager.GET_UNINSTALLED_PACKAGES, UserHandle.getUserId(ops.uidState.uid)); - } catch (NameNotFoundException e) { - curUid = -1; + } catch (RemoteException ignored) { } if (curUid != ops.uidState.uid) { Slog.i(TAG, "Pruning old package " + ops.packageName diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index dcaae5b8d2f..0b6f0ddb75a 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -947,13 +947,13 @@ private boolean isNetworkWithLinkPropertiesBlocked(LinkProperties lp, int uid) { uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); } - if ((uidRules & RULE_REJECT_ALL) != 0 - || (networkCostly && (uidRules & RULE_REJECT_METERED) != 0)) { + if (uidRules == RULE_REJECT_ALL) { return true; + } else if ((uidRules == RULE_REJECT_METERED) && networkCostly) { + return true; + } else { + return false; } - - // no restrictive rules; network is visible - return false; } /** @@ -1448,10 +1448,7 @@ private void enforceAccessPermission() { } private void enforceChangePermission() { - int uid = Binder.getCallingUid(); - Settings.checkAndNoteChangeNetworkStateOperation(mContext, uid, Settings - .getPackageNameForUid(mContext, uid), true); - + ConnectivityManager.enforceChangePermission(mContext); } private void enforceTetherAccessPermission() { @@ -3221,6 +3218,11 @@ public boolean updateLockdownVpn() { final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN)); final VpnProfile profile = VpnProfile.decode( profileName, mKeyStore.get(Credentials.VPN + profileName)); + if (profile == null) { + Slog.e(TAG, "Lockdown VPN configured invalid profile " + profileName); + setLockdownTracker(null); + return true; + } int user = UserHandle.getUserId(Binder.getCallingUid()); synchronized(mVpns) { setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpns.get(user), @@ -3367,7 +3369,7 @@ private void setProvNotificationVisibleIntent(boolean visible, int id, .setPriority(highPriority ? Notification.PRIORITY_HIGH : Notification.PRIORITY_DEFAULT) - .setDefaults(Notification.DEFAULT_ALL) + .setDefaults(highPriority ? Notification.DEFAULT_ALL : 0) .setOnlyAlertOnce(true) .build(); @@ -3733,7 +3735,7 @@ private void enforceMeteredApnPolicy(NetworkCapabilities networkCapabilities) { synchronized(mRulesLock) { uidRules = mUidRules.get(uid, RULE_ALLOW_ALL); } - if ((uidRules & (RULE_REJECT_METERED | RULE_REJECT_ALL)) != 0) { + if (uidRules != RULE_ALLOW_ALL) { // we could silently fail or we can filter the available nets to only give // them those they have access to. Chose the more useful networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED); diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java index 38adfbd882a..abe8f5c81c8 100644 --- a/services/core/java/com/android/server/DeviceIdleController.java +++ b/services/core/java/com/android/server/DeviceIdleController.java @@ -242,6 +242,14 @@ private static String stateToString(int state) { if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { int plugged = intent.getIntExtra("plugged", 0); updateChargingLocked(plugged != 0); + } else if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) { + if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { + Uri data = intent.getData(); + String ssp; + if (data != null && (ssp=data.getSchemeSpecificPart()) != null) { + removePowerSaveWhitelistAppInternal(ssp); + } + } } else if (ACTION_STEP_IDLE_STATE.equals(intent.getAction())) { synchronized (DeviceIdleController.this) { stepIdleStateLocked(); @@ -918,6 +926,10 @@ public void onBootPhase(int phase) { filter.addAction(Intent.ACTION_BATTERY_CHANGED); filter.addAction(ACTION_STEP_IDLE_STATE); getContext().registerReceiver(mReceiver, filter); + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_PACKAGE_REMOVED); + filter.addDataScheme("package"); + getContext().registerReceiver(mReceiver, filter); mLocalPowerManager.setDeviceIdleWhitelist(mPowerSaveWhitelistAllAppIdArray); @@ -930,7 +942,10 @@ public void onBootPhase(int phase) { public boolean addPowerSaveWhitelistAppInternal(String name) { synchronized (this) { try { - ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name, 0); + ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(name, + PackageManager.GET_UNINSTALLED_PACKAGES + | PackageManager.GET_DISABLED_COMPONENTS + | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); if (mPowerSaveWhitelistUserApps.put(name, UserHandle.getAppId(ai.uid)) == null) { reportPowerSaveWhitelistChangedLocked(); updateWhitelistAppIdsLocked(); @@ -1271,7 +1286,6 @@ void stepIdleStateLocked() { if (DEBUG) Slog.d(TAG, "Moved from STATE_IDLE_PENDING to STATE_SENSING."); EventLogTags.writeDeviceIdle(mState, "step"); scheduleSensingAlarmLocked(mConstants.SENSING_TIMEOUT); - cancelSensingAlarmLocked(); cancelLocatingLocked(); mAnyMotionDetector.checkForAnyMotion(); mNotMoving = false; @@ -1283,7 +1297,6 @@ void stepIdleStateLocked() { mState = STATE_LOCATING; if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING."); EventLogTags.writeDeviceIdle(mState, "step"); - cancelSensingAlarmLocked(); scheduleSensingAlarmLocked(mConstants.LOCATING_TIMEOUT); mLocating = true; mLocationManager.requestLocationUpdates(mLocationRequest, mGenericLocationListener, @@ -1341,6 +1354,7 @@ void handleMotionDetectedLocked(long timeout, String type) { mState = STATE_ACTIVE; mInactiveTimeout = timeout; EventLogTags.writeDeviceIdle(mState, type); + cancelSensingAlarmLocked(); becomeInactiveIfAppropriateLocked(); } } @@ -1524,7 +1538,6 @@ void readConfigFileLocked() { } catch (IOException e) { } } - } private void readConfigFileLocked(XmlPullParser parser) { @@ -1553,7 +1566,10 @@ private void readConfigFileLocked(XmlPullParser parser) { String name = parser.getAttributeValue(null, "n"); if (name != null) { try { - ApplicationInfo ai = pm.getApplicationInfo(name, 0); + ApplicationInfo ai = pm.getApplicationInfo(name, + PackageManager.GET_UNINSTALLED_PACKAGES + | PackageManager.GET_DISABLED_COMPONENTS + | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS); mPowerSaveWhitelistUserApps.put(ai.packageName, UserHandle.getAppId(ai.uid)); } catch (PackageManager.NameNotFoundException e) { diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java index 91282801b5f..b3bc4e79baa 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -40,6 +40,7 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import android.annotation.Nullable; import android.app.ActivityManagerNative; import android.app.AppGlobals; import android.app.AlertDialog; @@ -287,8 +288,19 @@ public String toString() { boolean mSystemReady; /** - * Id of the currently selected input method. + * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method. + * method. This is to be synchronized with the secure settings keyed with + * {@link Settings.Secure#DEFAULT_INPUT_METHOD}. + * + *

    This can be transiently {@code null} when the system is re-initializing input method + * settings, e.g., the system locale is just changed.

    + * + *

    Note that {@link #mCurId} is used to track which IME is being connected to + * {@link InputMethodManagerService}.

    + * + * @see #mCurId */ + @Nullable String mCurMethodId; /** @@ -318,9 +330,14 @@ public String toString() { EditorInfo mCurAttribute; /** - * The input method ID of the input method service that we are currently + * Id obtained with {@link InputMethodInfo#getId()} for the input method that we are currently * connected to or in the process of connecting to. + * + *

    This can be {@code null} when no input method is connected.

    + * + * @see #mCurMethodId */ + @Nullable String mCurId; /** @@ -976,7 +993,6 @@ private void resetAllInternalStateLocked(final boolean updateOnlyWhenLocaleChang || (newLocale != null && !newLocale.equals(mLastSystemLocale))) { if (!updateOnlyWhenLocaleChanged) { hideCurrentInputLocked(0, null); - mCurMethodId = null; unbindCurrentMethodLocked(true, false); } if (DEBUG) { @@ -1530,7 +1546,11 @@ void onSessionCreated(IInputMethod method, IInputMethodSession session, channel.dispose(); } - void unbindCurrentMethodLocked(boolean reportToClient, boolean savePosition) { + void unbindCurrentMethodLocked(boolean resetCurrentMethodAndClient, boolean savePosition) { + if (resetCurrentMethodAndClient) { + mCurMethodId = null; + } + if (mVisibleBound) { mContext.unbindService(mVisibleConnection); mVisibleBound = false; @@ -1557,9 +1577,8 @@ void unbindCurrentMethodLocked(boolean reportToClient, boolean savePosition) { mCurId = null; clearCurMethodLocked(); - if (reportToClient && mCurClient != null) { - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( - MSG_UNBIND_METHOD, mCurSeq, mCurClient.client)); + if (resetCurrentMethodAndClient) { + unbindCurrentClientLocked(); } } @@ -1910,13 +1929,11 @@ void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) { setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id)); } catch (IllegalArgumentException e) { Slog.w(TAG, "Unknown input method from prefs: " + id, e); - mCurMethodId = null; unbindCurrentMethodLocked(true, false); } mShortcutInputMethodsAndSubtypes.clear(); } else { // There is no longer an input method set, so stop any current one. - mCurMethodId = null; unbindCurrentMethodLocked(true, false); } // code to disable the CM Phone IME switcher with config_show_cmIMESwitcher set = false diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 2602ad32a1a..f40b9d08efc 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -2415,8 +2415,13 @@ private int encryptStorageExtended(int type, String password, boolean wipe) { } try { - mCryptConnector.execute("cryptfs", "enablecrypto", wipe ? "wipe" : "inplace", CRYPTO_TYPES[type], - new SensitiveArg(password)); + if (type == StorageManager.CRYPT_TYPE_DEFAULT) { + mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", + CRYPTO_TYPES[type]); + } else { + mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", + CRYPTO_TYPES[type], new SensitiveArg(password)); + } } catch (NativeDaemonConnectorException e) { // Encryption failed return e.getCode(); diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index ecfd042d075..64083b6f65f 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -2055,9 +2055,9 @@ public void setFirewallEgressDestRule(String addr, int port, boolean allow) { public void setFirewallChainEnabled(int chain, boolean enable) { enforceSystemUid(); synchronized (mQuotaLock) { - if (mFirewallChainStates.indexOfKey(chain) >= 0 && - mFirewallChainStates.get(chain) == enable) { - // All is the same, nothing to do. + if (mFirewallChainStates.get(chain, false) == enable) { + // All is the same, nothing to do. This relies on the fact that netd has child + // chains default detached. return; } mFirewallChainStates.put(chain, enable); diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java index a0d305cdf2c..3f0664defbf 100644 --- a/services/core/java/com/android/server/NetworkTimeUpdateService.java +++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java @@ -30,6 +30,7 @@ import android.os.Looper; import android.os.Message; import android.os.SystemClock; +import android.os.PowerManager; import android.provider.Settings; import android.util.Log; import android.util.NtpTrustedTime; @@ -75,6 +76,7 @@ public class NetworkTimeUpdateService { private SettingsObserver mSettingsObserver; // The last time that we successfully fetched the NTP time. private long mLastNtpFetchTime = NOT_SET; + private final PowerManager.WakeLock mWakeLock; // Normal polling frequency private final long mPollingIntervalMs; @@ -104,6 +106,9 @@ public NetworkTimeUpdateService(Context context) { com.android.internal.R.integer.config_ntpRetry); mTimeErrorThresholdMs = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpThreshold); + + mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock( + PowerManager.PARTIAL_WAKE_LOCK, TAG); } /** Initialize the receivers and initiate the first NTP request */ @@ -148,7 +153,15 @@ private void registerForConnectivityIntents() { private void onPollNetworkTime(int event) { // If Automatic time is not set, don't bother. if (!isAutomaticTimeRequested()) return; + mWakeLock.acquire(); + try { + onPollNetworkTimeUnderWakeLock(event); + } finally { + mWakeLock.release(); + } + } + private void onPollNetworkTimeUnderWakeLock(int event) { final long refTime = SystemClock.elapsedRealtime(); // If NITZ time was received less than mPollingIntervalMs time ago, // no need to sync to NTP. diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 476dc169d51..79386092752 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -3799,7 +3799,6 @@ private List getTypesVisibleToCaller(int callingUid, int userId, boolean isPermitted = isPermitted(opPackageName, callingUid, Manifest.permission.GET_ACCOUNTS, Manifest.permission.GET_ACCOUNTS_PRIVILEGED); - Log.i(TAG, String.format("getTypesVisibleToCaller: isPermitted? %s", isPermitted)); return getTypesForCaller(callingUid, userId, isPermitted); } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index b87e109b549..970f1b5f2c8 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -1314,6 +1314,15 @@ final void performServiceRestartLocked(ServiceRecord r) { if (!mRestartingServices.contains(r)) { return; } + if (!isServiceNeeded(r, false, false)) { + // Paranoia: is this service actually needed? In theory a service that is not + // needed should never remain on the restart list. In practice... well, there + // have been bugs where this happens, and bad things happen because the process + // ends up just being cached, so quickly killed, then restarted again and again. + // Let's not let that happen. + Slog.wtf(TAG, "Restarting service that is not needed: " + r); + return; + } try { bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true); } catch (TransactionTooLargeException e) { @@ -2043,6 +2052,13 @@ boolean attachApplicationLocked(ProcessRecord proc, String processName) mAm.mProcessStats); realStartServiceLocked(sr, proc, sr.createdFromFg); didSomething = true; + if (!isServiceNeeded(sr, false, false)) { + // We were waiting for this service to start, but it is actually no + // longer needed. This could happen because bringDownServiceIfNeeded + // won't bring down a service that is pending... so now the pending + // is done, so let's drop it. + bringDownServiceLocked(sr); + } } } catch (RemoteException e) { Slog.w(TAG, "Exception in new application when starting service " @@ -2055,7 +2071,7 @@ boolean attachApplicationLocked(ProcessRecord proc, String processName) // be weird to bring up the process but arbitrarily not let the services // run at this point just because their restart time hasn't come up. if (mRestartingServices.size() > 0) { - ServiceRecord sr = null; + ServiceRecord sr; for (int i=0; i= ActivityManager.PROCESS_STATE_HOME) { @@ -18922,6 +18927,11 @@ private final boolean applyOomAdjLocked(ProcessRecord app, boolean doingAll, lon } else { app.procStateChanged = true; } + } else if (app.reportedInteraction && (nowElapsed-app.interactionEventTime) + > USAGE_STATS_INTERACTION_INTERVAL) { + // For apps that sit around for a long time in the interactive state, we need + // to report this at least once a day so they don't go idle. + maybeUpdateUsageStatsLocked(app, nowElapsed); } if (changes != 0) { @@ -19016,7 +19026,7 @@ private void maybeUpdateProviderUsageStatsLocked(ProcessRecord app, String provi } } - private void maybeUpdateUsageStatsLocked(ProcessRecord app) { + private void maybeUpdateUsageStatsLocked(ProcessRecord app, long nowElapsed) { if (DEBUG_USAGE_STATS) { Slog.d(TAG, "Checking proc [" + Arrays.toString(app.getPackageList()) + "] state changes: old = " + app.setProcState + ", new = " @@ -19033,19 +19043,20 @@ private void maybeUpdateUsageStatsLocked(ProcessRecord app) { isInteraction = true; app.fgInteractionTime = 0; } else if (app.curProcState <= ActivityManager.PROCESS_STATE_TOP_SLEEPING) { - final long now = SystemClock.elapsedRealtime(); if (app.fgInteractionTime == 0) { - app.fgInteractionTime = now; + app.fgInteractionTime = nowElapsed; isInteraction = false; } else { - isInteraction = now > app.fgInteractionTime + SERVICE_USAGE_INTERACTION_TIME; + isInteraction = nowElapsed > app.fgInteractionTime + SERVICE_USAGE_INTERACTION_TIME; } } else { isInteraction = app.curProcState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; app.fgInteractionTime = 0; } - if (isInteraction && !app.reportedInteraction) { + if (isInteraction && (!app.reportedInteraction + || (nowElapsed-app.interactionEventTime) > USAGE_STATS_INTERACTION_INTERVAL)) { + app.interactionEventTime = nowElapsed; String[] packages = app.getPackageList(); if (packages != null) { for (int i = 0; i < packages.length; i++) { @@ -19055,6 +19066,9 @@ private void maybeUpdateUsageStatsLocked(ProcessRecord app) { } } app.reportedInteraction = isInteraction; + if (!isInteraction) { + app.interactionEventTime = 0; + } } private final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) { @@ -19077,7 +19091,7 @@ private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj, computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now); - return applyOomAdjLocked(app, doingAll, now); + return applyOomAdjLocked(app, doingAll, now, SystemClock.elapsedRealtime()); } final void updateProcessForegroundLocked(ProcessRecord proc, boolean isForeground, @@ -19169,6 +19183,7 @@ final void updateOomAdjLocked() { final ActivityRecord TOP_ACT = resumedAppLocked(); final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null; final long now = SystemClock.uptimeMillis(); + final long nowElapsed = SystemClock.elapsedRealtime(); final long oldTime = now - ProcessList.MAX_EMPTY_TIME; final int N = mLruProcesses.size(); @@ -19326,7 +19341,7 @@ final void updateOomAdjLocked() { } } - applyOomAdjLocked(app, true, now); + applyOomAdjLocked(app, true, now, nowElapsed); // Count the number of process types. switch (app.curProcState) { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 1385b137071..a99a66894d0 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2500,8 +2500,13 @@ final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord so final void doPendingActivityLaunchesLocked(boolean doResume) { while (!mPendingActivityLaunches.isEmpty()) { PendingActivityLaunch pal = mPendingActivityLaunches.remove(0); - startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags, - doResume && mPendingActivityLaunches.isEmpty(), null, null); + + try { + startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags, + doResume && mPendingActivityLaunches.isEmpty(), null, null); + } catch (Exception e) { + Slog.w(TAG, "Exception during pending activity launch pal=" + pal, e); + } } } @@ -4000,7 +4005,7 @@ void setLockTaskModeLocked(TaskRecord task, int lockTaskModeState, String reason mLockTaskModeTasks.add(task); if (task.mLockTaskUid == -1) { - task.mLockTaskUid = task.mCallingUid; + task.mLockTaskUid = task.effectiveUid; } if (andResume) { diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 960cbf15a96..6de85794c8e 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -44,6 +44,7 @@ import android.os.UserHandle; import android.util.EventLog; import android.util.Slog; +import android.util.TimeUtils; import com.android.server.DeviceIdleController; import static com.android.server.am.ActivityManagerDebugConfig.*; @@ -1284,6 +1285,7 @@ final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) { final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, int opti, boolean dumpAll, String dumpPackage, boolean needSep) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0 || mPendingBroadcast != null) { boolean printed = false; @@ -1301,7 +1303,7 @@ final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, pw.println(" Active broadcasts [" + mQueueName + "]:"); } pw.println(" Active Broadcast " + mQueueName + " #" + i + ":"); - br.dump(pw, " "); + br.dump(pw, " ", sdf); } printed = false; needSep = true; @@ -1319,7 +1321,7 @@ final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, pw.println(" Active ordered broadcasts [" + mQueueName + "]:"); } pw.println(" Active Ordered Broadcast " + mQueueName + " #" + i + ":"); - mOrderedBroadcasts.get(i).dump(pw, " "); + mOrderedBroadcasts.get(i).dump(pw, " ", sdf); } if (dumpPackage == null || (mPendingBroadcast != null && dumpPackage.equals(mPendingBroadcast.callerPackage))) { @@ -1328,7 +1330,7 @@ final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, } pw.println(" Pending broadcast [" + mQueueName + "]:"); if (mPendingBroadcast != null) { - mPendingBroadcast.dump(pw, " "); + mPendingBroadcast.dump(pw, " ", sdf); } else { pw.println(" (null)"); } @@ -1366,7 +1368,7 @@ final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, if (dumpAll) { pw.print(" Historical Broadcast " + mQueueName + " #"); pw.print(i); pw.println(":"); - r.dump(pw, " "); + r.dump(pw, " ", sdf); } else { pw.print(" #"); pw.print(i); pw.print(": "); pw.println(r); pw.print(" "); @@ -1400,7 +1402,6 @@ final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, } // done skipping; dump the remainder of the ring. 'i' is still the ordinal within // the overall broadcast history. - SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); do { ringIndex = ringAdvance(ringIndex, -1, MAX_BROADCAST_SUMMARY_HISTORY); Intent intent = mBroadcastSummaryHistory[ringIndex]; @@ -1422,9 +1423,19 @@ final boolean dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args, i++; pw.print(" #"); pw.print(i); pw.print(": "); pw.println(intent.toShortString(false, true, true, false)); - pw.print(" enq="); pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex]))); - pw.print(" disp="); pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex]))); - pw.print(" fin="); pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex]))); + pw.print(" "); + TimeUtils.formatDuration(mSummaryHistoryDispatchTime[ringIndex] + - mSummaryHistoryEnqueueTime[ringIndex], pw); + pw.print(" dispatch "); + TimeUtils.formatDuration(mSummaryHistoryFinishTime[ringIndex] + - mSummaryHistoryDispatchTime[ringIndex], pw); + pw.println(" finish"); + pw.print(" enq="); + pw.print(sdf.format(new Date(mSummaryHistoryEnqueueTime[ringIndex]))); + pw.print(" disp="); + pw.print(sdf.format(new Date(mSummaryHistoryDispatchTime[ringIndex]))); + pw.print(" fin="); + pw.println(sdf.format(new Date(mSummaryHistoryFinishTime[ringIndex]))); Bundle bundle = intent.getExtras(); if (bundle != null) { pw.print(" extras: "); pw.println(bundle.toString()); diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index 1fbfd9feff8..b42bcff15f8 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -32,6 +32,7 @@ import android.util.TimeUtils; import java.io.PrintWriter; +import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; import java.util.List; @@ -88,7 +89,7 @@ final class BroadcastRecord extends Binder { ComponentName curComponent; // the receiver class that is currently running. ActivityInfo curReceiver; // info about the receiver that is currently running. - void dump(PrintWriter pw, String prefix) { + void dump(PrintWriter pw, String prefix, SimpleDateFormat sdf) { final long now = SystemClock.uptimeMillis(); pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId); @@ -114,13 +115,19 @@ void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("options="); pw.println(options.toBundle()); } pw.print(prefix); pw.print("enqueueClockTime="); - pw.print(new Date(enqueueClockTime)); + pw.print(sdf.format(new Date(enqueueClockTime))); pw.print(" dispatchClockTime="); - pw.println(new Date(dispatchClockTime)); + pw.println(sdf.format(new Date(dispatchClockTime))); pw.print(prefix); pw.print("dispatchTime="); TimeUtils.formatDuration(dispatchTime, now, pw); + pw.print(" ("); + TimeUtils.formatDuration(dispatchClockTime-enqueueClockTime, pw); + pw.print(" since enq)"); if (finishTime != 0) { pw.print(" finishTime="); TimeUtils.formatDuration(finishTime, now, pw); + pw.print(" ("); + TimeUtils.formatDuration(finishTime-dispatchTime, pw); + pw.print(" since disp)"); } else { pw.print(" receiverTime="); TimeUtils.formatDuration(receiverTime, now, pw); } diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index bd31a218f4c..697b4e2b5c7 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -114,6 +114,7 @@ final class ProcessRecord { boolean killed; // True once we know the process has been killed boolean procStateChanged; // Keep track of whether we changed 'setAdj'. boolean reportedInteraction;// Whether we have told usage stats about it being an interaction + long interactionEventTime; // The time we sent the last interaction event long fgInteractionTime; // When we became foreground for interaction purposes String waitingToKill; // Process is waiting to be killed when in the bg, and reason IBinder forcingToForeground;// Token that is forcing this process to be foreground @@ -297,6 +298,10 @@ void dump(PrintWriter pw, String prefix) { if (reportedInteraction || fgInteractionTime != 0) { pw.print(prefix); pw.print("reportedInteraction="); pw.print(reportedInteraction); + if (interactionEventTime != 0) { + pw.print(" time="); + TimeUtils.formatDuration(interactionEventTime, SystemClock.elapsedRealtime(), pw); + } if (fgInteractionTime != 0) { pw.print(" fgInteractionTime="); TimeUtils.formatDuration(fgInteractionTime, SystemClock.elapsedRealtime(), pw); diff --git a/services/core/java/com/android/server/content/AppIdleMonitor.java b/services/core/java/com/android/server/content/AppIdleMonitor.java index fe5c2da4c6d..2d768d8b2aa 100644 --- a/services/core/java/com/android/server/content/AppIdleMonitor.java +++ b/services/core/java/com/android/server/content/AppIdleMonitor.java @@ -50,8 +50,8 @@ void setAppIdleParoleOn(boolean appIdleParoleOn) { } } - boolean isAppIdle(String packageName, int userId) { - return !mAppIdleParoleOn && mUsageStats.isAppIdle(packageName, userId); + boolean isAppIdle(String packageName, int uidForAppId, int userId) { + return !mAppIdleParoleOn && mUsageStats.isAppIdle(packageName, uidForAppId, userId); } @Override diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 334bc18b82f..3ec0bee85f6 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -104,6 +104,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Random; import java.util.Set; @@ -2622,9 +2623,18 @@ private long maybeStartNextSyncH() { continue; } String packageName = getPackageName(op.target); + ApplicationInfo ai = null; + if (packageName != null) { + try { + ai = mContext.getPackageManager().getApplicationInfo(packageName, + PackageManager.GET_UNINSTALLED_PACKAGES + | PackageManager.GET_DISABLED_COMPONENTS); + } catch (NameNotFoundException e) { + } + } // If app is considered idle, then skip for now and backoff - if (packageName != null - && mAppIdleMonitor.isAppIdle(packageName, op.target.userId)) { + if (ai != null + && mAppIdleMonitor.isAppIdle(packageName, ai.uid, op.target.userId)) { increaseBackoffSetting(op); op.appIdle = true; if (isLoggable) { @@ -3370,7 +3380,7 @@ public static boolean syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSync if (!smaller.containsKey(key)) { return false; } - if (!bigger.get(key).equals(smaller.get(key))) { + if (!Objects.equals(bigger.get(key), smaller.get(key))) { return false; } } @@ -3378,7 +3388,6 @@ public static boolean syncExtrasEquals(Bundle b1, Bundle b2, boolean includeSync } /** - * TODO: Get rid of this when we separate sync settings extras from dev specified extras. * @return true if the provided key is used by the SyncManager in scheduling the sync. */ private static boolean isSyncSetting(String key) { diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java index bb4dbc3b5c9..835ba1734da 100644 --- a/services/core/java/com/android/server/display/ColorFade.java +++ b/services/core/java/com/android/server/display/ColorFade.java @@ -74,6 +74,7 @@ final class ColorFade { // Set to true when the animation context has been fully prepared. private boolean mPrepared; + private boolean mCreatedResources; private int mMode; private final DisplayManagerInternal mDisplayManagerInternal; @@ -169,6 +170,7 @@ public boolean prepare(Context context, int mode) { } // Done. + mCreatedResources = true; mPrepared = true; // Dejanking optimization. @@ -313,18 +315,17 @@ private static void setQuad(FloatBuffer vtx, float x, float y, float w, float h) } /** - * Dismisses the color fade animation surface and cleans up. + * Dismisses the color fade animation resources. * - * To prevent stray photons from leaking out after the color fade has been - * turned off, it is a good idea to defer dismissing the animation until the - * color fade has been turned back on fully. + * This function destroys the resources that are created for the color fade + * animation but does not clean up the surface. */ - public void dismiss() { + public void dismissResources() { if (DEBUG) { - Slog.d(TAG, "dismiss"); + Slog.d(TAG, "dismissResources"); } - if (mPrepared) { + if (mCreatedResources) { attachEglContext(); try { destroyScreenshotTexture(); @@ -334,8 +335,28 @@ public void dismiss() { } finally { detachEglContext(); } - destroySurface(); + // This is being called with no active context so shouldn't be + // needed but is safer to not change for now. GLES20.glFlush(); + mCreatedResources = false; + } + } + + /** + * Dismisses the color fade animation surface and cleans up. + * + * To prevent stray photons from leaking out after the color fade has been + * turned off, it is a good idea to defer dismissing the animation until the + * color fade has been turned back on fully. + */ + public void dismiss() { + if (DEBUG) { + Slog.d(TAG, "dismiss"); + } + + if (mPrepared) { + dismissResources(); + destroySurface(); mPrepared = false; } } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 21e431cfa5a..5c3b9bd8bce 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -858,6 +858,7 @@ private void animateScreenStateChange(int target, boolean performScreenOffTransi if (mPendingScreenOff && target != Display.STATE_OFF) { setScreenState(Display.STATE_OFF); mPendingScreenOff = false; + mPowerState.dismissColorFadeResources(); } if (target == Display.STATE_ON) { @@ -931,6 +932,7 @@ private void animateScreenStateChange(int target, boolean performScreenOffTransi // A black surface is already hiding the contents of the screen. setScreenState(Display.STATE_OFF); mPendingScreenOff = false; + mPowerState.dismissColorFadeResources(); } else if (performScreenOffTransition && mPowerState.prepareColorFade(mContext, mColorFadeFadesConfig ? diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 2eabd326f21..98625161a70 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -187,7 +187,7 @@ public boolean prepareColorFade(Context context, int mode) { } /** - * Dismisses the electron beam surface. + * Dismisses the color fade surface. */ public void dismissColorFade() { mColorFade.dismiss(); @@ -195,6 +195,13 @@ public void dismissColorFade() { mColorFadeReady = true; } + /** + * Dismisses the color fade resources. + */ + public void dismissColorFadeResources() { + mColorFade.dismissResources(); + } + /** * Sets the level of the electron beam steering current. * diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 0393be364e2..b2520be76f2 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -844,9 +844,18 @@ public void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int } } + @Override // Binder call + public int isInTabletMode() { + if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE, + "isInTabletMode()")) { + throw new SecurityException("Requires TABLET_MODE permission"); + } + return getSwitchState(-1, InputDevice.SOURCE_ANY, SW_TABLET_MODE); + } + @Override // Binder call public void registerTabletModeChangedListener(ITabletModeChangedListener listener) { - if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE_LISTENER, + if (!checkCallingPermission(android.Manifest.permission.TABLET_MODE, "registerTabletModeChangedListener()")) { throw new SecurityException("Requires TABLET_MODE_LISTENER permission"); } @@ -1567,7 +1576,7 @@ private void notifySwitch(long whenNanos, int switchValues, int switchMask) { switchMask); } - if ((switchMask & SW_TABLET_MODE) != 0) { + if ((switchMask & SW_TABLET_MODE_BIT) != 0) { SomeArgs args = SomeArgs.obtain(); args.argi1 = (int) (whenNanos & 0xFFFFFFFF); args.argi2 = (int) (whenNanos >> 32); diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index ecda36a0d4c..3c50102157f 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -73,7 +73,7 @@ */ public class JobSchedulerService extends com.android.server.SystemService implements StateChangedListener, JobCompletedListener { - static final boolean DEBUG = false; + public static final boolean DEBUG = false; /** The number of concurrent jobs we run at one time. */ private static final int MAX_JOB_CONTEXTS_COUNT = ActivityManager.isLowRamDeviceStatic() ? 1 : 3; @@ -99,7 +99,7 @@ public class JobSchedulerService extends com.android.server.SystemService * Minimum # of connectivity jobs that must be ready in order to force the JMS to schedule * things early. */ - static final int MIN_CONNECTIVITY_COUNT = 2; + static final int MIN_CONNECTIVITY_COUNT = 1; // Run connectivity jobs as soon as ready. /** * Minimum # of jobs (with no particular constraints) for which the JMS will be happy running * some work early. @@ -443,13 +443,16 @@ private boolean isCurrentlyActiveLocked(JobStatus job) { } /** - * A job is rescheduled with exponential back-off if the client requests this from their - * execution logic. - * A caveat is for idle-mode jobs, for which the idle-mode constraint will usurp the - * timeliness of the reschedule. For an idle-mode job, no deadline is given. + * Reschedules the given job based on the job's backoff policy. It doesn't make sense to + * specify an override deadline on a failed job (the failed job will run even though it's not + * ready), so we reschedule it with {@link JobStatus#NO_LATEST_RUNTIME}, but specify that any + * ready job with {@link JobStatus#numFailures} > 0 will be executed. + * * @param failureToReschedule Provided job status that we will reschedule. * @return A newly instantiated JobStatus with the same constraints as the last job except * with adjusted timing constraints. + * + * @see JobHandler#maybeQueueReadyJobsForExecutionLockedH */ private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) { final long elapsedNowMillis = SystemClock.elapsedRealtime(); @@ -479,8 +482,9 @@ private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) { } /** - * Called after a periodic has executed so we can to re-add it. We take the last execution time - * of the job to be the time of completion (i.e. the time at which this function is called). + * Called after a periodic has executed so we can reschedule it. We take the last execution + * time of the job to be the time of completion (i.e. the time at which this function is + * called). * This could be inaccurate b/c the job can run for as long as * {@link com.android.server.job.JobServiceContext#EXECUTING_TIMESLICE_MILLIS}, but will lead * to underscheduling at least, rather than if we had taken the last execution time to be the @@ -491,7 +495,12 @@ private JobStatus getRescheduleJobForFailure(JobStatus failureToReschedule) { private JobStatus getRescheduleJobForPeriodic(JobStatus periodicToReschedule) { final long elapsedNow = SystemClock.elapsedRealtime(); // Compute how much of the period is remaining. - long runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0); + long runEarly = 0L; + + // If this periodic was rescheduled it won't have a deadline. + if (periodicToReschedule.hasDeadlineConstraint()) { + runEarly = Math.max(periodicToReschedule.getLatestRunTimeElapsed() - elapsedNow, 0L); + } long newEarliestRunTimeElapsed = elapsedNow + runEarly; long period = periodicToReschedule.getJob().getIntervalMillis(); long newLatestRuntimeElapsed = newEarliestRunTimeElapsed + period; diff --git a/services/core/java/com/android/server/job/JobServiceContext.java b/services/core/java/com/android/server/job/JobServiceContext.java index bb4e38849a5..551539365d4 100644 --- a/services/core/java/com/android/server/job/JobServiceContext.java +++ b/services/core/java/com/android/server/job/JobServiceContext.java @@ -62,13 +62,13 @@ * */ public class JobServiceContext extends IJobCallback.Stub implements ServiceConnection { - private static final boolean DEBUG = false; + private static final boolean DEBUG = JobSchedulerService.DEBUG; private static final String TAG = "JobServiceContext"; /** Define the maximum # of jobs allowed to run on a service at once. */ private static final int defaultMaxActiveJobsPerService = ActivityManager.isLowRamDeviceStatic() ? 1 : 3; /** Amount of time a job is allowed to execute for before being considered timed-out. */ - private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; + private static final long EXECUTING_TIMESLICE_MILLIS = 10 * 60 * 1000; // 10mins. /** Amount of time the JobScheduler will wait for a response from an app for a message. */ private static final long OP_TIMEOUT_MILLIS = 8 * 1000; @@ -108,7 +108,13 @@ public class JobServiceContext extends IJobCallback.Stub implements ServiceConne int mVerb; private AtomicBoolean mCancelled = new AtomicBoolean(); - /** All the information maintained about the job currently being executed. */ + /** + * All the information maintained about the job currently being executed. + * + * Any reads (dereferences) not done from the handler thread must be synchronized on + * {@link #mLock}. + * Writes can only be done from the handler thread, or {@link #executeRunnableJob(JobStatus)}. + */ private JobStatus mRunningJob; /** Binder to the client service. */ IJobService service; @@ -192,7 +198,8 @@ boolean executeRunnableJob(JobStatus job) { */ JobStatus getRunningJob() { synchronized (mLock) { - return mRunningJob; + return mRunningJob == null ? + null : new JobStatus(mRunningJob); } } @@ -253,15 +260,22 @@ public void acknowledgeStartMessage(int jobId, boolean ongoing) { */ @Override public void onServiceConnected(ComponentName name, IBinder service) { - if (!name.equals(mRunningJob.getServiceComponent())) { + JobStatus runningJob; + synchronized (mLock) { + // This isn't strictly necessary b/c the JobServiceHandler is running on the main + // looper and at this point we can't get any binder callbacks from the client. Better + // safe than sorry. + runningJob = mRunningJob; + } + if (runningJob == null || !name.equals(runningJob.getServiceComponent())) { mCallbackHandler.obtainMessage(MSG_SHUTDOWN_EXECUTION).sendToTarget(); return; } this.service = IJobService.Stub.asInterface(service); final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mRunningJob.getTag()); - mWakeLock.setWorkSource(new WorkSource(mRunningJob.getUid())); + mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, runningJob.getTag()); + mWakeLock.setWorkSource(new WorkSource(runningJob.getUid())); mWakeLock.setReferenceCounted(false); mWakeLock.acquire(); mCallbackHandler.obtainMessage(MSG_SERVICE_BOUND).sendToTarget(); @@ -279,13 +293,15 @@ public void onServiceDisconnected(ComponentName name) { * @return True if the binder calling is coming from the client we expect. */ private boolean verifyCallingUid() { - if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) { - if (DEBUG) { - Slog.d(TAG, "Stale callback received, ignoring."); + synchronized (mLock) { + if (mRunningJob == null || Binder.getCallingUid() != mRunningJob.getUid()) { + if (DEBUG) { + Slog.d(TAG, "Stale callback received, ignoring."); + } + return false; } - return false; + return true; } - return true; } /** diff --git a/services/core/java/com/android/server/job/JobStore.java b/services/core/java/com/android/server/job/JobStore.java index 24d4f15208d..53125c0c2ac 100644 --- a/services/core/java/com/android/server/job/JobStore.java +++ b/services/core/java/com/android/server/job/JobStore.java @@ -24,6 +24,7 @@ import android.os.PersistableBundle; import android.os.SystemClock; import android.os.UserHandle; +import android.text.format.DateUtils; import android.util.AtomicFile; import android.util.ArraySet; import android.util.Pair; @@ -552,9 +553,10 @@ private JobStatus restoreJobFromXml(XmlPullParser parser) throws XmlPullParserEx return null; } - Pair runtimes; + // Tuple of (earliest runtime, latest runtime) in elapsed realtime after disk load. + Pair elapsedRuntimes; try { - runtimes = buildExecutionTimesFromXml(parser); + elapsedRuntimes = buildExecutionTimesFromXml(parser); } catch (NumberFormatException e) { if (DEBUG) { Slog.d(TAG, "Error parsing execution time parameters, skipping."); @@ -562,22 +564,45 @@ private JobStatus restoreJobFromXml(XmlPullParser parser) throws XmlPullParserEx return null; } + final long elapsedNow = SystemClock.elapsedRealtime(); if (XML_TAG_PERIODIC.equals(parser.getName())) { try { String val = parser.getAttributeValue(null, "period"); - jobBuilder.setPeriodic(Long.valueOf(val)); + final long periodMillis = Long.valueOf(val); + jobBuilder.setPeriodic(periodMillis); + // As a sanity check, cap the recreated run time to be no later than 2 periods + // from now. This is the latest the periodic could be pushed out. This could + // happen if the periodic ran early (at the start of its period), and then the + // device rebooted. + if (elapsedRuntimes.second > elapsedNow + 2 * periodMillis) { + final long clampedEarlyRuntimeElapsed = elapsedNow + periodMillis; + final long clampedLateRuntimeElapsed = elapsedNow + 2 * periodMillis; + Slog.w(TAG, + String.format("Periodic job for uid='%d' persisted run-time is" + + " too big [%s, %s]. Clamping to [%s,%s]", + uid, + DateUtils.formatElapsedTime(elapsedRuntimes.first / 1000), + DateUtils.formatElapsedTime(elapsedRuntimes.second / 1000), + DateUtils.formatElapsedTime( + clampedEarlyRuntimeElapsed / 1000), + DateUtils.formatElapsedTime( + clampedLateRuntimeElapsed / 1000)) + ); + elapsedRuntimes = + Pair.create(clampedEarlyRuntimeElapsed, clampedLateRuntimeElapsed); + } } catch (NumberFormatException e) { Slog.d(TAG, "Error reading periodic execution criteria, skipping."); return null; } } else if (XML_TAG_ONEOFF.equals(parser.getName())) { try { - if (runtimes.first != JobStatus.NO_EARLIEST_RUNTIME) { - jobBuilder.setMinimumLatency(runtimes.first - SystemClock.elapsedRealtime()); + if (elapsedRuntimes.first != JobStatus.NO_EARLIEST_RUNTIME) { + jobBuilder.setMinimumLatency(elapsedRuntimes.first - elapsedNow); } - if (runtimes.second != JobStatus.NO_LATEST_RUNTIME) { + if (elapsedRuntimes.second != JobStatus.NO_LATEST_RUNTIME) { jobBuilder.setOverrideDeadline( - runtimes.second - SystemClock.elapsedRealtime()); + elapsedRuntimes.second - elapsedNow); } } catch (NumberFormatException e) { Slog.d(TAG, "Error reading job execution criteria, skipping."); @@ -598,7 +623,8 @@ private JobStatus restoreJobFromXml(XmlPullParser parser) throws XmlPullParserEx do { eventType = parser.next(); } while (eventType == XmlPullParser.TEXT); - if (!(eventType == XmlPullParser.START_TAG && XML_TAG_EXTRAS.equals(parser.getName()))) { + if (!(eventType == XmlPullParser.START_TAG + && XML_TAG_EXTRAS.equals(parser.getName()))) { if (DEBUG) { Slog.d(TAG, "Error reading extras, skipping."); } @@ -609,7 +635,8 @@ private JobStatus restoreJobFromXml(XmlPullParser parser) throws XmlPullParserEx jobBuilder.setExtras(extras); parser.nextTag(); // Consume - return new JobStatus(jobBuilder.build(), uid, runtimes.first, runtimes.second); + return new JobStatus( + jobBuilder.build(), uid, elapsedRuntimes.first, elapsedRuntimes.second); } private JobInfo.Builder buildBuilderFromXml(XmlPullParser parser) throws NumberFormatException { diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java index 02d4f403424..6fc02f6eba1 100644 --- a/services/core/java/com/android/server/job/controllers/AppIdleController.java +++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java @@ -67,7 +67,7 @@ public void maybeStartTrackingJob(JobStatus jobStatus) { mTrackedTasks.add(jobStatus); String packageName = jobStatus.job.getService().getPackageName(); final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName, - jobStatus.getUserId()); + jobStatus.uId, jobStatus.getUserId()); if (DEBUG) { Slog.d(LOG_TAG, "Start tracking, setting idle state of " + packageName + " to " + appIdle); @@ -108,7 +108,7 @@ void setAppIdleParoleOn(boolean isAppIdleParoleOn) { for (JobStatus task : mTrackedTasks) { String packageName = task.job.getService().getPackageName(); final boolean appIdle = !mAppIdleParoleOn && mUsageStatsInternal.isAppIdle(packageName, - task.getUserId()); + task.uId, task.getUserId()); if (DEBUG) { Slog.d(LOG_TAG, "Setting idle state of " + packageName + " to " + appIdle); } diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java index 69c63f399a7..c02611f4fdc 100644 --- a/services/core/java/com/android/server/job/controllers/JobStatus.java +++ b/services/core/java/com/android/server/job/controllers/JobStatus.java @@ -82,6 +82,13 @@ private JobStatus(JobInfo job, int uId, int numFailures) { this.numFailures = numFailures; } + /** Copy constructor. */ + public JobStatus(JobStatus jobStatus) { + this(jobStatus.getJob(), jobStatus.getUid(), jobStatus.getNumFailures()); + this.earliestRunTimeElapsedMillis = jobStatus.getEarliestRunTime(); + this.latestRunTimeElapsedMillis = jobStatus.getLatestRunTimeElapsed(); + } + /** Create a newly scheduled job. */ public JobStatus(JobInfo job, int uId) { this(job, uId, 0); diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java index cda7c326c37..21c30c7a012 100644 --- a/services/core/java/com/android/server/job/controllers/StateController.java +++ b/services/core/java/com/android/server/job/controllers/StateController.java @@ -18,6 +18,7 @@ import android.content.Context; +import com.android.server.job.JobSchedulerService; import com.android.server.job.StateChangedListener; import java.io.PrintWriter; @@ -28,7 +29,7 @@ * are ready to run, or whether they must be stopped. */ public abstract class StateController { - protected static final boolean DEBUG = false; + protected static final boolean DEBUG = JobSchedulerService.DEBUG; protected Context mContext; protected StateChangedListener mStateChangedListener; protected boolean mDeviceIdleMode; diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 522ec48fa7b..6ae3c8ef4d1 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -39,17 +39,17 @@ import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE; import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY; -import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW; +import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT; import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY; import static android.net.NetworkPolicyManager.POLICY_ALLOW_BACKGROUND_BATTERY_SAVE; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; +import static android.net.NetworkPolicyManager.RULE_REJECT_ALL; import static android.net.NetworkPolicyManager.RULE_REJECT_METERED; +import static android.net.NetworkPolicyManager.RULE_UNKNOWN; import static android.net.NetworkPolicyManager.computeLastCycleBoundary; -import static android.net.NetworkPolicyManager.dumpPolicy; -import static android.net.NetworkPolicyManager.dumpRules; import static android.net.NetworkTemplate.MATCH_MOBILE_3G_LOWER; import static android.net.NetworkTemplate.MATCH_MOBILE_4G; import static android.net.NetworkTemplate.MATCH_MOBILE_ALL; @@ -108,6 +108,7 @@ import android.net.NetworkIdentity; import android.net.NetworkInfo; import android.net.NetworkPolicy; +import android.net.NetworkPolicyManager; import android.net.NetworkQuotaInfo; import android.net.NetworkState; import android.net.NetworkTemplate; @@ -138,6 +139,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.AtomicFile; +import android.util.DebugUtils; import android.util.Log; import android.util.NtpTrustedTime; import android.util.Pair; @@ -147,8 +149,6 @@ import android.util.TrustedTime; import android.util.Xml; -import com.android.server.DeviceIdleController; -import com.android.server.EventLogTags; import libcore.io.IoUtils; import com.android.internal.R; @@ -156,6 +156,8 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.DeviceIdleController; +import com.android.server.EventLogTags; import com.android.server.LocalServices; import com.google.android.collect.Lists; @@ -279,6 +281,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final SparseIntArray mUidPolicy = new SparseIntArray(); /** Currently derived rules for each UID. */ final SparseIntArray mUidRules = new SparseIntArray(); + + final SparseIntArray mUidFirewallStandbyRules = new SparseIntArray(); + final SparseIntArray mUidFirewallDozableRules = new SparseIntArray(); + /** Set of states for the child firewall chains. True if the chain is active. */ final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray(); @@ -446,14 +452,8 @@ public void onLowPowerModeChanged(boolean enabled) { // read policy from disk readPolicyLocked(); - if (mRestrictBackground || mRestrictPower || mDeviceIdleMode) { - updateRulesForGlobalChangeLocked(false); - updateNotificationsLocked(); - } else { - // If we are not in any special mode, we just need to make sure the current - // app idle state is updated. - updateRulesForAppIdleLocked(); - } + updateRulesForGlobalChangeLocked(false); + updateNotificationsLocked(); } updateScreenOn(); @@ -1800,7 +1800,9 @@ public void setDeviceIdleMode(boolean enabled) { if (mDeviceIdleMode != enabled) { mDeviceIdleMode = enabled; if (mSystemReady) { - updateRulesForDeviceIdleLocked(); + // Device idle change means we need to rebuild rules for all + // known apps, so do a global refresh. + updateRulesForGlobalChangeLocked(false); } if (enabled) { EventLogTags.writeDeviceIdleOnPhase("net"); @@ -1938,7 +1940,7 @@ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { fout.print("UID="); fout.print(uid); fout.print(" policy="); - dumpPolicy(fout, policy); + fout.print(DebugUtils.flagsToString(NetworkPolicyManager.class, "POLICY_", policy)); fout.println(); } fout.decreaseIndent(); @@ -1983,18 +1985,14 @@ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { fout.print("UID="); fout.print(uid); - int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); + final int state = mUidState.get(uid, ActivityManager.PROCESS_STATE_CACHED_EMPTY); fout.print(" state="); fout.print(state); fout.print(state <= ActivityManager.PROCESS_STATE_TOP ? " (fg)" : " (bg)"); - fout.print(" rules="); - final int rulesIndex = mUidRules.indexOfKey(uid); - if (rulesIndex < 0) { - fout.print("UNKNOWN"); - } else { - dumpRules(fout, mUidRules.valueAt(rulesIndex)); - } + final int rule = mUidRules.get(uid, RULE_UNKNOWN); + fout.print(" rule="); + fout.print(DebugUtils.valueToString(NetworkPolicyManager.class, "RULE_", rule)); fout.println(); } @@ -2029,7 +2027,7 @@ void updateUidStateLocked(int uid, int uidState) { updateRulesForUidStateChangeLocked(uid, oldUidState, uidState); if (mDeviceIdleMode && isProcStateAllowedWhileIdle(oldUidState) != isProcStateAllowedWhileIdle(uidState)) { - updateRulesForDeviceIdleLocked(); + updateRuleForDeviceIdleLocked(uid); } } } @@ -2043,7 +2041,7 @@ void removeUidStateLocked(int uid) { updateRulesForUidStateChangeLocked(uid, oldUidState, ActivityManager.PROCESS_STATE_CACHED_EMPTY); if (mDeviceIdleMode) { - updateRulesForDeviceIdleLocked(); + updateRuleForDeviceIdleLocked(uid); } } } @@ -2090,7 +2088,8 @@ void updateRulesForDeviceIdleLocked() { if (mDeviceIdleMode) { // sync the whitelists before enable dozable chain. We don't care about the rules if // we are disabling the chain. - SparseIntArray uidRules = new SparseIntArray(); + final SparseIntArray uidRules = mUidFirewallDozableRules; + uidRules.clear(); final List users = mUserManager.getUsers(); for (int ui = users.size() - 1; ui >= 0; ui--) { UserInfo user = users.get(ui); @@ -2114,6 +2113,7 @@ void updateRulesForDeviceIdleLocked() { } setUidFirewallRules(FIREWALL_CHAIN_DOZABLE, uidRules); } + enableFirewallChainLocked(FIREWALL_CHAIN_DOZABLE, mDeviceIdleMode); } @@ -2127,11 +2127,15 @@ void updateRuleForDeviceIdleLocked(int uid) { setUidFirewallRule(FIREWALL_CHAIN_DOZABLE, uid, FIREWALL_RULE_DEFAULT); } } + + updateRulesForUidLocked(uid); } void updateRulesForAppIdleLocked() { + final SparseIntArray uidRules = mUidFirewallStandbyRules; + uidRules.clear(); + // Fully update the app idle firewall chain. - SparseIntArray uidRules = new SparseIntArray(); final List users = mUserManager.getUsers(); for (int ui = users.size() - 1; ui >= 0; ui--) { UserInfo user = users.get(ui); @@ -2142,6 +2146,7 @@ void updateRulesForAppIdleLocked() { } } } + setUidFirewallRules(FIREWALL_CHAIN_STANDBY, uidRules); } @@ -2154,11 +2159,14 @@ void updateRuleForAppIdleLocked(int uid) { } else { setUidFirewallRule(FIREWALL_CHAIN_STANDBY, uid, FIREWALL_RULE_DEFAULT); } + + updateRulesForUidLocked(uid); } void updateRulesForAppIdleParoleLocked() { boolean enableChain = !mUsageStats.isAppIdleParoleOn(); enableFirewallChainLocked(FIREWALL_CHAIN_STANDBY, enableChain); + updateRulesForUidsLocked(mUidFirewallStandbyRules); } /** @@ -2221,13 +2229,19 @@ private boolean isUidIdle(int uid) { final int userId = UserHandle.getUserId(uid); for (String packageName : packages) { - if (!mUsageStats.isAppIdle(packageName, userId)) { + if (!mUsageStats.isAppIdle(packageName, uid, userId)) { return false; } } return true; } + void updateRulesForUidsLocked(SparseIntArray uids) { + for (int i = 0; i < uids.size(); i++) { + updateRulesForUidLocked(uids.keyAt(i)); + } + } + /** * Applies network rules to bandwidth and firewall controllers based on uid policy. * @param uid The uid for which to apply the latest policy @@ -2249,8 +2263,7 @@ void updateRulesForUidLocked(int uid) { final int uidPolicy = mUidPolicy.get(uid, POLICY_NONE); final boolean uidForeground = isUidForegroundLocked(uid); - // derive active rules based on policy and active state - + // Derive active rules based on policy and active state int appId = UserHandle.getAppId(uid); int uidRules = RULE_ALLOW_ALL; if (!uidForeground && (uidPolicy & POLICY_REJECT_METERED_BACKGROUND) != 0) { @@ -2273,20 +2286,27 @@ void updateRulesForUidLocked(int uid) { } } - final int oldRules = mUidRules.get(uid); + // Check dozable state, which is whitelist + if (mFirewallChainStates.get(FIREWALL_CHAIN_DOZABLE) + && mUidFirewallDozableRules.get(uid, FIREWALL_RULE_DEFAULT) != FIREWALL_RULE_ALLOW) { + uidRules = RULE_REJECT_ALL; + } + + // Check standby state, which is blacklist + if (mFirewallChainStates.get(FIREWALL_CHAIN_STANDBY) + && mUidFirewallStandbyRules.get(uid, FIREWALL_RULE_DEFAULT) == FIREWALL_RULE_DENY) { + uidRules = RULE_REJECT_ALL; + } + final int oldRules = mUidRules.get(uid); if (uidRules == RULE_ALLOW_ALL) { mUidRules.delete(uid); } else { mUidRules.put(uid, uidRules); } - // Update bandwidth rules if necessary - final boolean oldRejectMetered = (oldRules & RULE_REJECT_METERED) != 0; - final boolean rejectMetered = (uidRules & RULE_REJECT_METERED) != 0; - if (oldRejectMetered != rejectMetered) { - setUidNetworkRules(uid, rejectMetered); - } + final boolean rejectMetered = (uidRules == RULE_REJECT_METERED); + setUidNetworkRules(uid, rejectMetered); // dispatch changed rule to existing listeners if (oldRules != uidRules) { @@ -2473,6 +2493,12 @@ private void setUidFirewallRules(int chain, SparseIntArray uidRules) { * Add or remove a uid to the firewall blacklist for all network ifaces. */ private void setUidFirewallRule(int chain, int uid, int rule) { + if (chain == FIREWALL_CHAIN_DOZABLE) { + mUidFirewallDozableRules.put(uid, rule); + } else if (chain == FIREWALL_CHAIN_STANDBY) { + mUidFirewallStandbyRules.put(uid, rule); + } + try { mNetworkManager.setFirewallUidRule(chain, uid, rule); } catch (IllegalStateException e) { @@ -2486,9 +2512,9 @@ private void setUidFirewallRule(int chain, int uid, int rule) { * Add or remove a uid to the firewall blacklist for all network ifaces. */ private void enableFirewallChainLocked(int chain, boolean enable) { - if (mFirewallChainStates.indexOfKey(chain) >= 0 && - mFirewallChainStates.get(chain) == enable) { - // All is the same, nothing to do. + if (mFirewallChainStates.get(chain, false) == enable) { + // All is the same, nothing to do. This relies on the fact that netd has child + // chains default detached. return; } mFirewallChainStates.put(chain, enable); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6aaf8233887..b6f7e1baaf4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1738,13 +1738,16 @@ private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, return; } - PermissionsState permissionsState = sb.getPermissionsState(); - - for (String permission : pkg.requestedPermissions) { - BasePermission bp = mSettings.mPermissions.get(permission); - if (bp != null && bp.isRuntime() && (grantedPermissions == null - || ArrayUtils.contains(grantedPermissions, permission))) { - permissionsState.grantRuntimePermission(bp, userId); + synchronized (mPackages) { + for (String permission : pkg.requestedPermissions) { + BasePermission bp = mSettings.mPermissions.get(permission); + if (bp != null && (bp.isRuntime() || bp.isDevelopment()) + && (grantedPermissions == null + || ArrayUtils.contains(grantedPermissions, permission)) + && (getPermissionFlags(permission, pkg.packageName, userId) + & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) == 0) { + grantRuntimePermission(pkg.packageName, permission, userId); + } } } } @@ -2314,7 +2317,7 @@ public PackageManagerService(Context context, Installer installer, + mSdkVersion + "; regranting permissions for internal storage"); updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL; } - updatePermissionsLPw(null, null, updateFlags); + updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags); ver.sdkVersion = mSdkVersion; // Remove any stale app permissions (declared permission that now are undeclared @@ -2802,26 +2805,38 @@ public String[] canonicalToCurrentPackageNames(String[] names) { @Override public int getPackageUid(String packageName, int userId) { + return getPackageUidEtc(packageName, 0, userId); + } + + @Override + public int getPackageUidEtc(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) return -1; enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package uid"); // reader synchronized (mPackages) { - PackageParser.Package p = mPackages.get(packageName); - if(p != null) { + final PackageParser.Package p = mPackages.get(packageName); + if (p != null) { return UserHandle.getUid(userId, p.applicationInfo.uid); } - PackageSetting ps = mSettings.mPackages.get(packageName); - if((ps == null) || (ps.pkg == null) || (ps.pkg.applicationInfo == null)) { - return -1; + if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps != null) { + return UserHandle.getUid(userId, ps.appId); + } } - p = ps.pkg; - return p != null ? UserHandle.getUid(userId, p.applicationInfo.uid) : -1; } + + return -1; } @Override - public int[] getPackageGids(String packageName, int userId) throws RemoteException { + public int[] getPackageGids(String packageName, int userId) { + return getPackageGidsEtc(packageName, 0, userId); + } + + @Override + public int[] getPackageGidsEtc(String packageName, int flags, int userId) { if (!sUserManager.exists(userId)) { return null; } @@ -2831,14 +2846,17 @@ public int[] getPackageGids(String packageName, int userId) throws RemoteExcepti // reader synchronized (mPackages) { - PackageParser.Package p = mPackages.get(packageName); - if (DEBUG_PACKAGE_INFO) { - Log.v(TAG, "getPackageGids" + packageName + ": " + p); - } + final PackageParser.Package p = mPackages.get(packageName); if (p != null) { PackageSetting ps = (PackageSetting) p.mExtras; return ps.getPermissionsState().computeGids(userId); } + if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0) { + final PackageSetting ps = mSettings.mPackages.get(packageName); + if (ps != null) { + return ps.getPermissionsState().computeGids(userId); + } + } } return null; @@ -3589,7 +3607,8 @@ public void run() { killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED); } }); - } break; + } + break; } mOnPermissionChangeListeners.onPermissionsChanged(uid); @@ -4842,18 +4861,13 @@ private List filterCandidatesWithDomainPreferredActivitiesLPr(Inten // First try to add the "always" resolution(s) for the current user, if any if (alwaysList.size() > 0) { result.addAll(alwaysList); - // if there is an "always" for the parent user, add it. - } else if (xpDomainInfo != null && xpDomainInfo.bestDomainVerificationStatus - == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) { - result.add(xpDomainInfo.resolveInfo); } else { // Add all undefined apps as we want them to appear in the disambiguation dialog. result.addAll(undefinedList); + // Maybe add one for the other profile. if (xpDomainInfo != null && ( xpDomainInfo.bestDomainVerificationStatus - == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED - || xpDomainInfo.bestDomainVerificationStatus - == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK)) { + != INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER)) { result.add(xpDomainInfo.resolveInfo); } includeBrowser = true; @@ -7629,8 +7643,8 @@ public void derivePackageAbi(PackageParser.Package pkg, File scanFile, // We would never need to extract libs for forward-locked and external packages, // since the container service will do it for us. We shouldn't attempt to // extract libs from system app when it was not updated. - if (pkg.isForwardLocked() || isExternal(pkg) || - (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) ) { + if (pkg.isForwardLocked() || pkg.applicationInfo.isExternalAsec() || + (isSystemApp(pkg) && !pkg.isUpdatedSystemApp())) { extractLibs = false; } @@ -7907,7 +7921,7 @@ private void setNativeLibraryPaths(PackageParser.Package pkg) { final String codePath = pkg.codePath; final File codeFile = new File(codePath); final boolean bundledApp = info.isSystemApp() && !info.isUpdatedSystemApp(); - final boolean asecApp = info.isForwardLocked() || isExternal(info); + final boolean asecApp = info.isForwardLocked() || info.isExternalAsec(); info.nativeLibraryRootDir = null; info.nativeLibraryRootRequiresIsa = false; @@ -8305,8 +8319,14 @@ private static boolean hasPermission(PackageParser.Package pkgInfo, String perm) static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1; static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2; + private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo, + int flags) { + final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null; + updatePermissionsLPw(changingPkg, pkgInfo, volumeUuid, flags); + } + private void updatePermissionsLPw(String changingPkg, - PackageParser.Package pkgInfo, int flags) { + PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) { // Make sure there are no dangling permission trees. Iterator it = mSettings.mPermissionTrees.values().iterator(); while (it.hasNext()) { @@ -8375,14 +8395,21 @@ private void updatePermissionsLPw(String changingPkg, if ((flags&UPDATE_PERMISSIONS_ALL) != 0) { for (PackageParser.Package pkg : mPackages.values()) { if (pkg != pkgInfo) { - grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0, - changingPkg); + // Only replace for packages on requested volume + final String volumeUuid = getVolumeUuidForPackage(pkg); + final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0) + && Objects.equals(replaceVolumeUuid, volumeUuid); + grantPermissionsLPw(pkg, replace, changingPkg); } } } if (pkgInfo != null) { - grantPermissionsLPw(pkgInfo, (flags&UPDATE_PERMISSIONS_REPLACE_PKG) != 0, changingPkg); + // Only replace for packages on requested volume + final String volumeUuid = getVolumeUuidForPackage(pkgInfo); + final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0) + && Objects.equals(replaceVolumeUuid, volumeUuid); + grantPermissionsLPw(pkgInfo, replace, changingPkg); } } @@ -8409,6 +8436,7 @@ private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace, final int[] currentUserIds = UserManagerService.getInstance().getUserIds(); + boolean runtimePermissionsRevoked = false; int[] changedRuntimePermissionUserIds = EMPTY_INT_ARRAY; boolean changedInstallPermission = false; @@ -8418,6 +8446,17 @@ private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace, if (!ps.isSharedUser()) { origPermissions = new PermissionsState(permissionsState); permissionsState.reset(); + } else { + // We need to know only about runtime permission changes since the + // calling code always writes the install permissions state but + // the runtime ones are written only if changed. The only cases of + // changed runtime permissions here are promotion of an install to + // runtime and revocation of a runtime from a shared user. + changedRuntimePermissionUserIds = revokeUnusedSharedUserPermissionsLPw( + ps.sharedUser, UserManagerService.getInstance().getUserIds()); + if (!ArrayUtils.isEmpty(changedRuntimePermissionUserIds)) { + runtimePermissionsRevoked = true; + } } } @@ -8633,9 +8672,11 @@ && isSystemApp(ps) ps.installPermissionsFixed = true; } - // Persist the runtime permissions state for users with changes. + // Persist the runtime permissions state for users with changes. If permissions + // were revoked because no app in the shared user declares them we have to + // write synchronously to avoid losing runtime permissions state. for (int userId : changedRuntimePermissionUserIds) { - mSettings.writeRuntimePermissionsForUserLPr(userId, false); + mSettings.writeRuntimePermissionsForUserLPr(userId, runtimePermissionsRevoked); } } @@ -12133,6 +12174,66 @@ private void replaceSystemPackageLI(PackageParser.Package deletedPackage, } } + private int[] revokeUnusedSharedUserPermissionsLPw(SharedUserSetting su, int[] allUserIds) { + // Collect all used permissions in the UID + ArraySet usedPermissions = new ArraySet<>(); + final int packageCount = su.packages.size(); + for (int i = 0; i < packageCount; i++) { + PackageSetting ps = su.packages.valueAt(i); + if (ps.pkg == null) { + continue; + } + final int requestedPermCount = ps.pkg.requestedPermissions.size(); + for (int j = 0; j < requestedPermCount; j++) { + String permission = ps.pkg.requestedPermissions.get(j); + BasePermission bp = mSettings.mPermissions.get(permission); + if (bp != null) { + usedPermissions.add(permission); + } + } + } + + PermissionsState permissionsState = su.getPermissionsState(); + // Prune install permissions + List installPermStates = permissionsState.getInstallPermissionStates(); + final int installPermCount = installPermStates.size(); + for (int i = installPermCount - 1; i >= 0; i--) { + PermissionState permissionState = installPermStates.get(i); + if (!usedPermissions.contains(permissionState.getName())) { + BasePermission bp = mSettings.mPermissions.get(permissionState.getName()); + if (bp != null) { + permissionsState.revokeInstallPermission(bp); + permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, + PackageManager.MASK_PERMISSION_FLAGS, 0); + } + } + } + + int[] runtimePermissionChangedUserIds = EmptyArray.INT; + + // Prune runtime permissions + for (int userId : allUserIds) { + List runtimePermStates = permissionsState + .getRuntimePermissionStates(userId); + final int runtimePermCount = runtimePermStates.size(); + for (int i = runtimePermCount - 1; i >= 0; i--) { + PermissionState permissionState = runtimePermStates.get(i); + if (!usedPermissions.contains(permissionState.getName())) { + BasePermission bp = mSettings.mPermissions.get(permissionState.getName()); + if (bp != null) { + permissionsState.revokeRuntimePermission(bp, userId); + permissionsState.updatePermissionFlags(bp, userId, + PackageManager.MASK_PERMISSION_FLAGS, 0); + runtimePermissionChangedUserIds = ArrayUtils.appendInt( + runtimePermissionChangedUserIds, userId); + } + } + } + } + + return runtimePermissionChangedUserIds; + } + private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName, String volumeUuid, int[] allUsers, boolean[] perUserInstalled, PackageInstalledInfo res, UserHandle user) { @@ -12630,6 +12731,18 @@ private int packageFlagsToInstallFlags(PackageSetting ps) { return installFlags; } + private String getVolumeUuidForPackage(PackageParser.Package pkg) { + if (isExternal(pkg)) { + if (TextUtils.isEmpty(pkg.volumeUuid)) { + return StorageManager.UUID_PRIMARY_PHYSICAL; + } else { + return pkg.volumeUuid; + } + } else { + return StorageManager.UUID_PRIVATE_INTERNAL; + } + } + private VersionInfo getSettingsVersionForPackage(PackageParser.Package pkg) { if (isExternal(pkg)) { if (TextUtils.isEmpty(pkg.volumeUuid)) { @@ -13652,7 +13765,7 @@ private boolean getPackageSizeInfoLI(String packageName, int userHandle, if (ps != null) { libDirRoot = ps.legacyNativeLibraryPathString; } - if (p != null && (isExternal(p) || p.isForwardLocked())) { + if (p != null && (p.isForwardLocked() || p.applicationInfo.isExternalAsec())) { final long token = Binder.clearCallingIdentity(); try { String secureContainerId = cidFromCodePath(p.applicationInfo.getBaseCodePath()); @@ -15513,7 +15626,7 @@ private void updateExternalMediaStatusInner(boolean isMounted, boolean reportSta if (isMounted) { if (DEBUG_SD_INSTALL) Log.i(TAG, "Loading packages"); - loadMediaPackages(processCids, uidArr); + loadMediaPackages(processCids, uidArr, externalStorage); startCleaningPackages(); mInstallerService.onSecureContainersAvailable(); } else { @@ -15568,7 +15681,8 @@ private void sendResourcesChangedBroadcast(boolean mediaStatus, boolean replacin * the cid is added to list of removeCids. We currently don't delete stale * containers. */ - private void loadMediaPackages(ArrayMap processCids, int[] uidArr) { + private void loadMediaPackages(ArrayMap processCids, int[] uidArr, + boolean externalStorage) { ArrayList pkgList = new ArrayList(); Set keys = processCids.keySet(); @@ -15640,7 +15754,10 @@ private void loadMediaPackages(ArrayMap processCids, in // cases get permissions that the user didn't initially explicitly // allow... it would be nice to have some better way to handle // this situation. - final VersionInfo ver = mSettings.getExternalVersion(); + final VersionInfo ver = externalStorage ? mSettings.getExternalVersion() + : mSettings.getInternalVersion(); + final String volumeUuid = externalStorage ? StorageManager.UUID_PRIMARY_PHYSICAL + : StorageManager.UUID_PRIVATE_INTERNAL; int updateFlags = UPDATE_PERMISSIONS_ALL; if (ver.sdkVersion != mSdkVersion) { @@ -15648,7 +15765,7 @@ private void loadMediaPackages(ArrayMap processCids, in + mSdkVersion + "; regranting permissions for external"); updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL; } - updatePermissionsLPw(null, null, updateFlags); + updatePermissionsLPw(null, null, volumeUuid, updateFlags); // Yay, everything is now upgraded ver.forceCurrent(); @@ -15781,7 +15898,7 @@ private void loadPrivatePackagesInner(VolumeInfo vol) { + mSdkVersion + "; regranting permissions for " + vol.fsUuid); updateFlags |= UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL; } - updatePermissionsLPw(null, null, updateFlags); + updatePermissionsLPw(null, null, vol.fsUuid, updateFlags); // Yay, everything is now upgraded ver.forceCurrent(); @@ -15955,13 +16072,18 @@ public int movePackage(final String packageName, final String volumeUuid) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MOVE_PACKAGE, null); final int moveId = mNextMoveId.getAndIncrement(); - try { - movePackageInternal(packageName, volumeUuid, moveId); - } catch (PackageManagerException e) { - Slog.w(TAG, "Failed to move " + packageName, e); - mMoveCallbacks.notifyStatusChanged(moveId, - PackageManager.MOVE_FAILED_INTERNAL_ERROR); - } + mHandler.post(new Runnable() { + @Override + public void run() { + try { + movePackageInternal(packageName, volumeUuid, moveId); + } catch (PackageManagerException e) { + Slog.w(TAG, "Failed to move " + packageName, e); + mMoveCallbacks.notifyStatusChanged(moveId, + PackageManager.MOVE_FAILED_INTERNAL_ERROR); + } + } + }); return moveId; } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index bbdfe31828d..78328f5a73e 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -239,6 +239,7 @@ public void copyFrom(PackageSettingBase base) { keySetData = base.keySetData; verificationInfo = base.verificationInfo; installerPackageName = base.installerPackageName; + volumeUuid = base.volumeUuid; } private PackageUserState modifyUserState(int userId) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index c041178986b..7dfb6c3e742 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -517,7 +517,18 @@ void pruneSharedUsersLPw() { ArrayList removeStage = new ArrayList(); for (Map.Entry entry : mSharedUsers.entrySet()) { final SharedUserSetting sus = entry.getValue(); - if (sus == null || sus.packages.size() == 0) { + if (sus == null) { + removeStage.add(entry.getKey()); + continue; + } + // remove packages that are no longer installed + for (Iterator iter = sus.packages.iterator(); iter.hasNext();) { + PackageSetting ps = iter.next(); + if (mPackages.get(ps.name) == null) { + iter.remove(); + } + } + if (sus.packages.size() == 0) { removeStage.add(entry.getKey()); } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 9bf2f634caf..3c5d53e2fb2 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3532,7 +3532,7 @@ public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int p // Display task switcher for ALT-TAB. if (down && repeatCount == 0 && keyCode == KeyEvent.KEYCODE_TAB) { - if (mRecentAppsHeldModifiers == 0 && !keyguardOn) { + if (mRecentAppsHeldModifiers == 0 && !keyguardOn && isUserSetupComplete()) { final int shiftlessModifiers = event.getModifiers() & ~KeyEvent.META_SHIFT_MASK; if (KeyEvent.metaStateHasModifiers(shiftlessModifiers, KeyEvent.META_ALT_ON)) { mRecentAppsHeldModifiers = shiftlessModifiers; diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 726d29da533..42042b94bc9 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1064,6 +1064,8 @@ void computeShownFrameLocked() { mAnimator.getScreenRotationAnimationLocked(displayId); final boolean screenAnimation = screenRotationAnimation != null && screenRotationAnimation.isAnimating(); + + mHasClipRect = false; if (selfTransformation || attachedTransformation != null || appTransformation != null || screenAnimation) { // cache often used attributes locally @@ -1139,7 +1141,6 @@ void computeShownFrameLocked() { // transforming since it is more important to have that // animation be smooth. mShownAlpha = mAlpha; - mHasClipRect = false; if (!mService.mLimitedAlphaCompositing || (!PixelFormat.formatHasAlpha(mWin.mAttrs.format) || (mWin.isIdentityMatrix(mDsDx, mDtDx, mDsDy, mDtDy) diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java index 3ecfd550946..c6d5a7e268b 100644 --- a/services/midi/java/com/android/server/midi/MidiService.java +++ b/services/midi/java/com/android/server/midi/MidiService.java @@ -27,6 +27,7 @@ import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.XmlResourceParser; +import android.media.midi.IBluetoothMidiService; import android.media.midi.IMidiDeviceListener; import android.media.midi.IMidiDeviceOpenCallback; import android.media.midi.IMidiDeviceServer; @@ -394,7 +395,20 @@ public void addDeviceConnection(DeviceConnection connection) { mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { - IMidiDeviceServer server = IMidiDeviceServer.Stub.asInterface(service); + IMidiDeviceServer server = null; + if (mBluetoothDevice != null) { + IBluetoothMidiService mBluetoothMidiService = IBluetoothMidiService.Stub.asInterface(service); + try { + // We need to explicitly add the device in a separate method + // because onBind() is only called once. + IBinder deviceBinder = mBluetoothMidiService.addBluetoothDevice(mBluetoothDevice); + server = IMidiDeviceServer.Stub.asInterface(deviceBinder); + } catch(RemoteException e) { + Log.e(TAG, "Could not call addBluetoothDevice()", e); + } + } else { + server = IMidiDeviceServer.Stub.asInterface(service); + } setDeviceServer(server); } @@ -411,7 +425,6 @@ public void onServiceDisconnected(ComponentName name) { intent.setComponent(new ComponentName( MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, MidiManager.BLUETOOTH_MIDI_SERVICE_CLASS)); - intent.putExtra("device", mBluetoothDevice); } else { intent = new Intent(MidiDeviceService.SERVICE_INTERFACE); intent.setComponent( @@ -575,6 +588,8 @@ public void registerListener(IBinder token, IMidiDeviceListener listener) { Client client = getClient(token); if (client == null) return; client.addListener(listener); + // Let listener know whether any ports are already busy. + updateStickyDeviceStatus(client.mUid, listener); } @Override @@ -584,6 +599,25 @@ public void unregisterListener(IBinder token, IMidiDeviceListener listener) { client.removeListener(listener); } + // Inform listener of the status of all known devices. + private void updateStickyDeviceStatus(int uid, IMidiDeviceListener listener) { + synchronized (mDevicesByInfo) { + for (Device device : mDevicesByInfo.values()) { + // ignore private devices that our client cannot access + if (device.isUidAllowed(uid)) { + try { + MidiDeviceStatus status = device.getDeviceStatus(); + if (status != null) { + listener.onDeviceStatusChanged(status); + } + } catch (RemoteException e) { + Log.e(TAG, "remote exception", e); + } + } + } + } + } + private static final MidiDeviceInfo[] EMPTY_DEVICE_INFO_ARRAY = new MidiDeviceInfo[0]; public MidiDeviceInfo[] getDevices() { diff --git a/services/net/java/android/net/dhcp/DhcpAckPacket.java b/services/net/java/android/net/dhcp/DhcpAckPacket.java index 334f708b88a..df44b11fffa 100644 --- a/services/net/java/android/net/dhcp/DhcpAckPacket.java +++ b/services/net/java/android/net/dhcp/DhcpAckPacket.java @@ -46,7 +46,7 @@ public String toString() { return s + " ACK: your new IP " + mYourIp + ", netmask " + mSubnetMask + - ", gateway " + mGateway + dnsServers + + ", gateways " + mGateways + dnsServers + ", lease time " + mLeaseTime; } @@ -79,7 +79,7 @@ void finishPacket(ByteBuffer buffer) { } addTlv(buffer, DHCP_SUBNET_MASK, mSubnetMask); - addTlv(buffer, DHCP_ROUTER, mGateway); + addTlv(buffer, DHCP_ROUTER, mGateways); addTlv(buffer, DHCP_DOMAIN_NAME, mDomainName); addTlv(buffer, DHCP_BROADCAST_ADDRESS, mBroadcastAddress); addTlv(buffer, DHCP_DNS_SERVER, mDnsServers); diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java index e0d2ac12be2..c9efc69b597 100644 --- a/services/net/java/android/net/dhcp/DhcpClient.java +++ b/services/net/java/android/net/dhcp/DhcpClient.java @@ -299,6 +299,7 @@ private boolean initSockets() { Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1); Os.setsockoptIfreq(mUdpSock, SOL_SOCKET, SO_BINDTODEVICE, mIfaceName); Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1); + Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_RCVBUF, 0); Os.bind(mUdpSock, Inet4Address.ANY, DhcpPacket.DHCP_CLIENT); NetworkUtils.protectFromVpn(mUdpSock); } catch(SocketException|ErrnoException e) { @@ -308,6 +309,16 @@ private boolean initSockets() { return true; } + private boolean connectUdpSock(Inet4Address to) { + try { + Os.connect(mUdpSock, to, DhcpPacket.DHCP_SERVER); + return true; + } catch (SocketException|ErrnoException e) { + Log.e(TAG, "Error connecting UDP socket", e); + return false; + } + } + private static void closeQuietly(FileDescriptor fd) { try { IoBridge.closeAndSignalBlockedThreads(fd); @@ -325,7 +336,7 @@ private boolean setIpAddress(LinkAddress address) { try { mNMService.setInterfaceConfig(mIfaceName, ifcg); } catch (RemoteException|IllegalStateException e) { - Log.e(TAG, "Error configuring IP address : " + e); + Log.e(TAG, "Error configuring IP address " + address + ": ", e); return false; } return true; @@ -345,21 +356,22 @@ public void halt() { public void run() { maybeLog("Receive thread started"); while (!stopped) { + int length = 0; // Or compiler can't tell it's initialized if a parse error occurs. try { - int length = Os.read(mPacketSock, mPacket, 0, mPacket.length); + length = Os.read(mPacketSock, mPacket, 0, mPacket.length); DhcpPacket packet = null; packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2); - if (packet != null) { - maybeLog("Received packet: " + packet); - sendMessage(CMD_RECEIVED_PACKET, packet); - } else if (PACKET_DBG) { - Log.d(TAG, - "Can't parse packet" + HexDump.dumpHexString(mPacket, 0, length)); - } + maybeLog("Received packet: " + packet); + sendMessage(CMD_RECEIVED_PACKET, packet); } catch (IOException|ErrnoException e) { if (!stopped) { Log.e(TAG, "Read error", e); } + } catch (DhcpPacket.ParseException e) { + Log.e(TAG, "Can't parse packet: " + e.getMessage()); + if (PACKET_DBG) { + Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length)); + } } } maybeLog("Receive thread stopped"); @@ -376,8 +388,10 @@ private boolean transmitPacket(ByteBuffer buf, String description, Inet4Address maybeLog("Broadcasting " + description); Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr); } else { - maybeLog("Unicasting " + description + " to " + to.getHostAddress()); - Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER); + // It's safe to call getpeername here, because we only send unicast packets if we + // have an IP address, and we connect the UDP socket in DhcpHaveAddressState#enter. + maybeLog("Unicasting " + description + " to " + Os.getpeername(mUdpSock)); + Os.write(mUdpSock, buf); } } catch(ErrnoException|IOException e) { Log.e(TAG, "Can't send packet: ", e); @@ -403,9 +417,10 @@ private boolean sendRequestPacket( encap, mTransactionId, getSecs(), clientAddress, DO_UNICAST, mHwAddr, requestedAddress, serverAddress, REQUESTED_PARAMS, null); + String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null; String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() + " request=" + requestedAddress.getHostAddress() + - " to=" + serverAddress.getHostAddress(); + " serverid=" + serverStr; return transmitPacket(packet, description, to); } @@ -789,6 +804,7 @@ protected void receivePacket(DhcpPacket packet) { transitionTo(mDhcpBoundState); } } else if (packet instanceof DhcpNakPacket) { + // TODO: Wait a while before returning into INIT state. Log.d(TAG, "Received NAK, returning to INIT"); mOffer = null; transitionTo(mDhcpInitState); @@ -806,10 +822,9 @@ class DhcpHaveAddressState extends LoggingState { @Override public void enter() { super.enter(); - if (setIpAddress(mDhcpLease.ipAddress)) { - maybeLog("Configured IP address " + mDhcpLease.ipAddress); - } else { - Log.e(TAG, "Failed to configure IP address " + mDhcpLease.ipAddress); + if (!setIpAddress(mDhcpLease.ipAddress) || + (mDhcpLease.serverAddress != null && + !connectUdpSock((mDhcpLease.serverAddress)))) { notifyFailure(); // There's likely no point in going into DhcpInitState here, we'll probably just // repeat the transaction, get the same IP address as before, and fail. @@ -865,11 +880,15 @@ public void enter() { } protected boolean sendPacket() { + // Not specifying a SERVER_IDENTIFIER option is a violation of RFC 2131, but... + // http://b/25343517 . Try to make things work anyway by using broadcast renews. + Inet4Address to = (mDhcpLease.serverAddress != null) ? + mDhcpLease.serverAddress : INADDR_BROADCAST; return sendRequestPacket( (Inet4Address) mDhcpLease.ipAddress.getAddress(), // ciaddr INADDR_ANY, // DHCP_REQUESTED_IP - INADDR_ANY, // DHCP_SERVER_IDENTIFIER - (Inet4Address) mDhcpLease.serverAddress); // packet destination address + null, // DHCP_SERVER_IDENTIFIER + to); // packet destination address } protected void receivePacket(DhcpPacket packet) { diff --git a/services/net/java/android/net/dhcp/DhcpOfferPacket.java b/services/net/java/android/net/dhcp/DhcpOfferPacket.java index 7ca7100bbab..99154ef0498 100644 --- a/services/net/java/android/net/dhcp/DhcpOfferPacket.java +++ b/services/net/java/android/net/dhcp/DhcpOfferPacket.java @@ -48,7 +48,7 @@ public String toString() { } return s + " OFFER, ip " + mYourIp + ", mask " + mSubnetMask + - dnsServers + ", gateway " + mGateway + + dnsServers + ", gateways " + mGateways + " lease time " + mLeaseTime + ", domain " + mDomainName; } @@ -81,7 +81,7 @@ void finishPacket(ByteBuffer buffer) { } addTlv(buffer, DHCP_SUBNET_MASK, mSubnetMask); - addTlv(buffer, DHCP_ROUTER, mGateway); + addTlv(buffer, DHCP_ROUTER, mGateways); addTlv(buffer, DHCP_DOMAIN_NAME, mDomainName); addTlv(buffer, DHCP_BROADCAST_ADDRESS, mBroadcastAddress); addTlv(buffer, DHCP_DNS_SERVER, mDnsServers); diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java index cbf8fc21b80..8927bfa2565 100644 --- a/services/net/java/android/net/dhcp/DhcpPacket.java +++ b/services/net/java/android/net/dhcp/DhcpPacket.java @@ -113,6 +113,11 @@ abstract class DhcpPacket { */ protected static final int MAX_LENGTH = 1500; + /** + * The magic cookie that identifies this as a DHCP packet instead of BOOTP. + */ + private static final int DHCP_MAGIC_COOKIE = 0x63825363; + /** * DHCP Optional Type: DHCP Subnet Mask */ @@ -123,7 +128,7 @@ abstract class DhcpPacket { * DHCP Optional Type: DHCP Router */ protected static final byte DHCP_ROUTER = 3; - protected Inet4Address mGateway; + protected List mGateways; /** * DHCP Optional Type: DHCP DNS Server @@ -403,7 +408,7 @@ protected void fillInPacket(int encap, Inet4Address destIp, (HWADDR_LEN - mClientMac.length) // pad addr to 16 bytes + 64 // empty server host name (64 bytes) + 128); // empty boot file name (128 bytes) - buf.putInt(0x63825363); // magic number + buf.putInt(DHCP_MAGIC_COOKIE); // magic number finishPacket(buf); // round up to an even number of octets @@ -668,6 +673,20 @@ private static String readAsciiString(ByteBuffer buf, int byteCount, boolean nul return new String(bytes, 0, length, StandardCharsets.US_ASCII); } + private static boolean isPacketToOrFromClient(short udpSrcPort, short udpDstPort) { + return (udpSrcPort == DHCP_CLIENT) || (udpDstPort == DHCP_CLIENT); + } + + private static boolean isPacketServerToServer(short udpSrcPort, short udpDstPort) { + return (udpSrcPort == DHCP_SERVER) && (udpDstPort == DHCP_SERVER); + } + + public static class ParseException extends Exception { + public ParseException(String msg, Object... args) { + super(String.format(msg, args)); + } + } + /** * Creates a concrete DhcpPacket from the supplied ByteBuffer. The * buffer may have an L2 encapsulation (which is the full EthernetII @@ -677,7 +696,7 @@ private static String readAsciiString(ByteBuffer buf, int byteCount, boolean nul * A subset of the optional parameters are parsed and are stored * in object fields. */ - public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) + public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) throws ParseException { // bootp parameters int transactionId; @@ -687,8 +706,8 @@ public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) Inet4Address nextIp; Inet4Address relayIp; byte[] clientMac; - List dnsServers = new ArrayList(); - Inet4Address gateway = null; // aka router + List dnsServers = new ArrayList<>(); + List gateways = new ArrayList<>(); // aka router Inet4Address serverIdentifier = null; Inet4Address netMask = null; String message = null; @@ -720,7 +739,8 @@ public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) // check to see if we need to parse L2, IP, and UDP encaps if (pktType == ENCAP_L2) { if (packet.remaining() < MIN_PACKET_LENGTH_L2) { - return null; + throw new ParseException("L2 packet too short, %d < %d", + packet.remaining(), MIN_PACKET_LENGTH_L2); } byte[] l2dst = new byte[6]; @@ -732,18 +752,20 @@ public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) short l2type = packet.getShort(); if (l2type != OsConstants.ETH_P_IP) - return null; + throw new ParseException("Unexpected L2 type 0x%04x, expected 0x%04x", + l2type, OsConstants.ETH_P_IP); } if (pktType <= ENCAP_L3) { if (packet.remaining() < MIN_PACKET_LENGTH_L3) { - return null; + throw new ParseException("L3 packet too short, %d < %d", + packet.remaining(), MIN_PACKET_LENGTH_L3); } byte ipTypeAndLength = packet.get(); int ipVersion = (ipTypeAndLength & 0xf0) >> 4; if (ipVersion != 4) { - return null; + throw new ParseException("Invalid IP version %d", ipVersion); } // System.out.println("ipType is " + ipType); @@ -759,8 +781,9 @@ public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) ipSrc = readIpAddress(packet); ipDst = readIpAddress(packet); - if (ipProto != IP_TYPE_UDP) // UDP - return null; + if (ipProto != IP_TYPE_UDP) { + throw new ParseException("Protocol not UDP: %d", ipProto); + } // Skip options. This cannot cause us to read beyond the end of the buffer because the // IPv4 header cannot be more than (0x0f * 4) = 60 bytes long, and that is less than @@ -776,13 +799,19 @@ public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) short udpLen = packet.getShort(); short udpChkSum = packet.getShort(); - if ((udpSrcPort != DHCP_SERVER) && (udpSrcPort != DHCP_CLIENT)) + // Only accept packets to or from the well-known client port (expressly permitting + // packets from ports other than the well-known server port; http://b/24687559), and + // server-to-server packets, e.g. for relays. + if (!isPacketToOrFromClient(udpSrcPort, udpDstPort) && + !isPacketServerToServer(udpSrcPort, udpDstPort)) { return null; + } } // We need to check the length even for ENCAP_L3 because the IPv4 header is variable-length. if (pktType > ENCAP_BOOTP || packet.remaining() < MIN_PACKET_LENGTH_BOOTP) { - return null; + throw new ParseException("Invalid type or BOOTP packet too short, %d < %d", + packet.remaining(), MIN_PACKET_LENGTH_BOOTP); } byte type = packet.get(); @@ -805,7 +834,7 @@ public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) packet.get(ipv4addr); relayIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr); } catch (UnknownHostException ex) { - return null; + throw new ParseException("Invalid IPv4 address: %s", Arrays.toString(ipv4addr)); } // Some DHCP servers have been known to announce invalid client hardware address values such @@ -828,8 +857,10 @@ public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) int dhcpMagicCookie = packet.getInt(); - if (dhcpMagicCookie != 0x63825363) - return null; + if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) { + throw new ParseException("Bad magic cookie 0x%08x, should be 0x%08x", dhcpMagicCookie, + DHCP_MAGIC_COOKIE); + } // parse options boolean notFinishedOptions = true; @@ -852,8 +883,9 @@ public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) expectedLen = 4; break; case DHCP_ROUTER: - gateway = readIpAddress(packet); - expectedLen = 4; + for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) { + gateways.add(readIpAddress(packet)); + } break; case DHCP_DNS_SERVER: for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) { @@ -937,18 +969,20 @@ public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) } if (expectedLen != optionLen) { - return null; + throw new ParseException("Invalid length %d for option %d, expected %d", + optionLen, optionType, expectedLen); } } } catch (BufferUnderflowException e) { - return null; + throw new ParseException("BufferUnderflowException"); } } DhcpPacket newPacket; switch(dhcpType) { - case -1: return null; + case (byte) 0xFF: + throw new ParseException("No DHCP message type option"); case DHCP_MESSAGE_TYPE_DISCOVER: newPacket = new DhcpDiscoverPacket( transactionId, secs, clientMac, broadcast); @@ -981,14 +1015,13 @@ public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) clientMac); break; default: - System.out.println("Unimplemented type: " + dhcpType); - return null; + throw new ParseException("Unimplemented DHCP type %d", dhcpType); } newPacket.mBroadcastAddress = bcAddr; newPacket.mDnsServers = dnsServers; newPacket.mDomainName = domainName; - newPacket.mGateway = gateway; + newPacket.mGateways = gateways; newPacket.mHostName = hostName; newPacket.mLeaseTime = leaseTime; newPacket.mMessage = message; @@ -1009,7 +1042,7 @@ public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) * Parse a packet from an array of bytes, stopping at the given length. */ public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType) - { + throws ParseException { ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN); return decodeFullPacket(buffer, pktType); } @@ -1044,7 +1077,11 @@ public DhcpResults toDhcpResults() { } catch (IllegalArgumentException e) { return null; } - results.gateway = mGateway; + + if (mGateways.size() > 0) { + results.gateway = mGateways.get(0); + } + results.dnsServers.addAll(mDnsServers); results.domains = mDomainName; results.serverAddress = mServerIdentifier; @@ -1086,11 +1123,11 @@ public static ByteBuffer buildDiscoverPacket(int encap, int transactionId, public static ByteBuffer buildOfferPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address clientIpAddr, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, - Inet4Address gateway, List dnsServers, + List gateways, List dnsServers, Inet4Address dhcpServerIdentifier, String domainName) { DhcpPacket pkt = new DhcpOfferPacket( transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac); - pkt.mGateway = gateway; + pkt.mGateways = gateways; pkt.mDnsServers = dnsServers; pkt.mLeaseTime = timeout; pkt.mDomainName = domainName; @@ -1106,11 +1143,11 @@ public static ByteBuffer buildOfferPacket(int encap, int transactionId, public static ByteBuffer buildAckPacket(int encap, int transactionId, boolean broadcast, Inet4Address serverIpAddr, Inet4Address clientIpAddr, byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr, - Inet4Address gateway, List dnsServers, + List gateways, List dnsServers, Inet4Address dhcpServerIdentifier, String domainName) { DhcpPacket pkt = new DhcpAckPacket( transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac); - pkt.mGateway = gateway; + pkt.mGateways = gateways; pkt.mDnsServers = dnsServers; pkt.mLeaseTime = timeout; pkt.mDomainName = domainName; diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java index cd3b8bb7e0b..7e60bf17d96 100644 --- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java +++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java @@ -117,7 +117,7 @@ public ByteBuffer build() { private void assertDomainAndVendorInfoParses( String expectedDomain, byte[] domainBytes, - String expectedVendorInfo, byte[] vendorInfoBytes) { + String expectedVendorInfo, byte[] vendorInfoBytes) throws Exception { ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER) .setDomainBytes(domainBytes) .setVendorInfoBytes(vendorInfoBytes) @@ -158,17 +158,25 @@ public void testDomainName() throws Exception { } private void assertLeaseTimeParses(boolean expectValid, Integer rawLeaseTime, - long leaseTimeMillis, byte[] leaseTimeBytes) { + long leaseTimeMillis, byte[] leaseTimeBytes) throws Exception { TestDhcpPacket testPacket = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER); if (leaseTimeBytes != null) { testPacket.setLeaseTimeBytes(leaseTimeBytes); } ByteBuffer packet = testPacket.build(); - DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP); + DhcpPacket offerPacket = null; + if (!expectValid) { - assertNull(offerPacket); + try { + offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP); + fail("Invalid packet parsed successfully: " + offerPacket); + } catch (ParseException expected) { + } return; } + + offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP); + assertNotNull(offerPacket); assertEquals(rawLeaseTime, offerPacket.mLeaseTime); DhcpResults dhcpResults = offerPacket.toDhcpResults(); // Just check this doesn't crash. assertEquals(leaseTimeMillis, offerPacket.getLeaseTimeMillis()); @@ -200,14 +208,14 @@ public void testLeaseTime() throws Exception { } private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp, - byte[] netmaskBytes) { + byte[] netmaskBytes) throws Exception { checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes); checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes); } private void checkIpAddress(String expected, byte type, Inet4Address clientIp, Inet4Address yourIp, - byte[] netmaskBytes) { + byte[] netmaskBytes) throws Exception { ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp) .setNetmaskBytes(netmaskBytes) .build(); @@ -506,4 +514,74 @@ public void testBug2136() throws Exception { assertDhcpResults("10.32.158.205/20", "10.32.144.1", "148.88.65.52,148.88.65.53", "lancs.ac.uk", "10.32.255.128", null, 7200, false, dhcpResults); } + + @SmallTest + public void testUdpServerAnySourcePort() throws Exception { + final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode(( + // Ethernet header. + "9cd917000000001c2e0000000800" + + // IP header. + "45a00148000040003d115087d18194fb0a0f7af2" + + // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation). + // NOTE: The server source port is not the canonical port 67. + "C29F004401341268" + + // BOOTP header. + "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" + + // MAC address. + "9cd91700000000000000000000000000" + + // Server name. + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + // File. + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + // Options. + "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" + + "d18180060f0777766d2e6564751c040a0fffffff000000" + ).toCharArray(), false)); + + DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2); + assertTrue(offerPacket instanceof DhcpOfferPacket); + assertEquals("9CD917000000", HexDump.toHexString(offerPacket.getClientMac())); + DhcpResults dhcpResults = offerPacket.toDhcpResults(); + assertDhcpResults("10.15.122.242/16", "10.15.200.23", + "209.129.128.3,209.129.148.3,209.129.128.6", + "wvm.edu", "10.1.105.252", null, 86400, false, dhcpResults); + } + + @SmallTest + public void testMultipleRouters() throws Exception { + final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode(( + // Ethernet header. + "fc3d93000000" + "081735000000" + "0800" + + // IP header. + "45000148c2370000ff117ac2c0a8bd02ffffffff" + + // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation). + "0043004401343beb" + + // BOOTP header. + "0201060027f518e20000800000000000c0a8bd310000000000000000" + + // MAC address. + "fc3d9300000000000000000000000000" + + // Server name. + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + // File. + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + "0000000000000000000000000000000000000000000000000000000000000000" + + // Options. + "638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" + + "0308c0a8bd01ffffff0006080808080808080404ff000000000000" + ).toCharArray(), false)); + + DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2); + assertTrue(offerPacket instanceof DhcpOfferPacket); + assertEquals("FC3D93000000", HexDump.toHexString(offerPacket.getClientMac())); + DhcpResults dhcpResults = offerPacket.toDhcpResults(); + assertDhcpResults("192.168.189.49/24", "192.168.189.1", "8.8.8.8,8.8.4.4", + null, "192.171.189.2", null, 28800, false, dhcpResults); + } } diff --git a/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java new file mode 100644 index 00000000000..be6861c4369 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/content/SyncManagerTest.java @@ -0,0 +1,64 @@ +package com.android.server.content; + +import android.os.Bundle; + +import junit.framework.TestCase; + +public class SyncManagerTest extends TestCase { + + final String KEY_1 = "key_1"; + final String KEY_2 = "key_2"; + + public void testSyncExtrasEquals_WithNull() throws Exception { + Bundle b1 = new Bundle(); + Bundle b2 = new Bundle(); + + b1.putString(KEY_1, null); + b2.putString(KEY_1, null); + + assertTrue("Null extra not properly compared between bundles.", + SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */)); + } + + public void testSyncExtrasEqualsBigger_WithNull() throws Exception { + Bundle b1 = new Bundle(); + Bundle b2 = new Bundle(); + + b1.putString(KEY_1, null); + b2.putString(KEY_1, null); + + b1.putString(KEY_2, "bla"); + b2.putString(KEY_2, "bla"); + + assertTrue("Extras not properly compared between bundles.", + SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */)); + } + + public void testSyncExtrasEqualsFails_differentValues() throws Exception { + Bundle b1 = new Bundle(); + Bundle b2 = new Bundle(); + + b1.putString(KEY_1, null); + b2.putString(KEY_1, null); + + b1.putString(KEY_2, "bla"); + b2.putString(KEY_2, "ble"); // different key + + assertFalse("Extras considered equal when they are different.", + SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */)); + } + + public void testSyncExtrasEqualsFails_differentNulls() throws Exception { + Bundle b1 = new Bundle(); + Bundle b2 = new Bundle(); + + b1.putString(KEY_1, null); + b2.putString(KEY_1, "bla"); // different key + + b1.putString(KEY_2, "ble"); + b2.putString(KEY_2, "ble"); + + assertFalse("Extras considered equal when they are different.", + SyncManager.syncExtrasEquals(b1, b2, false /* don't care about system extras */)); + } +} diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java index bd64392bb3a..0b73beb8e3b 100644 --- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java @@ -6,6 +6,7 @@ import android.app.job.JobInfo; import android.app.job.JobInfo.Builder; import android.os.PersistableBundle; +import android.os.SystemClock; import android.test.AndroidTestCase; import android.test.RenamingDelegatingContext; import android.util.Log; @@ -102,6 +103,14 @@ public void testWritingTwoFilesToDisk() throws Exception { Iterator it = jobStatusSet.iterator(); JobStatus loaded1 = it.next(); JobStatus loaded2 = it.next(); + + // Reverse them so we know which comparison to make. + if (loaded1.getJobId() != 8) { + JobStatus tmp = loaded1; + loaded1 = loaded2; + loaded2 = tmp; + } + assertTasksEqual(task1, loaded1.getJob()); assertTasksEqual(task2, loaded2.getJob()); assertTrue("JobStore#contains invalid.", mTaskStoreUnderTest.containsJob(taskStatus1)); @@ -143,6 +152,36 @@ public void testWritingTaskWithExtras() throws Exception { assertTasksEqual(task, loaded.getJob()); } + public void testMassivePeriodClampedOnRead() throws Exception { + final long TEN_SECONDS = 10000L; + JobInfo.Builder b = new Builder(8, mComponent) + .setPeriodic(TEN_SECONDS) + .setPersisted(true); + final long invalidLateRuntimeElapsedMillis = + SystemClock.elapsedRealtime() + (TEN_SECONDS * 2) + 5000; // >2P from now. + final long invalidEarlyRuntimeElapsedMillis = + invalidLateRuntimeElapsedMillis - TEN_SECONDS; // Early is (late - period). + final JobStatus js = new JobStatus(b.build(), SOME_UID, + invalidEarlyRuntimeElapsedMillis, invalidLateRuntimeElapsedMillis); + + mTaskStoreUnderTest.add(js); + Thread.sleep(IO_WAIT); + + final ArraySet jobStatusSet = new ArraySet(); + mTaskStoreUnderTest.readJobMapFromDisk(jobStatusSet); + assertEquals("Incorrect # of persisted tasks.", 1, jobStatusSet.size()); + JobStatus loaded = jobStatusSet.iterator().next(); + + // Assert early runtime was clamped to be under now + period. We can do <= here b/c we'll + // call SystemClock.elapsedRealtime after doing the disk i/o. + final long newNowElapsed = SystemClock.elapsedRealtime(); + assertTrue("Early runtime wasn't correctly clamped.", + loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS); + // Assert late runtime was clamped to be now + period*2. + assertTrue("Early runtime wasn't correctly clamped.", + loaded.getEarliestRunTime() <= newNowElapsed + TEN_SECONDS*2); + } + /** * Helper function to throw an error if the provided task and TaskStatus objects are not equal. */ diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 4146c1c0111..2b8afba57a3 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -314,6 +314,8 @@ void setAppIdleParoled(boolean paroled) { mAppIdleParoled = paroled; if (DEBUG) Slog.d(TAG, "Changing paroled to " + mAppIdleParoled); if (paroled) { + postParoleEndTimeout(); + } else { mLastAppIdleParoledTime = checkAndGetTimeLocked(); postNextParoleTimeout(); } @@ -383,9 +385,11 @@ void checkIdleStates(int checkUserId) { timeNow); final int packageCount = packages.size(); for (int p = 0; p < packageCount; p++) { - final String packageName = packages.get(p).packageName; - final boolean isIdle = isAppIdleFiltered(packageName, userId, service, timeNow, - screenOnTime); + final PackageInfo pi = packages.get(p); + final String packageName = pi.packageName; + final boolean isIdle = isAppIdleFiltered(packageName, + UserHandle.getAppId(pi.applicationInfo.uid), + userId, service, timeNow, screenOnTime); mHandler.sendMessage(mHandler.obtainMessage(MSG_INFORM_LISTENERS, userId, isIdle ? 1 : 0, packageName)); mAppIdleHistory.addEntry(packageName, userId, isIdle, timeNow); @@ -404,8 +408,6 @@ void checkParoleTimeout() { if (timeSinceLastParole > mAppIdleParoleIntervalMillis) { if (DEBUG) Slog.d(TAG, "Crossed default parole interval"); setAppIdleParoled(true); - // Make sure it ends at some point - postParoleEndTimeout(); } else { if (DEBUG) Slog.d(TAG, "Not long enough to go to parole"); postNextParoleTimeout(); @@ -492,7 +494,6 @@ void onDeviceIdleModeChanged() { if (!deviceIdle && timeSinceLastParole >= mAppIdleParoleIntervalMillis) { if (DEBUG) Slog.i(TAG, "Bringing idle apps out of inactive state due to deviceIdleMode=false"); - postNextParoleTimeout(); setAppIdleParoled(true); } else if (deviceIdle) { if (DEBUG) Slog.i(TAG, "Device idle, back to prison"); @@ -770,10 +771,17 @@ boolean isAppIdleFilteredOrParoled(String packageName, int userId, long timeNow) if (mAppIdleParoled) { return false; } - return isAppIdleFiltered(packageName, userId, timeNow); + try { + ApplicationInfo ai = getContext().getPackageManager().getApplicationInfo(packageName, + PackageManager.GET_UNINSTALLED_PACKAGES + | PackageManager.GET_DISABLED_COMPONENTS); + return isAppIdleFiltered(packageName, ai.uid, userId, timeNow); + } catch (PackageManager.NameNotFoundException e) { + } + return false; } - boolean isAppIdleFiltered(String packageName, int userId, long timeNow) { + boolean isAppIdleFiltered(String packageName, int uidForAppId, int userId, long timeNow) { final UserUsageStatsService userService; final long screenOnTime; synchronized (mLock) { @@ -783,7 +791,8 @@ boolean isAppIdleFiltered(String packageName, int userId, long timeNow) { userService = getUserDataAndInitializeIfNeededLocked(userId, timeNow); screenOnTime = getScreenOnTimeLocked(timeNow); } - return isAppIdleFiltered(packageName, userId, userService, timeNow, screenOnTime); + return isAppIdleFiltered(packageName, UserHandle.getAppId(uidForAppId), userId, + userService, timeNow, screenOnTime); } /** @@ -792,14 +801,22 @@ boolean isAppIdleFiltered(String packageName, int userId, long timeNow) { * This happens if the device is plugged in or temporarily allowed to make exceptions. * Called by interface impls. */ - private boolean isAppIdleFiltered(String packageName, int userId, + private boolean isAppIdleFiltered(String packageName, int appId, int userId, UserUsageStatsService userService, long timeNow, long screenOnTime) { if (packageName == null) return false; // If not enabled at all, of course nobody is ever idle. if (!mAppIdleEnabled) { return false; } - if (packageName.equals("android")) return false; + if (appId < Process.FIRST_APPLICATION_UID) { + // System uids never go idle. + return false; + } + if (packageName.equals("android")) { + // Nor does the framework (which should be redundant with the above, but for MR1 we will + // retain this for safety). + return false; + } try { // We allow all whitelisted apps, including those that don't want to be whitelisted // for idle mode, because app idle (aka app standby) is really not as big an issue @@ -866,8 +883,8 @@ int[] getIdleUidsForUser(int userId) { ApplicationInfo ai = apps.get(i); // Check whether this app is idle. - boolean idle = isAppIdleFiltered(ai.packageName, userId, userService, timeNow, - screenOnTime); + boolean idle = isAppIdleFiltered(ai.packageName, UserHandle.getAppId(ai.uid), + userId, userService, timeNow, screenOnTime); int index = uidStates.indexOfKey(ai.uid); if (index < 0) { @@ -1353,8 +1370,8 @@ public void reportContentProviderUsage(String name, String packageName, int user } @Override - public boolean isAppIdle(String packageName, int userId) { - return UsageStatsService.this.isAppIdleFiltered(packageName, userId, -1); + public boolean isAppIdle(String packageName, int uidForAppId, int userId) { + return UsageStatsService.this.isAppIdleFiltered(packageName, uidForAppId, userId, -1); } @Override diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 769ee465537..a4a4d84b149 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -715,6 +715,8 @@ public void handleMessage(Message msg) { if (active && mCurrentUser != UserHandle.USER_NULL) { Slog.v(TAG, "Current user switched to " + mCurrentUser + "; resetting USB host stack for MTP or PTP"); + // avoid leaking sensitive data from previous user + mUsbDataUnlocked = false; setEnabledFunctions(mCurrentFunctions, true); } mCurrentUser = msg.arg1; diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java index 3ca0c84543d..31d859f654b 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoundTriggerHelper.java @@ -87,6 +87,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // This is an indirect indication of the microphone being open in some other application. private boolean mServiceDisabled = false; private boolean mStarted = false; + private boolean mRecognitionAborted = false; private PowerSaveModeListener mPowerSaveModeListener; SoundTriggerHelper(Context context) { @@ -386,8 +387,9 @@ private void onServiceStateChangedLocked(boolean disabled) { private void onRecognitionAbortLocked() { Slog.w(TAG, "Recognition aborted"); - // No-op - // This is handled via service state changes instead. + // If abort has been called, the hardware has already stopped recognition, so we shouldn't + // call it again when we process the state change. + mRecognitionAborted = true; } private void onRecognitionFailureLocked() { @@ -490,8 +492,13 @@ private int updateRecognitionLocked(boolean notify) { } return status; } else { - // Stop recognition. - int status = mModule.stopRecognition(mCurrentSoundModelHandle); + // Stop recognition (only if we haven't been aborted). + int status = STATUS_OK; + if (!mRecognitionAborted) { + status = mModule.stopRecognition(mCurrentSoundModelHandle); + } else { + mRecognitionAborted = false; + } if (status != SoundTrigger.STATUS_OK) { Slog.w(TAG, "stopRecognition call failed with " + status); if (notify) { diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index a8874d0e11a..a96c164b545 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -925,7 +925,7 @@ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { return; } synchronized (this) { - pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)\n"); + pw.println("VOICE INTERACTION MANAGER (dumpsys voiceinteraction)"); pw.println(" mEnableService: " + mEnableService); if (mImpl == null) { pw.println(" (No active implementation)"); diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java index 28520be8b7d..30296e15e63 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java @@ -36,6 +36,7 @@ import android.service.voice.IVoiceInteractionSession; import android.service.voice.VoiceInteractionService; import android.service.voice.VoiceInteractionServiceInfo; +import android.util.PrintWriterPrinter; import android.util.Slog; import android.view.IWindowManager; @@ -114,9 +115,9 @@ public void onServiceDisconnected(ComponentName name) { mAm = ActivityManagerNative.getDefault(); VoiceInteractionServiceInfo info; try { - info = new VoiceInteractionServiceInfo(context.getPackageManager(), service); - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "Voice interaction service not found: " + service); + info = new VoiceInteractionServiceInfo(context.getPackageManager(), service, mUser); + } catch (RemoteException|PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Voice interaction service not found: " + service, e); mInfo = null; mSessionComponentName = null; mIWindowManager = null; @@ -260,9 +261,18 @@ public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args) { } return; } + pw.print(" mUser="); pw.println(mUser); pw.print(" mComponent="); pw.println(mComponent.flattenToShortString()); pw.print(" Session service="); pw.println(mInfo.getSessionService()); + pw.println(" Service info:"); + mInfo.getServiceInfo().dump(new PrintWriterPrinter(pw), " "); + pw.println(" Application info:"); + mInfo.getServiceInfo().applicationInfo.dump(new PrintWriterPrinter(pw), " "); + pw.print(" Recognition service="); pw.println(mInfo.getRecognitionService()); pw.print(" Settings activity="); pw.println(mInfo.getSettingsActivity()); + pw.print(" Supports assist="); pw.println(mInfo.getSupportsAssist()); + pw.print(" Supports launch from keyguard="); + pw.println(mInfo.getSupportsLaunchFromKeyguard()); if (mDisabledShowContext != 0) { pw.print(" mDisabledShowContext="); pw.println(Integer.toHexString(mDisabledShowContext)); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 3ac921bc41b..160d8b3fe05 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -191,7 +191,7 @@ public CarrierConfigManager() { public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool"; - /** + /** * Override the platform's notion of a network operator being considered roaming. * Value is string array of MCCMNCs to be considered roaming for 3GPP RATs. */ @@ -267,6 +267,13 @@ public CarrierConfigManager() { public static final String KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL = "carrier_allow_turnoff_ims_bool"; + /** + * Flag specifying whether Generic Bootstrapping Architecture capable SIM is required for IMS. + * @hide + */ + public static final String KEY_CARRIER_IMS_GBA_REQUIRED_BOOL + = "carrier_ims_gba_required_bool"; + /** * Flag specifying whether IMS instant lettering is available for the carrier. {@code True} if * instant lettering is available for the carrier, {@code false} otherwise. @@ -380,9 +387,9 @@ public CarrierConfigManager() { "ci_action_on_sys_update_extra_val_string"; /** - * Specifies the amount of gap to be added in millis between DTMF tones. When a non-zero value - * is specified, the UE shall wait for the specified amount of time before it sends out - * successive DTMF tones on the network. + * Specifies the amount of gap to be added in millis between postdial DTMF tones. When a + * non-zero value is specified, the UE shall wait for the specified amount of time before it + * sends out successive DTMF tones on the network. * @hide */ public static final String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int"; @@ -420,6 +427,14 @@ public CarrierConfigManager() { */ public static final String KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL = "hide_preferred_network_type_bool"; + /** + * Specifies the amount of gap to be added in millis between postdial DTMF tones. When a + * non-zero value is specified, the UE shall wait for the specified amount of time before it + * sends out successive DTMF tones on the network. + * @hide + */ + public static final String KEY_CDMA_DTMF_TONE_DELAY_INT = "cdma_dtmf_tone_delay_int"; + /** * If this is true, the SIM card (through Customer Service Profile EF file) will be able to * prevent manual operator selection. If false, this SIM setting will be ignored and manual @@ -428,6 +443,12 @@ public CarrierConfigManager() { */ public static final String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool"; + /** + * Allow user to add APNs + * @hide + */ + public static final String KEY_ALLOW_ADDING_APNS_BOOL = "allow_adding_apns_bool"; + // These variables are used by the MMS service and exposed through another API, {@link // SmsManager}. The variable names and string values are copied from there. public static final String KEY_MMS_ALIAS_ENABLED_BOOL = "aliasEnabled"; @@ -489,6 +510,7 @@ public CarrierConfigManager() { sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true); sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true); + sDefaults.putBoolean(KEY_CARRIER_IMS_GBA_REQUIRED_BOOL, false); sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false); sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING, ""); sDefaults.putString(KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING, ""); @@ -525,6 +547,7 @@ public CarrierConfigManager() { sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING, ""); sDefaults.putString(KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING, ""); sDefaults.putBoolean(KEY_CSP_ENABLED_BOOL, false); + sDefaults.putBoolean(KEY_ALLOW_ADDING_APNS_BOOL, true); sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false); sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null); @@ -538,6 +561,7 @@ public CarrierConfigManager() { sDefaults.putBoolean(KEY_EDITABLE_ENHANCED_4G_LTE_BOOL, true); sDefaults.putBoolean(KEY_HIDE_IMS_APN_BOOL, false); sDefaults.putBoolean(KEY_HIDE_PREFERRED_NETWORK_TYPE_BOOL, false); + sDefaults.putInt(KEY_CDMA_DTMF_TONE_DELAY_INT, 100); // MMS defaults sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false); diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index a0be0f3f094..6d973e948c9 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -926,7 +926,7 @@ public int getTdScdmaLevel() { else if (tdScdmaDbm >= -49) level = SIGNAL_STRENGTH_GREAT; else if (tdScdmaDbm >= -73) level = SIGNAL_STRENGTH_GOOD; else if (tdScdmaDbm >= -97) level = SIGNAL_STRENGTH_MODERATE; - else if (tdScdmaDbm >= -120) level = SIGNAL_STRENGTH_POOR; + else if (tdScdmaDbm >= -110) level = SIGNAL_STRENGTH_POOR; else level = SIGNAL_STRENGTH_NONE_OR_UNKNOWN; if (DBG) log("getTdScdmaLevel = " + level); diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java index 37ffa063c34..d1d6e0dcdec 100644 --- a/telephony/java/android/telephony/SubscriptionInfo.java +++ b/telephony/java/android/telephony/SubscriptionInfo.java @@ -332,9 +332,25 @@ public int describeContents() { return 0; } + /** + * @hide + */ + public static String givePrintableIccid(String iccId) { + String iccIdToPrint = null; + if (iccId != null) { + if (iccId.length() > 9) { + iccIdToPrint = iccId.substring(0, 9) + "XXXXXXXXXXX"; + } else { + iccIdToPrint = iccId; + } + } + return iccIdToPrint; + } + @Override public String toString() { - return "{id=" + mId + ", iccId=" + mIccId + " simSlotIndex=" + mSimSlotIndex + String iccIdToPrint = givePrintableIccid(mIccId); + return "{id=" + mId + ", iccId=" + iccIdToPrint + " simSlotIndex=" + mSimSlotIndex + " displayName=" + mDisplayName + " carrierName=" + mCarrierName + " nameSource=" + mNameSource + " iconTint=" + mIconTint + " dataRoaming=" + mDataRoaming + " iconBitmap=" + mIconBitmap + " mcc " + mMcc diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index e75b0910de0..28bc1110d76 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -2919,7 +2919,7 @@ public boolean isSmsCapable() { /** * Returns all observed cell information from all radios on the * device including the primary and neighboring cells. This does - * not cause or change the rate of PhoneStateListner#onCellInfoChanged. + * not cause or change the rate of PhoneStateListener#onCellInfoChanged. *

    * The list can include one or more of {@link android.telephony.CellInfoGsm CellInfoGsm}, * {@link android.telephony.CellInfoCdma CellInfoCdma}, @@ -2933,6 +2933,9 @@ public boolean isSmsCapable() { * devices this may return null in which case getCellLocation should * be called. *

    + * This API will return valid data for registered cells on devices with + * {@link android.content.pm.PackageManager#FEATURE_TELEPHONY} + *

    * @return List of CellInfo or null if info unavailable. * *

    Requires Permission: {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java index d73b2bd219d..50ee5438d39 100644 --- a/telephony/java/com/android/ims/ImsCallProfile.java +++ b/telephony/java/com/android/ims/ImsCallProfile.java @@ -178,6 +178,7 @@ public class ImsCallProfile implements Parcelable { * Codec: Codec info. * DisplayText: Display text for the call. * AdditionalCallInfo: Additional call info. + * CallRadioTech: The radio tech on which the call is placed. */ public static final String EXTRA_OI = "oi"; public static final String EXTRA_CNA = "cna"; @@ -189,6 +190,21 @@ public class ImsCallProfile implements Parcelable { public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo"; public static final String EXTRA_IS_CALL_PULL = "CallPull"; + /** + * Extra key which the RIL can use to indicate the radio technology used for a call. + * Valid values are: + * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}, + * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_IWLAN}, and the other defined + * {@code RIL_RADIO_TECHNOLOGY_*} constants. + * Note: Despite the fact the {@link android.telephony.ServiceState} values are integer + * constants, the values passed for the {@link #EXTRA_CALL_RAT_TYPE} should be strings (e.g. + * "14" vs (int) 14). + * Note: This is used by {@link com.android.internal.telephony.imsphone.ImsPhoneConnection# + * updateWifiStateFromExtras(Bundle)} to determine whether to set the + * {@link android.telecom.Connection#CAPABILITY_WIFI} capability on a connection. + */ + public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech"; + public int mServiceType; public int mCallType; public int mRestrictCause = CALL_RESTRICT_CAUSE_NONE; diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index e611ea44849..59b22bda083 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -101,9 +101,18 @@ public class WifiEnterpriseConfig implements Parcelable { public static final String REALM_KEY = "realm"; /** @hide */ public static final String PLMN_KEY = "plmn"; + /** @hide */ + public static final String PHASE1_KEY = "phase1"; + /** {@hide} */ + public static final String ENABLE_TLS_1_2 = "\"tls_disable_tlsv1_2=0\""; + /** {@hide} */ + public static final String DISABLE_TLS_1_2 = "\"tls_disable_tlsv1_2=1\""; private HashMap mFields = new HashMap(); + //By default, we enable TLS1.2. However, due to a known bug on some radius, we may disable it to + // fall back to TLS 1.1. + private boolean mTls12Enable = true; private X509Certificate mCaCert; private PrivateKey mClientPrivateKey; private X509Certificate mClientCertificate; @@ -149,6 +158,7 @@ public void writeToParcel(Parcel dest, int flags) { } writeCertificate(dest, mClientCertificate); + dest.writeInt(mTls12Enable ? 1: 0); } private void writeCertificate(Parcel dest, X509Certificate cert) { @@ -196,6 +206,7 @@ public WifiEnterpriseConfig createFromParcel(Parcel in) { enterpriseConfig.mClientPrivateKey = userKey; enterpriseConfig.mClientCertificate = readCertificate(in); + enterpriseConfig.mTls12Enable = (in.readInt() == 1); return enterpriseConfig; } @@ -299,6 +310,26 @@ public void setEapMethod(int eapMethod) { } } + /** + * Set the TLS version + * @param enable: true -- enable TLS1.2 false -- disable TLS1.2 + * @hide + */ + public void setTls12Enable(boolean enable) { + mTls12Enable = enable; + mFields.put(PHASE1_KEY, + enable ? ENABLE_TLS_1_2 : DISABLE_TLS_1_2); + } + + /** + * Get the TLS1.2 enabled or not + * @return eap method configured + * @hide + */ + public boolean getTls12Enable() { + return mTls12Enable; + } + /** * Get the eap method. * @return eap method configured