summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/accounts/AccountManager.java49
-rw-r--r--core/java/android/app/Activity.java222
-rw-r--r--core/java/android/app/ActivityManager.java22
-rw-r--r--core/java/android/app/ActivityThread.java9
-rw-r--r--core/java/android/app/AlarmManager.java43
-rw-r--r--core/java/android/app/AppOpsManager.java201
-rw-r--r--core/java/android/app/ApplicationPackageManager.java40
-rw-r--r--core/java/android/app/AssistStructure.java11
-rw-r--r--core/java/android/app/BackStackRecord.java12
-rw-r--r--core/java/android/app/DialogFragment.java2
-rw-r--r--core/java/android/app/ExitTransitionCoordinator.java2
-rw-r--r--core/java/android/app/Fragment.java143
-rw-r--r--core/java/android/app/FragmentContainer.java38
-rw-r--r--core/java/android/app/FragmentController.java384
-rw-r--r--core/java/android/app/FragmentHostCallback.java314
-rw-r--r--core/java/android/app/FragmentManager.java100
-rw-r--r--core/java/android/app/IntentService.java2
-rw-r--r--core/java/android/app/KeyguardManager.java20
-rw-r--r--core/java/android/app/LoaderManager.java40
-rw-r--r--core/java/android/app/Notification.java472
-rw-r--r--core/java/android/app/SearchDialog.java16
-rw-r--r--core/java/android/app/StatusBarManager.java12
-rw-r--r--core/java/android/app/admin/DeviceAdminReceiver.java8
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java102
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl9
-rw-r--r--core/java/android/app/backup/BackupAgent.java276
-rw-r--r--core/java/android/app/backup/FullBackup.java401
-rw-r--r--core/java/android/app/trust/ITrustManager.aidl1
-rw-r--r--core/java/android/app/trust/TrustManager.java17
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java14
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java36
-rw-r--r--core/java/android/bluetooth/BluetoothHealthCallback.java3
-rw-r--r--core/java/android/bluetooth/BluetoothSap.java59
-rw-r--r--core/java/android/bluetooth/BluetoothSocket.java10
-rw-r--r--core/java/android/bluetooth/IBluetoothManager.aidl2
-rw-r--r--core/java/android/bluetooth/le/ScanCallback.java6
-rw-r--r--core/java/android/bluetooth/le/ScanSettings.java15
-rw-r--r--core/java/android/content/ContentProvider.java22
-rw-r--r--core/java/android/content/ContentProviderNative.java3
-rw-r--r--core/java/android/content/Context.java29
-rw-r--r--core/java/android/content/pm/ActivityInfo.java24
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java23
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl5
-rw-r--r--core/java/android/content/pm/IPackageMoveObserver.aidl4
-rw-r--r--core/java/android/content/pm/IntentFilterVerificationInfo.java30
-rw-r--r--core/java/android/content/pm/PackageManager.java15
-rw-r--r--core/java/android/content/pm/PackageParser.java21
-rw-r--r--core/java/android/content/res/AssetManager.java1
-rw-r--r--core/java/android/content/res/ColorStateList.java27
-rw-r--r--core/java/android/content/res/Resources.java26
-rw-r--r--core/java/android/hardware/Camera.java5
-rw-r--r--core/java/android/hardware/ICameraService.aidl6
-rw-r--r--core/java/android/hardware/SystemSensorManager.java80
-rw-r--r--core/java/android/hardware/camera2/CameraCaptureSession.java5
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java161
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java32
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java37
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java86
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java35
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java46
-rw-r--r--core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java14
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java7
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java47
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl12
-rw-r--r--core/java/android/hardware/usb/UsbDevice.java19
-rw-r--r--core/java/android/net/ConnectivityManager.java149
-rw-r--r--core/java/android/net/INetworkPolicyManager.aidl2
-rw-r--r--core/java/android/net/NetworkCapabilities.java19
-rw-r--r--core/java/android/net/NetworkPolicyManager.java20
-rw-r--r--core/java/android/net/Uri.java2
-rw-r--r--core/java/android/os/AsyncTask.java13
-rw-r--r--core/java/android/os/Bundle.java8
-rw-r--r--core/java/android/os/Environment.java13
-rw-r--r--core/java/android/os/FileUtils.java2
-rw-r--r--core/java/android/os/IDeviceIdleController.aidl26
-rw-r--r--core/java/android/os/INetworkManagementService.aidl2
-rw-r--r--core/java/android/os/IPermissionController.aidl2
-rw-r--r--core/java/android/os/Parcel.java20
-rw-r--r--core/java/android/os/PowerManager.java8
-rw-r--r--core/java/android/os/Process.java3
-rw-r--r--core/java/android/os/storage/IMountService.java84
-rw-r--r--core/java/android/os/storage/IMountServiceListener.java44
-rw-r--r--core/java/android/os/storage/StorageEventListener.java5
-rw-r--r--core/java/android/os/storage/StorageManager.java158
-rw-r--r--core/java/android/os/storage/VolumeInfo.java42
-rw-r--r--core/java/android/os/storage/VolumeRecord.java139
-rw-r--r--core/java/android/preference/GenericInflater.java1
-rw-r--r--core/java/android/provider/ContactsContract.java10
-rw-r--r--core/java/android/provider/Settings.java9
-rw-r--r--core/java/android/security/keymaster/KeyCharacteristics.java30
-rw-r--r--core/java/android/security/keymaster/KeymasterDefs.java5
-rw-r--r--core/java/android/service/carrier/CarrierMessagingService.java77
-rw-r--r--core/java/android/service/carrier/ICarrierMessagingService.aidl9
-rw-r--r--core/java/android/service/notification/ZenModeConfig.java240
-rw-r--r--core/java/android/service/voice/IVoiceInteractionService.aidl1
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java21
-rw-r--r--core/java/android/service/voice/VoiceInteractionServiceInfo.java18
-rw-r--r--core/java/android/speech/RecognizerIntent.java8
-rw-r--r--core/java/android/text/DynamicLayout.java5
-rw-r--r--core/java/android/text/Html.java2
-rw-r--r--core/java/android/text/StaticLayout.java161
-rw-r--r--core/java/android/transition/Transition.java3
-rw-r--r--core/java/android/transition/TransitionInflater.java2
-rw-r--r--core/java/android/transition/Visibility.java117
-rw-r--r--core/java/android/util/EventLog.java18
-rw-r--r--core/java/android/view/DisplayListCanvas.java30
-rw-r--r--core/java/android/view/GhostView.java6
-rw-r--r--core/java/android/view/HapticFeedbackConstants.java5
-rw-r--r--core/java/android/view/LayoutInflater.java30
-rw-r--r--core/java/android/view/MenuInflater.java1
-rw-r--r--core/java/android/view/MotionEvent.java12
-rw-r--r--core/java/android/view/PhoneWindow.java2
-rw-r--r--core/java/android/view/TextureView.java8
-rw-r--r--core/java/android/view/ThreadedRenderer.java45
-rw-r--r--core/java/android/view/View.java698
-rw-r--r--core/java/android/view/ViewAssistStructure.java2
-rw-r--r--core/java/android/view/ViewGroup.java26
-rw-r--r--core/java/android/view/ViewRootImpl.java6
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java14
-rw-r--r--core/java/android/view/accessibility/AccessibilityNodeInfo.java62
-rw-r--r--core/java/android/widget/AbsListView.java15
-rw-r--r--core/java/android/widget/CursorAdapter.java2
-rw-r--r--core/java/android/widget/DayPickerPagerAdapter.java6
-rw-r--r--core/java/android/widget/DayPickerView.java20
-rw-r--r--core/java/android/widget/Editor.java123
-rw-r--r--core/java/android/widget/FastScroller.java14
-rw-r--r--core/java/android/widget/HorizontalScrollView.java14
-rw-r--r--core/java/android/widget/MediaController.java10
-rw-r--r--core/java/android/widget/RemoteViewsAdapter.java10
-rw-r--r--core/java/android/widget/ScrollView.java13
-rw-r--r--core/java/android/widget/SearchView.java69
-rw-r--r--core/java/android/widget/SimpleMonthView.java49
-rw-r--r--core/java/android/widget/Switch.java2
-rw-r--r--core/java/android/widget/TextView.java24
134 files changed, 4851 insertions, 1900 deletions
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 480d171..fd40d99 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -16,6 +16,8 @@
package android.accounts;
+import android.annotation.RequiresPermission;
+import android.annotation.Size;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -49,6 +51,11 @@ import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import static android.Manifest.permission.AUTHENTICATE_ACCOUNTS;
+import static android.Manifest.permission.GET_ACCOUNTS;
+import static android.Manifest.permission.MANAGE_ACCOUNTS;
+import static android.Manifest.permission.USE_CREDENTIALS;
+
/**
* This class provides access to a centralized registry of the user's
* online accounts. The user enters credentials (username and password) once
@@ -207,8 +214,7 @@ public class AccountManager {
* were authenticated successfully. Time is specified in milliseconds since
* epoch.
*/
- public static final String KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH =
- "lastAuthenticatedTimeMillisEpoch";
+ public static final String KEY_LAST_AUTHENTICATED_TIME = "lastAuthenticatedTime";
/**
* Authenticators using 'customTokens' option will also get the UID of the
@@ -320,6 +326,7 @@ public class AccountManager {
* @param account The account to query for a password
* @return The account's password, null if none or if the account doesn't exist
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public String getPassword(final Account account) {
if (account == null) throw new IllegalArgumentException("account is null");
try {
@@ -345,6 +352,7 @@ public class AccountManager {
* @param account The account to query for user data
* @return The user data, null if the account or key doesn't exist
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public String getUserData(final Account account, final String key) {
if (account == null) throw new IllegalArgumentException("account is null");
if (key == null) throw new IllegalArgumentException("key is null");
@@ -410,6 +418,7 @@ public class AccountManager {
* @return An array of {@link Account}, one for each account. Empty
* (never null) if no accounts have been added.
*/
+ @RequiresPermission(GET_ACCOUNTS)
public Account[] getAccounts() {
try {
return mService.getAccounts(null);
@@ -432,6 +441,7 @@ public class AccountManager {
* @return An array of {@link Account}, one for each account. Empty
* (never null) if no accounts have been added.
*/
+ @RequiresPermission(GET_ACCOUNTS)
public Account[] getAccountsAsUser(int userId) {
try {
return mService.getAccountsAsUser(null, userId);
@@ -491,6 +501,7 @@ public class AccountManager {
* @return An array of {@link Account}, one per matching account. Empty
* (never null) if no accounts of the specified type have been added.
*/
+ @RequiresPermission(GET_ACCOUNTS)
public Account[] getAccountsByType(String type) {
return getAccountsByTypeAsUser(type, Process.myUserHandle());
}
@@ -577,6 +588,7 @@ public class AccountManager {
* @return An {@link AccountManagerFuture} which resolves to a Boolean,
* true if the account exists and has all of the specified features.
*/
+ @RequiresPermission(GET_ACCOUNTS)
public AccountManagerFuture<Boolean> hasFeatures(final Account account,
final String[] features,
AccountManagerCallback<Boolean> callback, Handler handler) {
@@ -622,6 +634,7 @@ public class AccountManager {
* {@link Account}, one per account of the specified type which
* matches the requested features.
*/
+ @RequiresPermission(GET_ACCOUNTS)
public AccountManagerFuture<Account[]> getAccountsByTypeAndFeatures(
final String type, final String[] features,
AccountManagerCallback<Account[]> callback, Handler handler) {
@@ -660,6 +673,7 @@ public class AccountManager {
* @return True if the account was successfully added, false if the account
* already exists, the account is null, or another error occurs.
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
if (account == null) throw new IllegalArgumentException("account is null");
try {
@@ -671,8 +685,8 @@ public class AccountManager {
}
/**
- * Informs the system that the account has been authenticated recently. This
- * recency may be used by other applications to verify the account. This
+ * Notifies the system that the account has just been authenticated. This
+ * information may be used by other applications to verify the account. This
* should be called only when the user has entered correct credentials for
* the account.
* <p>
@@ -685,7 +699,8 @@ public class AccountManager {
*
* @param account The {@link Account} to be updated.
*/
- public boolean accountAuthenticated(Account account) {
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
+ public boolean notifyAccountAuthenticated(Account account) {
if (account == null)
throw new IllegalArgumentException("account is null");
try {
@@ -716,9 +731,10 @@ public class AccountManager {
* after the name change. If successful the account's name will be the
* specified new name.
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public AccountManagerFuture<Account> renameAccount(
final Account account,
- final String newName,
+ @Size(min = 1) final String newName,
AccountManagerCallback<Account> callback,
Handler handler) {
if (account == null) throw new IllegalArgumentException("account is null.");
@@ -784,6 +800,7 @@ public class AccountManager {
* {@link #removeAccount(Account, Activity, AccountManagerCallback, Handler)}
* instead
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
@Deprecated
public AccountManagerFuture<Boolean> removeAccount(final Account account,
AccountManagerCallback<Boolean> callback, Handler handler) {
@@ -838,6 +855,7 @@ public class AccountManager {
* adding accounts (of this type) has been disabled by policy
* </ul>
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> removeAccount(final Account account,
final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
if (account == null) throw new IllegalArgumentException("account is null");
@@ -910,6 +928,7 @@ public class AccountManager {
* account did not exist, the account is null, or another error
* occurs.
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public boolean removeAccountExplicitly(Account account) {
if (account == null) throw new IllegalArgumentException("account is null");
try {
@@ -936,6 +955,7 @@ public class AccountManager {
* @param accountType The account type of the auth token to invalidate, must not be null
* @param authToken The auth token to invalidate, may be null
*/
+ @RequiresPermission(anyOf = {MANAGE_ACCOUNTS, USE_CREDENTIALS})
public void invalidateAuthToken(final String accountType, final String authToken) {
if (accountType == null) throw new IllegalArgumentException("accountType is null");
try {
@@ -965,6 +985,7 @@ public class AccountManager {
* @return The cached auth token for this account and type, or null if
* no auth token is cached or the account does not exist.
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public String peekAuthToken(final Account account, final String authTokenType) {
if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
@@ -991,6 +1012,7 @@ public class AccountManager {
* @param account The account to set a password for
* @param password The password to set, null to clear the password
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public void setPassword(final Account account, final String password) {
if (account == null) throw new IllegalArgumentException("account is null");
try {
@@ -1015,6 +1037,7 @@ public class AccountManager {
*
* @param account The account whose password to clear
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
public void clearPassword(final Account account) {
if (account == null) throw new IllegalArgumentException("account is null");
try {
@@ -1040,6 +1063,7 @@ public class AccountManager {
* @param key The userdata key to set. Must not be null
* @param value The value to set, null to clear this userdata key
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public void setUserData(final Account account, final String key, final String value) {
if (account == null) throw new IllegalArgumentException("account is null");
if (key == null) throw new IllegalArgumentException("key is null");
@@ -1067,6 +1091,7 @@ public class AccountManager {
* @param authTokenType The type of the auth token, see {#getAuthToken}
* @param authToken The auth token to add to the cache
*/
+ @RequiresPermission(AUTHENTICATE_ACCOUNTS)
public void setAuthToken(Account account, final String authTokenType, final String authToken) {
if (account == null) throw new IllegalArgumentException("account is null");
if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null");
@@ -1101,6 +1126,7 @@ public class AccountManager {
* @throws java.io.IOException if the authenticator experienced an I/O problem
* creating a new auth token, usually because of network trouble
*/
+ @RequiresPermission(USE_CREDENTIALS)
public String blockingGetAuthToken(Account account, String authTokenType,
boolean notifyAuthFailure)
throws OperationCanceledException, IOException, AuthenticatorException {
@@ -1175,6 +1201,7 @@ public class AccountManager {
* authenticator-dependent. The caller should verify the validity of the
* account before requesting an auth token.
*/
+ @RequiresPermission(USE_CREDENTIALS)
public AccountManagerFuture<Bundle> getAuthToken(
final Account account, final String authTokenType, final Bundle options,
final Activity activity, AccountManagerCallback<Bundle> callback, Handler handler) {
@@ -1265,6 +1292,7 @@ public class AccountManager {
* boolean, AccountManagerCallback, android.os.Handler)} instead
*/
@Deprecated
+ @RequiresPermission(USE_CREDENTIALS)
public AccountManagerFuture<Bundle> getAuthToken(
final Account account, final String authTokenType,
final boolean notifyAuthFailure,
@@ -1343,6 +1371,7 @@ public class AccountManager {
* authenticator-dependent. The caller should verify the validity of the
* account before requesting an auth token.
*/
+ @RequiresPermission(USE_CREDENTIALS)
public AccountManagerFuture<Bundle> getAuthToken(
final Account account, final String authTokenType, final Bundle options,
final boolean notifyAuthFailure,
@@ -1412,6 +1441,7 @@ public class AccountManager {
* creating a new account, usually because of network trouble
* </ul>
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> addAccount(final String accountType,
final String authTokenType, final String[] requiredFeatures,
final Bundle addAccountOptions,
@@ -1587,7 +1617,7 @@ public class AccountManager {
* password prompt.
*
* <p>Also the returning Bundle may contain {@link
- * #KEY_LAST_AUTHENTICATE_TIME_MILLIS_EPOCH} indicating the last time the
+ * #KEY_LAST_AUTHENTICATED_TIME} indicating the last time the
* credential was validated/created.
*
* If an error occurred,{@link AccountManagerFuture#getResult()} throws:
@@ -1599,6 +1629,7 @@ public class AccountManager {
* verifying the password, usually because of network trouble
* </ul>
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> confirmCredentials(final Account account,
final Bundle options,
final Activity activity,
@@ -1675,6 +1706,7 @@ public class AccountManager {
* verifying the password, usually because of network trouble
* </ul>
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> updateCredentials(final Account account,
final String authTokenType,
final Bundle options, final Activity activity,
@@ -1726,6 +1758,7 @@ public class AccountManager {
* updating settings, usually because of network trouble
* </ul>
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> editProperties(final String accountType,
final Activity activity, final AccountManagerCallback<Bundle> callback,
final Handler handler) {
@@ -2259,6 +2292,7 @@ public class AccountManager {
* updating settings, usually because of network trouble
* </ul>
*/
+ @RequiresPermission(MANAGE_ACCOUNTS)
public AccountManagerFuture<Bundle> getAuthTokenByFeatures(
final String accountType, final String authTokenType, final String[] features,
final Activity activity, final Bundle addAccountOptions,
@@ -2383,6 +2417,7 @@ public class AccountManager {
* @throws IllegalArgumentException if listener is null
* @throws IllegalStateException if listener was already added
*/
+ @RequiresPermission(GET_ACCOUNTS)
public void addOnAccountsUpdatedListener(final OnAccountsUpdateListener listener,
Handler handler, boolean updateImmediately) {
if (listener == null) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 69cba78..7260d10 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -21,6 +21,7 @@ import android.annotation.DrawableRes;
import android.annotation.IdRes;
import android.annotation.IntDef;
import android.annotation.LayoutRes;
+import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StyleRes;
@@ -108,6 +109,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
/**
* An activity is a single, focused thing that the user can do. Almost all
@@ -706,8 +708,6 @@ public class Activity extends ContextThemeWrapper
/*package*/ ActivityThread mMainThread;
Activity mParent;
boolean mCalled;
- boolean mCheckedForLoaderManager;
- boolean mLoadersStarted;
/*package*/ boolean mResumed;
private boolean mStopped;
boolean mFinished;
@@ -726,8 +726,8 @@ public class Activity extends ContextThemeWrapper
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
- ArrayList<Fragment> fragments;
- ArrayMap<String, LoaderManagerImpl> loaders;
+ List<Fragment> fragments;
+ ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
/* package */ NonConfigurationInstances mLastNonConfigurationInstances;
@@ -747,26 +747,13 @@ public class Activity extends ContextThemeWrapper
private CharSequence mTitle;
private int mTitleColor = 0;
- final FragmentManagerImpl mFragments = new FragmentManagerImpl();
- final FragmentContainer mContainer = new FragmentContainer() {
- @Override
- @Nullable
- public View findViewById(int id) {
- return Activity.this.findViewById(id);
- }
- @Override
- public boolean hasView() {
- Window window = Activity.this.getWindow();
- return (window != null && window.peekDecorView() != null);
- }
- };
+ // we must have a handler before the FragmentController is constructed
+ final Handler mHandler = new Handler();
+ final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
// Most recent call to requestVisibleBehind().
boolean mVisibleBehind;
- ArrayMap<String, LoaderManagerImpl> mAllLoaderManagers;
- LoaderManagerImpl mLoaderManager;
-
private static final class ManagedCursor {
ManagedCursor(Cursor cursor) {
mCursor = cursor;
@@ -802,7 +789,6 @@ public class Activity extends ContextThemeWrapper
private final Object mInstanceTracker = StrictMode.trackActivity(this);
private Thread mUiThread;
- final Handler mHandler = new Handler();
ActivityTransitionState mActivityTransitionState = new ActivityTransitionState();
SharedElementCallback mEnterTransitionListener = SharedElementCallback.NULL_CALLBACK;
@@ -863,28 +849,7 @@ public class Activity extends ContextThemeWrapper
* Return the LoaderManager for this activity, creating it if needed.
*/
public LoaderManager getLoaderManager() {
- if (mLoaderManager != null) {
- return mLoaderManager;
- }
- mCheckedForLoaderManager = true;
- mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);
- return mLoaderManager;
- }
-
- LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
- if (mAllLoaderManagers == null) {
- mAllLoaderManagers = new ArrayMap<String, LoaderManagerImpl>();
- }
- LoaderManagerImpl lm = mAllLoaderManagers.get(who);
- if (lm == null) {
- if (create) {
- lm = new LoaderManagerImpl(who, this, started);
- mAllLoaderManagers.put(who, lm);
- }
- } else {
- lm.updateActivity(this);
- }
- return lm;
+ return mFragments.getLoaderManager();
}
/**
@@ -927,11 +892,12 @@ public class Activity extends ContextThemeWrapper
* @see #onRestoreInstanceState
* @see #onPostCreate
*/
+ @MainThread
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState);
if (mLastNonConfigurationInstances != null) {
- mAllLoaderManagers = mLastNonConfigurationInstances.loaders;
+ mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders);
}
if (mActivityInfo.parentActivityName != null) {
if (mActionBar == null) {
@@ -1172,15 +1138,7 @@ public class Activity extends ContextThemeWrapper
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStart " + this);
mCalled = true;
- if (!mLoadersStarted) {
- mLoadersStarted = true;
- if (mLoaderManager != null) {
- mLoaderManager.doStart();
- } else if (!mCheckedForLoaderManager) {
- mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);
- }
- mCheckedForLoaderManager = true;
- }
+ mFragments.doLoaderStart();
getApplication().dispatchActivityStarted(this);
}
@@ -1873,27 +1831,9 @@ public class Activity extends ContextThemeWrapper
NonConfigurationInstances retainNonConfigurationInstances() {
Object activity = onRetainNonConfigurationInstance();
HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
- ArrayList<Fragment> fragments = mFragments.retainNonConfig();
- boolean retainLoaders = false;
- if (mAllLoaderManagers != null) {
- // prune out any loader managers that were already stopped and so
- // have nothing useful to retain.
- final int N = mAllLoaderManagers.size();
- LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
- for (int i=N-1; i>=0; i--) {
- loaders[i] = mAllLoaderManagers.valueAt(i);
- }
- for (int i=0; i<N; i++) {
- LoaderManagerImpl lm = loaders[i];
- if (lm.mRetaining) {
- retainLoaders = true;
- } else {
- lm.doDestroy();
- mAllLoaderManagers.remove(lm.mWho);
- }
- }
- }
- if (activity == null && children == null && fragments == null && !retainLoaders
+ List<Fragment> fragments = mFragments.retainNonConfig();
+ ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();
+ if (activity == null && children == null && fragments == null && loaders == null
&& mVoiceInteractor == null) {
return null;
}
@@ -1902,7 +1842,7 @@ public class Activity extends ContextThemeWrapper
nci.activity = activity;
nci.children = children;
nci.fragments = fragments;
- nci.loaders = mAllLoaderManagers;
+ nci.loaders = loaders;
nci.voiceInteractor = mVoiceInteractor;
return nci;
}
@@ -1924,18 +1864,7 @@ public class Activity extends ContextThemeWrapper
* with this activity.
*/
public FragmentManager getFragmentManager() {
- return mFragments;
- }
-
- void invalidateFragment(String who) {
- //Log.v(TAG, "invalidateFragmentIndex: index=" + index);
- if (mAllLoaderManagers != null) {
- LoaderManagerImpl lm = mAllLoaderManagers.get(who);
- if (lm != null && !lm.mRetaining) {
- lm.doDestroy();
- mAllLoaderManagers.remove(who);
- }
- }
+ return mFragments.getFragmentManager();
}
/**
@@ -2518,7 +2447,7 @@ public class Activity extends ContextThemeWrapper
return;
}
- if (!mFragments.popBackStackImmediate()) {
+ if (!mFragments.getFragmentManager().popBackStackImmediate()) {
finishAfterTransition();
}
}
@@ -5518,21 +5447,13 @@ public class Activity extends ContextThemeWrapper
writer.print(mResumed); writer.print(" mStopped=");
writer.print(mStopped); writer.print(" mFinished=");
writer.println(mFinished);
- writer.print(innerPrefix); writer.print("mLoadersStarted=");
- writer.println(mLoadersStarted);
writer.print(innerPrefix); writer.print("mChangingConfigurations=");
writer.println(mChangingConfigurations);
writer.print(innerPrefix); writer.print("mCurrentConfig=");
writer.println(mCurrentConfig);
- if (mLoaderManager != null) {
- writer.print(prefix); writer.print("Loader Manager ");
- writer.print(Integer.toHexString(System.identityHashCode(mLoaderManager)));
- writer.println(":");
- mLoaderManager.dump(prefix + " ", fd, writer, args);
- }
-
- mFragments.dump(prefix, fd, writer, args);
+ mFragments.dumpLoaders(innerPrefix, fd, writer, args);
+ mFragments.getFragmentManager().dump(innerPrefix, fd, writer, args);
if (getWindow() != null &&
getWindow().peekDecorView() != null &&
@@ -6128,7 +6049,7 @@ public class Activity extends ContextThemeWrapper
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);
- mFragments.attachActivity(this, mContainer, null);
+ mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
@@ -6211,18 +6132,7 @@ public class Activity extends ContextThemeWrapper
" did not call through to super.onStart()");
}
mFragments.dispatchStart();
- if (mAllLoaderManagers != null) {
- final int N = mAllLoaderManagers.size();
- LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
- for (int i=N-1; i>=0; i--) {
- loaders[i] = mAllLoaderManagers.valueAt(i);
- }
- for (int i=0; i<N; i++) {
- LoaderManagerImpl lm = loaders[i];
- lm.finishRetain();
- lm.doReportStart();
- }
- }
+ mFragments.reportLoaderStart();
mActivityTransitionState.enterReady(this);
}
@@ -6328,16 +6238,7 @@ public class Activity extends ContextThemeWrapper
final void performStop() {
mDoReportFullyDrawn = false;
- if (mLoadersStarted) {
- mLoadersStarted = false;
- if (mLoaderManager != null) {
- if (!mChangingConfigurations) {
- mLoaderManager.doStop();
- } else {
- mLoaderManager.doRetain();
- }
- }
- }
+ mFragments.doLoaderStop(mChangingConfigurations /*retain*/);
if (!mStopped) {
if (mWindow != null) {
@@ -6379,9 +6280,7 @@ public class Activity extends ContextThemeWrapper
mWindow.destroy();
mFragments.dispatchDestroy();
onDestroy();
- if (mLoaderManager != null) {
- mLoaderManager.doDestroy();
- }
+ mFragments.doLoaderDestroy();
if (mVoiceInteractor != null) {
mVoiceInteractor.detachActivity();
}
@@ -6541,4 +6440,79 @@ public class Activity extends ContextThemeWrapper
return intent != null
&& PackageManager.ACTION_REQUEST_PERMISSIONS.equals(intent.getAction());
}
+
+ class HostCallbacks extends FragmentHostCallback<Activity> {
+ public HostCallbacks() {
+ super(Activity.this /*activity*/);
+ }
+
+ @Override
+ public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ Activity.this.dump(prefix, fd, writer, args);
+ }
+
+ @Override
+ public boolean onShouldSaveFragmentState(Fragment fragment) {
+ return !isFinishing();
+ }
+
+ @Override
+ public LayoutInflater onGetLayoutInflater() {
+ final LayoutInflater result = Activity.this.getLayoutInflater();
+ if (onUseFragmentManagerInflaterFactory()) {
+ return result.cloneInContext(Activity.this);
+ }
+ return result;
+ }
+
+ @Override
+ public boolean onUseFragmentManagerInflaterFactory() {
+ // Newer platform versions use the child fragment manager's LayoutInflaterFactory.
+ return getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP;
+ }
+
+ @Override
+ public Activity onGetHost() {
+ return Activity.this;
+ }
+
+ @Override
+ public void onInvalidateOptionsMenu() {
+ Activity.this.invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode,
+ Bundle options) {
+ Activity.this.startActivityFromFragment(fragment, intent, requestCode, options);
+ }
+
+ @Override
+ public boolean onHasWindowAnimations() {
+ return getWindow() != null;
+ }
+
+ @Override
+ public int onGetWindowAnimations() {
+ final Window w = getWindow();
+ return (w == null) ? 0 : w.getAttributes().windowAnimations;
+ }
+
+ @Override
+ public void onAttachFragment(Fragment fragment) {
+ Activity.this.onAttachFragment(fragment);
+ }
+
+ @Nullable
+ @Override
+ public View onFindViewById(int id) {
+ return Activity.this.findViewById(id);
+ }
+
+ @Override
+ public boolean onHasView() {
+ final Window w = getWindow();
+ return (w != null && w.peekDecorView() != null);
+ }
+ }
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 576a046..9bbb4be 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -16,8 +16,10 @@
package android.app;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.graphics.Canvas;
import android.graphics.Matrix;
@@ -26,6 +28,7 @@ import android.os.BatteryStats;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.util.Log;
import com.android.internal.app.ProcessStats;
import com.android.internal.os.TransferPipe;
import com.android.internal.util.FastPrintWriter;
@@ -2396,7 +2399,24 @@ public class ActivityManager {
} catch (RemoteException e) {
}
}
-
+
+ /**
+ * Kills the specified UID.
+ * @param uid The UID to kill.
+ * @param reason The reason for the kill.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.KILL_UID)
+ public void killUid(int uid, String reason) {
+ try {
+ ActivityManagerNative.getDefault().killUid(uid, reason);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Couldn't kill uid:" + uid, e);
+ }
+ }
+
/**
* Have the system perform a force stop of everything associated with
* the given application package. All processes that share its uid
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 9bad9bb..da6d8c5 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -114,7 +114,6 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.net.InetAddress;
-import java.security.Security;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.List;
@@ -1639,6 +1638,12 @@ public final class ActivityThread {
return sCurrentActivityThread;
}
+ public static String currentOpPackageName() {
+ ActivityThread am = currentActivityThread();
+ return (am != null && am.getApplication() != null)
+ ? am.getApplication().getOpPackageName() : null;
+ }
+
public static String currentPackageName() {
ActivityThread am = currentActivityThread();
return (am != null && am.mBoundApplication != null)
@@ -5338,7 +5343,7 @@ public final class ActivityThread {
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
- Security.addProvider(new AndroidKeyStoreProvider());
+ AndroidKeyStoreProvider.install();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 9d1d312..b0fda9c 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -26,6 +26,10 @@ import android.os.Parcelable;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.text.TextUtils;
+import libcore.util.ZoneInfoDB;
+
+import java.io.IOException;
/**
* This class provides access to the system alarm services. These allow you
@@ -151,6 +155,7 @@ public class AlarmManager
private final IAlarmManager mService;
private final boolean mAlwaysExact;
+ private final int mTargetSdkVersion;
/**
@@ -159,8 +164,8 @@ public class AlarmManager
AlarmManager(IAlarmManager service, Context ctx) {
mService = service;
- final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion;
- mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KITKAT);
+ mTargetSdkVersion = ctx.getApplicationInfo().targetSdkVersion;
+ mAlwaysExact = (mTargetSdkVersion < Build.VERSION_CODES.KITKAT);
}
private long legacyExactLength() {
@@ -585,12 +590,38 @@ public class AlarmManager
}
/**
- * Set the system default time zone.
- * Requires the permission android.permission.SET_TIME_ZONE.
- *
- * @param timeZone in the format understood by {@link java.util.TimeZone}
+ * Sets the system's persistent default time zone. This is the time zone for all apps, even
+ * after a reboot. Use {@link java.util.TimeZone#setDefault} if you just want to change the
+ * time zone within your app, and even then prefer to pass an explicit
+ * {@link java.util.TimeZone} to APIs that require it rather than changing the time zone for
+ * all threads.
+ *
+ * <p> On android M and above, it is an error to pass in a non-Olson timezone to this
+ * function. Note that this is a bad idea on all Android releases because POSIX and
+ * the {@code TimeZone} class have opposite interpretations of {@code '+'} and {@code '-'}
+ * in the same non-Olson ID.
+ *
+ * @param timeZone one of the Olson ids from the list returned by
+ * {@link java.util.TimeZone#getAvailableIDs}
*/
public void setTimeZone(String timeZone) {
+ if (TextUtils.isEmpty(timeZone)) {
+ return;
+ }
+
+ // Reject this timezone if it isn't an Olson zone we recognize.
+ if (mTargetSdkVersion >= Build.VERSION_CODES.MNC) {
+ boolean hasTimeZone = false;
+ try {
+ hasTimeZone = ZoneInfoDB.getInstance().hasTimeZone(timeZone);
+ } catch (IOException ignored) {
+ }
+
+ if (!hasTimeZone) {
+ throw new IllegalArgumentException("Timezone: " + timeZone + " is not an Olson ID");
+ }
+ }
+
try {
mService.setTimeZone(timeZone);
} catch (RemoteException ex) {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 223d528..5aa399b 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -217,12 +217,21 @@ public class AppOpsManager {
public static final int OP_READ_PHONE_STATE = 51;
/** @hide Add voicemail messages to the voicemail content provider. */
public static final int OP_ADD_VOICEMAIL = 52;
+ /** @hide Access APIs for SIP calling over VOIP or WiFi. */
+ public static final int OP_USE_SIP = 53;
+ /** @hide Intercept outgoing calls. */
+ public static final int OP_PROCESS_OUTGOING_CALLS = 54;
+ /** @hide User the fingerprint API. */
+ public static final int OP_USE_FINGERPRINT = 55;
+ /** @hide Access to body sensors such as heart rate, etc. */
+ public static final int OP_BODY_SENSORS = 56;
+ /** @hide Read previously received cell broadcast messages. */
+ public static final int OP_READ_CELL_BROADCASTS = 57;
/** @hide */
- public static final int _NUM_OP = 53;
+ public static final int _NUM_OP = 58;
/** Access to coarse location information. */
- public static final String OPSTR_COARSE_LOCATION =
- "android:coarse_location";
+ public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
/** Access to fine location information. */
public static final String OPSTR_FINE_LOCATION =
"android:fine_location";
@@ -237,7 +246,68 @@ public class AppOpsManager {
= "android:get_usage_stats";
/** Activate a VPN connection without user intervention. @hide */
@SystemApi
- public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
+ public static final String OPSTR_ACTIVATE_VPN
+ = "android:activate_vpn";
+ /** @hide Allows an application to read the user's contacts data. */
+ public static final String OPSTR_READ_CONTACTS
+ = "android:read_contacts";
+ /** @hide Allows an application to write to the user's contacts data. */
+ public static final String OPSTR_WRITE_CONTACTS
+ = "android:write_contacts";
+ /** @hide Allows an application to read the user's call log. */
+ public static final String OPSTR_READ_CALL_LOG
+ = "android:read_call_log";
+ /** @hide Allows an application to write to the user's call log. */
+ public static final String OPSTR_WRITE_CALL_LOG
+ = "android:write_call_log";
+ /** @hide Allows an application to read the user's calendar data. */
+ public static final String OPSTR_READ_CALENDAR
+ = "android:read_calendar";
+ /** @hide Allows an application to write to the user's calendar data. */
+ public static final String OPSTR_WRITE_CALENDAR
+ = "android:write_calendar";
+ /** @hide Allows an application to initiate a phone call. */
+ public static final String OPSTR_CALL_PHONE
+ = "android:call_phone";
+ /** @hide Allows an application to read SMS messages. */
+ public static final String OPSTR_READ_SMS
+ = "android:read_sms";
+ /** @hide Allows an application to receive SMS messages. */
+ public static final String OPSTR_RECEIVE_SMS
+ = "android:receive_sms";
+ /** @hide Allows an application to receive MMS messages. */
+ public static final String OPSTR_RECEIVE_MMS
+ = "android:receive_mms";
+ /** @hide Allows an application to receive WAP push messages. */
+ public static final String OPSTR_RECEIVE_WAP_PUSH
+ = "android:receive_wap_push";
+ /** @hide Allows an application to send SMS messages. */
+ public static final String OPSTR_SEND_SMS
+ = "android:send_sms";
+ /** @hide Required to be able to access the camera device. */
+ public static final String OPSTR_CAMERA
+ = "android:camera";
+ /** @hide Required to be able to access the microphone device. */
+ public static final String OPSTR_RECORD_AUDIO
+ = "android:record_audio";
+ /** @hide Required to access phone state related information. */
+ public static final String OPSTR_READ_PHONE_STATE
+ = "android:read_phone_state";
+ /** @hide Required to access phone state related information. */
+ public static final String OPSTR_ADD_VOICEMAIL
+ = "android:add_voicemail";
+ /** @hide Access APIs for SIP calling over VOIP or WiFi */
+ public static final String OPSTR_USE_SIP
+ = "android:use_sip";
+ /** @hide Use the fingerprint API. */
+ public static final String OPSTR_USE_FINGERPRINT
+ = "android:use_fingerprint";
+ /** @hide Access to body sensors such as heart rate, etc. */
+ public static final String OPSTR_BODY_SENSORS
+ = "android:body_sensors";
+ /** @hide Read previously received cell broadcast messages. */
+ public static final String OPSTR_READ_CELL_BROADCASTS
+ = "android:read_cell_broadcasts";
/**
* This maps each operation to the operation that serves as the
@@ -300,7 +370,12 @@ public class AppOpsManager {
OP_ASSIST_STRUCTURE,
OP_ASSIST_SCREENSHOT,
OP_READ_PHONE_STATE,
- OP_ADD_VOICEMAIL
+ OP_ADD_VOICEMAIL,
+ OP_USE_SIP,
+ OP_PROCESS_OUTGOING_CALLS,
+ OP_USE_FINGERPRINT,
+ OP_BODY_SENSORS,
+ OP_READ_CELL_BROADCASTS
};
/**
@@ -312,30 +387,30 @@ public class AppOpsManager {
OPSTR_FINE_LOCATION,
null,
null,
+ OPSTR_READ_CONTACTS,
+ OPSTR_WRITE_CONTACTS,
+ OPSTR_READ_CALL_LOG,
+ OPSTR_WRITE_CALL_LOG,
+ OPSTR_READ_CALENDAR,
+ OPSTR_WRITE_CALENDAR,
null,
null,
null,
+ OPSTR_CALL_PHONE,
+ OPSTR_READ_SMS,
null,
+ OPSTR_RECEIVE_SMS,
null,
+ OPSTR_RECEIVE_MMS,
+ OPSTR_RECEIVE_WAP_PUSH,
+ OPSTR_SEND_SMS,
null,
null,
null,
null,
null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
- null,
+ OPSTR_CAMERA,
+ OPSTR_RECORD_AUDIO,
null,
null,
null,
@@ -359,8 +434,13 @@ public class AppOpsManager {
null,
null,
null,
+ OPSTR_READ_PHONE_STATE,
+ OPSTR_ADD_VOICEMAIL,
+ OPSTR_USE_SIP,
null,
- null
+ OPSTR_USE_FINGERPRINT,
+ OPSTR_BODY_SENSORS,
+ OPSTR_READ_CELL_BROADCASTS
};
/**
@@ -420,7 +500,12 @@ public class AppOpsManager {
"ASSIST_STRUCTURE",
"ASSIST_SCREENSHOT",
"OP_READ_PHONE_STATE",
- "ADD_VOICEMAIL"
+ "ADD_VOICEMAIL",
+ "USE_SIP",
+ "PROCESS_OUTGOING_CALLS",
+ "USE_FINGERPRINT",
+ "BODY_SENSORS",
+ "READ_CELL_BROADCASTS"
};
/**
@@ -480,7 +565,12 @@ public class AppOpsManager {
null, // no permission for receiving assist structure
null, // no permission for receiving assist screenshot
Manifest.permission.READ_PHONE_STATE,
- Manifest.permission.ADD_VOICEMAIL
+ Manifest.permission.ADD_VOICEMAIL,
+ Manifest.permission.USE_SIP,
+ Manifest.permission.PROCESS_OUTGOING_CALLS,
+ Manifest.permission.USE_FINGERPRINT,
+ Manifest.permission.BODY_SENSORS,
+ Manifest.permission.READ_CELL_BROADCASTS
};
/**
@@ -541,7 +631,12 @@ public class AppOpsManager {
null, // ASSIST_STRUCTURE
null, // ASSIST_SCREENSHOT
null, // READ_PHONE_STATE
- null // ADD_VOICEMAIL
+ null, // ADD_VOICEMAIL
+ null, // USE_SIP
+ null, // PROCESS_OUTGOING_CALLS
+ null, // USE_FINGERPRINT
+ null, // BODY_SENSORS
+ null // READ_CELL_BROADCASTS
};
/**
@@ -601,7 +696,12 @@ public class AppOpsManager {
false, //ASSIST_STRUCTURE
false, //ASSIST_SCREENSHOT
false, //READ_PHONE_STATE
- false //ADD_VOICEMAIL
+ false, //ADD_VOICEMAIL
+ false, // USE_SIP
+ false, // PROCESS_OUTGOING_CALLS
+ false, // USE_FINGERPRINT
+ false, // BODY_SENSORS
+ false // READ_CELL_BROADCASTS
};
/**
@@ -660,6 +760,11 @@ public class AppOpsManager {
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED,
+ AppOpsManager.MODE_ALLOWED,
AppOpsManager.MODE_ALLOWED
};
@@ -723,10 +828,23 @@ public class AppOpsManager {
false,
false,
false,
+ false,
+ false,
+ false,
+ false,
+ false,
false
};
- private static HashMap<String, Integer> sOpStrToOp = new HashMap<String, Integer>();
+ /**
+ * Mapping from an app op name to the app op code.
+ */
+ private static HashMap<String, Integer> sOpStrToOp = new HashMap<>();
+
+ /**
+ * Mapping from a permission to the corresponding app op.
+ */
+ private static HashMap<String, Integer> sPermToOp = new HashMap<>();
static {
if (sOpToSwitch.length != _NUM_OP) {
@@ -766,6 +884,11 @@ public class AppOpsManager {
sOpStrToOp.put(sOpToString[i], i);
}
}
+ for (int i=0; i<_NUM_OP; i++) {
+ if (sOpPerms[i] != null) {
+ sPermToOp.put(sOpPerms[i], i);
+ }
+ }
}
/**
@@ -814,6 +937,15 @@ public class AppOpsManager {
}
/**
+ * Retrieve the app op code for a permission, or null if there is not one.
+ * @hide
+ */
+ public static int permissionToOpCode(String permission) {
+ Integer boxedOpCode = sPermToOp.get(permission);
+ return boxedOpCode != null ? boxedOpCode : OP_NONE;
+ }
+
+ /**
* Retrieve whether the op allows the system (and system ui) to
* bypass the user restriction.
* @hide
@@ -1066,6 +1198,25 @@ public class AppOpsManager {
}
/**
+ * Gets the app op name associated with a given permission.
+ * The app op name is one of the public constants defined
+ * in this class such as {@link #OPSTR_COARSE_LOCATION}.
+ *
+ * @param permission The permission.
+ * @return The app op associated with the permission or null.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static String permissionToOp(String permission) {
+ final Integer opCode = sPermToOp.get(permission);
+ if (opCode == null) {
+ return null;
+ }
+ return sOpToString[opCode];
+ }
+
+ /**
* Monitor for changes to the operating mode for the given op in the given app package.
* @param op The operation to monitor, one of OPSTR_*.
* @param packageName The name of the application to monitor.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 16a2430..90293a4 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -62,6 +62,7 @@ import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -79,6 +80,7 @@ import android.view.Display;
import dalvik.system.VMRuntime;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.os.SomeArgs;
import com.android.internal.util.Preconditions;
import com.android.internal.util.UserIcons;
@@ -1560,13 +1562,7 @@ final class ApplicationPackageManager extends PackageManager {
public @Nullable VolumeInfo getPrimaryStorageCurrentVolume() {
final StorageManager storage = mContext.getSystemService(StorageManager.class);
final String volumeUuid = storage.getPrimaryStorageUuid();
- if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
- return storage.findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
- } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
- return storage.getPrimaryPhysicalVolume();
- } else {
- return storage.findVolumeByUuid(volumeUuid);
- }
+ return storage.findVolumeByQualifiedUuid(volumeUuid);
}
@Override
@@ -2054,7 +2050,7 @@ final class ApplicationPackageManager extends PackageManager {
/** {@hide} */
private static class MoveCallbackDelegate extends IPackageMoveObserver.Stub implements
Handler.Callback {
- private static final int MSG_STARTED = 1;
+ private static final int MSG_CREATED = 1;
private static final int MSG_STATUS_CHANGED = 2;
final MoveCallback mCallback;
@@ -2067,26 +2063,38 @@ final class ApplicationPackageManager extends PackageManager {
@Override
public boolean handleMessage(Message msg) {
- final int moveId = msg.arg1;
switch (msg.what) {
- case MSG_STARTED:
- mCallback.onStarted(moveId, (String) msg.obj);
+ case MSG_CREATED: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ mCallback.onCreated(args.argi1, (Bundle) args.arg2);
+ args.recycle();
return true;
- case MSG_STATUS_CHANGED:
- mCallback.onStatusChanged(moveId, msg.arg2, (long) msg.obj);
+ }
+ case MSG_STATUS_CHANGED: {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ mCallback.onStatusChanged(args.argi1, args.argi2, (long) args.arg3);
+ args.recycle();
return true;
+ }
}
return false;
}
@Override
- public void onStarted(int moveId, String title) {
- mHandler.obtainMessage(MSG_STARTED, moveId, 0, title).sendToTarget();
+ public void onCreated(int moveId, Bundle extras) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.argi1 = moveId;
+ args.arg2 = extras;
+ mHandler.obtainMessage(MSG_CREATED, args).sendToTarget();
}
@Override
public void onStatusChanged(int moveId, int status, long estMillis) {
- mHandler.obtainMessage(MSG_STATUS_CHANGED, moveId, status, estMillis).sendToTarget();
+ final SomeArgs args = SomeArgs.obtain();
+ args.argi1 = moveId;
+ args.argi2 = status;
+ args.arg3 = estMillis;
+ mHandler.obtainMessage(MSG_STATUS_CHANGED, args).sendToTarget();
}
}
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java
index 9946d79..3abbb5b 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -224,6 +224,7 @@ final public class AssistStructure implements Parcelable {
static final int FLAGS_CHECKED = 0x00000200;
static final int FLAGS_CLICKABLE = 0x00004000;
static final int FLAGS_LONG_CLICKABLE = 0x00200000;
+ static final int FLAGS_STYLUS_BUTTON_PRESSABLE = 0x00400000;
int mFlags;
@@ -401,6 +402,10 @@ final public class AssistStructure implements Parcelable {
return (mFlags&ViewNode.FLAGS_LONG_CLICKABLE) != 0;
}
+ public boolean isStylusButtonPressable() {
+ return (mFlags&ViewNode.FLAGS_STYLUS_BUTTON_PRESSABLE) != 0;
+ }
+
public String getClassName() {
return mClassName;
}
@@ -513,6 +518,12 @@ final public class AssistStructure implements Parcelable {
}
@Override
+ public void setStylusButtonPressable(boolean state) {
+ mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_STYLUS_BUTTON_PRESSABLE)
+ | (state ? ViewNode.FLAGS_STYLUS_BUTTON_PRESSABLE : 0);
+ }
+
+ @Override
public void setFocusable(boolean state) {
mNode.mFlags = (mNode.mFlags&~ViewNode.FLAGS_FOCUSABLE)
| (state ? ViewNode.FLAGS_FOCUSABLE : 0);
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 8fb048b..49644a7 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -416,14 +416,14 @@ final class BackStackRecord extends FragmentTransaction implements
public CharSequence getBreadCrumbTitle() {
if (mBreadCrumbTitleRes != 0) {
- return mManager.mActivity.getText(mBreadCrumbTitleRes);
+ return mManager.mHost.getContext().getText(mBreadCrumbTitleRes);
}
return mBreadCrumbTitleText;
}
public CharSequence getBreadCrumbShortTitle() {
if (mBreadCrumbShortTitleRes != 0) {
- return mManager.mActivity.getText(mBreadCrumbShortTitleRes);
+ return mManager.mHost.getContext().getText(mBreadCrumbShortTitleRes);
}
return mBreadCrumbShortTitleText;
}
@@ -868,7 +868,7 @@ final class BackStackRecord extends FragmentTransaction implements
*/
private void calculateFragments(SparseArray<Fragment> firstOutFragments,
SparseArray<Fragment> lastInFragments) {
- if (!mManager.mContainer.hasView()) {
+ if (!mManager.mContainer.onHasView()) {
return; // nothing to see, so no transitions
}
Op op = mHead;
@@ -926,7 +926,7 @@ final class BackStackRecord extends FragmentTransaction implements
*/
public void calculateBackFragments(SparseArray<Fragment> firstOutFragments,
SparseArray<Fragment> lastInFragments) {
- if (!mManager.mContainer.hasView()) {
+ if (!mManager.mContainer.onHasView()) {
return; // nothing to see, so no transitions
}
Op op = mHead;
@@ -1002,7 +1002,7 @@ final class BackStackRecord extends FragmentTransaction implements
// Adding a non-existent target view makes sure that the transitions don't target
// any views by default. They'll only target the views we tell add. If we don't
// add any, then no views will be targeted.
- state.nonExistentView = new View(mManager.mActivity);
+ state.nonExistentView = new View(mManager.mHost.getContext());
// Go over all leaving fragments.
for (int i = 0; i < firstOutFragments.size(); i++) {
@@ -1275,7 +1275,7 @@ final class BackStackRecord extends FragmentTransaction implements
*/
private void configureTransitions(int containerId, TransitionState state, boolean isBack,
SparseArray<Fragment> firstOutFragments, SparseArray<Fragment> lastInFragments) {
- ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.findViewById(containerId);
+ ViewGroup sceneRoot = (ViewGroup) mManager.mContainer.onFindViewById(containerId);
if (sceneRoot != null) {
Fragment inFragment = lastInFragments.get(containerId);
Fragment outFragment = firstOutFragments.get(containerId);
diff --git a/core/java/android/app/DialogFragment.java b/core/java/android/app/DialogFragment.java
index bde5a61..2fb8cc2 100644
--- a/core/java/android/app/DialogFragment.java
+++ b/core/java/android/app/DialogFragment.java
@@ -410,7 +410,7 @@ public class DialogFragment extends Fragment
return (LayoutInflater)mDialog.getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
}
- return (LayoutInflater)mActivity.getSystemService(
+ return (LayoutInflater) mHost.getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
}
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 169952a..0f286fb 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -239,7 +239,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
}
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this,
- mAllSharedElementNames, resultCode, data);
+ mSharedElementNames, resultCode, data);
mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
@Override
public void onTranslucentConversionComplete(boolean drawComplete) {
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 4fdae7f..40c5c64 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -26,7 +26,6 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -94,19 +93,20 @@ final class FragmentState implements Parcelable {
mSavedFragmentState = in.readBundle();
}
- public Fragment instantiate(Activity activity, Fragment parent) {
+ public Fragment instantiate(FragmentHostCallback host, Fragment parent) {
if (mInstance != null) {
return mInstance;
}
+ final Context context = host.getContext();
if (mArguments != null) {
- mArguments.setClassLoader(activity.getClassLoader());
+ mArguments.setClassLoader(context.getClassLoader());
}
- mInstance = Fragment.instantiate(activity, mClassName, mArguments);
+ mInstance = Fragment.instantiate(context, mClassName, mArguments);
if (mSavedFragmentState != null) {
- mSavedFragmentState.setClassLoader(activity.getClassLoader());
+ mSavedFragmentState.setClassLoader(context.getClassLoader());
mInstance.mSavedFragmentState = mSavedFragmentState;
}
mInstance.setIndex(mIndex, parent);
@@ -117,7 +117,7 @@ final class FragmentState implements Parcelable {
mInstance.mTag = mTag;
mInstance.mRetainInstance = mRetainInstance;
mInstance.mDetached = mDetached;
- mInstance.mFragmentManager = activity.mFragments;
+ mInstance.mFragmentManager = host.mFragmentManager;
if (FragmentManagerImpl.DEBUG) Log.v(FragmentManagerImpl.TAG,
"Instantiated fragment " + mInstance);
@@ -425,7 +425,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
FragmentManagerImpl mFragmentManager;
// Activity this fragment is attached to.
- Activity mActivity;
+ FragmentHostCallback mHost;
// Private fragment manager for child fragments inside of this one.
FragmentManagerImpl mChildFragmentManager;
@@ -775,20 +775,36 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
}
/**
+ * Return the {@link Context} this fragment is currently associated with.
+ */
+ public Context getContext() {
+ return mHost == null ? null : mHost.getContext();
+ }
+
+ /**
* Return the Activity this fragment is currently associated with.
*/
final public Activity getActivity() {
- return mActivity;
+ return mHost == null ? null : mHost.getActivity();
+ }
+
+ /**
+ * Return the host object of this fragment. May return {@code null} if the fragment
+ * isn't currently being hosted.
+ */
+ @Nullable
+ final public Object getHost() {
+ return mHost == null ? null : mHost.onGetHost();
}
/**
* Return <code>getActivity().getResources()</code>.
*/
final public Resources getResources() {
- if (mActivity == null) {
+ if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
- return mActivity.getResources();
+ return mHost.getContext().getResources();
}
/**
@@ -870,7 +886,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
* Return true if the fragment is currently added to its activity.
*/
final public boolean isAdded() {
- return mActivity != null && mAdded;
+ return mHost != null && mAdded;
}
/**
@@ -1037,11 +1053,11 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
if (mLoaderManager != null) {
return mLoaderManager;
}
- if (mActivity == null) {
+ if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, true);
+ mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, true);
return mLoaderManager;
}
@@ -1065,15 +1081,15 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
* Context.startActivity(Intent, Bundle)} for more details.
*/
public void startActivity(Intent intent, Bundle options) {
- if (mActivity == null) {
+ if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
if (options != null) {
- mActivity.startActivityFromFragment(this, intent, -1, options);
+ mHost.onStartActivityFromFragment(this, intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
- mActivity.startActivityFromFragment(this, intent, -1);
+ mHost.onStartActivityFromFragment(this, intent, -1, null /*options*/);
}
}
@@ -1090,10 +1106,10 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
* containing Activity.
*/
public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
- if (mActivity == null) {
+ if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
- mActivity.startActivityFromFragment(this, intent, requestCode, options);
+ mHost.onStartActivityFromFragment(this, intent, requestCode, options);
}
/**
@@ -1181,11 +1197,12 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
* @see android.content.Context#checkSelfPermission(String)
*/
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
- if (mActivity == null) {
+ if (mHost == null) {
throw new IllegalStateException("Fragment " + this + " not attached to Activity");
}
- Intent intent = mActivity.getPackageManager().buildRequestPermissionsIntent(permissions);
- mActivity.startActivityFromFragment(this, intent, requestCode, null);
+ Intent intent =
+ mHost.getContext().getPackageManager().buildRequestPermissionsIntent(permissions);
+ mHost.onStartActivityFromFragment(this, intent, requestCode, null);
}
/**
@@ -1211,19 +1228,16 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
* inflation. Maybe this should become a public API. Note sure.
*/
public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
- // Newer platform versions use the child fragment manager's LayoutInflaterFactory.
- if (mActivity.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
- LayoutInflater result = mActivity.getLayoutInflater().cloneInContext(mActivity);
+ final LayoutInflater result = mHost.onGetLayoutInflater();
+ if (mHost.onUseFragmentManagerInflaterFactory()) {
getChildFragmentManager(); // Init if needed; use raw implementation below.
result.setPrivateFactory(mChildFragmentManager.getLayoutInflaterFactory());
- return result;
- } else {
- return mActivity.getLayoutInflater();
}
+ return result;
}
/**
- * @deprecated Use {@link #onInflate(Activity, AttributeSet, Bundle)} instead.
+ * @deprecated Use {@link #onInflate(Context, AttributeSet, Bundle)} instead.
*/
@Deprecated
public void onInflate(AttributeSet attrs, Bundle savedInstanceState) {
@@ -1266,29 +1280,29 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
* {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentArguments.java
* create}
*
- * @param activity The Activity that is inflating this fragment.
+ * @param context The Context that is inflating this fragment.
* @param attrs The attributes at the tag where the fragment is
* being created.
* @param savedInstanceState If the fragment is being re-created from
* a previous saved state, this is the state.
*/
- public void onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState) {
+ public void onInflate(Context context, AttributeSet attrs, Bundle savedInstanceState) {
onInflate(attrs, savedInstanceState);
mCalled = true;
- TypedArray a = activity.obtainStyledAttributes(attrs,
+ TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.Fragment);
- mEnterTransition = loadTransition(activity, a, mEnterTransition, null,
+ mEnterTransition = loadTransition(context, a, mEnterTransition, null,
com.android.internal.R.styleable.Fragment_fragmentEnterTransition);
- mReturnTransition = loadTransition(activity, a, mReturnTransition, USE_DEFAULT_TRANSITION,
+ mReturnTransition = loadTransition(context, a, mReturnTransition, USE_DEFAULT_TRANSITION,
com.android.internal.R.styleable.Fragment_fragmentReturnTransition);
- mExitTransition = loadTransition(activity, a, mExitTransition, null,
+ mExitTransition = loadTransition(context, a, mExitTransition, null,
com.android.internal.R.styleable.Fragment_fragmentExitTransition);
- mReenterTransition = loadTransition(activity, a, mReenterTransition, USE_DEFAULT_TRANSITION,
+ mReenterTransition = loadTransition(context, a, mReenterTransition, USE_DEFAULT_TRANSITION,
com.android.internal.R.styleable.Fragment_fragmentReenterTransition);
- mSharedElementEnterTransition = loadTransition(activity, a, mSharedElementEnterTransition,
+ mSharedElementEnterTransition = loadTransition(context, a, mSharedElementEnterTransition,
null, com.android.internal.R.styleable.Fragment_fragmentSharedElementEnterTransition);
- mSharedElementReturnTransition = loadTransition(activity, a, mSharedElementReturnTransition,
+ mSharedElementReturnTransition = loadTransition(context, a, mSharedElementReturnTransition,
USE_DEFAULT_TRANSITION,
com.android.internal.R.styleable.Fragment_fragmentSharedElementReturnTransition);
if (mAllowEnterTransitionOverlap == null) {
@@ -1300,12 +1314,39 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
com.android.internal.R.styleable.Fragment_fragmentAllowReturnTransitionOverlap, true);
}
a.recycle();
+
+ final Activity hostActivity = mHost == null ? null : mHost.getActivity();
+ if (hostActivity != null) {
+ mCalled = false;
+ onInflate(hostActivity, attrs, savedInstanceState);
+ }
}
/**
- * Called when a fragment is first attached to its activity.
+ * @deprecated Use {@link #onInflate(Context, AttributeSet, Bundle)} instead.
+ */
+ @Deprecated
+ public void onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState) {
+ mCalled = true;
+ }
+
+ /**
+ * Called when a fragment is first attached to its context.
* {@link #onCreate(Bundle)} will be called after this.
*/
+ public void onAttach(Context context) {
+ mCalled = true;
+ final Activity hostActivity = mHost == null ? null : mHost.getActivity();
+ if (hostActivity != null) {
+ mCalled = false;
+ onAttach(hostActivity);
+ }
+ }
+
+ /**
+ * @deprecated Use {@link #onAttach(Context)} instead.
+ */
+ @Deprecated
public void onAttach(Activity activity) {
mCalled = true;
}
@@ -1428,7 +1469,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
mLoadersStarted = true;
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);
+ mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
}
if (mLoaderManager != null) {
mLoaderManager.doStart();
@@ -1521,7 +1562,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
// + " mLoaderManager=" + mLoaderManager);
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);
+ mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
}
if (mLoaderManager != null) {
mLoaderManager.doDestroy();
@@ -1546,7 +1587,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
mBackStackNesting = 0;
mFragmentManager = null;
mChildFragmentManager = null;
- mActivity = null;
+ mHost = null;
mFragmentId = 0;
mContainerId = 0;
mTag = null;
@@ -2034,9 +2075,9 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
writer.print(prefix); writer.print("mFragmentManager=");
writer.println(mFragmentManager);
}
- if (mActivity != null) {
- writer.print(prefix); writer.print("mActivity=");
- writer.println(mActivity);
+ if (mHost != null) {
+ writer.print(prefix); writer.print("mHost=");
+ writer.println(mHost);
}
if (mParentFragment != null) {
writer.print(prefix); writer.print("mParentFragment=");
@@ -2094,10 +2135,10 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
void instantiateChildFragmentManager() {
mChildFragmentManager = new FragmentManagerImpl();
- mChildFragmentManager.attachActivity(mActivity, new FragmentContainer() {
+ mChildFragmentManager.attachController(mHost, new FragmentContainer() {
@Override
@Nullable
- public View findViewById(int id) {
+ public View onFindViewById(int id) {
if (mView == null) {
throw new IllegalStateException("Fragment does not have a view");
}
@@ -2105,7 +2146,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
}
@Override
- public boolean hasView() {
+ public boolean onHasView() {
return (mView != null);
}
}, this);
@@ -2319,13 +2360,13 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene
mLoadersStarted = false;
if (!mCheckedForLoaderManager) {
mCheckedForLoaderManager = true;
- mLoaderManager = mActivity.getLoaderManager(mWho, mLoadersStarted, false);
+ mLoaderManager = mHost.getLoaderManager(mWho, mLoadersStarted, false);
}
if (mLoaderManager != null) {
- if (mActivity == null || !mActivity.mChangingConfigurations) {
- mLoaderManager.doStop();
- } else {
+ if (mRetaining) {
mLoaderManager.doRetain();
+ } else {
+ mLoaderManager.doStop();
}
}
}
diff --git a/core/java/android/app/FragmentContainer.java b/core/java/android/app/FragmentContainer.java
new file mode 100644
index 0000000..b2e0300
--- /dev/null
+++ b/core/java/android/app/FragmentContainer.java
@@ -0,0 +1,38 @@
+/*
+ * 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.app;
+
+import android.annotation.IdRes;
+import android.annotation.Nullable;
+import android.view.View;
+
+/**
+ * Callbacks to a {@link Fragment}'s container.
+ */
+public abstract class FragmentContainer {
+ /**
+ * Return the view with the given resource ID. May return {@code null} if the
+ * view is not a child of this container.
+ */
+ @Nullable
+ public abstract View onFindViewById(@IdRes int id);
+
+ /**
+ * Return {@code true} if the container holds any view.
+ */
+ public abstract boolean onHasView();
+}
diff --git a/core/java/android/app/FragmentController.java b/core/java/android/app/FragmentController.java
new file mode 100644
index 0000000..28dadfa
--- /dev/null
+++ b/core/java/android/app/FragmentController.java
@@ -0,0 +1,384 @@
+/*
+ * 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.app;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Provides integration points with a {@link FragmentManager} for a fragment host.
+ * <p>
+ * It is the responsibility of the host to take care of the Fragment's lifecycle.
+ * The methods provided by {@link FragmentController} are for that purpose.
+ */
+public class FragmentController {
+ private final FragmentHostCallback<?> mHost;
+
+ /**
+ * Returns a {@link FragmentController}.
+ */
+ public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
+ return new FragmentController(callbacks);
+ }
+
+ private FragmentController(FragmentHostCallback<?> callbacks) {
+ mHost = callbacks;
+ }
+
+ /**
+ * Returns a {@link FragmentManager} for this controller.
+ */
+ public FragmentManager getFragmentManager() {
+ return mHost.getFragmentManagerImpl();
+ }
+
+ /**
+ * Returns a {@link LoaderManager}.
+ */
+ public LoaderManager getLoaderManager() {
+ return mHost.getLoaderManagerImpl();
+ }
+
+ /**
+ * Returns a fragment with the given identifier.
+ */
+ @Nullable
+ public Fragment findFragmentByWho(String who) {
+ return mHost.mFragmentManager.findFragmentByWho(who);
+ }
+
+ /**
+ * Attaches the host to the FragmentManager for this controller. The host must be
+ * attached before the FragmentManager can be used to manage Fragments.
+ * */
+ public void attachHost(Fragment parent) {
+ mHost.mFragmentManager.attachController(
+ mHost, mHost /*container*/, parent);
+ }
+
+ /**
+ * Instantiates a Fragment's view.
+ *
+ * @param parent The parent that the created view will be placed
+ * in; <em>note that this may be null</em>.
+ * @param name Tag name to be inflated.
+ * @param context The context the view is being created in.
+ * @param attrs Inflation attributes as specified in XML file.
+ *
+ * @return view the newly created view
+ */
+ public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+ return mHost.mFragmentManager.onCreateView(parent, name, context, attrs);
+ }
+
+ /**
+ * Marks the fragment state as unsaved. This allows for "state loss" detection.
+ */
+ public void noteStateNotSaved() {
+ mHost.mFragmentManager.noteStateNotSaved();
+ }
+
+ /**
+ * Saves the state for all Fragments.
+ */
+ public Parcelable saveAllState() {
+ return mHost.mFragmentManager.saveAllState();
+ }
+
+ /**
+ * Restores the saved state for all Fragments. The given Fragment list are Fragment
+ * instances retained across configuration changes.
+ *
+ * @see #retainNonConfig()
+ */
+ public void restoreAllState(Parcelable state, List<Fragment> nonConfigList) {
+ mHost.mFragmentManager.restoreAllState(state, nonConfigList);
+ }
+
+ /**
+ * Returns a list of Fragments that have opted to retain their instance across
+ * configuration changes.
+ */
+ public List<Fragment> retainNonConfig() {
+ return mHost.mFragmentManager.retainNonConfig();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the create state.
+ * <p>Call when Fragments should be created.
+ *
+ * @see Fragment#onCreate(Bundle)
+ */
+ public void dispatchCreate() {
+ mHost.mFragmentManager.dispatchCreate();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the activity created state.
+ * <p>Call when Fragments should be informed their host has been created.
+ *
+ * @see Fragment#onActivityCreated(Bundle)
+ */
+ public void dispatchActivityCreated() {
+ mHost.mFragmentManager.dispatchActivityCreated();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the start state.
+ * <p>Call when Fragments should be started.
+ *
+ * @see Fragment#onStart()
+ */
+ public void dispatchStart() {
+ mHost.mFragmentManager.dispatchStart();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the resume state.
+ * <p>Call when Fragments should be resumed.
+ *
+ * @see Fragment#onResume()
+ */
+ public void dispatchResume() {
+ mHost.mFragmentManager.dispatchResume();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the pause state.
+ * <p>Call when Fragments should be paused.
+ *
+ * @see Fragment#onPause()
+ */
+ public void dispatchPause() {
+ mHost.mFragmentManager.dispatchPause();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the stop state.
+ * <p>Call when Fragments should be stopped.
+ *
+ * @see Fragment#onStop()
+ */
+ public void dispatchStop() {
+ mHost.mFragmentManager.dispatchStop();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the destroy view state.
+ * <p>Call when the Fragment's views should be destroyed.
+ *
+ * @see Fragment#onDestroyView()
+ */
+ public void dispatchDestroyView() {
+ mHost.mFragmentManager.dispatchDestroyView();
+ }
+
+ /**
+ * Moves all Fragments managed by the controller's FragmentManager
+ * into the destroy state.
+ * <p>Call when Fragments should be destroyed.
+ *
+ * @see Fragment#onDestroy()
+ */
+ public void dispatchDestroy() {
+ mHost.mFragmentManager.dispatchDestroy();
+ }
+
+ /**
+ * Lets all Fragments managed by the controller's FragmentManager
+ * know a configuration change occurred.
+ * <p>Call when there is a configuration change.
+ *
+ * @see Fragment#onConfigurationChanged(Configuration)
+ */
+ public void dispatchConfigurationChanged(Configuration newConfig) {
+ mHost.mFragmentManager.dispatchConfigurationChanged(newConfig);
+ }
+
+ /**
+ * Lets all Fragments managed by the controller's FragmentManager
+ * know the device is in a low memory condition.
+ * <p>Call when the device is low on memory and Fragment's should trim
+ * their memory usage.
+ *
+ * @see Fragment#onLowMemory()
+ */
+ public void dispatchLowMemory() {
+ mHost.mFragmentManager.dispatchLowMemory();
+ }
+
+ /**
+ * Lets all Fragments managed by the controller's FragmentManager
+ * know they should trim their memory usage.
+ * <p>Call when the Fragment can release allocated memory [such as if
+ * the Fragment is in the background].
+ *
+ * @see Fragment#onTrimMemory(int)
+ */
+ public void dispatchTrimMemory(int level) {
+ mHost.mFragmentManager.dispatchTrimMemory(level);
+ }
+
+ /**
+ * Lets all Fragments managed by the controller's FragmentManager
+ * know they should create an options menu.
+ * <p>Call when the Fragment should create an options menu.
+ *
+ * @return {@code true} if the options menu contains items to display
+ * @see Fragment#onCreateOptionsMenu(Menu, MenuInflater)
+ */
+ public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ return mHost.mFragmentManager.dispatchCreateOptionsMenu(menu, inflater);
+ }
+
+ /**
+ * Lets all Fragments managed by the controller's FragmentManager
+ * know they should prepare their options menu for display.
+ * <p>Call immediately before displaying the Fragment's options menu.
+ *
+ * @return {@code true} if the options menu contains items to display
+ * @see Fragment#onPrepareOptionsMenu(Menu)
+ */
+ public boolean dispatchPrepareOptionsMenu(Menu menu) {
+ return mHost.mFragmentManager.dispatchPrepareOptionsMenu(menu);
+ }
+
+ /**
+ * Sends an option item selection event to the Fragments managed by the
+ * controller's FragmentManager. Once the event has been consumed,
+ * no additional handling will be performed.
+ * <p>Call immediately after an options menu item has been selected
+ *
+ * @return {@code true} if the options menu selection event was consumed
+ * @see Fragment#onOptionsItemSelected(MenuItem)
+ */
+ public boolean dispatchOptionsItemSelected(MenuItem item) {
+ return mHost.mFragmentManager.dispatchOptionsItemSelected(item);
+ }
+
+ /**
+ * Sends a context item selection event to the Fragments managed by the
+ * controller's FragmentManager. Once the event has been consumed,
+ * no additional handling will be performed.
+ * <p>Call immediately after an options menu item has been selected
+ *
+ * @return {@code true} if the context menu selection event was consumed
+ * @see Fragment#onContextItemSelected(MenuItem)
+ */
+ public boolean dispatchContextItemSelected(MenuItem item) {
+ return mHost.mFragmentManager.dispatchContextItemSelected(item);
+ }
+
+ /**
+ * Lets all Fragments managed by the controller's FragmentManager
+ * know their options menu has closed.
+ * <p>Call immediately after closing the Fragment's options menu.
+ *
+ * @see Fragment#onOptionsMenuClosed(Menu)
+ */
+ public void dispatchOptionsMenuClosed(Menu menu) {
+ mHost.mFragmentManager.dispatchOptionsMenuClosed(menu);
+ }
+
+ /**
+ * Execute any pending actions for the Fragments managed by the
+ * controller's FragmentManager.
+ * <p>Call when queued actions can be performed [eg when the
+ * Fragment moves into a start or resume state].
+ * @return {@code true} if queued actions were performed
+ */
+ public boolean execPendingActions() {
+ return mHost.mFragmentManager.execPendingActions();
+ }
+
+ /**
+ * Starts the loaders.
+ */
+ public void doLoaderStart() {
+ mHost.doLoaderStart();
+ }
+
+ /**
+ * Stops the loaders, optionally retaining their state. This is useful for keeping the
+ * loader state across configuration changes.
+ *
+ * @param retain When {@code true}, the loaders aren't stopped, but, their instances
+ * are retained in a started state
+ */
+ public void doLoaderStop(boolean retain) {
+ mHost.doLoaderStop(retain);
+ }
+
+ /**
+ * Destroys the loaders and, if their state is not being retained, removes them.
+ */
+ public void doLoaderDestroy() {
+ mHost.doLoaderDestroy();
+ }
+
+ /**
+ * Lets the loaders know the host is ready to receive notifications.
+ */
+ public void reportLoaderStart() {
+ mHost.reportLoaderStart();
+ }
+
+ /**
+ * Returns a list of LoaderManagers that have opted to retain their instance across
+ * configuration changes.
+ */
+ public ArrayMap<String, LoaderManager> retainLoaderNonConfig() {
+ return mHost.retainLoaderNonConfig();
+ }
+
+ /**
+ * Restores the saved state for all LoaderManagers. The given LoaderManager list are
+ * LoaderManager instances retained across configuration changes.
+ *
+ * @see #retainLoaderNonConfig()
+ */
+ public void restoreLoaderNonConfig(ArrayMap<String, LoaderManager> loaderManagers) {
+ mHost.restoreLoaderNonConfig(loaderManagers);
+ }
+
+ /**
+ * Dumps the current state of the loaders.
+ */
+ public void dumpLoaders(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ mHost.dumpLoaders(prefix, fd, writer, args);
+ }
+}
diff --git a/core/java/android/app/FragmentHostCallback.java b/core/java/android/app/FragmentHostCallback.java
new file mode 100644
index 0000000..3e753f0
--- /dev/null
+++ b/core/java/android/app/FragmentHostCallback.java
@@ -0,0 +1,314 @@
+/*
+ * 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.app;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.ArrayMap;
+import android.view.LayoutInflater;
+import android.view.View;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Integration points with the Fragment host.
+ * <p>
+ * Fragments may be hosted by any object; such as an {@link Activity}. In order to
+ * host fragments, implement {@link FragmentHostCallback}, overriding the methods
+ * applicable to the host.
+ */
+public abstract class FragmentHostCallback<E> extends FragmentContainer {
+ private final Activity mActivity;
+ final Context mContext;
+ private final Handler mHandler;
+ final int mWindowAnimations;
+ final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
+ private ArrayMap<String, LoaderManager> mAllLoaderManagers;
+ private LoaderManagerImpl mLoaderManager;
+ private boolean mCheckedForLoaderManager;
+ private boolean mLoadersStarted;
+
+ public FragmentHostCallback(Context context, Handler handler, int windowAnimations) {
+ this(null /*activity*/, context, handler, windowAnimations);
+ }
+
+ FragmentHostCallback(Activity activity) {
+ this(activity, activity /*context*/, activity.mHandler, 0 /*windowAnimations*/);
+ }
+
+ FragmentHostCallback(Activity activity, Context context, Handler handler,
+ int windowAnimations) {
+ mActivity = activity;
+ mContext = context;
+ mHandler = handler;
+ mWindowAnimations = windowAnimations;
+ }
+
+ /**
+ * Print internal state into the given stream.
+ *
+ * @param prefix Desired prefix to prepend at each line of output.
+ * @param fd The raw file descriptor that the dump is being sent to.
+ * @param writer The PrintWriter to which you should dump your state. This will be closed
+ * for you after you return.
+ * @param args additional arguments to the dump request.
+ */
+ public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ }
+
+ /**
+ * Return {@code true} if the fragment's state needs to be saved.
+ */
+ public boolean onShouldSaveFragmentState(Fragment fragment) {
+ return true;
+ }
+
+ /**
+ * Return a {@link LayoutInflater}.
+ * See {@link Activity#getLayoutInflater()}.
+ */
+ public LayoutInflater onGetLayoutInflater() {
+ return (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ /**
+ * Return {@code true} if the FragmentManager's LayoutInflaterFactory should be used.
+ */
+ public boolean onUseFragmentManagerInflaterFactory() {
+ return false;
+ }
+
+ /**
+ * Return the object that's currently hosting the fragment. If a {@link Fragment}
+ * is hosted by a {@link Activity}, the object returned here should be the same
+ * object returned from {@link Fragment#getActivity()}.
+ */
+ @Nullable
+ public abstract E onGetHost();
+
+ /**
+ * Invalidates the activity's options menu.
+ * See {@link Activity#invalidateOptionsMenu()}
+ */
+ public void onInvalidateOptionsMenu() {
+ }
+
+ /**
+ * Starts a new {@link Activity} from the given fragment.
+ * See {@link Activity#startActivityForResult(Intent, int)}.
+ */
+ public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode,
+ Bundle options) {
+ if (requestCode != -1) {
+ throw new IllegalStateException(
+ "Starting activity with a requestCode requires a FragmentActivity host");
+ }
+ mContext.startActivity(intent);
+ }
+
+ /**
+ * Return {@code true} if there are window animations.
+ */
+ public boolean onHasWindowAnimations() {
+ return true;
+ }
+
+ /**
+ * Return the window animations.
+ */
+ public int onGetWindowAnimations() {
+ return mWindowAnimations;
+ }
+
+ /**
+ * Called when a {@link Fragment} is being attached to this host, immediately
+ * after the call to its {@link Fragment#onAttach(Context)} method and before
+ * {@link Fragment#onCreate(Bundle)}.
+ */
+ public void onAttachFragment(Fragment fragment) {
+ }
+
+ @Nullable
+ @Override
+ public View onFindViewById(int id) {
+ return null;
+ }
+
+ @Override
+ public boolean onHasView() {
+ return true;
+ }
+
+ Activity getActivity() {
+ return mActivity;
+ }
+
+ Context getContext() {
+ return mContext;
+ }
+
+ Handler getHandler() {
+ return mHandler;
+ }
+
+ FragmentManagerImpl getFragmentManagerImpl() {
+ return mFragmentManager;
+ }
+
+ LoaderManagerImpl getLoaderManagerImpl() {
+ if (mLoaderManager != null) {
+ return mLoaderManager;
+ }
+ mCheckedForLoaderManager = true;
+ mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true /*create*/);
+ return mLoaderManager;
+ }
+
+ void inactivateFragment(String who) {
+ //Log.v(TAG, "invalidateSupportFragment: who=" + who);
+ if (mAllLoaderManagers != null) {
+ LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who);
+ if (lm != null && !lm.mRetaining) {
+ lm.doDestroy();
+ mAllLoaderManagers.remove(who);
+ }
+ }
+ }
+
+ void doLoaderStart() {
+ if (mLoadersStarted) {
+ return;
+ }
+ mLoadersStarted = true;
+
+ if (mLoaderManager != null) {
+ mLoaderManager.doStart();
+ } else if (!mCheckedForLoaderManager) {
+ mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);
+ }
+ mCheckedForLoaderManager = true;
+ }
+
+ void doLoaderStop(boolean retain) {
+ if (mLoaderManager == null) {
+ return;
+ }
+
+ if (!mLoadersStarted) {
+ return;
+ }
+ mLoadersStarted = false;
+
+ if (retain) {
+ mLoaderManager.doRetain();
+ } else {
+ mLoaderManager.doStop();
+ }
+ }
+
+ void doLoaderRetain() {
+ if (mLoaderManager == null) {
+ return;
+ }
+ mLoaderManager.doRetain();
+ }
+
+ void doLoaderDestroy() {
+ if (mLoaderManager == null) {
+ return;
+ }
+ mLoaderManager.doDestroy();
+ }
+
+ void reportLoaderStart() {
+ if (mAllLoaderManagers != null) {
+ final int N = mAllLoaderManagers.size();
+ LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
+ for (int i=N-1; i>=0; i--) {
+ loaders[i] = (LoaderManagerImpl) mAllLoaderManagers.valueAt(i);
+ }
+ for (int i=0; i<N; i++) {
+ LoaderManagerImpl lm = loaders[i];
+ lm.finishRetain();
+ lm.doReportStart();
+ }
+ }
+ }
+
+ LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
+ if (mAllLoaderManagers == null) {
+ mAllLoaderManagers = new ArrayMap<String, LoaderManager>();
+ }
+ LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who);
+ if (lm == null) {
+ if (create) {
+ lm = new LoaderManagerImpl(who, this, started);
+ mAllLoaderManagers.put(who, lm);
+ }
+ } else {
+ lm.updateHostController(this);
+ }
+ return lm;
+ }
+
+ ArrayMap<String, LoaderManager> retainLoaderNonConfig() {
+ boolean retainLoaders = false;
+ if (mAllLoaderManagers != null) {
+ // prune out any loader managers that were already stopped and so
+ // have nothing useful to retain.
+ final int N = mAllLoaderManagers.size();
+ LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
+ for (int i=N-1; i>=0; i--) {
+ loaders[i] = (LoaderManagerImpl) mAllLoaderManagers.valueAt(i);
+ }
+ for (int i=0; i<N; i++) {
+ LoaderManagerImpl lm = loaders[i];
+ if (lm.mRetaining) {
+ retainLoaders = true;
+ } else {
+ lm.doDestroy();
+ mAllLoaderManagers.remove(lm.mWho);
+ }
+ }
+ }
+
+ if (retainLoaders) {
+ return mAllLoaderManagers;
+ }
+ return null;
+ }
+
+ void restoreLoaderNonConfig(ArrayMap<String, LoaderManager> loaderManagers) {
+ mAllLoaderManagers = loaderManagers;
+ }
+
+ void dumpLoaders(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
+ writer.print(prefix); writer.print("mLoadersStarted=");
+ writer.println(mLoadersStarted);
+ if (mLoaderManager != null) {
+ writer.print(prefix); writer.print("Loader Manager ");
+ writer.print(Integer.toHexString(System.identityHashCode(mLoaderManager)));
+ writer.println(":");
+ mLoaderManager.dump(prefix + " ", fd, writer, args);
+ }
+ }
+}
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index 975b20d..6b5239d 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -19,9 +19,7 @@ package android.app;
import android.animation.Animator;
import android.animation.AnimatorInflater;
import android.animation.AnimatorListenerAdapter;
-import android.annotation.Nullable;
import android.content.Context;
-import android.annotation.IdRes;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.os.Bundle;
@@ -48,6 +46,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
/**
* Interface for interacting with {@link Fragment} objects inside of an
@@ -393,15 +392,6 @@ final class FragmentManagerState implements Parcelable {
}
/**
- * Callbacks from FragmentManagerImpl to its container.
- */
-interface FragmentContainer {
- @Nullable
- public View findViewById(@IdRes int id);
- public boolean hasView();
-}
-
-/**
* Container for fragments associated with an activity.
*/
final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
@@ -430,7 +420,8 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
ArrayList<OnBackStackChangedListener> mBackStackChangeListeners;
int mCurState = Fragment.INITIALIZING;
- Activity mActivity;
+ FragmentHostCallback<?> mHost;
+ FragmentController mController;
FragmentContainer mContainer;
Fragment mParent;
@@ -455,10 +446,10 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
Log.e(TAG, ex.getMessage());
LogWriter logw = new LogWriter(Log.ERROR, TAG);
PrintWriter pw = new FastPrintWriter(logw, false, 1024);
- if (mActivity != null) {
+ if (mHost != null) {
Log.e(TAG, "Activity state:");
try {
- mActivity.dump(" ", null, pw, new String[] { });
+ mHost.onDump(" ", null, pw, new String[] { });
} catch (Exception e) {
pw.flush();
Log.e(TAG, "Failed dumping state", e);
@@ -490,7 +481,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
public void popBackStack() {
enqueueAction(new Runnable() {
@Override public void run() {
- popBackStackState(mActivity.mHandler, null, -1, 0);
+ popBackStackState(mHost.getHandler(), null, -1, 0);
}
}, false);
}
@@ -499,14 +490,14 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
public boolean popBackStackImmediate() {
checkStateLoss();
executePendingTransactions();
- return popBackStackState(mActivity.mHandler, null, -1, 0);
+ return popBackStackState(mHost.getHandler(), null, -1, 0);
}
@Override
public void popBackStack(final String name, final int flags) {
enqueueAction(new Runnable() {
@Override public void run() {
- popBackStackState(mActivity.mHandler, name, -1, flags);
+ popBackStackState(mHost.getHandler(), name, -1, flags);
}
}, false);
}
@@ -515,7 +506,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
public boolean popBackStackImmediate(String name, int flags) {
checkStateLoss();
executePendingTransactions();
- return popBackStackState(mActivity.mHandler, name, -1, flags);
+ return popBackStackState(mHost.getHandler(), name, -1, flags);
}
@Override
@@ -525,7 +516,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
}
enqueueAction(new Runnable() {
@Override public void run() {
- popBackStackState(mActivity.mHandler, null, id, flags);
+ popBackStackState(mHost.getHandler(), null, id, flags);
}
}, false);
}
@@ -537,7 +528,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
if (id < 0) {
throw new IllegalArgumentException("Bad id: " + id);
}
- return popBackStackState(mActivity.mHandler, null, id, flags);
+ return popBackStackState(mHost.getHandler(), null, id, flags);
}
@Override
@@ -619,7 +610,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
if (mParent != null) {
DebugUtils.buildShortClassTag(mParent, sb);
} else {
- DebugUtils.buildShortClassTag(mActivity, sb);
+ DebugUtils.buildShortClassTag(mHost, sb);
}
sb.append("}}");
return sb.toString();
@@ -716,7 +707,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
}
writer.print(prefix); writer.println("FragmentManager misc state:");
- writer.print(prefix); writer.print(" mActivity="); writer.println(mActivity);
+ writer.print(prefix); writer.print(" mHost="); writer.println(mHost);
writer.print(prefix); writer.print(" mContainer="); writer.println(mContainer);
if (mParent != null) {
writer.print(prefix); writer.print(" mParent="); writer.println(mParent);
@@ -747,7 +738,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
}
if (fragment.mNextAnim != 0) {
- Animator anim = AnimatorInflater.loadAnimator(mActivity, fragment.mNextAnim);
+ Animator anim = AnimatorInflater.loadAnimator(mHost.getContext(), fragment.mNextAnim);
if (anim != null) {
return anim;
}
@@ -762,14 +753,14 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
return null;
}
- if (transitionStyle == 0 && mActivity.getWindow() != null) {
- transitionStyle = mActivity.getWindow().getAttributes().windowAnimations;
+ if (transitionStyle == 0 && mHost.onHasWindowAnimations()) {
+ transitionStyle = mHost.onGetWindowAnimations();
}
if (transitionStyle == 0) {
return null;
}
- TypedArray attrs = mActivity.obtainStyledAttributes(transitionStyle,
+ TypedArray attrs = mHost.getContext().obtainStyledAttributes(transitionStyle,
com.android.internal.R.styleable.FragmentAnimation);
int anim = attrs.getResourceId(styleIndex, 0);
attrs.recycle();
@@ -778,7 +769,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
return null;
}
- return AnimatorInflater.loadAnimator(mActivity, anim);
+ return AnimatorInflater.loadAnimator(mHost.getContext(), anim);
}
public void performPendingDeferredStart(Fragment f) {
@@ -848,18 +839,18 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
}
}
}
- f.mActivity = mActivity;
+ f.mHost = mHost;
f.mParentFragment = mParent;
f.mFragmentManager = mParent != null
- ? mParent.mChildFragmentManager : mActivity.mFragments;
+ ? mParent.mChildFragmentManager : mHost.getFragmentManagerImpl();
f.mCalled = false;
- f.onAttach(mActivity);
+ f.onAttach(mHost.getContext());
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onAttach()");
}
if (f.mParentFragment == null) {
- mActivity.onAttachFragment(f);
+ mHost.onAttachFragment(f);
}
if (!f.mRetaining) {
@@ -884,7 +875,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
if (!f.mFromLayout) {
ViewGroup container = null;
if (f.mContainerId != 0) {
- container = (ViewGroup)mContainer.findViewById(f.mContainerId);
+ container = (ViewGroup)mContainer.onFindViewById(f.mContainerId);
if (container == null && !f.mRestored) {
throwException(new IllegalArgumentException(
"No view found for id 0x"
@@ -954,7 +945,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
if (f.mView != null) {
// Need to save the current view state if not
// done already.
- if (!mActivity.isFinishing() && f.mSavedViewState == null) {
+ if (!mHost.onShouldSaveFragmentState(f) && f.mSavedViewState == null) {
saveFragmentViewState(f);
}
}
@@ -1030,7 +1021,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
if (!f.mRetaining) {
makeInactive(f);
} else {
- f.mActivity = null;
+ f.mHost = null;
f.mParentFragment = null;
f.mFragmentManager = null;
f.mChildFragmentManager = null;
@@ -1053,7 +1044,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
}
void moveToState(int newState, int transit, int transitStyle, boolean always) {
- if (mActivity == null && newState != Fragment.INITIALIZING) {
+ if (mHost == null && newState != Fragment.INITIALIZING) {
throw new IllegalStateException("No activity");
}
@@ -1078,8 +1069,8 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
startPendingDeferredFragments();
}
- if (mNeedMenuInvalidate && mActivity != null && mCurState == Fragment.RESUMED) {
- mActivity.invalidateOptionsMenu();
+ if (mNeedMenuInvalidate && mHost != null && mCurState == Fragment.RESUMED) {
+ mHost.onInvalidateOptionsMenu();
mNeedMenuInvalidate = false;
}
}
@@ -1126,7 +1117,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
mAvailIndices = new ArrayList<Integer>();
}
mAvailIndices.add(f.mIndex);
- mActivity.invalidateFragment(f.mWho);
+ mHost.inactivateFragment(f.mWho);
f.initState();
}
@@ -1349,7 +1340,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
checkStateLoss();
}
synchronized (this) {
- if (mDestroyed || mActivity == null) {
+ if (mDestroyed || mHost == null) {
throw new IllegalStateException("Activity has been destroyed");
}
if (mPendingActions == null) {
@@ -1357,8 +1348,8 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
}
mPendingActions.add(action);
if (mPendingActions.size() == 1) {
- mActivity.mHandler.removeCallbacks(mExecCommit);
- mActivity.mHandler.post(mExecCommit);
+ mHost.getHandler().removeCallbacks(mExecCommit);
+ mHost.getHandler().post(mExecCommit);
}
}
}
@@ -1427,7 +1418,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
throw new IllegalStateException("Recursive entry to executePendingTransactions");
}
- if (Looper.myLooper() != mActivity.mHandler.getLooper()) {
+ if (Looper.myLooper() != mHost.getHandler().getLooper()) {
throw new IllegalStateException("Must be called from main thread of process");
}
@@ -1447,7 +1438,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
}
mPendingActions.toArray(mTmpActions);
mPendingActions.clear();
- mActivity.mHandler.removeCallbacks(mExecCommit);
+ mHost.getHandler().removeCallbacks(mExecCommit);
}
mExecutingActions = true;
@@ -1737,7 +1728,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
return fms;
}
- void restoreAllState(Parcelable state, ArrayList<Fragment> nonConfig) {
+ void restoreAllState(Parcelable state, List<Fragment> nonConfig) {
// If there is no saved state at all, then there can not be
// any nonConfig fragments either, so that is that.
if (state == null) return;
@@ -1758,7 +1749,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
f.mAdded = false;
f.mTarget = null;
if (fs.mSavedFragmentState != null) {
- fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader());
+ fs.mSavedFragmentState.setClassLoader(mHost.getContext().getClassLoader());
f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray(
FragmentManagerImpl.VIEW_STATE_TAG);
f.mSavedFragmentState = fs.mSavedFragmentState;
@@ -1775,7 +1766,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
for (int i=0; i<fms.mActive.length; i++) {
FragmentState fs = fms.mActive[i];
if (fs != null) {
- Fragment f = fs.instantiate(mActivity, mParent);
+ Fragment f = fs.instantiate(mHost, mParent);
if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
mActive.add(f);
// Now that the fragment is instantiated (or came from being
@@ -1851,9 +1842,10 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
}
}
- public void attachActivity(Activity activity, FragmentContainer container, Fragment parent) {
- if (mActivity != null) throw new IllegalStateException("Already attached");
- mActivity = activity;
+ public void attachController(FragmentHostCallback<?> host, FragmentContainer container,
+ Fragment parent) {
+ if (mHost != null) throw new IllegalStateException("Already attached");
+ mHost = host;
mContainer = container;
mParent = parent;
}
@@ -1898,7 +1890,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
mDestroyed = true;
execPendingActions();
moveToState(Fragment.INITIALIZING, false);
- mActivity = null;
+ mHost = null;
mContainer = null;
mParent = null;
}
@@ -2024,8 +2016,8 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
@Override
public void invalidateOptionsMenu() {
- if (mActivity != null && mCurState == Fragment.RESUMED) {
- mActivity.invalidateOptionsMenu();
+ if (mHost != null && mCurState == Fragment.RESUMED) {
+ mHost.onInvalidateOptionsMenu();
} else {
mNeedMenuInvalidate = true;
}
@@ -2115,7 +2107,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
fragment.mTag = tag;
fragment.mInLayout = true;
fragment.mFragmentManager = this;
- fragment.onInflate(mActivity, attrs, fragment.mSavedFragmentState);
+ fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
addFragment(fragment, true);
} else if (fragment.mInLayout) {
// A fragment already exists and it is not one we restored from
@@ -2132,7 +2124,7 @@ final class FragmentManagerImpl extends FragmentManager implements LayoutInflate
// from last saved state), then give it the attributes to
// initialize itself.
if (!fragment.mRetaining) {
- fragment.onInflate(mActivity, attrs, fragment.mSavedFragmentState);
+ fragment.onInflate(mHost.getContext(), attrs, fragment.mSavedFragmentState);
}
}
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
index 96767ae..3cda973 100644
--- a/core/java/android/app/IntentService.java
+++ b/core/java/android/app/IntentService.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.WorkerThread;
import android.content.Intent;
import android.os.Handler;
import android.os.HandlerThread;
@@ -158,5 +159,6 @@ public abstract class IntentService extends Service {
* @param intent The value passed to {@link
* android.content.Context#startService(Intent)}.
*/
+ @WorkerThread
protected abstract void onHandleIntent(Intent intent);
}
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index ebb3c43..2c12317 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -199,9 +199,12 @@ public class KeyguardManager {
}
/**
- * Return whether the keyguard requires a password to unlock.
+ * Return whether the keyguard is secured by a PIN, pattern or password or a SIM card
+ * is currently locked.
*
- * @return true if keyguard is secure.
+ * <p>See also {@link #isDeviceSecure()} which ignores SIM locked states.
+ *
+ * @return true if a PIN, pattern or password is set or a SIM card is locked.
*/
public boolean isKeyguardSecure() {
try {
@@ -240,12 +243,8 @@ public class KeyguardManager {
}
/**
- * Returns whether the device is currently locked and requires a PIN, pattern or
- * password to unlock.
+ * Per-user version of {@link #isDeviceLocked()}.
*
- * @param userId the user for which the locked state should be reported.
- * @return true if unlocking the device currently requires a PIN, pattern or
- * password.
* @hide
*/
public boolean isDeviceLocked(int userId) {
@@ -260,6 +259,8 @@ public class KeyguardManager {
* Returns whether the device is secured with a PIN, pattern or
* password.
*
+ * <p>See also {@link #isKeyguardSecure} which treats SIM locked states as secure.
+ *
* @return true if a PIN, pattern or password was set.
*/
public boolean isDeviceSecure() {
@@ -267,11 +268,8 @@ public class KeyguardManager {
}
/**
- * Returns whether the device is secured with a PIN, pattern or
- * password.
+ * Per-user version of {@link #isDeviceSecure()}.
*
- * @param userId the user for which the secure state should be reported.
- * @return true if a PIN, pattern or password was set.
* @hide
*/
public boolean isDeviceSecure(int userId) {
diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java
index b13b24a..f0e35c9 100644
--- a/core/java/android/app/LoaderManager.java
+++ b/core/java/android/app/LoaderManager.java
@@ -214,12 +214,12 @@ class LoaderManagerImpl extends LoaderManager {
final String mWho;
- Activity mActivity;
boolean mStarted;
boolean mRetaining;
boolean mRetainingStarted;
boolean mCreatingLoader;
+ private FragmentHostCallback mHost;
final class LoaderInfo implements Loader.OnLoadCompleteListener<Object>,
Loader.OnLoadCanceledListener<Object> {
@@ -356,15 +356,15 @@ class LoaderManagerImpl extends LoaderManager {
if (mCallbacks != null && mLoader != null && mHaveData && needReset) {
if (DEBUG) Log.v(TAG, " Reseting: " + this);
String lastBecause = null;
- if (mActivity != null) {
- lastBecause = mActivity.mFragments.mNoTransactionsBecause;
- mActivity.mFragments.mNoTransactionsBecause = "onLoaderReset";
+ if (mHost != null) {
+ lastBecause = mHost.mFragmentManager.mNoTransactionsBecause;
+ mHost.mFragmentManager.mNoTransactionsBecause = "onLoaderReset";
}
try {
mCallbacks.onLoaderReset(mLoader);
} finally {
- if (mActivity != null) {
- mActivity.mFragments.mNoTransactionsBecause = lastBecause;
+ if (mHost != null) {
+ mHost.mFragmentManager.mNoTransactionsBecause = lastBecause;
}
}
}
@@ -465,25 +465,25 @@ class LoaderManagerImpl extends LoaderManager {
mInactiveLoaders.remove(mId);
}
- if (mActivity != null && !hasRunningLoaders()) {
- mActivity.mFragments.startPendingDeferredFragments();
+ if (mHost != null && !hasRunningLoaders()) {
+ mHost.mFragmentManager.startPendingDeferredFragments();
}
}
void callOnLoadFinished(Loader<Object> loader, Object data) {
if (mCallbacks != null) {
String lastBecause = null;
- if (mActivity != null) {
- lastBecause = mActivity.mFragments.mNoTransactionsBecause;
- mActivity.mFragments.mNoTransactionsBecause = "onLoadFinished";
+ if (mHost != null) {
+ lastBecause = mHost.mFragmentManager.mNoTransactionsBecause;
+ mHost.mFragmentManager.mNoTransactionsBecause = "onLoadFinished";
}
try {
if (DEBUG) Log.v(TAG, " onLoadFinished in " + loader + ": "
+ loader.dataToString(data));
mCallbacks.onLoadFinished(loader, data);
} finally {
- if (mActivity != null) {
- mActivity.mFragments.mNoTransactionsBecause = lastBecause;
+ if (mHost != null) {
+ mHost.mFragmentManager.mNoTransactionsBecause = lastBecause;
}
}
mDeliveredData = true;
@@ -530,14 +530,14 @@ class LoaderManagerImpl extends LoaderManager {
}
}
- LoaderManagerImpl(String who, Activity activity, boolean started) {
+ LoaderManagerImpl(String who, FragmentHostCallback host, boolean started) {
mWho = who;
- mActivity = activity;
+ mHost = host;
mStarted = started;
}
- void updateActivity(Activity activity) {
- mActivity = activity;
+ void updateHostController(FragmentHostCallback host) {
+ mHost = host;
}
private LoaderInfo createLoader(int id, Bundle args,
@@ -730,8 +730,8 @@ class LoaderManagerImpl extends LoaderManager {
mInactiveLoaders.removeAt(idx);
info.destroy();
}
- if (mActivity != null && !hasRunningLoaders()) {
- mActivity.mFragments.startPendingDeferredFragments();
+ if (mHost != null && !hasRunningLoaders()) {
+ mHost.mFragmentManager.startPendingDeferredFragments();
}
}
@@ -849,7 +849,7 @@ class LoaderManagerImpl extends LoaderManager {
sb.append("LoaderManager{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" in ");
- DebugUtils.buildShortClassTag(mActivity, sb);
+ DebugUtils.buildShortClassTag(mHost, sb);
sb.append("}}");
return sb.toString();
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 2cf23af..49b2549 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -2095,6 +2095,7 @@ public class Notification implements Parcelable
try {
Constructor<? extends Style> constructor = styleClass.getConstructor();
+ constructor.setAccessible(true);
style = constructor.newInstance();
style.restoreFromExtras(extras);
} catch (Throwable t) {
@@ -5493,477 +5494,6 @@ public class Notification implements Parcelable
}
/**
- * <p>
- * Helper class to add content info extensions to notifications. To create a notification with
- * content info extensions:
- * <ol>
- * <li>Create an {@link Notification.Builder}, setting any desired properties.
- * <li>Create a {@link ContentInfoExtender}.
- * <li>Set content info specific properties using the {@code add} and {@code set} methods of
- * {@link ContentInfoExtender}.
- * <li>Call {@link Notification.Builder#extend(Notification.Extender)} to apply the extensions
- * to a notification.
- * </ol>
- *
- * <pre class="prettyprint">Notification notification = new Notification.Builder(context) * ... * .extend(new ContentInfoExtender() * .set*(...)) * .build(); * </pre>
- * <p>
- * Content info extensions can be accessed on an existing notification by using the
- * {@code ContentInfoExtender(Notification)} constructor, and then using the {@code get} methods
- * to access values.
- */
- public static final class ContentInfoExtender implements Extender {
- private static final String TAG = "ContentInfoExtender";
-
- // Key for the Content info extensions bundle in the main Notification extras bundle
- private static final String EXTRA_CONTENT_INFO_EXTENDER = "android.CONTENT_INFO_EXTENSIONS";
-
- // Keys within EXTRA_CONTENT_INFO_EXTENDER for individual content info options.
-
- private static final String KEY_CONTENT_TYPE = "android.contentType";
-
- private static final String KEY_CONTENT_GENRES = "android.contentGenre";
-
- private static final String KEY_CONTENT_PRICING_TYPE = "android.contentPricing.type";
-
- private static final String KEY_CONTENT_PRICING_VALUE = "android.contentPricing.value";
-
- private static final String KEY_CONTENT_STATUS = "android.contentStatus";
-
- private static final String KEY_CONTENT_MATURITY_RATING = "android.contentMaturity";
-
- private static final String KEY_CONTENT_RUN_LENGTH = "android.contentLength";
-
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a video clip.
- */
- public static final String CONTENT_TYPE_VIDEO = "android.contentType.video";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a movie.
- */
- public static final String CONTENT_TYPE_MOVIE = "android.contentType.movie";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a trailer.
- */
- public static final String CONTENT_TYPE_TRAILER = "android.contentType.trailer";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is serial. It can refer to an entire show, a single season or
- * series, or a single episode.
- */
- public static final String CONTENT_TYPE_SERIAL = "android.contentType.serial";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a song or album.
- */
- public static final String CONTENT_TYPE_MUSIC = "android.contentType.music";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a radio station.
- */
- public static final String CONTENT_TYPE_RADIO = "android.contentType.radio";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a podcast.
- */
- public static final String CONTENT_TYPE_PODCAST = "android.contentType.podcast";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a news item.
- */
- public static final String CONTENT_TYPE_NEWS = "android.contentType.news";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is sports.
- */
- public static final String CONTENT_TYPE_SPORTS = "android.contentType.sports";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is an application.
- */
- public static final String CONTENT_TYPE_APP = "android.contentType.app";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a game.
- */
- public static final String CONTENT_TYPE_GAME = "android.contentType.game";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a book.
- */
- public static final String CONTENT_TYPE_BOOK = "android.contentType.book";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a comic book.
- */
- public static final String CONTENT_TYPE_COMIC = "android.contentType.comic";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a magazine.
- */
- public static final String CONTENT_TYPE_MAGAZINE = "android.contentType.magazine";
-
- /**
- * Value to be used with {@link #setContentTypes} to indicate that the content referred by
- * the notification item is a website.
- */
- public static final String CONTENT_TYPE_WEBSITE = "android.contentType.website";
-
-
- /**
- * Value to be used with {@link #setPricingInformation} to indicate that the content
- * referred by the notification item is free to consume.
- */
- public static final String CONTENT_PRICING_FREE = "android.contentPrice.free";
-
- /**
- * Value to be used with {@link #setPricingInformation} to indicate that the content
- * referred by the notification item is available as a rental, and the price value provided
- * is the rental price for the item.
- */
- public static final String CONTENT_PRICING_RENTAL = "android.contentPrice.rental";
-
- /**
- * Value to be used with {@link #setPricingInformation} to indicate that the content
- * referred by the notification item is available for purchase, and the price value provided
- * is the purchase price for the item.
- */
- public static final String CONTENT_PRICING_PURCHASE = "android.contentPrice.purchase";
-
- /**
- * Value to be used with {@link #setPricingInformation} to indicate that the content
- * referred by the notification item is available currently as a pre-order, and the price
- * value provided is the purchase price for the item.
- */
- public static final String CONTENT_PRICING_PREORDER = "android.contentPrice.preorder";
-
- /**
- * Value to be used with {@link #setPricingInformation} to indicate that the content
- * referred by the notification item is available as part of a subscription based service,
- * and the price value provided is the subscription price for the service.
- */
- public static final String CONTENT_PRICING_SUBSCRIPTION =
- "android.contentPrice.subscription";
-
- /**
- * Value to be used with {@link #setStatus} to indicate that the content referred by the
- * notification is available and ready to be consumed immediately.
- */
- public static final int CONTENT_STATUS_READY = 0;
-
- /**
- * Value to be used with {@link #setStatus} to indicate that the content referred by the
- * notification is pending, waiting on either a download or purchase operation to complete
- * before it can be consumed.
- */
- public static final int CONTENT_STATUS_PENDING = 1;
-
- /**
- * Value to be used with {@link #setStatus} to indicate that the content referred by the
- * notification is available, but needs to be first purchased, rented, subscribed or
- * downloaded before it can be consumed.
- */
- public static final int CONTENT_STATUS_AVAILABLE = 2;
-
- /**
- * Value to be used with {@link #setStatus} to indicate that the content referred by the
- * notification is not available. This could be content not available in a certain region or
- * incompatible with the device in use.
- */
- public static final int CONTENT_STATUS_UNAVAILABLE = 3;
-
- /**
- * Value to be used with {@link #setMaturityRating} to indicate that the content referred by
- * the notification is suitable for all audiences.
- */
- public static final String CONTENT_MATURITY_ALL = "android.contentMaturity.all";
-
- /**
- * Value to be used with {@link #setMaturityRating} to indicate that the content
- * referred by the notification is suitable for audiences of low maturity and above.
- */
- public static final String CONTENT_MATURITY_LOW = "android.contentMaturity.low";
-
- /**
- * Value to be used with {@link #setMaturityRating} to indicate that the content
- * referred by the notification is suitable for audiences of medium maturity and above.
- */
- public static final String CONTENT_MATURITY_MEDIUM = "android.contentMaturity.medium";
-
- /**
- * Value to be used with {@link #setMaturityRating} to indicate that the content
- * referred by the notification is suitable for audiences of high maturity and above.
- */
- public static final String CONTENT_MATURITY_HIGH = "android.contentMaturity.high";
-
- private String[] mTypes;
- private String[] mGenres;
- private String mPricingType;
- private String mPricingValue;
- private int mContentStatus = -1;
- private String mMaturityRating;
- private long mRunLength = -1;
-
- /**
- * Create a {@link ContentInfoExtender} with default options.
- */
- public ContentInfoExtender() {
- }
-
- /**
- * Create a {@link ContentInfoExtender} from the ContentInfoExtender options of an existing
- * Notification.
- *
- * @param notif The notification from which to copy options.
- */
- public ContentInfoExtender(Notification notif) {
- Bundle contentBundle = notif.extras == null ?
- null : notif.extras.getBundle(EXTRA_CONTENT_INFO_EXTENDER);
- if (contentBundle != null) {
- mTypes = contentBundle.getStringArray(KEY_CONTENT_TYPE);
- mGenres = contentBundle.getStringArray(KEY_CONTENT_GENRES);
- mPricingType = contentBundle.getString(KEY_CONTENT_PRICING_TYPE);
- mPricingValue = contentBundle.getString(KEY_CONTENT_PRICING_VALUE);
- mContentStatus = contentBundle.getInt(KEY_CONTENT_STATUS, -1);
- mMaturityRating = contentBundle.getString(KEY_CONTENT_MATURITY_RATING);
- mRunLength = contentBundle.getLong(KEY_CONTENT_RUN_LENGTH, -1);
- }
- }
-
- /**
- * Apply content extensions to a notification that is being built. This is typically called
- * by the {@link Notification.Builder#extend(Notification.Extender)} method of
- * {@link Notification.Builder}.
- */
- @Override
- public Notification.Builder extend(Notification.Builder builder) {
- Bundle contentBundle = new Bundle();
-
- if (mTypes != null) {
- contentBundle.putStringArray(KEY_CONTENT_TYPE, mTypes);
- }
- if (mGenres != null) {
- contentBundle.putStringArray(KEY_CONTENT_GENRES, mGenres);
- }
- if (mPricingType != null) {
- contentBundle.putString(KEY_CONTENT_PRICING_TYPE, mPricingType);
- }
- if (mPricingValue != null) {
- contentBundle.putString(KEY_CONTENT_PRICING_VALUE, mPricingValue);
- }
- if (mContentStatus != -1) {
- contentBundle.putInt(KEY_CONTENT_STATUS, mContentStatus);
- }
- if (mMaturityRating != null) {
- contentBundle.putString(KEY_CONTENT_MATURITY_RATING, mMaturityRating);
- }
- if (mRunLength > 0) {
- contentBundle.putLong(KEY_CONTENT_RUN_LENGTH, mRunLength);
- }
-
- builder.getExtras().putBundle(EXTRA_CONTENT_INFO_EXTENDER, contentBundle);
- return builder;
- }
-
- /**
- * Sets the content types associated with the notification content. The first tag entry will
- * be considered the primary type for the content and will be used for ranking purposes.
- * Other secondary type tags may be provided, if applicable, and may be used for filtering
- * purposes.
- *
- * @param types Array of predefined type tags (see the <code>CONTENT_TYPE_*</code>
- * constants) that describe the content referred to by a notification.
- */
- public ContentInfoExtender setContentTypes(String[] types) {
- mTypes = types;
- return this;
- }
-
- /**
- * Returns an array containing the content types that describe the content associated with
- * the notification. The first tag entry is considered the primary type for the content, and
- * is used for content ranking purposes.
- *
- * @return An array of predefined type tags (see the <code>CONTENT_TYPE_*</code> constants)
- * that describe the content associated with the notification.
- * @see ContentInfoExtender#setContentTypes
- */
- public String[] getContentTypes() {
- return mTypes;
- }
-
- /**
- * Returns the primary content type tag for the content associated with the notification.
- *
- * @return A predefined type tag (see the <code>CONTENT_TYPE_*</code> constants) indicating
- * the primary type for the content associated with the notification.
- * @see ContentInfoExtender#setContentTypes
- */
- public String getPrimaryContentType() {
- if (mTypes == null || mTypes.length == 0) {
- return null;
- }
- return mTypes[0];
- }
-
- /**
- * Sets the content genres associated with the notification content. These genres may be
- * used for content ranking. Genres are open ended String tags.
- * <p>
- * Some examples: "comedy", "action", "dance", "electronica", "racing", etc.
- *
- * @param genres Array of genre string tags that describe the content referred to by a
- * notification.
- */
- public ContentInfoExtender setGenres(String[] genres) {
- mGenres = genres;
- return this;
- }
-
- /**
- * Returns an array containing the content genres that describe the content associated with
- * the notification.
- *
- * @return An array of genre tags that describe the content associated with the
- * notification.
- * @see ContentInfoExtender#setGenres
- */
- public String[] getGenres() {
- return mGenres;
- }
-
- /**
- * Sets the pricing and availability information for the content associated with the
- * notification. The provided information will indicate the access model for the content
- * (free, rental, purchase or subscription) and the price value (if not free).
- *
- * @param priceType Pricing type for this content. Must be one of the predefined pricing
- * type tags (see the <code>CONTENT_PRICING_*</code> constants).
- * @param priceValue A string containing a representation of the content price in the
- * current locale and currency.
- * @return This object for method chaining.
- */
- public ContentInfoExtender setPricingInformation(String priceType, String priceValue) {
- mPricingType = priceType;
- mPricingValue = priceValue;
- return this;
- }
-
- /**
- * Gets the pricing type for the content associated with the notification.
- *
- * @return A predefined tag indicating the pricing type for the content (see the <code>
- * CONTENT_PRICING_*</code> constants).
- * @see ContentInfoExtender#setPricingInformation
- */
- public String getPricingType() {
- return mPricingType;
- }
-
- /**
- * Gets the price value (when applicable) for the content associated with a notification.
- * The value will be provided as a String containing the price in the appropriate currency
- * for the current locale.
- *
- * @return A string containing a representation of the content price in the current locale
- * and currency.
- * @see ContentInfoExtender#setPricingInformation
- */
- public String getPricingValue() {
- if (mPricingType == null || CONTENT_PRICING_FREE.equals(mPricingType)) {
- return null;
- }
- return mPricingValue;
- }
-
- /**
- * Sets the availability status for the content associated with the notification. This
- * status indicates whether the referred content is ready to be consumed on the device, or
- * if the user must first purchase, rent, subscribe to, or download the content.
- *
- * @param contentStatus The status value for this content. Must be one of the predefined
- * content status values (see the <code>CONTENT_STATUS_*</code> constants).
- */
- public ContentInfoExtender setStatus(int contentStatus) {
- mContentStatus = contentStatus;
- return this;
- }
-
- /**
- * Returns status value for the content associated with the notification. This status
- * indicates whether the referred content is ready to be consumed on the device, or if the
- * user must first purchase, rent, subscribe to, or download the content.
- *
- * @return The status value for this content, or -1 is a valid status has not been specified
- * (see the <code>CONTENT_STATUS_*</code> for the defined valid status values).
- * @see ContentInfoExtender#setStatus
- */
- public int getStatus() {
- return mContentStatus;
- }
-
- /**
- * Sets the maturity level rating for the content associated with the notification.
- *
- * @param maturityRating A tag indicating the maturity level rating for the content. This
- * tag must be one of the predefined maturity rating tags (see the <code>
- * CONTENT_MATURITY_*</code> constants).
- */
- public ContentInfoExtender setMaturityRating(String maturityRating) {
- mMaturityRating = maturityRating;
- return this;
- }
-
- /**
- * Returns the maturity level rating for the content associated with the notification.
- *
- * @return returns a predefined tag indicating the maturity level rating for the content
- * (see the <code> CONTENT_MATURITY_*</code> constants).
- * @see ContentInfoExtender#setMaturityRating
- */
- public String getMaturityRating() {
- return mMaturityRating;
- }
-
- /**
- * Sets the running time (when applicable) for the content associated with the notification.
- *
- * @param length The runing time, in seconds, of the content associated with the
- * notification.
- */
- public ContentInfoExtender setRunningTime(long length) {
- mRunLength = length;
- return this;
- }
-
- /**
- * Returns the running time for the content associated with the notification.
- *
- * @return The running time, in seconds, of the content associated with the notification.
- * @see ContentInfoExtender#setRunningTime
- */
- public long getRunningTime() {
- return mRunLength;
- }
- }
-
- /**
* Get an array of Notification objects from a parcelable array bundle field.
* Update the bundle to have a typed array so fetches in the future don't need
* to do an array copy.
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index b1a5d21..fa11221 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -165,8 +165,6 @@ public class SearchDialog extends Dialog {
setContentView(com.android.internal.R.layout.search_bar);
// get the view elements for local access
- SearchBar searchBar = (SearchBar) findViewById(com.android.internal.R.id.search_bar);
- searchBar.setSearchDialog(this);
mSearchView = (SearchView) findViewById(com.android.internal.R.id.search_view);
mSearchView.setIconified(false);
mSearchView.setOnCloseListener(mOnCloseListener);
@@ -618,8 +616,6 @@ public class SearchDialog extends Dialog {
*/
public static class SearchBar extends LinearLayout {
- private SearchDialog mSearchDialog;
-
public SearchBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -628,18 +624,6 @@ public class SearchDialog extends Dialog {
super(context);
}
- public void setSearchDialog(SearchDialog searchDialog) {
- mSearchDialog = searchDialog;
- }
-
- /**
- * Don't allow action modes in a SearchBar, it looks silly.
- */
- @Override
- public ActionMode startActionModeForChild(View child, ActionMode.Callback callback) {
- return null;
- }
-
@Override
public ActionMode startActionModeForChild(
View child, ActionMode.Callback callback, int type) {
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 207519c..31d1ab7 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -63,12 +63,20 @@ public class StatusBarManager {
| DISABLE_SYSTEM_INFO | DISABLE_RECENT | DISABLE_HOME | DISABLE_BACK | DISABLE_CLOCK
| DISABLE_SEARCH;
+ /**
+ * Flag to disable quick settings.
+ *
+ * Setting this flag disables quick settings completely, but does not disable expanding the
+ * notification shade.
+ */
+ public static final int DISABLE2_QUICK_SETTINGS = 0x00000001;
+
public static final int DISABLE2_NONE = 0x00000000;
- public static final int DISABLE2_MASK = 0x00000000;
+ public static final int DISABLE2_MASK = DISABLE2_QUICK_SETTINGS;
@IntDef(flag = true,
- value = {DISABLE2_NONE, DISABLE2_MASK})
+ value = {DISABLE2_NONE, DISABLE2_MASK, DISABLE2_QUICK_SETTINGS})
@Retention(RetentionPolicy.SOURCE)
public @interface Disable2Flags {}
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index aea413d..470804d 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -231,7 +231,7 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
* device owner app.
*
* <p>The broadcast will be limited to the {@link DeviceAdminReceiver} component specified in
- * the (@link DevicePolicyManager#EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME) field
+ * the {@link DevicePolicyManager#EXTRA_PROVISIONING_DEVICE_INITIALIZER_COMPONENT_NAME} field
* of the original intent or NFC bump that started the provisioning process. You will generally
* handle this in {@link DeviceAdminReceiver#onReadyForUserInitialization}.
*
@@ -450,9 +450,9 @@ public class DeviceAdminReceiver extends BroadcastReceiver {
*
* <p>It is not assumed that the device initializer is finished when it returns from
* this call, as it may do additional setup asynchronously. The device initializer must call
- * {DevicePolicyManager#setUserEnabled(ComponentName admin)} when it has finished any additional
- * setup (such as adding an account by using the {@link AccountManager}) in order for the user
- * to be functional.
+ * {@link DevicePolicyManager#setUserEnabled(ComponentName admin)} when it has finished any
+ * additional setup (such as adding an account by using the {@link AccountManager}) in order for
+ * the user to be functional.
*
* @param context The running context as per {@link #onReceive}.
* @param intent The received intent as per {@link #onReceive}.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ed814c3..8009b6c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -412,7 +412,7 @@ public class DevicePolicyManager {
= "android.app.action.MANAGED_PROFILE_PROVISIONED";
/**
- * A boolean extra indicating whether device encryption is required as part of Device Owner
+ * A boolean extra indicating whether device encryption can be skipped as part of Device Owner
* provisioning.
*
* <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC_V2} that starts device owner
@@ -807,6 +807,24 @@ public class DevicePolicyManager {
public static final String ACTION_SYSTEM_UPDATE_POLICY_CHANGED
= "android.app.action.SYSTEM_UPDATE_POLICY_CHANGED";
+ /**
+ * Permission policy to prompt user for new permission requests for runtime permissions.
+ * Already granted or denied permissions are not affected by this.
+ */
+ public static final int PERMISSION_POLICY_PROMPT = 0;
+
+ /**
+ * Permission policy to always grant new permission requests for runtime permissions.
+ * Already granted or denied permissions are not affected by this.
+ */
+ public static final int PERMISSION_POLICY_AUTO_GRANT = 1;
+
+ /**
+ * Permission policy to always deny new permission requests for runtime permissions.
+ * Already granted or denied permissions are not affected by this.
+ */
+ public static final int PERMISSION_POLICY_AUTO_DENY = 2;
+
/**
* Return true if the given administrator component is currently
@@ -2908,7 +2926,7 @@ public class DevicePolicyManager {
* the user has already been set up.
*/
@SystemApi
- public boolean setActiveProfileOwner(ComponentName admin, String ownerName)
+ public boolean setActiveProfileOwner(ComponentName admin, @Deprecated String ownerName)
throws IllegalArgumentException {
if (mService != null) {
try {
@@ -2974,8 +2992,8 @@ public class DevicePolicyManager {
* @throws IllegalArgumentException if admin is null, the package isn't installed, or the
* preconditions mentioned are not met.
*/
- public boolean setProfileOwner(ComponentName admin, String ownerName, int userHandle)
- throws IllegalArgumentException {
+ public boolean setProfileOwner(ComponentName admin, @Deprecated String ownerName,
+ int userHandle) throws IllegalArgumentException {
if (admin == null) {
throw new NullPointerException("admin cannot be null");
}
@@ -4276,14 +4294,14 @@ public class DevicePolicyManager {
* being disabled.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param enabled New state of the keyguard.
+ * @param disabled {@code true} disables the keyguard, {@code false} reenables it.
*
* @return {@code false} if attempting to disable the keyguard while a lock password was in
- * place. {@code true} otherwise."
+ * place. {@code true} otherwise.
*/
- public boolean setKeyguardEnabledState(ComponentName admin, boolean enabled) {
+ public boolean setKeyguardDisabled(ComponentName admin, boolean disabled) {
try {
- return mService.setKeyguardEnabledState(admin, enabled);
+ return mService.setKeyguardDisabled(admin, disabled);
} catch (RemoteException re) {
Log.w(TAG, "Failed talking with device policy service", re);
return false;
@@ -4291,18 +4309,22 @@ public class DevicePolicyManager {
}
/**
- * Called by device owner to set the enabled state of the status bar. Disabling the status
- * bar blocks notifications, quick settings and other screen overlays that allow escaping from
+ * Called by device owner to disable the status bar. Disabling the status bar blocks
+ * notifications, quick settings and other screen overlays that allow escaping from
* a single use device.
*
* @param admin Which {@link DeviceAdminReceiver} this request is associated with.
- * @param enabled New state of the status bar.
+ * @param disabled {@code true} disables the status bar, {@code false} reenables it.
+ *
+ * @return {@code false} if attempting to disable the status bar failed.
+ * {@code true} otherwise.
*/
- public void setStatusBarEnabledState(ComponentName admin, boolean enabled) {
+ public boolean setStatusBarDisabled(ComponentName admin, boolean disabled) {
try {
- mService.setStatusBarEnabledState(admin, enabled);
+ return mService.setStatusBarDisabled(admin, disabled);
} catch (RemoteException re) {
Log.w(TAG, "Failed talking with device policy service", re);
+ return false;
}
}
@@ -4342,4 +4364,58 @@ public class DevicePolicyManager {
Log.w(TAG, "Failed talking with device policy service", re);
}
}
+
+ /**
+ * Called by profile or device owners to set the default response for future runtime permission
+ * requests by applications. The policy can allow for normal operation which prompts the
+ * user to grant a permission, or can allow automatic granting or denying of runtime
+ * permission requests by an application. This also applies to new permissions declared by app
+ * updates.
+ * @param admin Which profile or device owner this request is associated with.
+ * @param policy One of the policy constants {@link #PERMISSION_POLICY_PROMPT},
+ * {@link #PERMISSION_POLICY_AUTO_GRANT} and {@link #PERMISSION_POLICY_AUTO_DENY}.
+ */
+ public void setPermissionPolicy(ComponentName admin, int policy) {
+ try {
+ mService.setPermissionPolicy(admin, policy);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed talking with device policy service", re);
+ }
+ }
+
+ /**
+ * Returns the current runtime permission policy set by the device or profile owner. The
+ * default is {@link #PERMISSION_POLICY_PROMPT}.
+ * @param admin Which profile or device owner this request is associated with.
+ * @return the current policy for future permission requests.
+ */
+ public int getPermissionPolicy(ComponentName admin) {
+ try {
+ return mService.getPermissionPolicy(admin);
+ } catch (RemoteException re) {
+ return PERMISSION_POLICY_PROMPT;
+ }
+ }
+
+ /**
+ * Grants or revokes a runtime permission to a specific application so that the user
+ * does not have to be prompted. This might affect all permissions in a group that the
+ * runtime permission belongs to. This method can only be called by a profile or device
+ * owner.
+ * @param admin Which profile or device owner this request is associated with.
+ * @param packageName The application to grant or revoke a permission to.
+ * @param permission The permission to grant or revoke.
+ * @param granted Whether or not to grant the permission. If false, all permissions in the
+ * associated permission group will be denied.
+ * @return whether the permission was successfully granted or revoked
+ */
+ public boolean setPermissionGranted(ComponentName admin, String packageName,
+ String permission, boolean granted) {
+ try {
+ return mService.setPermissionGranted(admin, packageName, permission, granted);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed talking with device policy service", re);
+ return false;
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index a678c51..e81e7c1 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -224,9 +224,14 @@ interface IDevicePolicyManager {
void setSystemUpdatePolicy(in ComponentName who, in PersistableBundle policy);
PersistableBundle getSystemUpdatePolicy();
- boolean setKeyguardEnabledState(in ComponentName admin, boolean enabled);
- void setStatusBarEnabledState(in ComponentName who, boolean enabled);
+ boolean setKeyguardDisabled(in ComponentName admin, boolean disabled);
+ boolean setStatusBarDisabled(in ComponentName who, boolean disabled);
boolean getDoNotAskCredentialsOnBoot();
void notifyPendingSystemUpdate(in long updateReceivedTime);
+
+ void setPermissionPolicy(in ComponentName admin, int policy);
+ int getPermissionPolicy(in ComponentName admin);
+ boolean setPermissionGranted(in ComponentName admin, String packageName, String permission,
+ boolean granted);
}
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index d8556a2..6fca0de 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -33,15 +33,21 @@ import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
+import android.util.ArraySet;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.util.HashSet;
+import java.util.Collection;
import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
+import org.xmlpull.v1.XmlPullParserException;
+
/**
* Provides the central interface between an
* application and Android's data backup infrastructure. An application that wishes
@@ -164,7 +170,6 @@ public abstract class BackupAgent extends ContextWrapper {
* to do one-time initialization before the actual backup or restore operation
* is begun.
* <p>
- * Agents do not need to override this method.
*/
public void onCreate() {
}
@@ -268,19 +273,41 @@ public abstract class BackupAgent extends ContextWrapper {
* listed above. Apps only need to override this method if they need to impose special
* limitations on which files are being stored beyond the control that
* {@link #getNoBackupFilesDir()} offers.
+ * Alternatively they can provide an xml resource to specify what data to include or exclude.
+ *
*
* @param data A structured wrapper pointing to the backup destination.
* @throws IOException
*
* @see Context#getNoBackupFilesDir()
+ * @see ApplicationInfo#fullBackupContent
* @see #fullBackupFile(File, FullBackupDataOutput)
* @see #onRestoreFile(ParcelFileDescriptor, long, File, int, long, long)
*/
public void onFullBackup(FullBackupDataOutput data) throws IOException {
- ApplicationInfo appInfo = getApplicationInfo();
+ FullBackup.BackupScheme backupScheme = FullBackup.getBackupScheme(this);
+ if (!backupScheme.isFullBackupContentEnabled()) {
+ return;
+ }
+
+ Map<String, Set<String>> manifestIncludeMap;
+ ArraySet<String> manifestExcludeSet;
+ try {
+ manifestIncludeMap =
+ backupScheme.maybeParseAndGetCanonicalIncludePaths();
+ manifestExcludeSet = backupScheme.maybeParseAndGetCanonicalExcludePaths();
+ } catch (IOException | XmlPullParserException e) {
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER,
+ "Exception trying to parse fullBackupContent xml file!"
+ + " Aborting full backup.", e);
+ }
+ return;
+ }
+
+ final String packageName = getPackageName();
+ final ApplicationInfo appInfo = getApplicationInfo();
- // Note that we don't need to think about the no_backup dir because it's outside
- // all of the ones we will be traversing
String rootDir = new File(appInfo.dataDir).getCanonicalPath();
String filesDir = getFilesDir().getCanonicalPath();
String nobackupDir = getNoBackupFilesDir().getCanonicalPath();
@@ -292,34 +319,49 @@ public abstract class BackupAgent extends ContextWrapper {
? new File(appInfo.nativeLibraryDir).getCanonicalPath()
: null;
- // Filters, the scan queue, and the set of resulting entities
- HashSet<String> filterSet = new HashSet<String>();
- String packageName = getPackageName();
+ // Maintain a set of excluded directories so that as we traverse the tree we know we're not
+ // going places we don't expect, and so the manifest includes can't take precedence over
+ // what the framework decides is not to be included.
+ final ArraySet<String> traversalExcludeSet = new ArraySet<String>();
- // Okay, start with the app's root tree, but exclude all of the canonical subdirs
+ // Add the directories we always exclude.
+ traversalExcludeSet.add(cacheDir);
+ traversalExcludeSet.add(codeCacheDir);
+ traversalExcludeSet.add(nobackupDir);
if (libDir != null) {
- filterSet.add(libDir);
+ traversalExcludeSet.add(libDir);
}
- filterSet.add(cacheDir);
- filterSet.add(codeCacheDir);
- filterSet.add(databaseDir);
- filterSet.add(sharedPrefsDir);
- filterSet.add(filesDir);
- filterSet.add(nobackupDir);
- fullBackupFileTree(packageName, FullBackup.ROOT_TREE_TOKEN, rootDir, filterSet, data);
-
- // Now do the same for the files dir, db dir, and shared prefs dir
- filterSet.add(rootDir);
- filterSet.remove(filesDir);
- fullBackupFileTree(packageName, FullBackup.DATA_TREE_TOKEN, filesDir, filterSet, data);
-
- filterSet.add(filesDir);
- filterSet.remove(databaseDir);
- fullBackupFileTree(packageName, FullBackup.DATABASE_TREE_TOKEN, databaseDir, filterSet, data);
-
- filterSet.add(databaseDir);
- filterSet.remove(sharedPrefsDir);
- fullBackupFileTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, sharedPrefsDir, filterSet, data);
+
+ traversalExcludeSet.add(databaseDir);
+ traversalExcludeSet.add(sharedPrefsDir);
+ traversalExcludeSet.add(filesDir);
+
+ // Root dir first.
+ applyXmlFiltersAndDoFullBackupForDomain(
+ packageName, FullBackup.ROOT_TREE_TOKEN, manifestIncludeMap,
+ manifestExcludeSet, traversalExcludeSet, data);
+ traversalExcludeSet.add(rootDir);
+
+ // Data dir next.
+ traversalExcludeSet.remove(filesDir);
+ applyXmlFiltersAndDoFullBackupForDomain(
+ packageName, FullBackup.DATA_TREE_TOKEN, manifestIncludeMap,
+ manifestExcludeSet, traversalExcludeSet, data);
+ traversalExcludeSet.add(filesDir);
+
+ // Database directory.
+ traversalExcludeSet.remove(databaseDir);
+ applyXmlFiltersAndDoFullBackupForDomain(
+ packageName, FullBackup.DATABASE_TREE_TOKEN, manifestIncludeMap,
+ manifestExcludeSet, traversalExcludeSet, data);
+ traversalExcludeSet.add(databaseDir);
+
+ // SharedPrefs.
+ traversalExcludeSet.remove(sharedPrefsDir);
+ applyXmlFiltersAndDoFullBackupForDomain(
+ packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, manifestIncludeMap,
+ manifestExcludeSet, traversalExcludeSet, data);
+ traversalExcludeSet.add(sharedPrefsDir);
// getExternalFilesDir() location associated with this app. Technically there should
// not be any files here if the app does not properly have permission to access
@@ -331,8 +373,36 @@ public abstract class BackupAgent extends ContextWrapper {
if (Process.myUid() != Process.SYSTEM_UID) {
File efLocation = getExternalFilesDir(null);
if (efLocation != null) {
- fullBackupFileTree(packageName, FullBackup.MANAGED_EXTERNAL_TREE_TOKEN,
- efLocation.getCanonicalPath(), null, data);
+ applyXmlFiltersAndDoFullBackupForDomain(
+ packageName, FullBackup.MANAGED_EXTERNAL_TREE_TOKEN, manifestIncludeMap,
+ manifestExcludeSet, traversalExcludeSet, data);
+ }
+
+ }
+ }
+
+ /**
+ * Check whether the xml yielded any <include/> tag for the provided <code>domainToken</code>.
+ * If so, perform a {@link #fullBackupFileTree} which backs up the file or recurses if the path
+ * is a directory.
+ */
+ private void applyXmlFiltersAndDoFullBackupForDomain(String packageName, String domainToken,
+ Map<String, Set<String>> includeMap,
+ ArraySet<String> filterSet,
+ ArraySet<String> traversalExcludeSet,
+ FullBackupDataOutput data)
+ throws IOException {
+ if (includeMap == null || includeMap.size() == 0) {
+ // Do entire sub-tree for the provided token.
+ fullBackupFileTree(packageName, domainToken,
+ FullBackup.getBackupScheme(this).tokenToDirectoryPath(domainToken),
+ filterSet, traversalExcludeSet, data);
+ } else if (includeMap.get(domainToken) != null) {
+ // This will be null if the xml parsing didn't yield any rules for
+ // this domain (there may still be rules for other domains).
+ for (String includeFile : includeMap.get(domainToken)) {
+ fullBackupFileTree(packageName, domainToken, includeFile, filterSet,
+ traversalExcludeSet, data);
}
}
}
@@ -430,21 +500,31 @@ public abstract class BackupAgent extends ContextWrapper {
// without transmitting any file data.
if (DEBUG) Log.i(TAG, "backupFile() of " + filePath + " => domain=" + domain
+ " rootpath=" + rootpath);
-
+
FullBackup.backupToTar(getPackageName(), domain, null, rootpath, filePath, output);
}
/**
* Scan the dir tree (if it actually exists) and process each entry we find. If the
- * 'excludes' parameter is non-null, it is consulted each time a new file system entity
+ * 'excludes' parameters are non-null, they are consulted each time a new file system entity
* is visited to see whether that entity (and its subtree, if appropriate) should be
* omitted from the backup process.
*
+ * @param systemExcludes An optional list of excludes.
* @hide
*/
- protected final void fullBackupFileTree(String packageName, String domain, String rootPath,
- HashSet<String> excludes, FullBackupDataOutput output) {
- File rootFile = new File(rootPath);
+ protected final void fullBackupFileTree(String packageName, String domain, String startingPath,
+ ArraySet<String> manifestExcludes,
+ ArraySet<String> systemExcludes,
+ FullBackupDataOutput output) {
+ // Pull out the domain and set it aside to use when making the tarball.
+ String domainPath = FullBackup.getBackupScheme(this).tokenToDirectoryPath(domain);
+ if (domainPath == null) {
+ // Should never happen.
+ return;
+ }
+
+ File rootFile = new File(startingPath);
if (rootFile.exists()) {
LinkedList<File> scanQueue = new LinkedList<File>();
scanQueue.add(rootFile);
@@ -456,7 +536,10 @@ public abstract class BackupAgent extends ContextWrapper {
filePath = file.getCanonicalPath();
// prune this subtree?
- if (excludes != null && excludes.contains(filePath)) {
+ if (manifestExcludes != null && manifestExcludes.contains(filePath)) {
+ continue;
+ }
+ if (systemExcludes != null && systemExcludes.contains(filePath)) {
continue;
}
@@ -475,14 +558,20 @@ public abstract class BackupAgent extends ContextWrapper {
}
} catch (IOException e) {
if (DEBUG) Log.w(TAG, "Error canonicalizing path of " + file);
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER, "Error canonicalizing path of " + file);
+ }
continue;
} catch (ErrnoException e) {
if (DEBUG) Log.w(TAG, "Error scanning file " + file + " : " + e);
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER, "Error scanning file " + file + " : " + e);
+ }
continue;
}
// Finally, back this file up (or measure it) before proceeding
- FullBackup.backupToTar(packageName, domain, null, rootPath, filePath, output);
+ FullBackup.backupToTar(packageName, domain, null, domainPath, filePath, output);
}
}
}
@@ -516,10 +605,91 @@ public abstract class BackupAgent extends ContextWrapper {
public void onRestoreFile(ParcelFileDescriptor data, long size,
File destination, int type, long mode, long mtime)
throws IOException {
+ FullBackup.BackupScheme bs = FullBackup.getBackupScheme(this);
+ if (!bs.isFullBackupContentEnabled()) {
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER,
+ "onRestoreFile \"" + destination.getCanonicalPath()
+ + "\" : fullBackupContent not enabled for " + getPackageName());
+ }
+ return;
+ }
+ Map<String, Set<String>> includes = null;
+ ArraySet<String> excludes = null;
+ final String destinationCanonicalPath = destination.getCanonicalPath();
+ try {
+ includes = bs.maybeParseAndGetCanonicalIncludePaths();
+ excludes = bs.maybeParseAndGetCanonicalExcludePaths();
+ } catch (XmlPullParserException e) {
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER,
+ "onRestoreFile \"" + destinationCanonicalPath
+ + "\" : Exception trying to parse fullBackupContent xml file!"
+ + " Aborting onRestoreFile.", e);
+ }
+ return;
+ }
+
+ if (excludes != null &&
+ isFileSpecifiedInPathList(destination, excludes)) {
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER,
+ "onRestoreFile: \"" + destinationCanonicalPath + "\": listed in"
+ + " excludes; skipping.");
+ }
+ return;
+ }
+
+ if (includes != null && !includes.isEmpty()) {
+ // Rather than figure out the <include/> domain based on the path (a lot of code, and
+ // it's a small list), we'll go through and look for it.
+ boolean explicitlyIncluded = false;
+ for (Set<String> domainIncludes : includes.values()) {
+ explicitlyIncluded |= isFileSpecifiedInPathList(destination, domainIncludes);
+ if (explicitlyIncluded) {
+ break;
+ }
+ }
+ if (!explicitlyIncluded) {
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER,
+ "onRestoreFile: Trying to restore \""
+ + destinationCanonicalPath + "\" but it isn't specified"
+ + " in the included files; skipping.");
+ }
+ return;
+ }
+ }
FullBackup.restoreFile(data, size, type, mode, mtime, destination);
}
/**
+ * @return True if the provided file is either directly in the provided list, or the provided
+ * file is within a directory in the list.
+ */
+ private boolean isFileSpecifiedInPathList(File file, Collection<String> canonicalPathList)
+ throws IOException {
+ for (String canonicalPath : canonicalPathList) {
+ File fileFromList = new File(canonicalPath);
+ if (fileFromList.isDirectory()) {
+ if (file.isDirectory()) {
+ // If they are both directories check exact equals.
+ return file.equals(fileFromList);
+ } else {
+ // O/w we have to check if the file is within the directory from the list.
+ return file.getCanonicalPath().startsWith(canonicalPath);
+ }
+ } else {
+ if (file.equals(fileFromList)) {
+ // Need to check the explicit "equals" so we don't end up with substrings.
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Only specialized platform agents should overload this entry point to support
* restores to crazy non-app locations.
* @hide
@@ -533,31 +703,9 @@ public abstract class BackupAgent extends ContextWrapper {
+ " domain=" + domain + " relpath=" + path + " mode=" + mode
+ " mtime=" + mtime);
- // Parse out the semantic domains into the correct physical location
- if (domain.equals(FullBackup.DATA_TREE_TOKEN)) {
- basePath = getFilesDir().getCanonicalPath();
- } else if (domain.equals(FullBackup.DATABASE_TREE_TOKEN)) {
- basePath = getDatabasePath("foo").getParentFile().getCanonicalPath();
- } else if (domain.equals(FullBackup.ROOT_TREE_TOKEN)) {
- basePath = new File(getApplicationInfo().dataDir).getCanonicalPath();
- } else if (domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)) {
- basePath = getSharedPrefsFile("foo").getParentFile().getCanonicalPath();
- } else if (domain.equals(FullBackup.CACHE_TREE_TOKEN)) {
- basePath = getCacheDir().getCanonicalPath();
- } else if (domain.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) {
- // make sure we can try to restore here before proceeding
- if (Process.myUid() != Process.SYSTEM_UID) {
- File efLocation = getExternalFilesDir(null);
- if (efLocation != null) {
- basePath = getExternalFilesDir(null).getCanonicalPath();
- mode = -1; // < 0 is a token to skip attempting a chmod()
- }
- }
- } else if (domain.equals(FullBackup.NO_BACKUP_TREE_TOKEN)) {
- basePath = getNoBackupFilesDir().getCanonicalPath();
- } else {
- // Not a supported location
- Log.i(TAG, "Unrecognized domain " + domain);
+ basePath = FullBackup.getBackupScheme(this).tokenToDirectoryPath(domain);
+ if (domain.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) {
+ mode = -1; // < 0 is a token to skip attempting a chmod()
}
// Now that we've figured out where the data goes, send it on its way
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index 259884e..7718a36 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -16,16 +16,31 @@
package android.app.backup;
-import android.os.ParcelFileDescriptor;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.XmlResourceParser;
+import android.os.*;
+import android.os.Process;
import android.system.ErrnoException;
import android.system.Os;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
+import org.xmlpull.v1.XmlPullParser;
+
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.xmlpull.v1.XmlPullParserException;
/**
* Global constant definitions et cetera related to the full-backup-to-fd
* binary format. Nothing in this namespace is part of any API; it's all
@@ -35,6 +50,8 @@ import java.io.IOException;
*/
public class FullBackup {
static final String TAG = "FullBackup";
+ /** Enable this log tag to get verbose information while parsing the client xml. */
+ static final String TAG_XML_PARSER = "BackupXmlParserLogging";
public static final String APK_TREE_TOKEN = "a";
public static final String OBB_TREE_TOKEN = "obb";
@@ -60,6 +77,27 @@ public class FullBackup {
static public native int backupToTar(String packageName, String domain,
String linkdomain, String rootpath, String path, FullBackupDataOutput output);
+ private static final Map<String, BackupScheme> kPackageBackupSchemeMap =
+ new ArrayMap<String, BackupScheme>();
+
+ static synchronized BackupScheme getBackupScheme(Context context) {
+ BackupScheme backupSchemeForPackage =
+ kPackageBackupSchemeMap.get(context.getPackageName());
+ if (backupSchemeForPackage == null) {
+ backupSchemeForPackage = new BackupScheme(context);
+ kPackageBackupSchemeMap.put(context.getPackageName(), backupSchemeForPackage);
+ }
+ return backupSchemeForPackage;
+ }
+
+ public static BackupScheme getBackupSchemeForTest(Context context) {
+ BackupScheme testing = new BackupScheme(context);
+ testing.mExcludes = new ArraySet();
+ testing.mIncludes = new ArrayMap();
+ return testing;
+ }
+
+
/**
* Copy data from a socket to the given File location on permanent storage. The
* modification time and access mode of the resulting file will be set if desired,
@@ -106,6 +144,8 @@ public class FullBackup {
if (!parent.exists()) {
// in practice this will only be for the default semantic directories,
// and using the default mode for those is appropriate.
+ // This can also happen for the case where a parent directory has been
+ // excluded, but a file within that directory has been included.
parent.mkdirs();
}
out = new FileOutputStream(outFile);
@@ -154,4 +194,363 @@ public class FullBackup {
outFile.setLastModified(mtime);
}
}
+
+ @VisibleForTesting
+ public static class BackupScheme {
+ private final File FILES_DIR;
+ private final File DATABASE_DIR;
+ private final File ROOT_DIR;
+ private final File SHAREDPREF_DIR;
+ private final File EXTERNAL_DIR;
+ private final File CACHE_DIR;
+ private final File NOBACKUP_DIR;
+
+ final int mFullBackupContent;
+ final PackageManager mPackageManager;
+ final String mPackageName;
+
+ /**
+ * Parse out the semantic domains into the correct physical location.
+ */
+ String tokenToDirectoryPath(String domainToken) {
+ try {
+ if (domainToken.equals(FullBackup.DATA_TREE_TOKEN)) {
+ return FILES_DIR.getCanonicalPath();
+ } else if (domainToken.equals(FullBackup.DATABASE_TREE_TOKEN)) {
+ return DATABASE_DIR.getCanonicalPath();
+ } else if (domainToken.equals(FullBackup.ROOT_TREE_TOKEN)) {
+ return ROOT_DIR.getCanonicalPath();
+ } else if (domainToken.equals(FullBackup.SHAREDPREFS_TREE_TOKEN)) {
+ return SHAREDPREF_DIR.getCanonicalPath();
+ } else if (domainToken.equals(FullBackup.CACHE_TREE_TOKEN)) {
+ return CACHE_DIR.getCanonicalPath();
+ } else if (domainToken.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN)) {
+ if (EXTERNAL_DIR != null) {
+ return EXTERNAL_DIR.getCanonicalPath();
+ } else {
+ return null;
+ }
+ } else if (domainToken.equals(FullBackup.NO_BACKUP_TREE_TOKEN)) {
+ return NOBACKUP_DIR.getCanonicalPath();
+ }
+ // Not a supported location
+ Log.i(TAG, "Unrecognized domain " + domainToken);
+ return null;
+ } catch (IOException e) {
+ Log.i(TAG, "Error reading directory for domain: " + domainToken);
+ return null;
+ }
+
+ }
+ /**
+ * A map of domain -> list of canonical file names in that domain that are to be included.
+ * We keep track of the domain so that we can go through the file system in order later on.
+ */
+ Map<String, Set<String>> mIncludes;
+ /**e
+ * List that will be populated with the canonical names of each file or directory that is
+ * to be excluded.
+ */
+ ArraySet<String> mExcludes;
+
+ BackupScheme(Context context) {
+ mFullBackupContent = context.getApplicationInfo().fullBackupContent;
+ mPackageManager = context.getPackageManager();
+ mPackageName = context.getPackageName();
+ FILES_DIR = context.getFilesDir();
+ DATABASE_DIR = context.getDatabasePath("foo").getParentFile();
+ ROOT_DIR = new File(context.getApplicationInfo().dataDir);
+ SHAREDPREF_DIR = context.getSharedPrefsFile("foo").getParentFile();
+ CACHE_DIR = context.getCacheDir();
+ NOBACKUP_DIR = context.getNoBackupFilesDir();
+ if (android.os.Process.myUid() != Process.SYSTEM_UID) {
+ EXTERNAL_DIR = context.getExternalFilesDir(null);
+ } else {
+ EXTERNAL_DIR = null;
+ }
+ }
+
+ boolean isFullBackupContentEnabled() {
+ if (mFullBackupContent < 0) {
+ // android:fullBackupContent="false", bail.
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER, "android:fullBackupContent - \"false\"");
+ }
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @return A mapping of domain -> canonical paths within that domain. Each of these paths
+ * specifies a file that the client has explicitly included in their backup set. If this
+ * map is empty we will back up the entire data directory (including managed external
+ * storage).
+ */
+ public synchronized Map<String, Set<String>> maybeParseAndGetCanonicalIncludePaths()
+ throws IOException, XmlPullParserException {
+ if (mIncludes == null) {
+ maybeParseBackupSchemeLocked();
+ }
+ return mIncludes;
+ }
+
+ /**
+ * @return A set of canonical paths that are to be excluded from the backup/restore set.
+ */
+ public synchronized ArraySet<String> maybeParseAndGetCanonicalExcludePaths()
+ throws IOException, XmlPullParserException {
+ if (mExcludes == null) {
+ maybeParseBackupSchemeLocked();
+ }
+ return mExcludes;
+ }
+
+ private void maybeParseBackupSchemeLocked() throws IOException, XmlPullParserException {
+ // This not being null is how we know that we've tried to parse the xml already.
+ mIncludes = new ArrayMap<String, Set<String>>();
+ mExcludes = new ArraySet<String>();
+
+ if (mFullBackupContent == 0) {
+ // android:fullBackupContent="true" which means that we'll do everything.
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER, "android:fullBackupContent - \"true\"");
+ }
+ } else {
+ // android:fullBackupContent="@xml/some_resource".
+ if (Log.isLoggable(FullBackup.TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(FullBackup.TAG_XML_PARSER,
+ "android:fullBackupContent - found xml resource");
+ }
+ XmlResourceParser parser = null;
+ try {
+ parser = mPackageManager
+ .getResourcesForApplication(mPackageName)
+ .getXml(mFullBackupContent);
+ parseBackupSchemeFromXmlLocked(parser, mExcludes, mIncludes);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Throw it as an IOException
+ throw new IOException(e);
+ } finally {
+ if (parser != null) {
+ parser.close();
+ }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ public void parseBackupSchemeFromXmlLocked(XmlPullParser parser,
+ Set<String> excludes,
+ Map<String, Set<String>> includes)
+ throws IOException, XmlPullParserException {
+ int event = parser.getEventType(); // START_DOCUMENT
+ while (event != XmlPullParser.START_TAG) {
+ event = parser.next();
+ }
+
+ if (!"full-backup-content".equals(parser.getName())) {
+ throw new XmlPullParserException("Xml file didn't start with correct tag" +
+ " (<full-backup-content>). Found \"" + parser.getName() + "\"");
+ }
+
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "\n");
+ Log.v(TAG_XML_PARSER, "====================================================");
+ Log.v(TAG_XML_PARSER, "Found valid fullBackupContent; parsing xml resource.");
+ Log.v(TAG_XML_PARSER, "====================================================");
+ Log.v(TAG_XML_PARSER, "");
+ }
+
+ while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ switch (event) {
+ case XmlPullParser.START_TAG:
+ validateInnerTagContents(parser);
+ final String domainFromXml = parser.getAttributeValue(null, "domain");
+ final File domainDirectory =
+ getDirectoryForCriteriaDomain(domainFromXml);
+ if (domainDirectory == null) {
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "...parsing \"" + parser.getName() + "\": "
+ + "domain=\"" + domainFromXml + "\" invalid; skipping");
+ }
+ break;
+ }
+ final File canonicalFile =
+ extractCanonicalFile(domainDirectory,
+ parser.getAttributeValue(null, "path"));
+ if (canonicalFile == null) {
+ break;
+ }
+
+ Set<String> activeSet = parseCurrentTagForDomain(
+ parser, excludes, includes, domainFromXml);
+ activeSet.add(canonicalFile.getCanonicalPath());
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "...parsed " + canonicalFile.getCanonicalPath()
+ + " for domain \"" + domainFromXml + "\"");
+ }
+
+ // Special case journal files (not dirs) for sqlite database. frowny-face.
+ // Note that for a restore, the file is never a directory (b/c it doesn't
+ // exist). We have no way of knowing a priori whether or not to expect a
+ // dir, so we add the -journal anyway to be safe.
+ if ("database".equals(domainFromXml) && !canonicalFile.isDirectory()) {
+ final String canonicalJournalPath =
+ canonicalFile.getCanonicalPath() + "-journal";
+ activeSet.add(canonicalJournalPath);
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "...automatically generated "
+ + canonicalJournalPath + ". Ignore if nonexistant.");
+ }
+ }
+ }
+ }
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "\n");
+ Log.v(TAG_XML_PARSER, "Xml resource parsing complete.");
+ Log.v(TAG_XML_PARSER, "Final tally.");
+ Log.v(TAG_XML_PARSER, "Includes:");
+ if (includes.isEmpty()) {
+ Log.v(TAG_XML_PARSER, " ...nothing specified (This means the entirety of app"
+ + " data minus excludes)");
+ } else {
+ for (Map.Entry<String, Set<String>> entry : includes.entrySet()) {
+ Log.v(TAG_XML_PARSER, " domain=" + entry.getKey());
+ for (String includeData : entry.getValue()) {
+ Log.v(TAG_XML_PARSER, " " + includeData);
+ }
+ }
+ }
+
+ Log.v(TAG_XML_PARSER, "Excludes:");
+ if (excludes.isEmpty()) {
+ Log.v(TAG_XML_PARSER, " ...nothing to exclude.");
+ } else {
+ for (String excludeData : excludes) {
+ Log.v(TAG_XML_PARSER, " " + excludeData);
+ }
+ }
+
+ Log.v(TAG_XML_PARSER, " ");
+ Log.v(TAG_XML_PARSER, "====================================================");
+ Log.v(TAG_XML_PARSER, "\n");
+ }
+ }
+
+ private Set<String> parseCurrentTagForDomain(XmlPullParser parser,
+ Set<String> excludes,
+ Map<String, Set<String>> includes,
+ String domain)
+ throws XmlPullParserException {
+ if ("include".equals(parser.getName())) {
+ final String domainToken = getTokenForXmlDomain(domain);
+ Set<String> includeSet = includes.get(domainToken);
+ if (includeSet == null) {
+ includeSet = new ArraySet<String>();
+ includes.put(domainToken, includeSet);
+ }
+ return includeSet;
+ } else if ("exclude".equals(parser.getName())) {
+ return excludes;
+ } else {
+ // Unrecognised tag => hard failure.
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "Invalid tag found in xml \""
+ + parser.getName() + "\"; aborting operation.");
+ }
+ throw new XmlPullParserException("Unrecognised tag in backup" +
+ " criteria xml (" + parser.getName() + ")");
+ }
+ }
+
+ /**
+ * Map xml specified domain (human-readable, what clients put in their manifest's xml) to
+ * BackupAgent internal data token.
+ * @return null if the xml domain was invalid.
+ */
+ private String getTokenForXmlDomain(String xmlDomain) {
+ if ("root".equals(xmlDomain)) {
+ return FullBackup.ROOT_TREE_TOKEN;
+ } else if ("file".equals(xmlDomain)) {
+ return FullBackup.DATA_TREE_TOKEN;
+ } else if ("database".equals(xmlDomain)) {
+ return FullBackup.DATABASE_TREE_TOKEN;
+ } else if ("sharedpref".equals(xmlDomain)) {
+ return FullBackup.SHAREDPREFS_TREE_TOKEN;
+ } else if ("external".equals(xmlDomain)) {
+ return FullBackup.MANAGED_EXTERNAL_TREE_TOKEN;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ *
+ * @param domain Directory where the specified file should exist. Not null.
+ * @param filePathFromXml parsed from xml. Not sanitised before calling this function so may be
+ * null.
+ * @return The canonical path of the file specified or null if no such file exists.
+ */
+ private File extractCanonicalFile(File domain, String filePathFromXml) {
+ if (filePathFromXml == null) {
+ // Allow things like <include domain="sharedpref"/>
+ filePathFromXml = "";
+ }
+ if (filePathFromXml.contains("..")) {
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "...resolved \"" + domain.getPath() + " " + filePathFromXml
+ + "\", but the \"..\" path is not permitted; skipping.");
+ }
+ return null;
+ }
+ if (filePathFromXml.contains("//")) {
+ if (Log.isLoggable(TAG_XML_PARSER, Log.VERBOSE)) {
+ Log.v(TAG_XML_PARSER, "...resolved \"" + domain.getPath() + " " + filePathFromXml
+ + "\", which contains the invalid \"//\" sequence; skipping.");
+ }
+ return null;
+ }
+ return new File(domain, filePathFromXml);
+ }
+
+ /**
+ * @param domain parsed from xml. Not sanitised before calling this function so may be null.
+ * @return The directory relevant to the domain specified.
+ */
+ private File getDirectoryForCriteriaDomain(String domain) {
+ if (TextUtils.isEmpty(domain)) {
+ return null;
+ }
+ if ("file".equals(domain)) {
+ return FILES_DIR;
+ } else if ("database".equals(domain)) {
+ return DATABASE_DIR;
+ } else if ("root".equals(domain)) {
+ return ROOT_DIR;
+ } else if ("sharedpref".equals(domain)) {
+ return SHAREDPREF_DIR;
+ } else if ("external".equals(domain)) {
+ return EXTERNAL_DIR;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Let's be strict about the type of xml the client can write. If we see anything untoward,
+ * throw an XmlPullParserException.
+ */
+ private void validateInnerTagContents(XmlPullParser parser)
+ throws XmlPullParserException {
+ if (parser.getAttributeCount() > 2) {
+ throw new XmlPullParserException("At most 2 tag attributes allowed for \""
+ + parser.getName() + "\" tag (\"domain\" & \"path\".");
+ }
+ if (!"include".equals(parser.getName()) && !"exclude".equals(parser.getName())) {
+ throw new XmlPullParserException("A valid tag is one of \"<include/>\" or" +
+ " \"<exclude/>. You provided \"" + parser.getName() + "\"");
+ }
+ }
+ }
}
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 17cff5c..32951d9 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -32,4 +32,5 @@ interface ITrustManager {
void reportKeyguardShowingChanged();
boolean isDeviceLocked(int userId);
boolean isDeviceSecure(int userId);
+ boolean hasUserAuthenticatedSinceBoot(int userId);
}
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index b5c5317..8cab565 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -147,6 +147,23 @@ public class TrustManager {
}
}
+ /**
+ * Checks whether the specified user has been authenticated since the last boot.
+ *
+ * @param userId the user id of the user to check for
+ * @return true if the user has authenticated since boot, false otherwise
+ *
+ * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+ */
+ public boolean hasUserAuthenticatedSinceBoot(int userId) {
+ try {
+ return mService.hasUserAuthenticatedSinceBoot(userId);
+ } catch (RemoteException e) {
+ onError(e);
+ return false;
+ }
+ }
+
private void onError(Exception e) {
Log.e(TAG, "Error while calling TrustManagerService", e);
}
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index 00248cc..1205708 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -1053,6 +1053,20 @@ public class AppWidgetManager {
}
}
+ /**
+ * @hide
+ */
+ public boolean isBoundWidgetPackage(String packageName, int userId) {
+ if (mService == null) {
+ return false;
+ }
+ try {
+ return mService.isBoundWidgetPackage(packageName, userId);
+ } catch (RemoteException re) {
+ throw new RuntimeException("system server dead?", re);
+ }
+ }
+
private boolean bindAppWidgetIdIfAllowed(int appWidgetId, int profileId,
ComponentName provider, Bundle options) {
if (mService == null) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 3044a94..0a77868 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -34,6 +34,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.app.ActivityThread;
import android.os.SystemProperties;
+import android.provider.Settings;
import android.os.Binder;
import android.util.Log;
import android.util.Pair;
@@ -389,7 +390,7 @@ public final class BluetoothAdapter {
* @hide
*/
public static final String ACTION_BLE_STATE_CHANGED =
- "anrdoid.bluetooth.adapter.action.BLE_STATE_CHANGED";
+ "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
/**
* Broadcast Action: The notifys Bluetooth ACL connected event. This will be
@@ -632,24 +633,6 @@ public final class BluetoothAdapter {
}
/**
- * Returns true if LE only mode is enabled, that is apps
- * have authorization to turn only BT ON and the calling
- * app has privilage to do so
- */
- private boolean isLEAlwaysOnEnabled() {
- boolean ret = false;
- if (SystemProperties.getBoolean("ro.bluetooth.blealwayson", true) == true) {
- Log.v(TAG, "LE always on mode is enabled");
- // TODO: System API authorization check
- ret = true;
- } else {
- Log.v(TAG, "LE always on mode is disabled");
- ret = false;
- }
- return ret;
- }
-
- /**
* Turns off Bluetooth LE which was earlier turned on by calling EnableBLE().
*
* <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition
@@ -676,7 +659,7 @@ public final class BluetoothAdapter {
* @hide
*/
public boolean disableBLE() {
- if (isLEAlwaysOnEnabled() != true) return false;
+ if (!isBleScanAlwaysAvailable()) return false;
int state = getLeState();
if (state == BluetoothAdapter.STATE_ON) {
@@ -738,7 +721,7 @@ public final class BluetoothAdapter {
* @hide
*/
public boolean enableBLE() {
- if (isLEAlwaysOnEnabled() != true) return false;
+ if (!isBleScanAlwaysAvailable()) return false;
if (isLeEnabled() == true) {
if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!");
@@ -1243,8 +1226,12 @@ public final class BluetoothAdapter {
*/
@SystemApi
public boolean isBleScanAlwaysAvailable() {
- // TODO: implement after Settings UI change.
- return false;
+ try {
+ return mManagerService.isBleScanAlwaysAvailable();
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote expection when calling isBleScanAlwaysAvailable", e);
+ return false;
+ }
}
/**
@@ -1705,7 +1692,8 @@ public final class BluetoothAdapter {
* @param context Context of the application
* @param listener The service Listener for connection callbacks.
* @param profile The Bluetooth profile; either {@link BluetoothProfile#HEALTH},
- * {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#A2DP}.
+ * {@link BluetoothProfile#HEADSET}, {@link BluetoothProfile#A2DP}.
+ * {@link BluetoothProfile#GATT} or {@link BluetoothProfile#GATT_SERVER}.
* @return true on success, false on error
*/
public boolean getProfileProxy(Context context, BluetoothProfile.ServiceListener listener,
diff --git a/core/java/android/bluetooth/BluetoothHealthCallback.java b/core/java/android/bluetooth/BluetoothHealthCallback.java
index baf2ade..128376f 100644
--- a/core/java/android/bluetooth/BluetoothHealthCallback.java
+++ b/core/java/android/bluetooth/BluetoothHealthCallback.java
@@ -17,6 +17,7 @@
package android.bluetooth;
+import android.annotation.BinderThread;
import android.os.ParcelFileDescriptor;
import android.util.Log;
@@ -39,6 +40,7 @@ public abstract class BluetoothHealthCallback {
* {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_SUCCESS} or
* {@link BluetoothHealth#APP_CONFIG_UNREGISTRATION_FAILURE}
*/
+ @BinderThread
public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration config,
int status) {
Log.d(TAG, "onHealthAppConfigurationStatusChange: " + config + "Status: " + status);
@@ -58,6 +60,7 @@ public abstract class BluetoothHealthCallback {
* @param channelId The id associated with the channel. This id will be used
* in future calls like when disconnecting the channel.
*/
+ @BinderThread
public void onHealthChannelStateChange(BluetoothHealthAppConfiguration config,
BluetoothDevice device, int prevState, int newState, ParcelFileDescriptor fd,
int channelId) {
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index 7b4c6f9..014cb22 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -28,13 +28,41 @@ import android.os.IBinder;
import android.os.ServiceManager;
import android.util.Log;
-
+/**
+ * This class provides the APIs to control the Bluetooth SIM
+ * Access Profile (SAP).
+ *
+ * <p>BluetoothSap is a proxy object for controlling the Bluetooth
+ * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get
+ * the BluetoothSap proxy object.
+ *
+ * <p>Each method is protected with its appropriate permission.
+ * @hide
+ */
public final class BluetoothSap implements BluetoothProfile {
private static final String TAG = "BluetoothSap";
private static final boolean DBG = true;
private static final boolean VDBG = false;
+ /**
+ * Intent used to broadcast the change in connection state of the profile.
+ *
+ * <p>This intent will have 4 extras:
+ * <ul>
+ * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
+ * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
+ * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
+ * </ul>
+ *
+ * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
+ * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
+ * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
+ *
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
+ * receive.
+ * @hide
+ */
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
@@ -43,12 +71,22 @@ public final class BluetoothSap implements BluetoothProfile {
private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
- /** There was an error trying to obtain the state */
- public static final int STATE_ERROR = -1;
+ /**
+ * There was an error trying to obtain the state.
+ * @hide
+ */
+ public static final int STATE_ERROR = -1;
- public static final int RESULT_FAILURE = 0;
+ /**
+ * Connection state change succceeded.
+ * @hide
+ */
public static final int RESULT_SUCCESS = 1;
- /** Connection canceled before completion. */
+
+ /**
+ * Connection canceled before completion.
+ * @hide
+ */
public static final int RESULT_CANCELED = 2;
final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
@@ -124,6 +162,7 @@ public final class BluetoothSap implements BluetoothProfile {
* Other public functions of BluetoothSap will return default error
* results once close() has been called. Multiple invocations of close()
* are ok.
+ * @hide
*/
public synchronized void close() {
IBluetoothManager mgr = mAdapter.getBluetoothManager();
@@ -152,6 +191,7 @@ public final class BluetoothSap implements BluetoothProfile {
* Get the current state of the BluetoothSap service.
* @return One of the STATE_ return codes, or STATE_ERROR if this proxy
* object is currently not connected to the Sap service.
+ * @hide
*/
public int getState() {
if (VDBG) log("getState()");
@@ -171,6 +211,7 @@ public final class BluetoothSap implements BluetoothProfile {
* @return The remote Bluetooth device, or null if not in connected or
* connecting state, or if this proxy object is not connected to
* the Sap service.
+ * @hide
*/
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
@@ -189,6 +230,7 @@ public final class BluetoothSap implements BluetoothProfile {
* Returns true if the specified Bluetooth device is connected.
* Returns false if not connected, or if this proxy object is not
* currently connected to the Sap service.
+ * @hide
*/
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
@@ -206,6 +248,7 @@ public final class BluetoothSap implements BluetoothProfile {
/**
* Initiate connection. Initiation of outgoing connections is not
* supported for SAP server.
+ * @hide
*/
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")" + "not supported for SAPS");
@@ -218,6 +261,7 @@ public final class BluetoothSap implements BluetoothProfile {
* @param device Remote Bluetooth Device
* @return false on error,
* true otherwise
+ * @hide
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
@@ -238,6 +282,7 @@ public final class BluetoothSap implements BluetoothProfile {
* Get the list of connected devices. Currently at most one.
*
* @return list of connected devices
+ * @hide
*/
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
@@ -257,6 +302,7 @@ public final class BluetoothSap implements BluetoothProfile {
* Get the list of devices matching specified states. Currently at most one.
*
* @return list of matching devices
+ * @hide
*/
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
@@ -276,6 +322,7 @@ public final class BluetoothSap implements BluetoothProfile {
* Get connection state of device
*
* @return device connection state
+ * @hide
*/
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
@@ -300,6 +347,7 @@ public final class BluetoothSap implements BluetoothProfile {
* @param device Paired bluetooth device
* @param priority
* @return true if priority is set, false on error
+ * @hide
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
@@ -325,6 +373,7 @@ public final class BluetoothSap implements BluetoothProfile {
*
* @param device Bluetooth device
* @return priority of the device
+ * @hide
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 5702d11..5cf2300 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -91,9 +91,13 @@ public final class BluetoothSocket implements Closeable {
public static final int MAX_RFCOMM_CHANNEL = 30;
/*package*/ static final int MAX_L2CAP_PACKAGE_SIZE = 0xFFFF;
- /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */
+ /** RFCOMM socket */
public static final int TYPE_RFCOMM = 1;
+
+ /** SCO socket */
public static final int TYPE_SCO = 2;
+
+ /** L2CAP socket */
public static final int TYPE_L2CAP = 3;
/*package*/ static final int EBADFD = 77;
@@ -578,8 +582,8 @@ public final class BluetoothSocket implements Closeable {
}
/**
- * Get the type of the underlying connection
- * @return one of TYPE_
+ * Get the type of the underlying connection.
+ * @return one of {@link #TYPE_RFCOMM}, {@link #TYPE_SCO} or {@link #TYPE_L2CAP}
*/
public int getConnectionType() {
return mType;
diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl
index 8d1ce99..0b81ee8 100644
--- a/core/java/android/bluetooth/IBluetoothManager.aidl
+++ b/core/java/android/bluetooth/IBluetoothManager.aidl
@@ -44,6 +44,8 @@ interface IBluetoothManager
String getAddress();
String getName();
+
+ boolean isBleScanAlwaysAvailable();
int updateBleAppCount(IBinder b, boolean enable);
boolean isBleAppPresent();
}
diff --git a/core/java/android/bluetooth/le/ScanCallback.java b/core/java/android/bluetooth/le/ScanCallback.java
index 27b96bd..61b2e78 100644
--- a/core/java/android/bluetooth/le/ScanCallback.java
+++ b/core/java/android/bluetooth/le/ScanCallback.java
@@ -53,8 +53,10 @@ public abstract class ScanCallback {
/**
* Callback when a BLE advertisement has been found.
*
- * @param callbackType Determines how this callback was triggered. Could be of
- * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES}
+ * @param callbackType Determines how this callback was triggered. Could be one of
+ * {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES},
+ * {@link ScanSettings#CALLBACK_TYPE_FIRST_MATCH} or
+ * {@link ScanSettings#CALLBACK_TYPE_MATCH_LOST}
* @param result A Bluetooth LE scan result.
*/
public void onScanResult(int callbackType, ScanResult result) {
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index f103cae..4eeb577 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -59,17 +59,13 @@ public final class ScanSettings implements Parcelable {
/**
* A result callback is only triggered for the first advertisement packet received that matches
* the filter criteria.
- * @hide
*/
- @SystemApi
public static final int CALLBACK_TYPE_FIRST_MATCH = 2;
/**
* Receive a callback when advertisements are no longer received from a device that has been
* previously reported by a first match callback.
- * @hide
*/
- @SystemApi
public static final int CALLBACK_TYPE_MATCH_LOST = 4;
@@ -78,21 +74,18 @@ public final class ScanSettings implements Parcelable {
*/
/**
* Match one advertisement per filter
- * @hide
*/
public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1;
/**
* Match few advertisement per filter, depends on current capability and availibility of
* the resources in hw
- * @hide
*/
public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2;
/**
* Match as many advertisement per filter as hw could allow, depends on current
* capability and availibility of the resources in hw
- * @hide
*/
public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3;
@@ -100,14 +93,12 @@ public final class ScanSettings implements Parcelable {
/**
* In Aggressive mode, hw will determine a match sooner even with feeble signal strength
* and few number of sightings/match in a duration.
- * @hide
*/
public static final int MATCH_MODE_AGGRESSIVE = 1;
/**
* For sticky mode, higher threshold of signal strength and sightings is required
* before reporting by hw
- * @hide
*/
public static final int MATCH_MODE_STICKY = 2;
@@ -187,7 +178,7 @@ public final class ScanSettings implements Parcelable {
mScanResultType = scanResultType;
mReportDelayMillis = reportDelayMillis;
mNumOfMatchesPerFilter = numOfMatchesPerFilter;
- mMatchMode = numOfMatchesPerFilter;
+ mMatchMode = matchMode;
}
private ScanSettings(Parcel in) {
@@ -236,7 +227,7 @@ public final class ScanSettings implements Parcelable {
private int mScanResultType = SCAN_RESULT_TYPE_FULL;
private long mReportDelayMillis = 0;
private int mMatchMode = MATCH_MODE_AGGRESSIVE;
- private int mNumOfMatchesPerFilter = MATCH_NUM_ONE_ADVERTISEMENT;
+ private int mNumOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT;
/**
* Set scan mode for Bluetooth LE scan.
*
@@ -324,7 +315,6 @@ public final class ScanSettings implements Parcelable {
* {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or
* {@link ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT}
* @throws IllegalArgumentException If the {@code matchMode} is invalid.
- * @hide
*/
public Builder setNumOfMatches(int numOfMatches) {
if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT
@@ -342,7 +332,6 @@ public final class ScanSettings implements Parcelable {
* {@link ScanSettings#MATCH_MODE_AGGRESSIVE} or
* {@link ScanSettings#MATCH_MODE_STICKY}
* @throws IllegalArgumentException If the {@code matchMode} is invalid.
- * @hide
*/
public Builder setMatchMode(int matchMode) {
if (matchMode < MATCH_MODE_AGGRESSIVE
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index fd65d56..72e701d 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -211,7 +211,27 @@ public abstract class ContentProvider implements ComponentCallbacks2 {
// We do not call ContentProvider#query with a modified where clause since
// the implementation is not guaranteed to be backed by a SQL database, hence
// it may not handle properly the tautology where clause we would have created.
- return new MatrixCursor(projection, 0);
+ if (projection != null) {
+ return new MatrixCursor(projection, 0);
+ }
+
+ // Null projection means all columns but we have no idea which they are.
+ // However, the caller may be expecting to access them my index. Hence,
+ // we have to execute the query as if allowed to get a cursor with the
+ // columns. We then use the column names to return an empty cursor.
+ Cursor cursor = ContentProvider.this.query(uri, projection, selection,
+ selectionArgs, sortOrder, CancellationSignal.fromTransport(
+ cancellationSignal));
+
+ // Create a projection for all columns.
+ final int columnCount = cursor.getCount();
+ String[] allColumns = new String[columnCount];
+ for (int i = 0; i < columnCount; i++) {
+ allColumns[i] = cursor.getColumnName(i);
+ }
+
+ // Return an empty cursor for all columns.
+ return new MatrixCursor(allColumns, 0);
}
final String original = setCallingPackage(callingPkg);
try {
diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java
index f2e7fc4..4769bd0 100644
--- a/core/java/android/content/ContentProviderNative.java
+++ b/core/java/android/content/ContentProviderNative.java
@@ -593,7 +593,8 @@ final class ContentProviderProxy implements IContentProvider
DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(reply);
int has = reply.readInt();
- ParcelFileDescriptor fd = has != 0 ? reply.readFileDescriptor() : null;
+ ParcelFileDescriptor fd = has != 0 ? ParcelFileDescriptor.CREATOR
+ .createFromParcel(reply) : null;
return fd;
} finally {
data.recycle();
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5eacce3..8687c6b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -252,6 +252,21 @@ public abstract class Context {
public static final int BIND_ADJUST_WITH_ACTIVITY = 0x0080;
/**
+ * @hide Flag for {@link #bindService}: Like {@link #BIND_FOREGROUND_SERVICE},
+ * but only applies while the device is awake.
+ */
+ public static final int BIND_FOREGROUND_SERVICE_WHILE_AWAKE = 0x02000000;
+
+ /**
+ * @hide Flag for {@link #bindService}: For only the case where the binding
+ * is coming from the system, set the process state to FOREGROUND_SERVICE
+ * instead of the normal maximum of IMPORTANT_FOREGROUND. That is, this is
+ * saying that the process shouldn't participate in the normal power reduction
+ * modes (removing network access etc).
+ */
+ public static final int BIND_FOREGROUND_SERVICE = 0x04000000;
+
+ /**
* @hide Flag for {@link #bindService}: Treat the binding as hosting
* an activity, an unbinding as the activity going in the background.
* That is, when unbinding, the process when empty will go on the activity
@@ -374,24 +389,30 @@ public abstract class Context {
}
/**
- * Return a localized string from the application's package's
+ * Returns a localized string from the application's package's
* default string table.
*
* @param resId Resource id for the string
+ * @return The string data associated with the resource, stripped of styled
+ * text information.
*/
+ @NonNull
public final String getString(@StringRes int resId) {
return getResources().getString(resId);
}
/**
- * Return a localized formatted string from the application's package's
+ * Returns a localized formatted string from the application's package's
* default string table, substituting the format arguments as defined in
* {@link java.util.Formatter} and {@link java.lang.String#format}.
*
* @param resId Resource id for the format string
- * @param formatArgs The format arguments that will be used for substitution.
+ * @param formatArgs The format arguments that will be used for
+ * substitution.
+ * @return The string data associated with the resource, formatted and
+ * stripped of styled text information.
*/
-
+ @NonNull
public final String getString(@StringRes int resId, Object... formatArgs) {
return getResources().getString(resId, formatArgs);
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 16f6b1e..43cc63b 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -570,13 +570,16 @@ public class ActivityInfo extends ComponentInfo
Configuration.NATIVE_CONFIG_DENSITY, // DENSITY
Configuration.NATIVE_CONFIG_LAYOUTDIR, // LAYOUT DIRECTION
};
- /** @hide
+
+ /**
* Convert Java change bits to native.
+ *
+ * @hide
*/
public static int activityInfoConfigToNative(int input) {
int output = 0;
- for (int i=0; i<CONFIG_NATIVE_BITS.length; i++) {
- if ((input&(1<<i)) != 0) {
+ for (int i = 0; i < CONFIG_NATIVE_BITS.length; i++) {
+ if ((input & (1 << i)) != 0) {
output |= CONFIG_NATIVE_BITS[i];
}
}
@@ -584,6 +587,21 @@ public class ActivityInfo extends ComponentInfo
}
/**
+ * Convert native change bits to Java.
+ *
+ * @hide
+ */
+ public static int activityInfoConfigNativeToJava(int input) {
+ int output = 0;
+ for (int i = 0; i < CONFIG_NATIVE_BITS.length; i++) {
+ if ((input & CONFIG_NATIVE_BITS[i]) != 0) {
+ output |= (1 << i);
+ }
+ }
+ return output;
+ }
+
+ /**
* @hide
* Unfortunately some developers (OpenFeint I am looking at you) have
* compared the configChanges bit field against absolute values, so if we
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6c32873..707ef30 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -96,6 +96,21 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
public String backupAgentName;
/**
+ * An optional attribute that indicates the app supports automatic backup of app data.
+ * <p>0 is the default and means the app's entire data folder + managed external storage will
+ * be backed up;
+ * Any negative value indicates the app does not support full-data backup, though it may still
+ * want to participate via the traditional key/value backup API;
+ * A positive number specifies an xml resource in which the application has defined its backup
+ * include/exclude criteria.
+ * <p>If android:allowBackup is set to false, this attribute is ignored.
+ *
+ * @see {@link android.content.Context#getNoBackupFilesDir}
+ * @see {@link #FLAG_ALLOW_BACKUP}
+ */
+ public int fullBackupContent = 0;
+
+ /**
* The default extra UI options for activities in this application.
* Set from the {@link android.R.attr#uiOptions} attribute in the
* activity's manifest.
@@ -686,6 +701,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
pw.println(prefix + "uiOptions=0x" + Integer.toHexString(uiOptions));
}
pw.println(prefix + "supportsRtl=" + (hasRtlSupport() ? "true" : "false"));
+ if (fullBackupContent > 0) {
+ pw.println(prefix + "fullBackupContent=@xml/" + fullBackupContent);
+ } else {
+ pw.println(prefix + "fullBackupContent=" + (fullBackupContent < 0 ? "false" : "true"));
+ }
super.dumpBack(pw, prefix);
}
@@ -763,6 +783,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
uiOptions = orig.uiOptions;
backupAgentName = orig.backupAgentName;
hardwareAccelerated = orig.hardwareAccelerated;
+ fullBackupContent = orig.fullBackupContent;
}
@@ -816,6 +837,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
dest.writeInt(descriptionRes);
dest.writeInt(uiOptions);
dest.writeInt(hardwareAccelerated ? 1 : 0);
+ dest.writeInt(fullBackupContent);
}
public static final Parcelable.Creator<ApplicationInfo> CREATOR
@@ -868,6 +890,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
descriptionRes = source.readInt();
uiOptions = source.readInt();
hardwareAccelerated = source.readInt() != 0;
+ fullBackupContent = source.readInt();
}
/**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index ae59bfc..94b0223 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -58,6 +58,7 @@ import android.content.IntentSender;
* {@hide}
*/
interface IPackageManager {
+ boolean isPackageFrozen(String packageName);
boolean isPackageAvailable(String packageName, int userId);
PackageInfo getPackageInfo(String packageName, int flags, int userId);
int getPackageUid(String packageName, int userId);
@@ -95,9 +96,9 @@ interface IPackageManager {
void removePermission(String name);
- boolean grantPermission(String packageName, String permissionName, int userId);
+ void grantPermission(String packageName, String permissionName, int userId);
- boolean revokePermission(String packageName, String permissionName, int userId);
+ void revokePermission(String packageName, String permissionName, int userId);
boolean isProtectedBroadcast(String actionName);
diff --git a/core/java/android/content/pm/IPackageMoveObserver.aidl b/core/java/android/content/pm/IPackageMoveObserver.aidl
index 50ab3b5..86189fc 100644
--- a/core/java/android/content/pm/IPackageMoveObserver.aidl
+++ b/core/java/android/content/pm/IPackageMoveObserver.aidl
@@ -17,11 +17,13 @@
package android.content.pm;
+import android.os.Bundle;
+
/**
* Callback for moving package resources from the Package Manager.
* @hide
*/
oneway interface IPackageMoveObserver {
- void onStarted(int moveId, String title);
+ void onCreated(int moveId, in Bundle extras);
void onStatusChanged(int moveId, int status, long estMillis);
}
diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java
index e50b0ff..96000dd 100644
--- a/core/java/android/content/pm/IntentFilterVerificationInfo.java
+++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java
@@ -48,19 +48,18 @@ public final class IntentFilterVerificationInfo implements Parcelable {
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_STATUS = "status";
- private ArrayList<String> mDomains;
+ private ArraySet<String> mDomains = new ArraySet<>();
private String mPackageName;
private int mMainStatus;
public IntentFilterVerificationInfo() {
mPackageName = null;
- mDomains = new ArrayList<>();
mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
}
public IntentFilterVerificationInfo(String packageName, ArrayList<String> domains) {
mPackageName = packageName;
- mDomains = domains;
+ mDomains.addAll(domains);
mMainStatus = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
}
@@ -73,14 +72,6 @@ public final class IntentFilterVerificationInfo implements Parcelable {
readFromParcel(source);
}
- public ArrayList<String> getDomains() {
- return mDomains;
- }
-
- public ArraySet<String> getDomainsSet() {
- return new ArraySet<>(mDomains);
- }
-
public String getPackageName() {
return mPackageName;
}
@@ -98,6 +89,14 @@ public final class IntentFilterVerificationInfo implements Parcelable {
}
}
+ public ArraySet<String> getDomains() {
+ return mDomains;
+ }
+
+ public void setDomains(ArrayList<String> list) {
+ mDomains = new ArraySet<>(list);
+ }
+
public String getDomainsString() {
StringBuilder sb = new StringBuilder();
for (String str : mDomains) {
@@ -145,7 +144,6 @@ public final class IntentFilterVerificationInfo implements Parcelable {
}
mMainStatus = status;
- mDomains = new ArrayList<>();
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -201,15 +199,16 @@ public final class IntentFilterVerificationInfo implements Parcelable {
private void readFromParcel(Parcel source) {
mPackageName = source.readString();
mMainStatus = source.readInt();
- mDomains = new ArrayList<>();
- source.readStringList(mDomains);
+ ArrayList<String> list = new ArrayList<>();
+ source.readStringList(list);
+ mDomains.addAll(list);
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mPackageName);
dest.writeInt(mMainStatus);
- dest.writeStringList(mDomains);
+ dest.writeStringList(new ArrayList<>(mDomains));
}
public static final Creator<IntentFilterVerificationInfo> CREATOR =
@@ -221,5 +220,4 @@ public final class IntentFilterVerificationInfo implements Parcelable {
return new IntentFilterVerificationInfo[size];
}
};
-
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e1c271d..7ff6ec3 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -209,7 +209,14 @@ public abstract class PackageManager {
* matching. This is a synonym for including the CATEGORY_DEFAULT in your
* supplied Intent.
*/
- public static final int MATCH_DEFAULT_ONLY = 0x00010000;
+ public static final int MATCH_DEFAULT_ONLY = 0x00010000;
+
+ /**
+ * Querying flag: if set and if the platform is doing any filtering of the results, then
+ * the filtering will not happen. This is a synonym for saying that all results should
+ * be returned.
+ */
+ public static final int MATCH_ALL = 0x00020000;
/**
* Flag for {@link addCrossProfileIntentFilter}: if this flag is set:
@@ -2637,6 +2644,8 @@ public abstract class PackageManager {
* {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
* those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
*
+ * You can also set {@link #MATCH_ALL} for preventing the filtering of the results.
+ *
* @return A List&lt;ResolveInfo&gt; containing one entry for each matching
* Activity. These are ordered from best to worst match -- that
* is, the first item in the list is what is returned by
@@ -2658,6 +2667,8 @@ public abstract class PackageManager {
* {@link #MATCH_DEFAULT_ONLY}, to limit the resolution to only
* those activities that support the {@link android.content.Intent#CATEGORY_DEFAULT}.
*
+ * You can also set {@link #MATCH_ALL} for preventing the filtering of the results.
+ *
* @return A List&lt;ResolveInfo&gt; containing one entry for each matching
* Activity. These are ordered from best to worst match -- that
* is, the first item in the list is what is returned by
@@ -4201,7 +4212,7 @@ public abstract class PackageManager {
/** {@hide} */
public static abstract class MoveCallback {
- public abstract void onStarted(int moveId, String title);
+ public void onCreated(int moveId, Bundle extras) {}
public abstract void onStatusChanged(int moveId, int status, long estMillis);
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index fed9261..acc27c3 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2421,8 +2421,8 @@ public class PackageParser {
if (allowBackup) {
ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
- // backupAgent, killAfterRestore, and restoreAnyVersion are only relevant
- // if backup is possible for the given application.
+ // backupAgent, killAfterRestore, fullBackupContent and restoreAnyVersion are only
+ // relevant if backup is possible for the given application.
String backupAgent = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestApplication_backupAgent,
Configuration.NATIVE_CONFIG_VERSION);
@@ -2449,6 +2449,20 @@ public class PackageParser {
ai.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
}
}
+
+ TypedValue v = sa.peekValue(
+ com.android.internal.R.styleable.AndroidManifestApplication_fullBackupContent);
+ if (v != null && (ai.fullBackupContent = v.resourceId) == 0) {
+ if (DEBUG_BACKUP) {
+ Slog.v(TAG, "fullBackupContent specified as boolean=" +
+ (v.data == 0 ? "false" : "true"));
+ }
+ // "false" => -1, "true" => 0
+ ai.fullBackupContent = (v.data == 0 ? -1 : 0);
+ }
+ if (DEBUG_BACKUP) {
+ Slog.v(TAG, "fullBackupContent=" + ai.fullBackupContent + " for " + pkgName);
+ }
}
TypedValue v = sa.peekValue(
@@ -4315,9 +4329,6 @@ public class PackageParser {
// Additional data supplied by callers.
public Object mExtras;
- // Whether an operation is currently pending on this package
- public boolean mOperationPending;
-
// Applications hardware preferences
public ArrayList<ConfigurationInfo> configPreferences = null;
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index a176593..525059f 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -789,6 +789,7 @@ public final class AssetManager implements AutoCloseable {
TypedValue outValue,
boolean resolve);
/*package*/ native static final void dumpTheme(long theme, int priority, String tag, String prefix);
+ /*package*/ native static final int getThemeChangingConfigurations(long theme);
private native final long openXmlAssetNative(int cookie, String fileName);
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index fdafb04..14bfac5 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -271,7 +271,7 @@ public class ColorStateList implements Parcelable {
final TypedArray a = Resources.obtainAttributes(r, theme, attrs,
R.styleable.ColorStateListItem);
final int[] themeAttrs = a.extractThemeAttrs();
- final int baseColor = a.getColor(R.styleable.ColorStateListItem_color, 0);
+ final int baseColor = a.getColor(R.styleable.ColorStateListItem_color, Color.MAGENTA);
final float alphaMod = a.getFloat(R.styleable.ColorStateListItem_alpha, 1.0f);
changingConfigurations |= a.getChangingConfigurations();
@@ -296,7 +296,9 @@ public class ColorStateList implements Parcelable {
}
stateSpec = StateSet.trimStateSet(stateSpec, j);
- // Apply alpha modulation.
+ // Apply alpha modulation. If we couldn't resolve the color or
+ // alpha yet, the default values leave us enough information to
+ // modulate again during applyTheme().
final int color = modulateColorAlpha(baseColor, alphaMod);
if (listSize == 0 || stateSpec.length == 0) {
defaultColor = color;
@@ -365,14 +367,31 @@ public class ColorStateList implements Parcelable {
if (themeAttrsList[i] != null) {
final TypedArray a = t.resolveAttributes(themeAttrsList[i],
R.styleable.ColorStateListItem);
+
+ final float defaultAlphaMod;
+ if (themeAttrsList[i][R.styleable.ColorStateListItem_color] != 0) {
+ // If the base color hasn't been resolved yet, the current
+ // color's alpha channel is either full-opacity (if we
+ // haven't resolved the alpha modulation yet) or
+ // pre-modulated. Either is okay as a default value.
+ defaultAlphaMod = Color.alpha(mColors[i]) / 255.0f;
+ } else {
+ // Otherwise, the only correct default value is 1. Even if
+ // nothing is resolved during this call, we can apply this
+ // multiple times without losing of information.
+ defaultAlphaMod = 1.0f;
+ }
+
final int baseColor = a.getColor(
R.styleable.ColorStateListItem_color, mColors[i]);
final float alphaMod = a.getFloat(
- R.styleable.ColorStateListItem_alpha, 1.0f);
-
+ R.styleable.ColorStateListItem_alpha, defaultAlphaMod);
mColors[i] = modulateColorAlpha(baseColor, alphaMod);
+
+ // Account for any configuration changes.
mChangingConfigurations |= a.getChangingConfigurations();
+ // Extract the theme attributes, if any.
themeAttrsList[i] = a.extractThemeAttrs(themeAttrsList[i]);
if (themeAttrsList[i] != null) {
hasUnresolvedAttrs = true;
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 334d180..ae41b69 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -393,10 +393,11 @@ public class Resources {
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*
* @return String The string data associated with the resource,
- * stripped of styled text information.
+ * stripped of styled text information.
*/
+ @NonNull
public String getString(@StringRes int id) throws NotFoundException {
- CharSequence res = getText(id);
+ final CharSequence res = getText(id);
if (res != null) {
return res.toString();
}
@@ -421,11 +422,11 @@ public class Resources {
* @throws NotFoundException Throws NotFoundException if the given ID does not exist.
*
* @return String The string data associated with the resource,
- * stripped of styled text information.
+ * stripped of styled text information.
*/
- public String getString(@StringRes int id, Object... formatArgs)
- throws NotFoundException {
- String raw = getString(id);
+ @NonNull
+ public String getString(@StringRes int id, Object... formatArgs) throws NotFoundException {
+ final String raw = getString(id);
return String.format(mConfiguration.locale, raw, formatArgs);
}
@@ -1730,6 +1731,19 @@ public class Resources {
}
/**
+ * Returns a bit mask of configuration changes that will impact this
+ * theme (and thus require completely reloading it).
+ *
+ * @return a bit mask of configuration changes, as defined by
+ * {@link ActivityInfo}
+ * @see ActivityInfo
+ */
+ public int getChangingConfigurations() {
+ final int nativeChangingConfig = AssetManager.getThemeChangingConfigurations(mTheme);
+ return ActivityInfo.activityInfoConfigNativeToJava(nativeChangingConfig);
+ }
+
+ /**
* Print contents of this theme out to the log. For debugging only.
*
* @param priority The log priority to use.
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index d88594d..1fc69c0 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -460,9 +460,8 @@ public class Camera {
mEventHandler = null;
}
- String packageName = ActivityThread.currentPackageName();
-
- return native_setup(new WeakReference<Camera>(this), cameraId, halVersion, packageName);
+ return native_setup(new WeakReference<Camera>(this), cameraId, halVersion,
+ ActivityThread.currentOpPackageName());
}
private int cameraInitNormal(int cameraId) {
diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl
index 9bc2f46..7b96e20 100644
--- a/core/java/android/hardware/ICameraService.aidl
+++ b/core/java/android/hardware/ICameraService.aidl
@@ -38,13 +38,13 @@ interface ICameraService
int getCameraInfo(int cameraId, out CameraInfo info);
int connect(ICameraClient client, int cameraId,
- String clientPackageName,
+ String opPackageName,
int clientUid,
// Container for an ICamera object
out BinderHolder device);
int connectDevice(ICameraDeviceCallbacks callbacks, int cameraId,
- String clientPackageName,
+ String opPackageName,
int clientUid,
// Container for an ICameraDeviceUser object
out BinderHolder device);
@@ -69,7 +69,7 @@ interface ICameraService
int connectLegacy(ICameraClient client, int cameraId,
int halVersion,
- String clientPackageName,
+ String opPackageName,
int clientUid,
// Container for an ICamera object
out BinderHolder device);
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 11037fd..22a9e9c 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -41,16 +41,19 @@ import java.util.List;
*/
public class SystemSensorManager extends SensorManager {
private static native void nativeClassInit();
- private static native int nativeGetNextSensor(Sensor sensor, int next);
- private static native int nativeEnableDataInjection(boolean enable);
+ private static native long nativeCreate(String opPackageName);
+ private static native int nativeGetNextSensor(long nativeInstance, Sensor sensor, int next);
+ private static native int nativeEnableDataInjection(long nativeInstance, boolean enable);
private static boolean sSensorModuleInitialized = false;
- private static final Object sSensorModuleLock = new Object();
- private static final ArrayList<Sensor> sFullSensorsList = new ArrayList<Sensor>();
- private static final SparseArray<Sensor> sHandleToSensor = new SparseArray<Sensor>();
private static InjectEventQueue mInjectEventQueue = null;
private static boolean mDataInjectionMode = false;
+ private final Object mLock = new Object();
+
+ private final ArrayList<Sensor> mFullSensorsList = new ArrayList<>();
+ private final SparseArray<Sensor> mHandleToSensor = new SparseArray<>();
+
// Listener list
private final HashMap<SensorEventListener, SensorEventQueue> mSensorListeners =
new HashMap<SensorEventListener, SensorEventQueue>();
@@ -60,44 +63,44 @@ public class SystemSensorManager extends SensorManager {
// Looper associated with the context in which this instance was created.
private final Looper mMainLooper;
private final int mTargetSdkLevel;
- private final String mPackageName;
+ private final Context mContext;
private final boolean mHasDataInjectionPermissions;
+ private final long mNativeInstance;
/** {@hide} */
public SystemSensorManager(Context context, Looper mainLooper) {
mMainLooper = mainLooper;
mTargetSdkLevel = context.getApplicationInfo().targetSdkVersion;
- mPackageName = context.getPackageName();
- synchronized(sSensorModuleLock) {
+ mContext = context;
+ mNativeInstance = nativeCreate(context.getOpPackageName());
+
+ synchronized(mLock) {
if (!sSensorModuleInitialized) {
sSensorModuleInitialized = true;
-
nativeClassInit();
-
- // initialize the sensor list
- final ArrayList<Sensor> fullList = sFullSensorsList;
- int i = 0;
- do {
- Sensor sensor = new Sensor();
- i = nativeGetNextSensor(sensor, i);
- if (i>=0) {
- //Log.d(TAG, "found sensor: " + sensor.getName() +
- // ", handle=" + sensor.getHandle());
- fullList.add(sensor);
- sHandleToSensor.append(sensor.getHandle(), sensor);
- }
- } while (i>0);
}
mHasDataInjectionPermissions = context.checkSelfPermission(
Manifest.permission.HARDWARE_TEST) == PackageManager.PERMISSION_GRANTED;
}
+
+ // initialize the sensor list
+ int i = 0;
+ while(true) {
+ Sensor sensor = new Sensor();
+ i = nativeGetNextSensor(mNativeInstance, sensor, i);
+ if (i <= 0) {
+ break;
+ }
+ mFullSensorsList.add(sensor);
+ mHandleToSensor.append(sensor.getHandle(), sensor);
+ }
}
/** @hide */
@Override
protected List<Sensor> getFullSensorList() {
- return sFullSensorsList;
+ return mFullSensorsList;
}
@@ -232,8 +235,8 @@ public class SystemSensorManager extends SensorManager {
throw new SecurityException("Permission denial. Calling enableDataInjection without "
+ Manifest.permission.HARDWARE_TEST);
}
- synchronized (sSensorModuleLock) {
- int ret = nativeEnableDataInjection(enable);
+ synchronized (mLock) {
+ int ret = nativeEnableDataInjection(mNativeInstance, enable);
// The HAL does not support injection. Ignore.
if (ret != 0) {
Log.e(TAG, "HAL does not support data injection");
@@ -255,7 +258,7 @@ public class SystemSensorManager extends SensorManager {
throw new SecurityException("Permission denial. Calling injectSensorData without "
+ Manifest.permission.HARDWARE_TEST);
}
- synchronized (sSensorModuleLock) {
+ synchronized (mLock) {
if (!mDataInjectionMode) {
Log.e(TAG, "Data injection mode not activated before calling injectSensorData");
return false;
@@ -284,15 +287,17 @@ public class SystemSensorManager extends SensorManager {
* SensorManager instance.
*/
private static abstract class BaseEventQueue {
- private native long nativeInitBaseEventQueue(WeakReference<BaseEventQueue> eventQWeak,
- MessageQueue msgQ, float[] scratch, String packageName, int mode);
+ private static native long nativeInitBaseEventQueue(long nativeManager,
+ WeakReference<BaseEventQueue> eventQWeak, MessageQueue msgQ, float[] scratch,
+ String packageName, int mode, String opPackageName);
private static native int nativeEnableSensor(long eventQ, int handle, int rateUs,
int maxBatchReportLatencyUs);
private static native int nativeDisableSensor(long eventQ, int handle);
private static native void nativeDestroySensorEventQueue(long eventQ);
private static native int nativeFlushSensor(long eventQ);
private static native int nativeInjectSensorData(long eventQ, int handle,
- float[] values,int accuracy, long timestamp);
+ float[] values,int accuracy, long timestamp);
+
private long nSensorEventQueue;
private final SparseBooleanArray mActiveSensors = new SparseBooleanArray();
protected final SparseIntArray mSensorAccuracies = new SparseIntArray();
@@ -305,8 +310,9 @@ public class SystemSensorManager extends SensorManager {
protected static final int OPERATING_MODE_DATA_INJECTION = 1;
BaseEventQueue(Looper looper, SystemSensorManager manager, int mode) {
- nSensorEventQueue = nativeInitBaseEventQueue(new WeakReference<BaseEventQueue>(this),
- looper.getQueue(), mScratch, manager.mPackageName, mode);
+ nSensorEventQueue = nativeInitBaseEventQueue(manager.mNativeInstance,
+ new WeakReference<>(this), looper.getQueue(), mScratch,
+ manager.mContext.getPackageName(), mode, manager.mContext.getOpPackageName());
mCloseGuard.open("dispose");
mManager = manager;
}
@@ -339,7 +345,7 @@ public class SystemSensorManager extends SensorManager {
for (int i=0 ; i<mActiveSensors.size(); i++) {
if (mActiveSensors.valueAt(i) == true) {
int handle = mActiveSensors.keyAt(i);
- Sensor sensor = sHandleToSensor.get(handle);
+ Sensor sensor = mManager.mHandleToSensor.get(handle);
if (sensor != null) {
disableSensor(sensor);
mActiveSensors.put(handle, false);
@@ -452,7 +458,7 @@ public class SystemSensorManager extends SensorManager {
@Override
protected void dispatchSensorEvent(int handle, float[] values, int inAccuracy,
long timestamp) {
- final Sensor sensor = sHandleToSensor.get(handle);
+ final Sensor sensor = mManager.mHandleToSensor.get(handle);
SensorEvent t = null;
synchronized (mSensorsEvents) {
t = mSensorsEvents.get(handle);
@@ -481,7 +487,7 @@ public class SystemSensorManager extends SensorManager {
@SuppressWarnings("unused")
protected void dispatchFlushCompleteEvent(int handle) {
if (mListener instanceof SensorEventListener2) {
- final Sensor sensor = sHandleToSensor.get(handle);
+ final Sensor sensor = mManager.mHandleToSensor.get(handle);
((SensorEventListener2)mListener).onFlushCompleted(sensor);
}
return;
@@ -519,7 +525,7 @@ public class SystemSensorManager extends SensorManager {
@Override
protected void dispatchSensorEvent(int handle, float[] values, int accuracy,
long timestamp) {
- final Sensor sensor = sHandleToSensor.get(handle);
+ final Sensor sensor = mManager.mHandleToSensor.get(handle);
TriggerEvent t = null;
synchronized (mTriggerEvents) {
t = mTriggerEvents.get(handle);
@@ -546,7 +552,7 @@ public class SystemSensorManager extends SensorManager {
}
}
- static final class InjectEventQueue extends BaseEventQueue {
+ final class InjectEventQueue extends BaseEventQueue {
public InjectEventQueue(Looper looper, SystemSensorManager manager) {
super(looper, manager, OPERATING_MODE_DATA_INJECTION);
}
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 0cf8df1..aeddf03 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -114,6 +114,11 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* the Surface provided to prepare must not be used as a target of a CaptureRequest submitted
* to this session.</p>
*
+ * <p>{@link android.hardware.camera2.CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}
+ * devices cannot pre-allocate output buffers; for those devices,
+ * {@link StateCallback#onSurfacePrepared} will be immediately called, and no preallocation is
+ * done.</p>
+ *
* @param surface the output Surface for which buffers should be pre-allocated. Must be one of
* the output Surfaces used to create this session.
*
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 87a1ca9..19e821c 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -603,10 +603,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
/**
* <p>List of available high speed video size and fps range configurations
* supported by the camera device, in the format of (width, height, fps_min, fps_max).</p>
- * <p>When HIGH_SPEED_VIDEO is supported in {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes},
- * this metadata will list the supported high speed video size and fps range
- * configurations. All the sizes listed in this configuration will be a subset
- * of the sizes reported by StreamConfigurationMap#getOutputSizes for processed
+ * <p>When HIGH_SPEED_VIDEO is supported in {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes}, this metadata
+ * will list the supported high speed video size and fps range configurations. All the sizes
+ * listed in this configuration will be a subset of the sizes reported by {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes } for processed
* non-stalling formats.</p>
* <p>For the high speed video use case, where the application will set
* {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} to HIGH_SPEED_VIDEO in capture requests, the application must
@@ -1116,11 +1115,12 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* into the 3 stream types as below:</p>
* <ul>
* <li>Processed (but stalling): any non-RAW format with a stallDurations &gt; 0.
- * Typically JPEG format (ImageFormat#JPEG).</li>
- * <li>Raw formats: ImageFormat#RAW_SENSOR, ImageFormat#RAW10, ImageFormat#RAW12,
- * and ImageFormat#RAW_OPAQUE.</li>
+ * Typically {@link android.graphics.ImageFormat#JPEG JPEG format}.</li>
+ * <li>Raw formats: {@link android.graphics.ImageFormat#RAW_SENSOR RAW_SENSOR}, {@link android.graphics.ImageFormat#RAW10 RAW10}, or {@link android.graphics.ImageFormat#RAW12 RAW12}.</li>
* <li>Processed (but not-stalling): any non-RAW format without a stall duration.
- * Typically ImageFormat#YUV_420_888, ImageFormat#NV21, ImageFormat#YV12.</li>
+ * Typically {@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888},
+ * {@link android.graphics.ImageFormat#NV21 NV21}, or
+ * {@link android.graphics.ImageFormat#YV12 YV12}.</li>
* </ul>
* <p><b>Range of valid values:</b><br></p>
* <p>For processed (and stalling) format streams, &gt;= 1.</p>
@@ -1148,10 +1148,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* be any <code>RAW</code> and supported format provided by {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}.</p>
* <p>In particular, a <code>RAW</code> format is typically one of:</p>
* <ul>
- * <li>ImageFormat#RAW_SENSOR</li>
- * <li>ImageFormat#RAW10</li>
- * <li>ImageFormat#RAW12</li>
- * <li>Opaque <code>RAW</code></li>
+ * <li>{@link android.graphics.ImageFormat#RAW_SENSOR RAW_SENSOR}</li>
+ * <li>{@link android.graphics.ImageFormat#RAW10 RAW10}</li>
+ * <li>{@link android.graphics.ImageFormat#RAW12 RAW12}</li>
* </ul>
* <p>LEGACY mode devices ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} <code>==</code> LEGACY)
* never support raw streams.</p>
@@ -1180,13 +1179,13 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>Processed (but not-stalling) is defined as any non-RAW format without a stall duration.
* Typically:</p>
* <ul>
- * <li>ImageFormat#YUV_420_888</li>
- * <li>ImageFormat#NV21</li>
- * <li>ImageFormat#YV12</li>
- * <li>Implementation-defined formats, i.e. StreamConfiguration#isOutputSupportedFor(Class)</li>
+ * <li>{@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}</li>
+ * <li>{@link android.graphics.ImageFormat#NV21 NV21}</li>
+ * <li>{@link android.graphics.ImageFormat#YV12 YV12}</li>
+ * <li>Implementation-defined formats, i.e. {@link android.hardware.camera2.params.StreamConfigurationMap#isOutputSupportedFor(Class) }</li>
* </ul>
- * <p>For full guarantees, query StreamConfigurationMap#getOutputStallDuration with
- * a processed format -- it will return 0 for a non-stalling stream.</p>
+ * <p>For full guarantees, query {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration } with a
+ * processed format -- it will return 0 for a non-stalling stream.</p>
* <p>LEGACY devices will support at least 2 processing/non-stalling streams.</p>
* <p><b>Range of valid values:</b><br></p>
* <p>&gt;= 3
@@ -1212,10 +1211,11 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* the camera device. Using more streams simultaneously may require more hardware and
* CPU resources that will consume more power. The image format for this kind of an output stream can
* be any non-<code>RAW</code> and supported format provided by {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}.</p>
- * <p>A processed and stalling format is defined as any non-RAW format with a stallDurations &gt; 0.
- * Typically only the <code>JPEG</code> format (ImageFormat#JPEG) is a stalling format.</p>
- * <p>For full guarantees, query StreamConfigurationMap#getOutputStallDuration with
- * a processed format -- it will return a non-0 value for a stalling stream.</p>
+ * <p>A processed and stalling format is defined as any non-RAW format with a stallDurations
+ * &gt; 0. Typically only the {@link android.graphics.ImageFormat#JPEG JPEG format} is a
+ * stalling format.</p>
+ * <p>For full guarantees, query {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration } with a
+ * processed format -- it will return a non-0 value for a stalling stream.</p>
* <p>LEGACY devices will support up to 1 processing/stalling stream.</p>
* <p><b>Range of valid values:</b><br></p>
* <p>&gt;= 1</p>
@@ -1232,10 +1232,9 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>The maximum numbers of any type of input streams
* that can be configured and used simultaneously by a camera device.</p>
* <p>When set to 0, it means no input stream is supported.</p>
- * <p>The image format for a input stream can be any supported
- * format returned by StreamConfigurationMap#getInputFormats. When using an
- * input stream, there must be at least one output stream
- * configured to to receive the reprocessed images.</p>
+ * <p>The image format for a input stream can be any supported format returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats }. When using an
+ * input stream, there must be at least one output stream configured to to receive the
+ * reprocessed images.</p>
* <p>When an input stream and some output streams are used in a reprocessing request,
* only the input buffer will be used to produce these output stream buffers, and a
* new sensor image will not be captured.</p>
@@ -1352,7 +1351,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
/**
* <p>A list of all keys that the camera device has available
- * to use with CaptureRequest.</p>
+ * to use with {@link android.hardware.camera2.CaptureRequest }.</p>
* <p>Attempting to set a key into a CaptureRequest that is not
* listed here will result in an invalid request and will be rejected
* by the camera device.</p>
@@ -1370,7 +1369,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
/**
* <p>A list of all keys that the camera device has available
- * to use with CaptureResult.</p>
+ * to use with {@link android.hardware.camera2.CaptureResult }.</p>
* <p>Attempting to get a key from a CaptureResult that is not
* listed here will always return a <code>null</code> value. Getting a key from
* a CaptureResult that is listed here will generally never return a <code>null</code>
@@ -1396,7 +1395,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
/**
* <p>A list of all keys that the camera device has available
- * to use with CameraCharacteristics.</p>
+ * to use with {@link android.hardware.camera2.CameraCharacteristics }.</p>
* <p>This entry follows the same rules as
* android.request.availableResultKeys (except that it applies for
* CameraCharacteristics instead of CaptureResult). See above for more
@@ -1535,34 +1534,31 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* </thead>
* <tbody>
* <tr>
- * <td align="left">PRIVATE (ImageFormat#PRIVATE)</td>
- * <td align="left">JPEG</td>
+ * <td align="left">{@link android.graphics.ImageFormat#PRIVATE }</td>
+ * <td align="left">{@link android.graphics.ImageFormat#JPEG }</td>
* <td align="left">OPAQUE_REPROCESSING</td>
* </tr>
* <tr>
- * <td align="left">PRIVATE</td>
- * <td align="left">YUV_420_888</td>
+ * <td align="left">{@link android.graphics.ImageFormat#PRIVATE }</td>
+ * <td align="left">{@link android.graphics.ImageFormat#YUV_420_888 }</td>
* <td align="left">OPAQUE_REPROCESSING</td>
* </tr>
* <tr>
- * <td align="left">YUV_420_888</td>
- * <td align="left">JPEG</td>
+ * <td align="left">{@link android.graphics.ImageFormat#YUV_420_888 }</td>
+ * <td align="left">{@link android.graphics.ImageFormat#JPEG }</td>
* <td align="left">YUV_REPROCESSING</td>
* </tr>
* <tr>
- * <td align="left">YUV_420_888</td>
- * <td align="left">YUV_420_888</td>
+ * <td align="left">{@link android.graphics.ImageFormat#YUV_420_888 }</td>
+ * <td align="left">{@link android.graphics.ImageFormat#YUV_420_888 }</td>
* <td align="left">YUV_REPROCESSING</td>
* </tr>
* </tbody>
* </table>
- * <p>PRIVATE refers to a device-internal format that is not directly application-visible.
- * A PRIVATE input surface can be acquired by
- * ImageReader.newOpaqueInstance(width, height, maxImages).
- * For a OPAQUE_REPROCESSING-capable camera device, using the PRIVATE format
- * as either input or output will never hurt maximum frame rate (i.e.
- * StreamConfigurationMap#getOutputStallDuration(format, size) is always 0),
- * where format is ImageFormat#PRIVATE.</p>
+ * <p>PRIVATE refers to a device-internal format that is not directly application-visible. A
+ * PRIVATE input surface can be acquired by {@link android.media.ImageReader#newOpaqueInstance }.</p>
+ * <p>For a OPAQUE_REPROCESSING-capable camera device, using the PRIVATE format as either input
+ * or output will never hurt maximum frame rate (i.e. {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration getOutputStallDuration(ImageFormat.PRIVATE, size)} is always 0),</p>
* <p>Attempting to configure an input stream with output streams not
* listed as available in this map is not valid.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
@@ -1680,7 +1676,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* android.scaler.availableStallDurations for more details about
* calculating the max frame rate.</p>
* <p>(Keep in sync with
- * StreamConfigurationMap#getOutputMinFrameDuration)</p>
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration })</p>
* <p><b>Units</b>: (format, width, height, ns) x n</p>
* <p>This key is available on all devices.</p>
*
@@ -1692,7 +1688,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
/**
* <p>This lists the maximum stall duration for each
- * format/size combination.</p>
+ * output format/size combination.</p>
* <p>A stall duration is how much extra time would get added
* to the normal minimum frame duration for a repeating request
* that has streams with non-zero stall.</p>
@@ -1734,12 +1730,13 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* ignored).</p>
* <p>The following formats may always have a stall duration:</p>
* <ul>
- * <li>ImageFormat#JPEG</li>
- * <li>ImageFormat#RAW_SENSOR</li>
+ * <li>{@link android.graphics.ImageFormat#JPEG }</li>
+ * <li>{@link android.graphics.ImageFormat#RAW_SENSOR }</li>
* </ul>
* <p>The following formats will never have a stall duration:</p>
* <ul>
- * <li>ImageFormat#YUV_420_888</li>
+ * <li>{@link android.graphics.ImageFormat#YUV_420_888 }</li>
+ * <li>{@link android.graphics.ImageFormat#RAW10 }</li>
* </ul>
* <p>All other formats may or may not have an allowed stall duration on
* a per-capability basis; refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
@@ -1747,7 +1744,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} for more information about
* calculating the max frame rate (absent stalls).</p>
* <p>(Keep up to date with
- * StreamConfigurationMap#getOutputStallDuration(int, Size) )</p>
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration } )</p>
* <p><b>Units</b>: (format, width, height, ns) x n</p>
* <p>This key is available on all devices.</p>
*
@@ -1786,57 +1783,57 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* </thead>
* <tbody>
* <tr>
- * <td align="center">JPEG</td>
+ * <td align="center">{@link android.graphics.ImageFormat#JPEG }</td>
* <td align="center">{@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE android.sensor.info.activeArraySize}</td>
* <td align="center">Any</td>
* <td align="center"></td>
* </tr>
* <tr>
- * <td align="center">JPEG</td>
+ * <td align="center">{@link android.graphics.ImageFormat#JPEG }</td>
* <td align="center">1920x1080 (1080p)</td>
* <td align="center">Any</td>
* <td align="center">if 1080p &lt;= activeArraySize</td>
* </tr>
* <tr>
- * <td align="center">JPEG</td>
+ * <td align="center">{@link android.graphics.ImageFormat#JPEG }</td>
* <td align="center">1280x720 (720)</td>
* <td align="center">Any</td>
* <td align="center">if 720p &lt;= activeArraySize</td>
* </tr>
* <tr>
- * <td align="center">JPEG</td>
+ * <td align="center">{@link android.graphics.ImageFormat#JPEG }</td>
* <td align="center">640x480 (480p)</td>
* <td align="center">Any</td>
* <td align="center">if 480p &lt;= activeArraySize</td>
* </tr>
* <tr>
- * <td align="center">JPEG</td>
+ * <td align="center">{@link android.graphics.ImageFormat#JPEG }</td>
* <td align="center">320x240 (240p)</td>
* <td align="center">Any</td>
* <td align="center">if 240p &lt;= activeArraySize</td>
* </tr>
* <tr>
- * <td align="center">YUV_420_888</td>
+ * <td align="center">{@link android.graphics.ImageFormat#YUV_420_888 }</td>
* <td align="center">all output sizes available for JPEG</td>
* <td align="center">FULL</td>
* <td align="center"></td>
* </tr>
* <tr>
- * <td align="center">YUV_420_888</td>
+ * <td align="center">{@link android.graphics.ImageFormat#YUV_420_888 }</td>
* <td align="center">all output sizes available for JPEG, up to the maximum video size</td>
* <td align="center">LIMITED</td>
* <td align="center"></td>
* </tr>
* <tr>
- * <td align="center">IMPLEMENTATION_DEFINED</td>
+ * <td align="center">{@link android.graphics.ImageFormat#PRIVATE }</td>
* <td align="center">same as YUV_420_888</td>
* <td align="center">Any</td>
* <td align="center"></td>
* </tr>
* </tbody>
* </table>
- * <p>Refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} for additional
- * mandatory stream configurations on a per-capability basis.</p>
+ * <p>Refer to {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} and {@link android.hardware.camera2.CameraDevice#createCaptureSession } for additional mandatory
+ * stream configurations on a per-capability basis.</p>
* <p>This key is available on all devices.</p>
*
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
@@ -1973,8 +1970,8 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>Attempting to use frame durations beyond the maximum will result in the frame
* duration being clipped to the maximum. See that control for a full definition of frame
* durations.</p>
- * <p>Refer to StreamConfigurationMap#getOutputMinFrameDuration(int,Size) for the minimum
- * frame duration values.</p>
+ * <p>Refer to {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }
+ * for the minimum frame duration values.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
* For FULL capability devices
@@ -2634,6 +2631,41 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
new Key<Integer>("android.sync.maxLatency", int.class);
/**
+ * <p>The maximal camera capture pipeline stall (in unit of frame count) introduced by a
+ * reprocess capture request.</p>
+ * <p>The key describes the maximal interference that one reprocess (input) request
+ * can introduce to the camera simultaneous streaming of regular (output) capture
+ * requests, including repeating requests.</p>
+ * <p>When a reprocessing capture request is submitted while a camera output repeating request
+ * (e.g. preview) is being served by the camera device, it may preempt the camera capture
+ * pipeline for at least one frame duration so that the camera device is unable to process
+ * the following capture request in time for the next sensor start of exposure boundary.
+ * When this happens, the application may observe a capture time gap (longer than one frame
+ * duration) between adjacent capture output frames, which usually exhibits as preview
+ * glitch if the repeating request output targets include a preview surface. This key gives
+ * the worst-case number of frame stall introduced by one reprocess request with any kind of
+ * formats/sizes combination.</p>
+ * <p>If this key reports 0, it means a reprocess request doesn't introduce any glitch to the
+ * ongoing camera repeating request outputs, as if this reprocess request is never issued.</p>
+ * <p>This key is supported if the camera device supports OPAQUE or YUV reprocessing (
+ * i.e. {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains OPAQUE_REPROCESSING or
+ * YUV_REPROCESSING).</p>
+ * <p><b>Units</b>: Number of frames.</p>
+ * <p><b>Range of valid values:</b><br>
+ * &lt;= 4</p>
+ * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+ *
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ @PublicKey
+ public static final Key<Integer> REPROCESS_MAX_CAPTURE_STALL =
+ new Key<Integer>("android.reprocess.maxCaptureStall", int.class);
+
+ /**
* <p>The available depth dataspace stream
* configurations that this camera device supports
* (i.e. format, width, height, output/input stream).</p>
@@ -2672,8 +2704,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and
* android.scaler.availableStallDurations for more details about
* calculating the max frame rate.</p>
- * <p>(Keep in sync with
- * StreamConfigurationMap#getOutputMinFrameDuration)</p>
+ * <p>(Keep in sync with {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration })</p>
* <p><b>Units</b>: (format, width, height, ns) x n</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Limited capability</b> -
@@ -2689,7 +2720,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
/**
* <p>This lists the maximum stall duration for each
- * format/size combination for depth streams.</p>
+ * output format/size combination for depth streams.</p>
* <p>A stall duration is how much extra time would get added
* to the normal minimum frame duration for a repeating request
* that has streams with non-zero stall.</p>
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 51b326b..e9564b3 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -54,6 +54,7 @@ public abstract class CameraDevice implements AutoCloseable {
* means that high frame rate is given priority over the highest-quality
* post-processing. These requests would normally be used with the
* {@link CameraCaptureSession#setRepeatingRequest} method.
+ * This template is guaranteed to be supported on all camera devices.
*
* @see #createCaptureRequest
*/
@@ -63,6 +64,7 @@ public abstract class CameraDevice implements AutoCloseable {
* Create a request suitable for still image capture. Specifically, this
* means prioritizing image quality over frame rate. These requests would
* commonly be used with the {@link CameraCaptureSession#capture} method.
+ * This template is guaranteed to be supported on all camera devices.
*
* @see #createCaptureRequest
*/
@@ -73,6 +75,7 @@ public abstract class CameraDevice implements AutoCloseable {
* that a stable frame rate is used, and post-processing is set for
* recording quality. These requests would commonly be used with the
* {@link CameraCaptureSession#setRepeatingRequest} method.
+ * This template is guaranteed to be supported on all camera devices.
*
* @see #createCaptureRequest
*/
@@ -84,6 +87,9 @@ public abstract class CameraDevice implements AutoCloseable {
* disrupting the ongoing recording. These requests would commonly be used
* with the {@link CameraCaptureSession#capture} method while a request based on
* {@link #TEMPLATE_RECORD} is is in use with {@link CameraCaptureSession#setRepeatingRequest}.
+ * This template is guaranteed to be supported on all camera devices except
+ * legacy devices ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
+ * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY})
*
* @see #createCaptureRequest
*/
@@ -93,6 +99,11 @@ public abstract class CameraDevice implements AutoCloseable {
* Create a request suitable for zero shutter lag still capture. This means
* means maximizing image quality without compromising preview frame rate.
* AE/AWB/AF should be on auto mode.
+ * This template is guaranteed to be supported on camera devices that support the
+ * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING OPAQUE_REPROCESSING}
+ * capability or the
+ * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING YUV_REPROCESSING}
+ * capability.
*
* @see #createCaptureRequest
*/
@@ -105,6 +116,9 @@ public abstract class CameraDevice implements AutoCloseable {
* quality. The manual capture parameters (exposure, sensitivity, and so on)
* are set to reasonable defaults, but should be overriden by the
* application depending on the intended use case.
+ * This template is guaranteed to be supported on camera devices that support the
+ * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR MANUAL_SENSOR}
+ * capability.
*
* @see #createCaptureRequest
*/
@@ -473,12 +487,14 @@ public abstract class CameraDevice implements AutoCloseable {
* settings as desired, instead.</p>
*
* @param templateType An enumeration selecting the use case for this
- * request; one of the CameraDevice.TEMPLATE_ values.
+ * request; one of the CameraDevice.TEMPLATE_ values. Not all template
+ * types are supported on every device. See the documentation for each
+ * template type for details.
* @return a builder for a capture request, initialized with default
* settings for that template, and no output streams
*
- * @throws IllegalArgumentException if the templateType is not in the list
- * of supported templates.
+ * @throws IllegalArgumentException if the templateType is not supported by
+ * this device.
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
* @throws IllegalStateException if the camera device has been closed
@@ -564,7 +580,8 @@ public abstract class CameraDevice implements AutoCloseable {
* indicating that the camera device is in use already.
*
* <p>
- * This error can be produced when opening the camera fails.
+ * This error can be produced when opening the camera fails due to the camera
+ * being used by a higher-priority camera API client.
* </p>
*
* @see #onError
@@ -662,7 +679,7 @@ public abstract class CameraDevice implements AutoCloseable {
* {@link CameraAccessException}. The disconnection could be due to a
* change in security policy or permissions; the physical disconnection
* of a removable camera device; or the camera being needed for a
- * higher-priority use case.</p>
+ * higher-priority camera API client.</p>
*
* <p>There may still be capture callbacks that are invoked
* after this method is called, or new image buffers that are delivered
@@ -672,8 +689,9 @@ public abstract class CameraDevice implements AutoCloseable {
* about the disconnection.</p>
*
* <p>You should clean up the camera with {@link CameraDevice#close} after
- * this happens, as it is not recoverable until opening the camera again
- * after it becomes {@link CameraManager.AvailabilityCallback#onCameraAvailable available}.
+ * this happens, as it is not recoverable until the camera can be opened
+ * again. For most use cases, this will be when the camera again becomes
+ * {@link CameraManager.AvailabilityCallback#onCameraAvailable available}.
* </p>
*
* @param camera the device that has been disconnected
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 1a00a05..9327f00 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -77,8 +77,8 @@ public final class CameraManager {
}
/**
- * Return the list of currently connected camera devices by
- * identifier.
+ * Return the list of currently connected camera devices by identifier, including
+ * cameras that may be in use by other camera API clients.
*
* <p>Non-removable cameras use integers starting at 0 for their
* identifiers, while removable cameras have a unique identifier for each
@@ -103,6 +103,11 @@ public final class CameraManager {
* <p>The first time a callback is registered, it is immediately called
* with the availability status of all currently known camera devices.</p>
*
+ * <p>{@link AvailabilityCallback#onCameraUnavailable(String)} will be called whenever a camera
+ * device is opened by any camera API client. As of API level 23, other camera API clients may
+ * still be able to open such a camera device, evicting the existing client if they have higher
+ * priority than the existing client of a camera device. See open() for more details.</p>
+ *
* <p>Since this callback will be registered with the camera service, remember to unregister it
* once it is no longer needed; otherwise the callback will continue to receive events
* indefinitely and it may prevent other resources from being released. Specifically, the
@@ -259,14 +264,14 @@ public final class CameraManager {
}
/**
- * Helper for openning a connection to a camera with the given ID.
+ * Helper for opening a connection to a camera with the given ID.
*
* @param cameraId The unique identifier of the camera device to open
* @param callback The callback for the camera. Must not be null.
* @param handler The handler to invoke the callback on. Must not be null.
*
* @throws CameraAccessException if the camera is disabled by device policy,
- * or too many camera devices are already open, or the cameraId does not match
+ * too many camera devices are already open, or the cameraId does not match
* any currently available camera device.
*
* @throws SecurityException if the application does not have permission to
@@ -309,7 +314,7 @@ public final class CameraManager {
"Camera service is currently unavailable");
}
cameraService.connectDevice(callbacks, id,
- mContext.getPackageName(), USE_CALLING_UID, holder);
+ mContext.getOpPackageName(), USE_CALLING_UID, holder);
cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
} else {
// Use legacy camera implementation for HAL1 devices
@@ -330,7 +335,8 @@ public final class CameraManager {
deviceImpl.setRemoteFailure(e);
if (e.getReason() == CameraAccessException.CAMERA_DISABLED ||
- e.getReason() == CameraAccessException.CAMERA_DISCONNECTED) {
+ e.getReason() == CameraAccessException.CAMERA_DISCONNECTED ||
+ e.getReason() == CameraAccessException.CAMERA_IN_USE) {
// Per API docs, these failures call onError and throw
throw e.asChecked();
}
@@ -369,7 +375,19 @@ public final class CameraManager {
* <p>Use {@link #getCameraIdList} to get the list of available camera
* devices. Note that even if an id is listed, open may fail if the device
* is disconnected between the calls to {@link #getCameraIdList} and
- * {@link #openCamera}.</p>
+ * {@link #openCamera}, or if a higher-priority camera API client begins using the
+ * camera device.</p>
+ *
+ * <p>As of API level 23, devices for which the
+ * {@link AvailabilityCallback#onCameraUnavailable(String)} callback has been called due to the
+ * device being in use by a lower-priority, background camera API client can still potentially
+ * be opened by calling this method when the calling camera API client has a higher priority
+ * than the current camera API client using this device. In general, if the top, foreground
+ * activity is running within your application process, your process will be given the highest
+ * priority when accessing the camera, and this method will succeed even if the camera device is
+ * in use by another camera API client. Any lower-priority application that loses control of the
+ * camera in this way will receive an
+ * {@link android.hardware.camera2.CameraDevice.StateCallback#onDisconnected} callback.</p>
*
* <p>Once the camera is successfully opened, {@link CameraDevice.StateCallback#onOpened} will
* be invoked with the newly opened {@link CameraDevice}. The camera device can then be set up
@@ -401,7 +419,7 @@ public final class CameraManager {
* {@code null} to use the current thread's {@link android.os.Looper looper}.
*
* @throws CameraAccessException if the camera is disabled by device policy,
- * or the camera has become or was disconnected.
+ * has been disconnected, or is being used by a higher-priority camera API client.
*
* @throws IllegalArgumentException if cameraId or the callback was null,
* or the cameraId does not match any currently or previously available
@@ -477,8 +495,7 @@ public final class CameraManager {
}
/**
- * A callback for camera devices becoming available or
- * unavailable to open.
+ * A callback for camera devices becoming available or unavailable to open.
*
* <p>Cameras become available when they are no longer in use, or when a new
* removable camera is connected. They become unavailable when some
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index e3f1d73..ca9439b 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -461,22 +461,21 @@ public abstract class CameraMetadata<TKey> {
* <p>The camera device supports the Zero Shutter Lag reprocessing use case.</p>
* <ul>
* <li>One input stream is supported, that is, <code>{@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} == 1</code>.</li>
- * <li>ImageFormat#PRIVATE is supported as an output/input format, that is,
- * ImageFormat#PRIVATE is included in the lists of formats returned by
- * StreamConfigurationMap#getInputFormats and
- * StreamConfigurationMap#getOutputFormats.</li>
- * <li>StreamConfigurationMap#getValidOutputFormatsForInput returns non empty int[] for
- * each supported input format returned by StreamConfigurationMap#getInputFormats.</li>
- * <li>Each size returned by StreamConfigurationMap#getInputSizes(ImageFormat#PRIVATE)
- * is also included in StreamConfigurationMap#getOutputSizes(ImageFormat#PRIVATE)</li>
- * <li>Using ImageFormat#PRIVATE does not cause a frame rate drop
- * relative to the sensor's maximum capture rate (at that
- * resolution).</li>
- * <li>ImageFormat#PRIVATE will be reprocessable into both YUV_420_888
- * and JPEG formats.</li>
+ * <li>{@link android.graphics.ImageFormat#PRIVATE } is supported as an output/input format,
+ * that is, {@link android.graphics.ImageFormat#PRIVATE } is included in the lists of
+ * formats returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats } and {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputFormats }.</li>
+ * <li>{@link android.hardware.camera2.params.StreamConfigurationMap#getValidOutputFormatsForInput }
+ * returns non empty int[] for each supported input format returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats }.</li>
+ * <li>Each size returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputSizes getInputSizes(ImageFormat.PRIVATE)} is also included in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes getOutputSizes(ImageFormat.PRIVATE)}</li>
+ * <li>Using {@link android.graphics.ImageFormat#PRIVATE } does not cause a frame rate drop
+ * relative to the sensor's maximum capture rate (at that resolution).</li>
+ * <li>{@link android.graphics.ImageFormat#PRIVATE } will be reprocessable into both
+ * {@link android.graphics.ImageFormat#YUV_420_888 } and
+ * {@link android.graphics.ImageFormat#JPEG } formats.</li>
* <li>The maximum available resolution for OPAQUE streams
* (both input/output) will match the maximum available
* resolution of JPEG streams.</li>
+ * <li>Static metadata {@link CameraCharacteristics#REPROCESS_MAX_CAPTURE_STALL android.reprocess.maxCaptureStall}.</li>
* <li>Only below controls are effective for reprocessing requests and
* will be present in capture results, other controls in reprocess
* requests will be ignored by the camera device.<ul>
@@ -489,6 +488,7 @@ public abstract class CameraMetadata<TKey> {
*
* @see CaptureRequest#EDGE_MODE
* @see CaptureRequest#NOISE_REDUCTION_MODE
+ * @see CameraCharacteristics#REPROCESS_MAX_CAPTURE_STALL
* @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
@@ -569,25 +569,25 @@ public abstract class CameraMetadata<TKey> {
* following:</p>
* <ul>
* <li>One input stream is supported, that is, <code>{@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} == 1</code>.</li>
- * <li>YUV_420_888 is supported as an output/input format, that is,
+ * <li>{@link android.graphics.ImageFormat#YUV_420_888 } is supported as an output/input format, that is,
* YUV_420_888 is included in the lists of formats returned by
- * StreamConfigurationMap#getInputFormats and
- * StreamConfigurationMap#getOutputFormats.</li>
- * <li>StreamConfigurationMap#getValidOutputFormatsForInput returns non empty int[] for
- * each supported input format returned by StreamConfigurationMap#getInputFormats.</li>
- * <li>Each size returned by StreamConfigurationMap#getInputSizes(YUV_420_888)
- * is also included in StreamConfigurationMap#getOutputSizes(YUV_420_888)</li>
- * <li>Using YUV_420_888 does not cause a frame rate drop
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats } and
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputFormats }.</li>
+ * <li>{@link android.hardware.camera2.params.StreamConfigurationMap#getValidOutputFormatsForInput }
+ * returns non-empty int[] for each supported input format returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputFormats }.</li>
+ * <li>Each size returned by {@link android.hardware.camera2.params.StreamConfigurationMap#getInputSizes getInputSizes(YUV_420_888)} is also included in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes getOutputSizes(YUV_420_888)}</li>
+ * <li>Using {@link android.graphics.ImageFormat#YUV_420_888 } does not cause a frame rate drop
* relative to the sensor's maximum capture rate (at that resolution).</li>
- * <li>YUV_420_888 will be reprocessable into both YUV_420_888
- * and JPEG formats.</li>
- * <li>The maximum available resolution for YUV_420_888 streams
- * (both input/output) will match the maximum available
- * resolution of JPEG streams.</li>
- * <li>Only the below controls are effective for reprocessing requests and will be
- * present in capture results. The reprocess requests are from the original capture
- * results that are assocaited with the intermidate YUV_420_888 output buffers.
- * All other controls in the reprocess requests will be ignored by the camera device.<ul>
+ * <li>{@link android.graphics.ImageFormat#YUV_420_888 } will be reprocessable into both
+ * {@link android.graphics.ImageFormat#YUV_420_888 } and {@link android.graphics.ImageFormat#JPEG } formats.</li>
+ * <li>The maximum available resolution for {@link android.graphics.ImageFormat#YUV_420_888 } streams (both input/output) will match the
+ * maximum available resolution of {@link android.graphics.ImageFormat#JPEG } streams.</li>
+ * <li>Static metadata {@link CameraCharacteristics#REPROCESS_MAX_CAPTURE_STALL android.reprocess.maxCaptureStall}.</li>
+ * <li>Only the below controls are effective for reprocessing requests and will be present
+ * in capture results. The reprocess requests are from the original capture results that
+ * are associated with the intermediate {@link android.graphics.ImageFormat#YUV_420_888 }
+ * output buffers. All other controls in the reprocess requests will be ignored by the
+ * camera device.<ul>
* <li>android.jpeg.*</li>
* <li>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}</li>
* <li>{@link CaptureRequest#EDGE_MODE android.edge.mode}</li>
@@ -599,6 +599,7 @@ public abstract class CameraMetadata<TKey> {
* @see CaptureRequest#EDGE_MODE
* @see CaptureRequest#NOISE_REDUCTION_MODE
* @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
+ * @see CameraCharacteristics#REPROCESS_MAX_CAPTURE_STALL
* @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
@@ -608,11 +609,13 @@ public abstract class CameraMetadata<TKey> {
* <p>The camera device can produce depth measurements from its field of view.</p>
* <p>This capability requires the camera device to support the following:</p>
* <ul>
- * <li>DEPTH16 is supported as an output format.</li>
- * <li>DEPTH_POINT_CLOUD is optionally supported as an output format.</li>
- * <li>This camera device, and all camera devices with the same android.lens.info.facing,
- * will list the following calibration entries in both CameraCharacteristics and
- * CaptureResults:<ul>
+ * <li>{@link android.graphics.ImageFormat#DEPTH16 } is supported as an output format.</li>
+ * <li>{@link android.graphics.ImageFormat#DEPTH_POINT_CLOUD } is optionally supported as an
+ * output format.</li>
+ * <li>This camera device, and all camera devices with the same {@link CameraCharacteristics#LENS_FACING android.lens.facing},
+ * will list the following calibration entries in both
+ * {@link android.hardware.camera2.CameraCharacteristics } and
+ * {@link android.hardware.camera2.CaptureResult }:<ul>
* <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
* <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
* <li>android.lens.intrinsicCalibration</li>
@@ -627,13 +630,14 @@ public abstract class CameraMetadata<TKey> {
* <p>Generally, depth output operates at a slower frame rate than standard color capture,
* so the DEPTH16 and DEPTH_POINT_CLOUD formats will commonly have a stall duration that
* should be accounted for (see
- * android.hardware.camera2.StreamConfigurationMap#getOutputStallDuration). On a device
- * that supports both depth and color-based output, to enable smooth preview, using a
- * repeating burst is recommended, where a depth-output target is only included once
- * every N frames, where N is the ratio between preview output rate and depth output
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }).
+ * On a device that supports both depth and color-based output, to enable smooth preview,
+ * using a repeating burst is recommended, where a depth-output target is only included
+ * once every N frames, where N is the ratio between preview output rate and depth output
* rate, including depth stall time.</p>
*
* @see CameraCharacteristics#DEPTH_DEPTH_IS_EXCLUSIVE
+ * @see CameraCharacteristics#LENS_FACING
* @see CameraCharacteristics#LENS_POSE_ROTATION
* @see CameraCharacteristics#LENS_POSE_TRANSLATION
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
@@ -707,7 +711,7 @@ public abstract class CameraMetadata<TKey> {
/**
* <p>Timestamps from {@link CaptureResult#SENSOR_TIMESTAMP android.sensor.timestamp} are in the same timebase as
- * android.os.SystemClock#elapsedRealtimeNanos(),
+ * {@link android.os.SystemClock#elapsedRealtimeNanos },
* and they can be compared to other timestamps using that base.</p>
*
* @see CaptureResult#SENSOR_TIMESTAMP
@@ -866,7 +870,7 @@ public abstract class CameraMetadata<TKey> {
/**
* <p>Every frame has the requests immediately applied.</p>
* <p>Furthermore for all results,
- * <code>android.sync.frameNumber == CaptureResult#getFrameNumber()</code></p>
+ * <code>android.sync.frameNumber == {@link android.hardware.camera2.CaptureResult#getFrameNumber }</code></p>
* <p>Changing controls over multiple requests one after another will
* produce results that have those controls applied atomically
* each frame.</p>
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 19d17b1..ab6ce91 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1275,8 +1275,9 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* <p>This control (except for MANUAL) is only effective if
* <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p>
* <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
- * contains OPAQUE_REPROCESSING. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
- * contains MANUAL_SENSOR. Other intent values are always supported.</p>
+ * contains OPAQUE_REPROCESSING or YUV_REPROCESSING. MANUAL will be supported if
+ * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR. Other intent values are
+ * always supported.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #CONTROL_CAPTURE_INTENT_CUSTOM CUSTOM}</li>
@@ -2039,8 +2040,8 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* cannot process more than 1 capture at a time.</li>
* </ul>
* <p>The necessary information for the application, given the model above,
- * is provided via the {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} field
- * using StreamConfigurationMap#getOutputMinFrameDuration(int, Size).
+ * is provided via the {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} field using
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }.
* These are used to determine the maximum frame rate / minimum frame
* duration that is possible for a given stream configuration.</p>
* <p>Specifically, the application can use the following rules to
@@ -2049,21 +2050,19 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* <ol>
* <li>Let the set of currently configured input/output streams
* be called <code>S</code>.</li>
- * <li>Find the minimum frame durations for each stream in <code>S</code>, by
- * looking it up in {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} using
- * StreamConfigurationMap#getOutputMinFrameDuration(int, Size) (with
- * its respective size/format). Let this set of frame durations be called
- * <code>F</code>.</li>
+ * <li>Find the minimum frame durations for each stream in <code>S</code>, by looking
+ * it up in {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} using {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }
+ * (with its respective size/format). Let this set of frame durations be
+ * called <code>F</code>.</li>
* <li>For any given request <code>R</code>, the minimum frame duration allowed
* for <code>R</code> is the maximum out of all values in <code>F</code>. Let the streams
* used in <code>R</code> be called <code>S_r</code>.</li>
* </ol>
- * <p>If none of the streams in <code>S_r</code> have a stall time (listed in
- * StreamConfigurationMap#getOutputStallDuration(int,Size) using its
- * respective size/format), then the frame duration in
- * <code>F</code> determines the steady state frame rate that the application will
- * get if it uses <code>R</code> as a repeating request. Let this special kind
- * of request be called <code>Rsimple</code>.</p>
+ * <p>If none of the streams in <code>S_r</code> have a stall time (listed in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }
+ * using its respective size/format), then the frame duration in <code>F</code>
+ * determines the steady state frame rate that the application will get
+ * if it uses <code>R</code> as a repeating request. Let this special kind of
+ * request be called <code>Rsimple</code>.</p>
* <p>A repeating request <code>Rsimple</code> can be <em>occasionally</em> interleaved
* by a single capture of a new request <code>Rstall</code> (which has at least
* one in-use stream with a non-0 stall time) and if <code>Rstall</code> has the
@@ -2071,7 +2070,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* if all buffers from the previous <code>Rstall</code> have already been
* delivered.</p>
* <p>For more details about stalling, see
- * StreamConfigurationMap#getOutputStallDuration(int,Size).</p>
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
* <p><b>Units</b>: Nanoseconds</p>
@@ -2647,8 +2646,12 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
* <p><b>Range of valid values:</b><br>
* &gt;= 1.0</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
*
* @see CaptureRequest#EDGE_MODE
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CaptureRequest#NOISE_REDUCTION_MODE
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index ef5d75c..3dc8970 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -1698,8 +1698,9 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p>This control (except for MANUAL) is only effective if
* <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p>
* <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
- * contains OPAQUE_REPROCESSING. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
- * contains MANUAL_SENSOR. Other intent values are always supported.</p>
+ * contains OPAQUE_REPROCESSING or YUV_REPROCESSING. MANUAL will be supported if
+ * {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains MANUAL_SENSOR. Other intent values are
+ * always supported.</p>
* <p><b>Possible values:</b>
* <ul>
* <li>{@link #CONTROL_CAPTURE_INTENT_CUSTOM CUSTOM}</li>
@@ -2885,8 +2886,8 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* cannot process more than 1 capture at a time.</li>
* </ul>
* <p>The necessary information for the application, given the model above,
- * is provided via the {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} field
- * using StreamConfigurationMap#getOutputMinFrameDuration(int, Size).
+ * is provided via the {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} field using
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }.
* These are used to determine the maximum frame rate / minimum frame
* duration that is possible for a given stream configuration.</p>
* <p>Specifically, the application can use the following rules to
@@ -2895,21 +2896,19 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <ol>
* <li>Let the set of currently configured input/output streams
* be called <code>S</code>.</li>
- * <li>Find the minimum frame durations for each stream in <code>S</code>, by
- * looking it up in {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} using
- * StreamConfigurationMap#getOutputMinFrameDuration(int, Size) (with
- * its respective size/format). Let this set of frame durations be called
- * <code>F</code>.</li>
+ * <li>Find the minimum frame durations for each stream in <code>S</code>, by looking
+ * it up in {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} using {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }
+ * (with its respective size/format). Let this set of frame durations be
+ * called <code>F</code>.</li>
* <li>For any given request <code>R</code>, the minimum frame duration allowed
* for <code>R</code> is the maximum out of all values in <code>F</code>. Let the streams
* used in <code>R</code> be called <code>S_r</code>.</li>
* </ol>
- * <p>If none of the streams in <code>S_r</code> have a stall time (listed in
- * StreamConfigurationMap#getOutputStallDuration(int,Size) using its
- * respective size/format), then the frame duration in
- * <code>F</code> determines the steady state frame rate that the application will
- * get if it uses <code>R</code> as a repeating request. Let this special kind
- * of request be called <code>Rsimple</code>.</p>
+ * <p>If none of the streams in <code>S_r</code> have a stall time (listed in {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }
+ * using its respective size/format), then the frame duration in <code>F</code>
+ * determines the steady state frame rate that the application will get
+ * if it uses <code>R</code> as a repeating request. Let this special kind of
+ * request be called <code>Rsimple</code>.</p>
* <p>A repeating request <code>Rsimple</code> can be <em>occasionally</em> interleaved
* by a single capture of a new request <code>Rstall</code> (which has at least
* one in-use stream with a non-0 stall time) and if <code>Rstall</code> has the
@@ -2917,7 +2916,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* if all buffers from the previous <code>Rstall</code> have already been
* delivered.</p>
* <p>For more details about stalling, see
- * StreamConfigurationMap#getOutputStallDuration(int,Size).</p>
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputStallDuration }.</p>
* <p>This control is only effective if {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} or {@link CaptureRequest#CONTROL_MODE android.control.mode} is set to
* OFF; otherwise the auto-exposure algorithm will override this value.</p>
* <p><b>Units</b>: Nanoseconds</p>
@@ -2979,11 +2978,10 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* and are monotonically increasing. They can be compared with the
* timestamps for other captures from the same camera device, but are
* not guaranteed to be comparable to any other time source.</p>
- * <p>When {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE android.sensor.info.timestampSource} <code>==</code> REALTIME,
- * the timestamps measure time in the same timebase as
- * android.os.SystemClock#elapsedRealtimeNanos(), and they can be
- * compared to other timestamps from other subsystems that are using
- * that base.</p>
+ * <p>When {@link CameraCharacteristics#SENSOR_INFO_TIMESTAMP_SOURCE android.sensor.info.timestampSource} <code>==</code> REALTIME, the
+ * timestamps measure time in the same timebase as {@link android.os.SystemClock#elapsedRealtimeNanos }, and they can
+ * be compared to other timestamps from other subsystems that
+ * are using that base.</p>
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
* &gt; 0</p>
@@ -3141,7 +3139,7 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p><b>Units</b>: Nanoseconds</p>
* <p><b>Range of valid values:</b><br>
* &gt;= 0 and &lt;
- * StreamConfigurationMap#getOutputMinFrameDuration(int, Size).</p>
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputMinFrameDuration }.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Limited capability</b> -
* Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
@@ -3966,8 +3964,12 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <p><b>Range of valid values:</b><br>
* &gt;= 1.0</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+ * <p><b>Limited capability</b> -
+ * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+ * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
*
* @see CaptureRequest#EDGE_MODE
+ * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @see CaptureRequest#NOISE_REDUCTION_MODE
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
*/
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index abe26ea..edad00f 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -202,6 +202,7 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
private static final int CAMERA_IDLE = 1;
private static final int CAPTURE_STARTED = 2;
private static final int RESULT_RECEIVED = 3;
+ private static final int PREPARED = 4;
private final HandlerThread mHandlerThread;
private Handler mHandler;
@@ -253,7 +254,9 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
@Override
public void onPrepared(int streamId) {
- // TODO
+ Message msg = getHandler().obtainMessage(PREPARED,
+ /*arg1*/ streamId, /*arg2*/ 0);
+ getHandler().sendMessage(msg);
}
@Override
@@ -301,6 +304,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
mCallbacks.onResultReceived(result, resultExtras);
break;
}
+ case PREPARED: {
+ int streamId = msg.arg1;
+ mCallbacks.onPrepared(streamId);
+ break;
+ }
default:
throw new IllegalArgumentException(
"Unknown callback message " + msg.what);
@@ -631,7 +639,9 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
return CameraBinderDecorator.ENODEV;
}
- // TODO: Implement and fire callback
+ // LEGACY doesn't support actual prepare, just signal success right away
+ mCameraCallbacks.onPrepared(streamId);
+
return CameraBinderDecorator.NO_ERROR;
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index adab9be..02793f1 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -182,6 +182,10 @@ public abstract class DisplayManagerInternal {
// The screen auto-brightness adjustment factor in the range -1 (dimmer) to 1 (brighter).
public float screenAutoBrightnessAdjustment;
+ // Set to true if screenBrightness and screenAutoBrightnessAdjustment were both
+ // set by the user as opposed to being programmatically controlled by apps.
+ public boolean brightnessSetByUser;
+
// If true, enables automatic brightness control.
public boolean useAutoBrightness;
@@ -229,6 +233,7 @@ public abstract class DisplayManagerInternal {
useProximitySensor = other.useProximitySensor;
screenBrightness = other.screenBrightness;
screenAutoBrightnessAdjustment = other.screenAutoBrightnessAdjustment;
+ brightnessSetByUser = other.brightnessSetByUser;
useAutoBrightness = other.useAutoBrightness;
blockScreenOn = other.blockScreenOn;
lowPowerMode = other.lowPowerMode;
@@ -249,6 +254,7 @@ public abstract class DisplayManagerInternal {
&& useProximitySensor == other.useProximitySensor
&& screenBrightness == other.screenBrightness
&& screenAutoBrightnessAdjustment == other.screenAutoBrightnessAdjustment
+ && brightnessSetByUser == other.brightnessSetByUser
&& useAutoBrightness == other.useAutoBrightness
&& blockScreenOn == other.blockScreenOn
&& lowPowerMode == other.lowPowerMode
@@ -268,6 +274,7 @@ public abstract class DisplayManagerInternal {
+ ", useProximitySensor=" + useProximitySensor
+ ", screenBrightness=" + screenBrightness
+ ", screenAutoBrightnessAdjustment=" + screenAutoBrightnessAdjustment
+ + ", brightnessSetByUser=" + brightnessSetByUser
+ ", useAutoBrightness=" + useAutoBrightness
+ ", blockScreenOn=" + blockScreenOn
+ ", lowPowerMode=" + lowPowerMode
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 2257b0a..cf96145 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -42,6 +42,7 @@ import java.util.HashMap;
import java.util.List;
import javax.crypto.Cipher;
+import javax.crypto.Mac;
/**
* A class that coordinates access to the fingerprint hardware.
@@ -195,18 +196,26 @@ public class FingerprintManager {
/**
* A wrapper class for the crypto objects supported by FingerprintManager. Currently the
- * framework supports {@link Signature} and {@link Cipher} objects.
+ * framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
*/
public static class CryptoObject {
- public CryptoObject(Signature signature) {
+ public CryptoObject(@NonNull Signature signature) {
mSignature = signature;
mCipher = null;
+ mMac = null;
}
- public CryptoObject(Cipher cipher) {
+ public CryptoObject(@NonNull Cipher cipher) {
mCipher = cipher;
mSignature = null;
+ mMac = null;
+ }
+
+ public CryptoObject(@NonNull Mac mac) {
+ mMac = mac;
+ mCipher = null;
+ mSignature = null;
}
/**
@@ -222,6 +231,12 @@ public class FingerprintManager {
public Cipher getCipher() { return mCipher; }
/**
+ * Get {@link Mac} object.
+ * @return {@link Mac} object or null if this doesn't contain one.
+ */
+ public Mac getMac() { return mMac; }
+
+ /**
* @hide
* @return the opId associated with this object or 0 if none
*/
@@ -230,12 +245,15 @@ public class FingerprintManager {
return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mSignature);
} else if (mCipher != null) {
return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mCipher);
+ } else if (mMac != null) {
+ return AndroidKeyStoreProvider.getKeyStoreOperationHandle(mMac);
}
return 0;
}
private final Signature mSignature;
private final Cipher mCipher;
+ private final Mac mMac;
};
/**
@@ -416,7 +434,8 @@ public class FingerprintManager {
mAuthenticationCallback = callback;
mCryptoObject = crypto;
long sessionId = crypto != null ? crypto.getOpId() : 0;
- mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags);
+ mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags,
+ mContext.getOpPackageName());
} catch (RemoteException e) {
Log.w(TAG, "Remote exception while authenticating: ", e);
if (callback != null) {
@@ -537,7 +556,7 @@ public class FingerprintManager {
*/
public List<Fingerprint> getEnrolledFingerprints(int userId) {
if (mService != null) try {
- return mService.getEnrolledFingerprints(userId);
+ return mService.getEnrolledFingerprints(userId, mContext.getOpPackageName());
} catch (RemoteException e) {
Log.v(TAG, "Remote exception in getEnrolledFingerprints: ", e);
}
@@ -561,7 +580,8 @@ public class FingerprintManager {
*/
public boolean hasEnrolledFingerprints() {
if (mService != null) try {
- return mService.hasEnrolledFingerprints(UserHandle.myUserId());
+ return mService.hasEnrolledFingerprints(UserHandle.myUserId(),
+ mContext.getOpPackageName());
} catch (RemoteException e) {
Log.v(TAG, "Remote exception in getEnrolledFingerprints: ", e);
}
@@ -577,7 +597,7 @@ public class FingerprintManager {
if (mService != null) {
try {
long deviceId = 0; /* TODO: plumb hardware id to FPMS */
- return mService.isHardwareDetected(deviceId);
+ return mService.isHardwareDetected(deviceId, mContext.getOpPackageName());
} catch (RemoteException e) {
Log.v(TAG, "Remote exception in isFingerprintHardwareDetected(): ", e);
}
@@ -596,7 +616,7 @@ public class FingerprintManager {
public long getAuthenticatorId() {
if (mService != null) {
try {
- return mService.getAuthenticatorId();
+ return mService.getAuthenticatorId(mContext.getOpPackageName());
} catch (RemoteException e) {
Log.v(TAG, "Remote exception in getAuthenticatorId(): ", e);
}
@@ -606,7 +626,13 @@ public class FingerprintManager {
return 0;
}
- private Handler mHandler = new Handler() {
+ private Handler mHandler;
+
+ private class MyHandler extends Handler {
+ private MyHandler(Context context) {
+ super(context.getMainLooper());
+ }
+
public void handleMessage(android.os.Message msg) {
switch(msg.what) {
case MSG_ENROLL_RESULT:
@@ -691,6 +717,7 @@ public class FingerprintManager {
if (mService == null) {
Slog.v(TAG, "FingerprintManagerService was null");
}
+ mHandler = new MyHandler(context);
}
private int getCurrentUserId() {
@@ -718,7 +745,7 @@ public class FingerprintManager {
private void cancelAuthentication(CryptoObject cryptoObject) {
if (mService != null) try {
- mService.cancelAuthentication(mToken);
+ mService.cancelAuthentication(mToken, mContext.getOpPackageName());
} catch (RemoteException e) {
if (DEBUG) Log.w(TAG, "Remote exception while canceling enrollment");
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index c5ec08c..0484806 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -27,10 +27,10 @@ import java.util.List;
interface IFingerprintService {
// Authenticate the given sessionId with a fingerprint
void authenticate(IBinder token, long sessionId, int groupId,
- IFingerprintServiceReceiver receiver, int flags);
+ IFingerprintServiceReceiver receiver, int flags, String opPackageName);
// Cancel authentication for the given sessionId
- void cancelAuthentication(IBinder token);
+ void cancelAuthentication(IBinder token, String opPackageName);
// Start fingerprint enrollment
void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver,
@@ -46,16 +46,16 @@ interface IFingerprintService {
void rename(int fingerId, int groupId, String name);
// Get a list of enrolled fingerprints in the given group.
- List<Fingerprint> getEnrolledFingerprints(int groupId);
+ List<Fingerprint> getEnrolledFingerprints(int groupId, String opPackageName);
// Determine if HAL is loaded and ready
- boolean isHardwareDetected(long deviceId);
+ boolean isHardwareDetected(long deviceId, String opPackageName);
// Get a pre-enrollment authentication token
long preEnroll(IBinder token);
// Determine if a user has at least one enrolled fingerprint
- boolean hasEnrolledFingerprints(int groupId);
+ boolean hasEnrolledFingerprints(int groupId, String opPackageName);
// Gets the number of hardware devices
// int getHardwareDeviceCount();
@@ -64,5 +64,5 @@ interface IFingerprintService {
// long getHardwareDevice(int i);
// Gets the authenticator ID for fingerprint
- long getAuthenticatorId();
+ long getAuthenticatorId(String opPackageName);
}
diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java
index 1a42319..410d550 100644
--- a/core/java/android/hardware/usb/UsbDevice.java
+++ b/core/java/android/hardware/usb/UsbDevice.java
@@ -45,6 +45,7 @@ public class UsbDevice implements Parcelable {
private final String mName;
private final String mManufacturerName;
private final String mProductName;
+ private final String mVersion;
private final String mSerialNumber;
private final int mVendorId;
private final int mProductId;
@@ -62,7 +63,7 @@ public class UsbDevice implements Parcelable {
*/
public UsbDevice(String name, int vendorId, int productId,
int Class, int subClass, int protocol,
- String manufacturerName, String productName, String serialNumber) {
+ String manufacturerName, String productName, String version, String serialNumber) {
mName = name;
mVendorId = vendorId;
mProductId = productId;
@@ -71,6 +72,7 @@ public class UsbDevice implements Parcelable {
mProtocol = protocol;
mManufacturerName = manufacturerName;
mProductName = productName;
+ mVersion = version;
mSerialNumber = serialNumber;
}
@@ -104,6 +106,15 @@ public class UsbDevice implements Parcelable {
}
/**
+ * Returns the version number of the device.
+ *
+ * @return the device version
+ */
+ public String getVersion() {
+ return mVersion;
+ }
+
+ /**
* Returns the serial number of the device.
*
* @return the serial number name
@@ -263,7 +274,7 @@ public class UsbDevice implements Parcelable {
",mVendorId=" + mVendorId + ",mProductId=" + mProductId +
",mClass=" + mClass + ",mSubclass=" + mSubclass + ",mProtocol=" + mProtocol +
",mManufacturerName=" + mManufacturerName + ",mProductName=" + mProductName +
- ",mSerialNumber=" + mSerialNumber + ",mConfigurations=[");
+ ",mVersion=" + mVersion + ",mSerialNumber=" + mSerialNumber + ",mConfigurations=[");
for (int i = 0; i < mConfigurations.length; i++) {
builder.append("\n");
builder.append(mConfigurations[i].toString());
@@ -283,10 +294,11 @@ public class UsbDevice implements Parcelable {
int protocol = in.readInt();
String manufacturerName = in.readString();
String productName = in.readString();
+ String version = in.readString();
String serialNumber = in.readString();
Parcelable[] configurations = in.readParcelableArray(UsbInterface.class.getClassLoader());
UsbDevice device = new UsbDevice(name, vendorId, productId, clasz, subClass, protocol,
- manufacturerName, productName, serialNumber);
+ manufacturerName, productName, version, serialNumber);
device.setConfigurations(configurations);
return device;
}
@@ -309,6 +321,7 @@ public class UsbDevice implements Parcelable {
parcel.writeInt(mProtocol);
parcel.writeString(mManufacturerName);
parcel.writeString(mProductName);
+ parcel.writeString(mVersion);
parcel.writeString(mSerialNumber);
parcel.writeParcelableArray(mConfigurations, 0);
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c531e7e..d8c3361 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -341,7 +341,8 @@ public class ConnectivityManager {
* one. This is used by applications needing to talk to the carrier's
* Multimedia Messaging Service servers.
*
- * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+ * @deprecated Applications should instead use
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
* provides the {@link NetworkCapabilities#NET_CAPABILITY_MMS} capability.
*/
public static final int TYPE_MOBILE_MMS = 2;
@@ -351,7 +352,8 @@ public class ConnectivityManager {
* one. This is used by applications needing to talk to the carrier's
* Secure User Plane Location servers for help locating the device.
*
- * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+ * @deprecated Applications should instead use
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
* provides the {@link NetworkCapabilities#NET_CAPABILITY_SUPL} capability.
*/
public static final int TYPE_MOBILE_SUPL = 3;
@@ -367,7 +369,8 @@ public class ConnectivityManager {
* same network interface as {@link #TYPE_MOBILE} but the routing setup
* is different.
*
- * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+ * @deprecated Applications should instead use
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} to request a network that
* uses the {@link NetworkCapabilities#TRANSPORT_CELLULAR} transport.
*/
public static final int TYPE_MOBILE_HIPRI = 5;
@@ -910,7 +913,8 @@ public class ConnectivityManager {
* implementation+feature combination, except that the value {@code -1}
* always indicates failure.
*
- * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
+ * @deprecated Deprecated in favor of the cleaner
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} API.
* @removed
*/
public int startUsingNetworkFeature(int networkType, String feature) {
@@ -958,7 +962,7 @@ public class ConnectivityManager {
* implementation+feature combination, except that the value {@code -1}
* always indicates failure.
*
- * @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
+ * @deprecated Deprecated in favor of the cleaner {@link unregisterNetworkCallback} API.
* @removed
*/
public int stopUsingNetworkFeature(int networkType, String feature) {
@@ -1236,8 +1240,9 @@ public class ConnectivityManager {
* @param hostAddress the IP address of the host to which the route is desired
* @return {@code true} on success, {@code false} on failure
*
- * @deprecated Deprecated in favor of the {@link #requestNetwork},
- * {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} api.
+ * @deprecated Deprecated in favor of the
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)},
+ * {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} API.
* @removed
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
@@ -1256,7 +1261,7 @@ public class ConnectivityManager {
* @return {@code true} on success, {@code false} on failure
* @hide
* @deprecated Deprecated in favor of the {@link #requestNetwork} and
- * {@link #bindProcessToNetwork} api.
+ * {@link #bindProcessToNetwork} API.
* @removed
*/
public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
@@ -2144,14 +2149,22 @@ public class ConnectivityManager {
public static final int CANCELED = 8;
/**
- * @hide
- * Called whenever the framework connects to a network that it may use to
- * satisfy this request
+ * Called when the framework connects to a new network to evaluate whether it satisfies this
+ * request. If evaluation succeeds, this callback may be followed by an {@link #onAvailable}
+ * callback. There is no guarantee that this new network will satisfy any requests, or that
+ * the network will stay connected for longer than the time necessary to evaluate it.
+ * <p>
+ * Most applications <b>should not</b> act on this callback, and should instead use
+ * {@link #onAvailable}. This callback is intended for use by applications that can assist
+ * the framework in properly evaluating the network &mdash; for example, an application that
+ * can automatically log in to a captive portal without user intervention.
+ *
+ * @param network The {@link Network} of the network that is being evaluated.
*/
public void onPreCheck(Network network) {}
/**
- * Called when the framework connects and has declared new network ready for use.
+ * Called when the framework connects and has declared a new network ready for use.
* This callback may be called more than once if the {@link Network} that is
* satisfying the request changes.
*
@@ -2251,116 +2264,82 @@ public class ConnectivityManager {
@Override
public void handleMessage(Message message) {
Log.d(TAG, "CM callback handler got msg " + message.what);
+ NetworkRequest request = (NetworkRequest) getObject(message, NetworkRequest.class);
+ Network network = (Network) getObject(message, Network.class);
switch (message.what) {
case CALLBACK_PRECHECK: {
- NetworkRequest request = (NetworkRequest)getObject(message,
- NetworkRequest.class);
- NetworkCallback callbacks = getCallbacks(request);
- if (callbacks != null) {
- callbacks.onPreCheck((Network)getObject(message, Network.class));
- } else {
- Log.e(TAG, "callback not found for PRECHECK message");
+ NetworkCallback callback = getCallback(request, "PRECHECK");
+ if (callback != null) {
+ callback.onPreCheck(network);
}
break;
}
case CALLBACK_AVAILABLE: {
- NetworkRequest request = (NetworkRequest)getObject(message,
- NetworkRequest.class);
- NetworkCallback callbacks = getCallbacks(request);
- if (callbacks != null) {
- callbacks.onAvailable((Network)getObject(message, Network.class));
- } else {
- Log.e(TAG, "callback not found for AVAILABLE message");
+ NetworkCallback callback = getCallback(request, "AVAILABLE");
+ if (callback != null) {
+ callback.onAvailable(network);
}
break;
}
case CALLBACK_LOSING: {
- NetworkRequest request = (NetworkRequest)getObject(message,
- NetworkRequest.class);
- NetworkCallback callbacks = getCallbacks(request);
- if (callbacks != null) {
- callbacks.onLosing((Network)getObject(message, Network.class),
- message.arg1);
- } else {
- Log.e(TAG, "callback not found for LOSING message");
+ NetworkCallback callback = getCallback(request, "LOSING");
+ if (callback != null) {
+ callback.onLosing(network, message.arg1);
}
break;
}
case CALLBACK_LOST: {
- NetworkRequest request = (NetworkRequest)getObject(message,
- NetworkRequest.class);
-
- NetworkCallback callbacks = getCallbacks(request);
- if (callbacks != null) {
- callbacks.onLost((Network)getObject(message, Network.class));
- } else {
- Log.e(TAG, "callback not found for LOST message");
+ NetworkCallback callback = getCallback(request, "LOST");
+ if (callback != null) {
+ callback.onLost(network);
}
break;
}
case CALLBACK_UNAVAIL: {
- NetworkRequest request = (NetworkRequest)getObject(message,
- NetworkRequest.class);
- NetworkCallback callbacks = null;
- synchronized(mCallbackMap) {
- callbacks = mCallbackMap.get(request);
- }
- if (callbacks != null) {
- callbacks.onUnavailable();
- } else {
- Log.e(TAG, "callback not found for UNAVAIL message");
+ NetworkCallback callback = getCallback(request, "UNAVAIL");
+ if (callback != null) {
+ callback.onUnavailable();
}
break;
}
case CALLBACK_CAP_CHANGED: {
- NetworkRequest request = (NetworkRequest)getObject(message,
- NetworkRequest.class);
- NetworkCallback callbacks = getCallbacks(request);
- if (callbacks != null) {
- Network network = (Network)getObject(message, Network.class);
+ NetworkCallback callback = getCallback(request, "CAP_CHANGED");
+ if (callback != null) {
NetworkCapabilities cap = (NetworkCapabilities)getObject(message,
NetworkCapabilities.class);
- callbacks.onCapabilitiesChanged(network, cap);
- } else {
- Log.e(TAG, "callback not found for CAP_CHANGED message");
+ callback.onCapabilitiesChanged(network, cap);
}
break;
}
case CALLBACK_IP_CHANGED: {
- NetworkRequest request = (NetworkRequest)getObject(message,
- NetworkRequest.class);
- NetworkCallback callbacks = getCallbacks(request);
- if (callbacks != null) {
- Network network = (Network)getObject(message, Network.class);
+ NetworkCallback callback = getCallback(request, "IP_CHANGED");
+ if (callback != null) {
LinkProperties lp = (LinkProperties)getObject(message,
LinkProperties.class);
- callbacks.onLinkPropertiesChanged(network, lp);
- } else {
- Log.e(TAG, "callback not found for IP_CHANGED message");
+ callback.onLinkPropertiesChanged(network, lp);
}
break;
}
case CALLBACK_RELEASED: {
- NetworkRequest req = (NetworkRequest)getObject(message, NetworkRequest.class);
- NetworkCallback callbacks = null;
+ NetworkCallback callback = null;
synchronized(mCallbackMap) {
- callbacks = mCallbackMap.remove(req);
+ callback = mCallbackMap.remove(request);
}
- if (callbacks != null) {
+ if (callback != null) {
synchronized(mRefCount) {
if (mRefCount.decrementAndGet() == 0) {
getLooper().quit();
}
}
} else {
- Log.e(TAG, "callback not found for CANCELED message");
+ Log.e(TAG, "callback not found for RELEASED message");
}
break;
}
case CALLBACK_EXIT: {
- Log.d(TAG, "Listener quiting");
+ Log.d(TAG, "Listener quitting");
getLooper().quit();
break;
}
@@ -2374,10 +2353,16 @@ public class ConnectivityManager {
private Object getObject(Message msg, Class c) {
return msg.getData().getParcelable(c.getSimpleName());
}
- private NetworkCallback getCallbacks(NetworkRequest req) {
+
+ private NetworkCallback getCallback(NetworkRequest req, String name) {
+ NetworkCallback callback;
synchronized(mCallbackMap) {
- return mCallbackMap.get(req);
+ callback = mCallbackMap.get(req);
+ }
+ if (callback == null) {
+ Log.e(TAG, "callback not found for " + name + " message");
}
+ return callback;
}
}
@@ -2586,10 +2571,8 @@ public class ConnectivityManager {
* method assumes that the caller has previously called {@link #registerNetworkCallback} to
* listen for network changes.
*
- * @param network{@link Network} specifying which network you're interested.
+ * @param network {@link Network} specifying which network you're interested.
* @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
- *
- * @hide
*/
public boolean requestBandwidthUpdate(Network network) {
try {
@@ -2601,10 +2584,10 @@ public class ConnectivityManager {
/**
* Unregisters callbacks about and possibly releases networks originating from
- * {@link #requestNetwork} and {@link #registerNetworkCallback} calls. If the
- * given {@code NetworkCallback} had previously been used with {@code #requestNetwork},
- * any networks that had been connected to only to satisfy that request will be
- * disconnected.
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)} and {@link #registerNetworkCallback}
+ * calls. If the given {@code NetworkCallback} had previously been used with
+ * {@code #requestNetwork}, any networks that had been connected to only to satisfy that request
+ * will be disconnected.
*
* @param networkCallback The {@link NetworkCallback} used when making the request.
*/
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index c722fbc..7f5f377 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -38,8 +38,6 @@ interface INetworkPolicyManager {
boolean isUidForeground(int uid);
- int[] getPowerSaveAppIdWhitelist();
-
void registerListener(INetworkPolicyListener listener);
void unregisterListener(INetworkPolicyListener listener);
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 8c8bfab..ab70485 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -37,6 +37,11 @@ public final class NetworkCapabilities implements Parcelable {
* @hide
*/
public NetworkCapabilities() {
+ clearAll();
+ mNetworkCapabilities =
+ (1 << NET_CAPABILITY_NOT_RESTRICTED) |
+ (1 << NET_CAPABILITY_TRUSTED) |
+ (1 << NET_CAPABILITY_NOT_VPN);
}
public NetworkCapabilities(NetworkCapabilities nc) {
@@ -50,11 +55,21 @@ public final class NetworkCapabilities implements Parcelable {
}
/**
+ * Completely clears the contents of this object, removing even the capabilities that are set
+ * by default when the object is constructed.
+ * @hide
+ */
+ public void clearAll() {
+ mNetworkCapabilities = mTransportTypes = 0;
+ mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = 0;
+ mNetworkSpecifier = null;
+ }
+
+ /**
* Represents the network's capabilities. If any are specified they will be satisfied
* by any Network that matches all of them.
*/
- private long mNetworkCapabilities = (1 << NET_CAPABILITY_NOT_RESTRICTED) |
- (1 << NET_CAPABILITY_TRUSTED) | (1 << NET_CAPABILITY_NOT_VPN);
+ private long mNetworkCapabilities;
/**
* Indicates this is a network that has the ability to reach the
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index bc03637..ecc3fb4 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -41,6 +41,7 @@ import java.util.HashSet;
*/
public class NetworkPolicyManager {
+ /* POLICY_* are masks and can be ORed */
/** No specific network policy, use system default. */
public static final int POLICY_NONE = 0x0;
/** Reject network usage on metered networks when application in background. */
@@ -48,10 +49,17 @@ public class NetworkPolicyManager {
/** Allow network use (metered or not) in the background in battery save mode. */
public static final int POLICY_ALLOW_BACKGROUND_BATTERY_SAVE = 0x2;
+ /* RULE_* are not masks and they must be exclusive */
/** All network traffic should be allowed. */
public static final int RULE_ALLOW_ALL = 0x0;
/** Reject traffic on metered networks. */
public static final int RULE_REJECT_METERED = 0x1;
+ /** Reject traffic on all networks. */
+ public static final int RULE_REJECT_ALL = 0x2;
+
+ public static final int FIREWALL_RULE_DEFAULT = 0;
+ public static final int FIREWALL_RULE_ALLOW = 1;
+ public static final int FIREWALL_RULE_DENY = 2;
private static final boolean ALLOW_PLATFORM_APP_POLICY = true;
@@ -80,7 +88,7 @@ public class NetworkPolicyManager {
* Set policy flags for specific UID.
*
* @param policy {@link #POLICY_NONE} or combination of flags like
- * {@link #POLICY_REJECT_METERED_BACKGROUND}, {@link #POLICY_ALLOW_BACKGROUND_BATTERY_SAVE}.
+ * {@link #POLICY_REJECT_METERED_BACKGROUND} or {@link #POLICY_ALLOW_BACKGROUND_BATTERY_SAVE}.
*/
public void setUidPolicy(int uid, int policy) {
try {
@@ -129,14 +137,6 @@ public class NetworkPolicyManager {
}
}
- public int[] getPowerSaveAppIdWhitelist() {
- try {
- return mService.getPowerSaveAppIdWhitelist();
- } catch (RemoteException e) {
- return new int[0];
- }
- }
-
public void registerListener(INetworkPolicyListener listener) {
try {
mService.registerListener(listener);
@@ -330,6 +330,8 @@ public class NetworkPolicyManager {
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/Uri.java b/core/java/android/net/Uri.java
index f305b2a..4a8dfbc 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -1657,7 +1657,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
/**
* Searches the query string for the first value with the given key.
*
- * <p><strong>Warning:</strong> Prior to Ice Cream Sandwich, this decoded
+ * <p><strong>Warning:</strong> Prior to Jelly Bean, this decoded
* the '+' character as '+' rather than ' '.
*
* @param key which will be encoded
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 47e8e69..243ddf7 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -16,6 +16,9 @@
package android.os;
+import android.annotation.MainThread;
+import android.annotation.WorkerThread;
+
import java.util.ArrayDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
@@ -350,6 +353,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #onPostExecute
* @see #publishProgress
*/
+ @WorkerThread
protected abstract Result doInBackground(Params... params);
/**
@@ -358,6 +362,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #onPostExecute
* @see #doInBackground
*/
+ @MainThread
protected void onPreExecute() {
}
@@ -374,6 +379,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #onCancelled(Object)
*/
@SuppressWarnings({"UnusedDeclaration"})
+ @MainThread
protected void onPostExecute(Result result) {
}
@@ -387,6 +393,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #doInBackground
*/
@SuppressWarnings({"UnusedDeclaration"})
+ @MainThread
protected void onProgressUpdate(Progress... values) {
}
@@ -405,6 +412,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #isCancelled()
*/
@SuppressWarnings({"UnusedParameters"})
+ @MainThread
protected void onCancelled(Result result) {
onCancelled();
}
@@ -421,6 +429,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #cancel(boolean)
* @see #isCancelled()
*/
+ @MainThread
protected void onCancelled() {
}
@@ -535,6 +544,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
* @see #execute(Runnable)
*/
+ @MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
@@ -572,6 +582,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
*
* @see #execute(Object[])
*/
+ @MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
@@ -604,6 +615,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #execute(Object[])
* @see #executeOnExecutor(java.util.concurrent.Executor, Object[])
*/
+ @MainThread
public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}
@@ -622,6 +634,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
* @see #onProgressUpdate
* @see #doInBackground
*/
+ @WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 5e9b8c1..74699fd 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -211,8 +211,9 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
} else if (obj instanceof Parcelable[]) {
Parcelable[] array = (Parcelable[]) obj;
for (int n = array.length - 1; n >= 0; n--) {
- if ((array[n].describeContents()
- & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
+ Parcelable p = array[n];
+ if (p != null && ((p.describeContents()
+ & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0)) {
fdFound = true;
break;
}
@@ -221,7 +222,8 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
SparseArray<? extends Parcelable> array =
(SparseArray<? extends Parcelable>) obj;
for (int n = array.size() - 1; n >= 0; n--) {
- if ((array.valueAt(n).describeContents()
+ Parcelable p = array.valueAt(n);
+ if (p != null && (p.describeContents()
& Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
fdFound = true;
break;
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 2eb97f1..8e0584a 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -20,6 +20,7 @@ import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
+import android.text.TextUtils;
import android.util.Log;
import java.io.File;
@@ -242,6 +243,15 @@ public class Environment {
return DATA_DIRECTORY;
}
+ /** {@hide} */
+ public static File getDataAppDirectory(String volumeUuid) {
+ if (TextUtils.isEmpty(volumeUuid)) {
+ return new File("/data/app");
+ } else {
+ return new File("/mnt/expand/" + volumeUuid + "/app");
+ }
+ }
+
/**
* Return the primary external storage directory. This directory may not
* currently be accessible if it has been mounted by the user on their
@@ -758,7 +768,6 @@ public class Environment {
* @hide
*/
public static File maybeTranslateEmulatedPathToInternal(File path) {
- // TODO: bring back this optimization
- return path;
+ return StorageManager.maybeTranslateEmulatedPathToInternal(path);
}
}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index 931cd3e..021e5e4 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -390,7 +390,7 @@ public class FileUtils {
* attacks.
*/
public static boolean contains(File dir, File file) {
- if (file == null) return false;
+ if (dir == null || file == null) return false;
String dirPath = dir.getAbsolutePath();
String filePath = file.getAbsolutePath();
diff --git a/core/java/android/os/IDeviceIdleController.aidl b/core/java/android/os/IDeviceIdleController.aidl
new file mode 100644
index 0000000..3cb29ff
--- /dev/null
+++ b/core/java/android/os/IDeviceIdleController.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.os;
+
+/** @hide */
+interface IDeviceIdleController {
+ void addPowerSaveWhitelistApp(String name);
+ void removePowerSaveWhitelistApp(String name);
+ String[] getSystemPowerWhitelist();
+ String[] getFullPowerWhitelist();
+ int[] getAppIdWhitelist();
+}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index f93550a..b29e8d0 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -342,7 +342,7 @@ interface INetworkManagementService
void setFirewallInterfaceRule(String iface, boolean allow);
void setFirewallEgressSourceRule(String addr, boolean allow);
void setFirewallEgressDestRule(String addr, int port, boolean allow);
- void setFirewallUidRule(int uid, boolean allow);
+ void setFirewallUidRule(int uid, int rule);
/**
* Set all packets from users in ranges to go through VPN specified by netId.
diff --git a/core/java/android/os/IPermissionController.aidl b/core/java/android/os/IPermissionController.aidl
index 73a68f1..5e8590a 100644
--- a/core/java/android/os/IPermissionController.aidl
+++ b/core/java/android/os/IPermissionController.aidl
@@ -20,4 +20,6 @@ package android.os;
/** @hide */
interface IPermissionController {
boolean checkPermission(String permission, int pid, int uid);
+ String[] getPackagesForUid(int uid);
+ boolean isRuntimePermission(String permission);
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 8c1f44f..1273772 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -502,7 +502,25 @@ public final class Parcel {
* {@SystemApi}
*/
public final void writeBlob(byte[] b) {
- nativeWriteBlob(mNativePtr, b, 0, (b != null) ? b.length : 0);
+ writeBlob(b, 0, (b != null) ? b.length : 0);
+ }
+
+ /**
+ * Write a blob of data into the parcel at the current {@link #dataPosition},
+ * growing {@link #dataCapacity} if needed.
+ * @param b Bytes to place into the parcel.
+ * @param offset Index of first byte to be written.
+ * @param len Number of bytes to write.
+ * {@hide}
+ * {@SystemApi}
+ */
+ public final void writeBlob(byte[] b, int offset, int len) {
+ if (b == null) {
+ writeInt(-1);
+ return;
+ }
+ Arrays.checkOffsetAndCount(b.length, offset, len);
+ nativeWriteBlob(mNativePtr, b, offset, len);
}
/**
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 01c9a21..1d9d7d2 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -920,6 +920,14 @@ public final class PowerManager {
= "android.os.action.DEVICE_IDLE_MODE_CHANGED";
/**
+ * @hide Intent that is broadcast when the set of power save whitelist apps has changed.
+ * This broadcast is only sent to registered receivers.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_POWER_SAVE_WHITELIST_CHANGED
+ = "android.os.action.POWER_SAVE_WHITELIST_CHANGED";
+
+ /**
* Intent that is broadcast when the state of {@link #isPowerSaveMode()} is about to change.
* This broadcast is only sent to registered receivers.
*
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 355ec8c..009649f 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -634,6 +634,9 @@ public class Process {
if ((debugFlags & Zygote.DEBUG_ENABLE_JIT) != 0) {
argsForZygote.add("--enable-jit");
}
+ if ((debugFlags & Zygote.DEBUG_GENERATE_CFI) != 0) {
+ argsForZygote.add("--generate-cfi");
+ }
if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
argsForZygote.add("--enable-assert");
}
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 16e0bf7..fcde3f4 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -942,6 +942,24 @@ public interface IMountService extends IInterface {
}
@Override
+ public VolumeRecord[] getVolumeRecords(int _flags) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ VolumeRecord[] _result;
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(_flags);
+ mRemote.transact(Stub.TRANSACTION_getVolumeRecords, _data, _reply, 0);
+ _reply.readException();
+ _result = _reply.createTypedArray(VolumeRecord.CREATOR);
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ return _result;
+ }
+
+ @Override
public void mount(String volId) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
@@ -1033,12 +1051,12 @@ public interface IMountService extends IInterface {
}
@Override
- public void setVolumeNickname(String volId, String nickname) throws RemoteException {
+ public void setVolumeNickname(String fsUuid, String nickname) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeString(volId);
+ _data.writeString(fsUuid);
_data.writeString(nickname);
mRemote.transact(Stub.TRANSACTION_setVolumeNickname, _data, _reply, 0);
_reply.readException();
@@ -1049,12 +1067,12 @@ public interface IMountService extends IInterface {
}
@Override
- public void setVolumeUserFlags(String volId, int flags, int mask) throws RemoteException {
+ public void setVolumeUserFlags(String fsUuid, int flags, int mask) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeString(volId);
+ _data.writeString(fsUuid);
_data.writeInt(flags);
_data.writeInt(mask);
mRemote.transact(Stub.TRANSACTION_setVolumeUserFlags, _data, _reply, 0);
@@ -1066,6 +1084,21 @@ public interface IMountService extends IInterface {
}
@Override
+ public void forgetVolume(String fsUuid) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeString(fsUuid);
+ mRemote.transact(Stub.TRANSACTION_forgetVolume, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
public String getPrimaryStorageUuid() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
@@ -1192,20 +1225,22 @@ public interface IMountService extends IInterface {
static final int TRANSACTION_getDisks = IBinder.FIRST_CALL_TRANSACTION + 44;
static final int TRANSACTION_getVolumes = IBinder.FIRST_CALL_TRANSACTION + 45;
+ static final int TRANSACTION_getVolumeRecords = IBinder.FIRST_CALL_TRANSACTION + 46;
- static final int TRANSACTION_mount = IBinder.FIRST_CALL_TRANSACTION + 46;
- static final int TRANSACTION_unmount = IBinder.FIRST_CALL_TRANSACTION + 47;
- static final int TRANSACTION_format = IBinder.FIRST_CALL_TRANSACTION + 48;
+ static final int TRANSACTION_mount = IBinder.FIRST_CALL_TRANSACTION + 47;
+ static final int TRANSACTION_unmount = IBinder.FIRST_CALL_TRANSACTION + 48;
+ static final int TRANSACTION_format = IBinder.FIRST_CALL_TRANSACTION + 49;
- static final int TRANSACTION_partitionPublic = IBinder.FIRST_CALL_TRANSACTION + 49;
- static final int TRANSACTION_partitionPrivate = IBinder.FIRST_CALL_TRANSACTION + 50;
- static final int TRANSACTION_partitionMixed = IBinder.FIRST_CALL_TRANSACTION + 51;
+ static final int TRANSACTION_partitionPublic = IBinder.FIRST_CALL_TRANSACTION + 50;
+ static final int TRANSACTION_partitionPrivate = IBinder.FIRST_CALL_TRANSACTION + 51;
+ static final int TRANSACTION_partitionMixed = IBinder.FIRST_CALL_TRANSACTION + 52;
- static final int TRANSACTION_setVolumeNickname = IBinder.FIRST_CALL_TRANSACTION + 52;
- static final int TRANSACTION_setVolumeUserFlags = IBinder.FIRST_CALL_TRANSACTION + 53;
+ static final int TRANSACTION_setVolumeNickname = IBinder.FIRST_CALL_TRANSACTION + 53;
+ static final int TRANSACTION_setVolumeUserFlags = IBinder.FIRST_CALL_TRANSACTION + 54;
+ static final int TRANSACTION_forgetVolume = IBinder.FIRST_CALL_TRANSACTION + 55;
- static final int TRANSACTION_getPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 54;
- static final int TRANSACTION_setPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 55;
+ static final int TRANSACTION_getPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 56;
+ static final int TRANSACTION_setPrimaryStorageUuid = IBinder.FIRST_CALL_TRANSACTION + 57;
/**
* Cast an IBinder object into an IMountService interface, generating a
@@ -1647,6 +1682,14 @@ public interface IMountService extends IInterface {
reply.writeTypedArray(volumes, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
return true;
}
+ case TRANSACTION_getVolumeRecords: {
+ data.enforceInterface(DESCRIPTOR);
+ int _flags = data.readInt();
+ VolumeRecord[] volumes = getVolumeRecords(_flags);
+ reply.writeNoException();
+ reply.writeTypedArray(volumes, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ return true;
+ }
case TRANSACTION_mount: {
data.enforceInterface(DESCRIPTOR);
String volId = data.readString();
@@ -1707,6 +1750,13 @@ public interface IMountService extends IInterface {
reply.writeNoException();
return true;
}
+ case TRANSACTION_forgetVolume: {
+ data.enforceInterface(DESCRIPTOR);
+ String fsUuid = data.readString();
+ forgetVolume(fsUuid);
+ reply.writeNoException();
+ return true;
+ }
case TRANSACTION_getPrimaryStorageUuid: {
data.enforceInterface(DESCRIPTOR);
String volumeUuid = getPrimaryStorageUuid();
@@ -2012,6 +2062,7 @@ public interface IMountService extends IInterface {
public DiskInfo[] getDisks() throws RemoteException;
public VolumeInfo[] getVolumes(int flags) throws RemoteException;
+ public VolumeRecord[] getVolumeRecords(int flags) throws RemoteException;
public void mount(String volId) throws RemoteException;
public void unmount(String volId) throws RemoteException;
@@ -2021,8 +2072,9 @@ public interface IMountService extends IInterface {
public void partitionPrivate(String diskId) throws RemoteException;
public void partitionMixed(String diskId, int ratio) throws RemoteException;
- public void setVolumeNickname(String volId, String nickname) throws RemoteException;
- public void setVolumeUserFlags(String volId, int flags, int mask) throws RemoteException;
+ public void setVolumeNickname(String fsUuid, String nickname) throws RemoteException;
+ public void setVolumeUserFlags(String fsUuid, int flags, int mask) throws RemoteException;
+ public void forgetVolume(String fsUuid) throws RemoteException;
public String getPrimaryStorageUuid() throws RemoteException;
public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
diff --git a/core/java/android/os/storage/IMountServiceListener.java b/core/java/android/os/storage/IMountServiceListener.java
index fcb4779..c958fb0 100644
--- a/core/java/android/os/storage/IMountServiceListener.java
+++ b/core/java/android/os/storage/IMountServiceListener.java
@@ -91,10 +91,17 @@ public interface IMountServiceListener extends IInterface {
reply.writeNoException();
return true;
}
- case TRANSACTION_onVolumeMetadataChanged: {
+ case TRANSACTION_onVolumeRecordChanged: {
data.enforceInterface(DESCRIPTOR);
- final VolumeInfo vol = (VolumeInfo) data.readParcelable(null);
- onVolumeMetadataChanged(vol);
+ final VolumeRecord rec = (VolumeRecord) data.readParcelable(null);
+ onVolumeRecordChanged(rec);
+ reply.writeNoException();
+ return true;
+ }
+ case TRANSACTION_onVolumeForgotten: {
+ data.enforceInterface(DESCRIPTOR);
+ final String fsUuid = data.readString();
+ onVolumeForgotten(fsUuid);
reply.writeNoException();
return true;
}
@@ -192,13 +199,29 @@ public interface IMountServiceListener extends IInterface {
}
@Override
- public void onVolumeMetadataChanged(VolumeInfo vol) throws RemoteException {
+ public void onVolumeRecordChanged(VolumeRecord rec) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeParcelable(vol, 0);
- mRemote.transact(Stub.TRANSACTION_onVolumeMetadataChanged, _data, _reply,
+ _data.writeParcelable(rec, 0);
+ mRemote.transact(Stub.TRANSACTION_onVolumeRecordChanged, _data, _reply,
+ android.os.IBinder.FLAG_ONEWAY);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
+ public void onVolumeForgotten(String fsUuid) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeString(fsUuid);
+ mRemote.transact(Stub.TRANSACTION_onVolumeForgotten, _data, _reply,
android.os.IBinder.FLAG_ONEWAY);
_reply.readException();
} finally {
@@ -228,8 +251,9 @@ public interface IMountServiceListener extends IInterface {
static final int TRANSACTION_onUsbMassStorageConnectionChanged = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_onStorageStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_onVolumeStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 2);
- static final int TRANSACTION_onVolumeMetadataChanged = (IBinder.FIRST_CALL_TRANSACTION + 3);
- static final int TRANSACTION_onDiskScanned = (IBinder.FIRST_CALL_TRANSACTION + 4);
+ static final int TRANSACTION_onVolumeRecordChanged = (IBinder.FIRST_CALL_TRANSACTION + 3);
+ static final int TRANSACTION_onVolumeForgotten = (IBinder.FIRST_CALL_TRANSACTION + 4);
+ static final int TRANSACTION_onDiskScanned = (IBinder.FIRST_CALL_TRANSACTION + 5);
}
/**
@@ -252,8 +276,8 @@ public interface IMountServiceListener extends IInterface {
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState)
throws RemoteException;
-
- public void onVolumeMetadataChanged(VolumeInfo vol) throws RemoteException;
+ public void onVolumeRecordChanged(VolumeRecord rec) throws RemoteException;
+ public void onVolumeForgotten(String fsUuid) throws RemoteException;
public void onDiskScanned(DiskInfo disk, int volumeCount) throws RemoteException;
}
diff --git a/core/java/android/os/storage/StorageEventListener.java b/core/java/android/os/storage/StorageEventListener.java
index 6a0140e..214c60d 100644
--- a/core/java/android/os/storage/StorageEventListener.java
+++ b/core/java/android/os/storage/StorageEventListener.java
@@ -41,7 +41,10 @@ public class StorageEventListener {
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
}
- public void onVolumeMetadataChanged(VolumeInfo vol) {
+ public void onVolumeRecordChanged(VolumeRecord rec) {
+ }
+
+ public void onVolumeForgotten(String fsUuid) {
}
public void onDiskScanned(DiskInfo disk, int volumeCount) {
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 6116aef..3fdabee 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -34,6 +34,7 @@ import android.os.ServiceManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.os.SomeArgs;
@@ -79,9 +80,6 @@ public class StorageManager {
/** {@hide} */
public static final String UUID_PRIMARY_PHYSICAL = "primary_physical";
- /** {@hide} */
- public static final int FLAG_ALL_METADATA = 1 << 0;
-
private final Context mContext;
private final ContentResolver mResolver;
@@ -95,8 +93,9 @@ public class StorageManager {
Handler.Callback {
private static final int MSG_STORAGE_STATE_CHANGED = 1;
private static final int MSG_VOLUME_STATE_CHANGED = 2;
- private static final int MSG_VOLUME_METADATA_CHANGED = 3;
- private static final int MSG_DISK_SCANNED = 4;
+ private static final int MSG_VOLUME_RECORD_CHANGED = 3;
+ private static final int MSG_VOLUME_FORGOTTEN = 4;
+ private static final int MSG_DISK_SCANNED = 5;
final StorageEventListener mCallback;
final Handler mHandler;
@@ -119,8 +118,12 @@ public class StorageManager {
mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
args.recycle();
return true;
- case MSG_VOLUME_METADATA_CHANGED:
- mCallback.onVolumeMetadataChanged((VolumeInfo) args.arg1);
+ case MSG_VOLUME_RECORD_CHANGED:
+ mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1);
+ args.recycle();
+ return true;
+ case MSG_VOLUME_FORGOTTEN:
+ mCallback.onVolumeForgotten((String) args.arg1);
args.recycle();
return true;
case MSG_DISK_SCANNED:
@@ -156,10 +159,17 @@ public class StorageManager {
}
@Override
- public void onVolumeMetadataChanged(VolumeInfo vol) {
+ public void onVolumeRecordChanged(VolumeRecord rec) {
final SomeArgs args = SomeArgs.obtain();
- args.arg1 = vol;
- mHandler.obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget();
+ args.arg1 = rec;
+ mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget();
+ }
+
+ @Override
+ public void onVolumeForgotten(String fsUuid) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = fsUuid;
+ mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget();
}
@Override
@@ -248,8 +258,9 @@ public class StorageManager {
}
/** {@hide} */
+ @Deprecated
public static StorageManager from(Context context) {
- return (StorageManager) context.getSystemService(Context.STORAGE_SERVICE);
+ return context.getSystemService(StorageManager.class);
}
/**
@@ -516,6 +527,18 @@ public class StorageManager {
}
/** {@hide} */
+ public @Nullable VolumeRecord findRecordByUuid(String fsUuid) {
+ Preconditions.checkNotNull(fsUuid);
+ // TODO; go directly to service to make this faster
+ for (VolumeRecord rec : getVolumeRecords()) {
+ if (Objects.equals(rec.fsUuid, fsUuid)) {
+ return rec;
+ }
+ }
+ return null;
+ }
+
+ /** {@hide} */
public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) {
return findVolumeById(emulatedVol.getId().replace("emulated", "private"));
}
@@ -526,14 +549,29 @@ public class StorageManager {
}
/** {@hide} */
+ public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) {
+ if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) {
+ return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL);
+ } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) {
+ return getPrimaryPhysicalVolume();
+ } else {
+ return findVolumeByUuid(volumeUuid);
+ }
+ }
+
+ /** {@hide} */
public @NonNull List<VolumeInfo> getVolumes() {
- return getVolumes(0);
+ try {
+ return Arrays.asList(mMountService.getVolumes(0));
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
}
/** {@hide} */
- public @NonNull List<VolumeInfo> getVolumes(int flags) {
+ public @NonNull List<VolumeRecord> getVolumeRecords() {
try {
- return Arrays.asList(mMountService.getVolumes(flags));
+ return Arrays.asList(mMountService.getVolumeRecords(0));
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -541,13 +579,25 @@ public class StorageManager {
/** {@hide} */
public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
- String descrip = vol.getDescription();
- if (vol.disk != null) {
- if (TextUtils.isEmpty(descrip)) {
- descrip = vol.disk.getDescription();
+ if (vol == null) return null;
+
+ // Nickname always takes precedence when defined
+ if (!TextUtils.isEmpty(vol.fsUuid)) {
+ final VolumeRecord rec = findRecordByUuid(vol.fsUuid);
+ if (rec != null && !TextUtils.isEmpty(rec.nickname)) {
+ return rec.nickname;
}
}
- return descrip;
+
+ if (!TextUtils.isEmpty(vol.getDescription())) {
+ return vol.getDescription();
+ }
+
+ if (vol.disk != null) {
+ return vol.disk.getDescription();
+ }
+
+ return null;
}
/** {@hide} */
@@ -616,29 +666,62 @@ public class StorageManager {
}
/** {@hide} */
- public void setVolumeNickname(String volId, String nickname) {
+ public void wipeAdoptableDisks() {
+ // We only wipe devices in "adoptable" locations, which are in a
+ // long-term stable slot/location on the device, where apps have a
+ // reasonable chance of storing sensitive data. (Apps need to go through
+ // SAF to write to transient volumes.)
+ final List<DiskInfo> disks = getDisks();
+ for (DiskInfo disk : disks) {
+ final String diskId = disk.getId();
+ if (disk.isAdoptable()) {
+ Slog.d(TAG, "Found adoptable " + diskId + "; wiping");
+ try {
+ // TODO: switch to explicit wipe command when we have it,
+ // for now rely on the fact that vfat format does a wipe
+ mMountService.partitionPublic(diskId);
+ } catch (Exception e) {
+ Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e);
+ }
+ } else {
+ Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId());
+ }
+ }
+ }
+
+ /** {@hide} */
+ public void setVolumeNickname(String fsUuid, String nickname) {
try {
- mMountService.setVolumeNickname(volId, nickname);
+ mMountService.setVolumeNickname(fsUuid, nickname);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/** {@hide} */
- public void setVolumeInited(String volId, boolean inited) {
+ public void setVolumeInited(String fsUuid, boolean inited) {
try {
- mMountService.setVolumeUserFlags(volId, inited ? VolumeInfo.USER_FLAG_INITED : 0,
- VolumeInfo.USER_FLAG_INITED);
+ mMountService.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0,
+ VolumeRecord.USER_FLAG_INITED);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/** {@hide} */
- public void setVolumeSnoozed(String volId, boolean snoozed) {
+ public void setVolumeSnoozed(String fsUuid, boolean snoozed) {
try {
- mMountService.setVolumeUserFlags(volId, snoozed ? VolumeInfo.USER_FLAG_SNOOZED : 0,
- VolumeInfo.USER_FLAG_SNOOZED);
+ mMountService.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0,
+ VolumeRecord.USER_FLAG_SNOOZED);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public void forgetVolume(String fsUuid) {
+ try {
+ mMountService.forgetVolume(fsUuid);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -804,6 +887,27 @@ public class StorageManager {
DEFAULT_FULL_THRESHOLD_BYTES);
}
+ /** {@hide} */
+ public static File maybeTranslateEmulatedPathToInternal(File path) {
+ final IMountService mountService = IMountService.Stub.asInterface(
+ ServiceManager.getService("mount"));
+ try {
+ final VolumeInfo[] vols = mountService.getVolumes(0);
+ for (VolumeInfo vol : vols) {
+ if ((vol.getType() == VolumeInfo.TYPE_EMULATED
+ || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) {
+ final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(),
+ vol.getInternalPath(), path);
+ if (internalPath != null) {
+ return internalPath;
+ }
+ }
+ }
+ } catch (RemoteException ignored) {
+ }
+ return path;
+ }
+
/// Consts to match the password types in cryptfs.h
/** @hide */
public static final int CRYPT_TYPE_PASSWORD = 0;
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 4e9cfc7..2622ee0 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -78,9 +78,6 @@ public class VolumeInfo implements Parcelable {
public static final int MOUNT_FLAG_PRIMARY = 1 << 0;
public static final int MOUNT_FLAG_VISIBLE = 1 << 1;
- public static final int USER_FLAG_INITED = 1 << 0;
- public static final int USER_FLAG_SNOOZED = 1 << 1;
-
private static SparseArray<String> sStateToEnvironment = new SparseArray<>();
private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>();
@@ -132,11 +129,10 @@ public class VolumeInfo implements Parcelable {
public String fsUuid;
public String fsLabel;
public String path;
+ public String internalPath;
/** Framework state */
public final int mtpIndex;
- public String nickname;
- public int userFlags = 0;
public VolumeInfo(String id, int type, DiskInfo disk, int mtpIndex) {
this.id = Preconditions.checkNotNull(id);
@@ -160,9 +156,8 @@ public class VolumeInfo implements Parcelable {
fsUuid = parcel.readString();
fsLabel = parcel.readString();
path = parcel.readString();
+ internalPath = parcel.readString();
mtpIndex = parcel.readInt();
- nickname = parcel.readString();
- userFlags = parcel.readInt();
}
public static @NonNull String getEnvironmentForState(int state) {
@@ -210,10 +205,6 @@ public class VolumeInfo implements Parcelable {
return fsUuid;
}
- public @Nullable String getNickname() {
- return nickname;
- }
-
public int getMountUserId() {
return mountUserId;
}
@@ -221,8 +212,6 @@ public class VolumeInfo implements Parcelable {
public @Nullable String getDescription() {
if (ID_PRIVATE_INTERNAL.equals(id)) {
return Resources.getSystem().getString(com.android.internal.R.string.storage_internal);
- } else if (!TextUtils.isEmpty(nickname)) {
- return nickname;
} else if (!TextUtils.isEmpty(fsLabel)) {
return fsLabel;
} else {
@@ -250,14 +239,6 @@ public class VolumeInfo implements Parcelable {
return (mountFlags & MOUNT_FLAG_VISIBLE) != 0;
}
- public boolean isInited() {
- return (userFlags & USER_FLAG_INITED) != 0;
- }
-
- public boolean isSnoozed() {
- return (userFlags & USER_FLAG_SNOOZED) != 0;
- }
-
public boolean isVisibleToUser(int userId) {
if (type == TYPE_PUBLIC && userId == this.mountUserId) {
return isVisible();
@@ -269,7 +250,11 @@ public class VolumeInfo implements Parcelable {
}
public File getPath() {
- return new File(path);
+ return (path != null) ? new File(path) : null;
+ }
+
+ public File getInternalPath() {
+ return (internalPath != null) ? new File(internalPath) : null;
}
public File getPathForUser(int userId) {
@@ -356,14 +341,11 @@ public class VolumeInfo implements Parcelable {
final Uri uri;
if (type == VolumeInfo.TYPE_PUBLIC) {
uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, fsUuid);
- } else if (VolumeInfo.ID_EMULATED_INTERNAL.equals(id)) {
+ } else if (type == VolumeInfo.TYPE_EMULATED && isPrimary()) {
uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY,
DOCUMENT_ROOT_PRIMARY_EMULATED);
- } else if (type == VolumeInfo.TYPE_EMULATED) {
- // TODO: build intent once supported
- uri = null;
} else {
- throw new IllegalArgumentException();
+ return null;
}
final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT);
@@ -393,9 +375,8 @@ public class VolumeInfo implements Parcelable {
pw.printPair("fsLabel", fsLabel);
pw.println();
pw.printPair("path", path);
+ pw.printPair("internalPath", internalPath);
pw.printPair("mtpIndex", mtpIndex);
- pw.printPair("nickname", nickname);
- pw.printPair("userFlags", DebugUtils.flagsToString(getClass(), "USER_FLAG_", userFlags));
pw.decreaseIndent();
pw.println();
}
@@ -460,8 +441,7 @@ public class VolumeInfo implements Parcelable {
parcel.writeString(fsUuid);
parcel.writeString(fsLabel);
parcel.writeString(path);
+ parcel.writeString(internalPath);
parcel.writeInt(mtpIndex);
- parcel.writeString(nickname);
- parcel.writeInt(userFlags);
}
}
diff --git a/core/java/android/os/storage/VolumeRecord.java b/core/java/android/os/storage/VolumeRecord.java
new file mode 100644
index 0000000..096e2dd
--- /dev/null
+++ b/core/java/android/os/storage/VolumeRecord.java
@@ -0,0 +1,139 @@
+/*
+ * 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.os.storage;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.DebugUtils;
+
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Metadata for a storage volume which may not be currently present.
+ *
+ * @hide
+ */
+public class VolumeRecord implements Parcelable {
+ public static final String EXTRA_FS_UUID =
+ "android.os.storage.extra.FS_UUID";
+
+ public static final int USER_FLAG_INITED = 1 << 0;
+ public static final int USER_FLAG_SNOOZED = 1 << 1;
+
+ public final int type;
+ public final String fsUuid;
+ public String nickname;
+ public int userFlags;
+
+ public VolumeRecord(int type, String fsUuid) {
+ this.type = type;
+ this.fsUuid = Preconditions.checkNotNull(fsUuid);
+ }
+
+ public VolumeRecord(Parcel parcel) {
+ type = parcel.readInt();
+ fsUuid = parcel.readString();
+ nickname = parcel.readString();
+ userFlags = parcel.readInt();
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public String getFsUuid() {
+ return fsUuid;
+ }
+
+ public String getNickname() {
+ return nickname;
+ }
+
+ public boolean isInited() {
+ return (userFlags & USER_FLAG_INITED) != 0;
+ }
+
+ public boolean isSnoozed() {
+ return (userFlags & USER_FLAG_SNOOZED) != 0;
+ }
+
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("VolumeRecord:");
+ pw.increaseIndent();
+ pw.printPair("type", DebugUtils.valueToString(VolumeInfo.class, "TYPE_", type));
+ pw.printPair("fsUuid", fsUuid);
+ pw.printPair("nickname", nickname);
+ pw.printPair("userFlags",
+ DebugUtils.flagsToString(VolumeRecord.class, "USER_FLAG_", userFlags));
+ pw.decreaseIndent();
+ pw.println();
+ }
+
+ @Override
+ public VolumeRecord clone() {
+ final Parcel temp = Parcel.obtain();
+ try {
+ writeToParcel(temp, 0);
+ temp.setDataPosition(0);
+ return CREATOR.createFromParcel(temp);
+ } finally {
+ temp.recycle();
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof VolumeRecord) {
+ return Objects.equals(fsUuid, ((VolumeRecord) o).fsUuid);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return fsUuid.hashCode();
+ }
+
+ public static final Creator<VolumeRecord> CREATOR = new Creator<VolumeRecord>() {
+ @Override
+ public VolumeRecord createFromParcel(Parcel in) {
+ return new VolumeRecord(in);
+ }
+
+ @Override
+ public VolumeRecord[] newArray(int size) {
+ return new VolumeRecord[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(type);
+ parcel.writeString(fsUuid);
+ parcel.writeString(nickname);
+ parcel.writeInt(userFlags);
+ }
+}
diff --git a/core/java/android/preference/GenericInflater.java b/core/java/android/preference/GenericInflater.java
index 918933b..3319e64 100644
--- a/core/java/android/preference/GenericInflater.java
+++ b/core/java/android/preference/GenericInflater.java
@@ -376,6 +376,7 @@ abstract class GenericInflater<T, P extends GenericInflater.Parent> {
Class clazz = mContext.getClassLoader().loadClass(
prefix != null ? (prefix + name) : name);
constructor = clazz.getConstructor(mConstructorSignature);
+ constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 396cf19..e07e846 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -9062,5 +9062,15 @@ public final class ContactsContract {
*/
public static final Uri CONTENT_URI = Uri.withAppendedPath(METADATA_AUTHORITY_URI,
"metadata_sync");
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of contact metadata
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/contact_metadata";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} subdirectory of a single contact metadata.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/contact_metadata";
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 00c851b..293cf6f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -919,6 +919,15 @@ public final class Settings {
= "android.settings.ZEN_MODE_SCHEDULE_RULE_SETTINGS";
/**
+ * Activity Action: Show Zen Mode event rule configuration settings.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_ZEN_MODE_EVENT_RULE_SETTINGS
+ = "android.settings.ZEN_MODE_EVENT_RULE_SETTINGS";
+
+ /**
* Activity Action: Show Zen Mode external rule configuration settings.
*
* @hide
diff --git a/core/java/android/security/keymaster/KeyCharacteristics.java b/core/java/android/security/keymaster/KeyCharacteristics.java
index b3a3aad..03248e5 100644
--- a/core/java/android/security/keymaster/KeyCharacteristics.java
+++ b/core/java/android/security/keymaster/KeyCharacteristics.java
@@ -87,6 +87,28 @@ public class KeyCharacteristics implements Parcelable {
return result;
}
+ public Long getLong(int tag) {
+ if (hwEnforced.containsTag(tag)) {
+ return hwEnforced.getLong(tag, -1);
+ } else if (swEnforced.containsTag(tag)) {
+ return swEnforced.getLong(tag, -1);
+ } else {
+ return null;
+ }
+ }
+
+ public long getLong(int tag, long defaultValue) {
+ Long result = getLong(tag);
+ return (result != null) ? result : defaultValue;
+ }
+
+ public List<Long> getLongs(int tag) {
+ List<Long> result = new ArrayList<Long>();
+ result.addAll(hwEnforced.getLongs(tag));
+ result.addAll(swEnforced.getLongs(tag));
+ return result;
+ }
+
public Date getDate(int tag) {
Date result = hwEnforced.getDate(tag, null);
if (result == null) {
@@ -105,11 +127,11 @@ public class KeyCharacteristics implements Parcelable {
}
}
- public boolean getBoolean(KeyCharacteristics keyCharacteristics, int tag) {
- if (keyCharacteristics.hwEnforced.containsTag(tag)) {
- return keyCharacteristics.hwEnforced.getBoolean(tag, false);
+ public boolean getBoolean(int tag) {
+ if (hwEnforced.containsTag(tag)) {
+ return hwEnforced.getBoolean(tag, false);
} else {
- return keyCharacteristics.swEnforced.getBoolean(tag, false);
+ return swEnforced.getBoolean(tag, false);
}
}
}
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 40baf9c..0e2b8ba 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -194,6 +194,9 @@ public final class KeymasterDefs {
public static final int KM_ERROR_UNSUPPORTED_EC_FIELD = -50;
public static final int KM_ERROR_MISSING_NONCE = -51;
public static final int KM_ERROR_INVALID_NONCE = -52;
+ public static final int KM_ERROR_UNSUPPORTED_CHUNK_LENGTH = -53;
+ public static final int KM_ERROR_RESCOPABLE_KEY_NOT_USABLE = -54;
+ public static final int KM_ERROR_CALLER_NONCE_PROHIBITED = -55;
public static final int KM_ERROR_UNIMPLEMENTED = -100;
public static final int KM_ERROR_VERSION_MISMATCH = -101;
public static final int KM_ERROR_UNKNOWN_ERROR = -1000;
@@ -235,6 +238,8 @@ public final class KeymasterDefs {
sErrorCodeToString.put(KM_ERROR_UNSUPPORTED_EC_FIELD, "Unsupported EC field");
sErrorCodeToString.put(KM_ERROR_MISSING_NONCE, "Required IV missing");
sErrorCodeToString.put(KM_ERROR_INVALID_NONCE, "Invalid IV");
+ sErrorCodeToString.put(KM_ERROR_CALLER_NONCE_PROHIBITED,
+ "Caller-provided IV not permitted");
sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented");
sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error");
}
diff --git a/core/java/android/service/carrier/CarrierMessagingService.java b/core/java/android/service/carrier/CarrierMessagingService.java
index 0592a84..d7bf10c 100644
--- a/core/java/android/service/carrier/CarrierMessagingService.java
+++ b/core/java/android/service/carrier/CarrierMessagingService.java
@@ -80,6 +80,11 @@ public abstract class CarrierMessagingService extends Service {
*/
public static final int DOWNLOAD_STATUS_ERROR = 2;
+ /**
+ * Flag to request SMS delivery status report.
+ */
+ public static final int SEND_FLAG_REQUEST_DELIVERY_STATUS = 1;
+
private final ICarrierMessagingWrapper mWrapper = new ICarrierMessagingWrapper();
/**
@@ -103,12 +108,14 @@ public abstract class CarrierMessagingService extends Service {
/**
* Override this method to intercept text SMSs sent from the device.
+ * @deprecated Override {@link #onSendTextSms} below instead.
*
* @param text the text to send
* @param subId SMS subscription ID of the SIM
* @param destAddress phone number of the recipient of the message
* @param callback result callback. Call with a {@link SendSmsResult}.
*/
+ @Deprecated
public void onSendTextSms(
@NonNull String text, int subId, @NonNull String destAddress,
@NonNull ResultCallback<SendSmsResult> callback) {
@@ -120,7 +127,25 @@ public abstract class CarrierMessagingService extends Service {
}
/**
+ * Override this method to intercept text SMSs sent from the device.
+ *
+ * @param text the text to send
+ * @param subId SMS subscription ID of the SIM
+ * @param destAddress phone number of the recipient of the message
+ * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and
+ * {@link #SEND_FLAG_REQUEST_DELIVERY_STATUS}.
+ * @param callback result callback. Call with a {@link SendSmsResult}.
+ */
+ public void onSendTextSms(
+ @NonNull String text, int subId, @NonNull String destAddress,
+ int sendSmsFlag, @NonNull ResultCallback<SendSmsResult> callback) {
+ // optional
+ onSendTextSms(text, subId, destAddress, callback);
+ }
+
+ /**
* Override this method to intercept binary SMSs sent from the device.
+ * @deprecated Override {@link #onSendDataSms} below instead.
*
* @param data the binary content
* @param subId SMS subscription ID of the SIM
@@ -128,6 +153,7 @@ public abstract class CarrierMessagingService extends Service {
* @param destPort the destination port
* @param callback result callback. Call with a {@link SendSmsResult}.
*/
+ @Deprecated
public void onSendDataSms(@NonNull byte[] data, int subId,
@NonNull String destAddress, int destPort,
@NonNull ResultCallback<SendSmsResult> callback) {
@@ -139,13 +165,33 @@ public abstract class CarrierMessagingService extends Service {
}
/**
+ * Override this method to intercept binary SMSs sent from the device.
+ *
+ * @param data the binary content
+ * @param subId SMS subscription ID of the SIM
+ * @param destAddress phone number of the recipient of the message
+ * @param destPort the destination port
+ * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and
+ * {@link #SEND_FLAG_REQUEST_DELIVERY_STATUS}.
+ * @param callback result callback. Call with a {@link SendSmsResult}.
+ */
+ public void onSendDataSms(@NonNull byte[] data, int subId,
+ @NonNull String destAddress, int destPort, int sendSmsFlag,
+ @NonNull ResultCallback<SendSmsResult> callback) {
+ // optional
+ onSendDataSms(data, subId, destAddress, destPort, callback);
+ }
+
+ /**
* Override this method to intercept long SMSs sent from the device.
+ * @deprecated Override {@link #onSendMultipartTextSms} below instead.
*
* @param parts a {@link List} of the message parts
* @param subId SMS subscription ID of the SIM
* @param destAddress phone number of the recipient of the message
* @param callback result callback. Call with a {@link SendMultipartSmsResult}.
*/
+ @Deprecated
public void onSendMultipartTextSms(@NonNull List<String> parts,
int subId, @NonNull String destAddress,
@NonNull ResultCallback<SendMultipartSmsResult> callback) {
@@ -158,6 +204,23 @@ public abstract class CarrierMessagingService extends Service {
}
/**
+ * Override this method to intercept long SMSs sent from the device.
+ *
+ * @param parts a {@link List} of the message parts
+ * @param subId SMS subscription ID of the SIM
+ * @param destAddress phone number of the recipient of the message
+ * @param sendSmsFlag Flag for sending SMS. Acceptable values are 0 and
+ * {@link #SEND_FLAG_REQUEST_DELIVERY_STATUS}.
+ * @param callback result callback. Call with a {@link SendMultipartSmsResult}.
+ */
+ public void onSendMultipartTextSms(@NonNull List<String> parts,
+ int subId, @NonNull String destAddress, int sendSmsFlag,
+ @NonNull ResultCallback<SendMultipartSmsResult> callback) {
+ // optional
+ onSendMultipartTextSms(parts, subId, destAddress, callback);
+ }
+
+ /**
* Override this method to intercept MMSs sent from the device.
*
* @param pduUri the content provider URI of the PDU to send
@@ -355,8 +418,9 @@ public abstract class CarrierMessagingService extends Service {
@Override
public void sendTextSms(String text, int subId, String destAddress,
- final ICarrierMessagingCallback callback) {
- onSendTextSms(text, subId, destAddress, new ResultCallback<SendSmsResult>() {
+ int sendSmsFlag, final ICarrierMessagingCallback callback) {
+ onSendTextSms(text, subId, destAddress, sendSmsFlag,
+ new ResultCallback<SendSmsResult>() {
@Override
public void onReceiveResult(final SendSmsResult result) throws RemoteException {
callback.onSendSmsComplete(result.getSendStatus(), result.getMessageRef());
@@ -366,8 +430,9 @@ public abstract class CarrierMessagingService extends Service {
@Override
public void sendDataSms(byte[] data, int subId, String destAddress, int destPort,
- final ICarrierMessagingCallback callback) {
- onSendDataSms(data, subId, destAddress, destPort, new ResultCallback<SendSmsResult>() {
+ int sendSmsFlag, final ICarrierMessagingCallback callback) {
+ onSendDataSms(data, subId, destAddress, destPort, sendSmsFlag,
+ new ResultCallback<SendSmsResult>() {
@Override
public void onReceiveResult(final SendSmsResult result) throws RemoteException {
callback.onSendSmsComplete(result.getSendStatus(), result.getMessageRef());
@@ -377,8 +442,8 @@ public abstract class CarrierMessagingService extends Service {
@Override
public void sendMultipartTextSms(List<String> parts, int subId, String destAddress,
- final ICarrierMessagingCallback callback) {
- onSendMultipartTextSms(parts, subId, destAddress,
+ int sendSmsFlag, final ICarrierMessagingCallback callback) {
+ onSendMultipartTextSms(parts, subId, destAddress, sendSmsFlag,
new ResultCallback<SendMultipartSmsResult>() {
@Override
public void onReceiveResult(final SendMultipartSmsResult result)
diff --git a/core/java/android/service/carrier/ICarrierMessagingService.aidl b/core/java/android/service/carrier/ICarrierMessagingService.aidl
index 40a9047..2d96c3d 100644
--- a/core/java/android/service/carrier/ICarrierMessagingService.aidl
+++ b/core/java/android/service/carrier/ICarrierMessagingService.aidl
@@ -48,9 +48,10 @@ oneway interface ICarrierMessagingService {
* @param text the text to send
* @param subId SMS subscription ID of the SIM
* @param destAddress phone number of the recipient of the message
+ * @param sendSmsFlag flag for sending SMS
* @param callback the callback to notify upon completion
*/
- void sendTextSms(String text, int subId, String destAddress,
+ void sendTextSms(String text, int subId, String destAddress, int sendSmsFlag,
in ICarrierMessagingCallback callback);
/**
@@ -62,10 +63,11 @@ oneway interface ICarrierMessagingService {
* @param subId SMS subscription ID of the SIM
* @param destAddress phone number of the recipient of the message
* @param destPort port number of the recipient of the message
+ * @param sendSmsFlag flag for sending SMS
* @param callback the callback to notify upon completion
*/
void sendDataSms(in byte[] data, int subId, String destAddress, int destPort,
- in ICarrierMessagingCallback callback);
+ int sendSmsFlag, in ICarrierMessagingCallback callback);
/**
* Request sending a new multi-part text SMS from the device.
@@ -75,10 +77,11 @@ oneway interface ICarrierMessagingService {
* @param parts the parts of the multi-part text SMS to send
* @param subId SMS subscription ID of the SIM
* @param destAddress phone number of the recipient of the message
+ * @param sendSmsFlag flag for sending SMS
* @param callback the callback to notify upon completion
*/
void sendMultipartTextSms(in List<String> parts, int subId, String destAddress,
- in ICarrierMessagingCallback callback);
+ int sendSmsFlag, in ICarrierMessagingCallback callback);
/**
* Request sending a new MMS PDU from the device.
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index 14e947c..26bd10f 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -55,6 +55,7 @@ public class ZenModeConfig implements Parcelable {
public static final int SOURCE_CONTACT = 1;
public static final int SOURCE_STAR = 2;
public static final int MAX_SOURCE = SOURCE_STAR;
+ private static final int DEFAULT_SOURCE = SOURCE_CONTACT;
public static final int[] ALL_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY,
Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY };
@@ -62,11 +63,13 @@ public class ZenModeConfig implements Parcelable {
Calendar.WEDNESDAY, Calendar.THURSDAY };
public static final int[] WEEKEND_DAYS = { Calendar.FRIDAY, Calendar.SATURDAY };
- public static final int[] MINUTE_BUCKETS = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 };
+ 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 ZERO_VALUE_MS = 10 * SECONDS_MS;
+ private static final boolean DEFAULT_ALLOW_CALLS = true;
+ private static final boolean DEFAULT_ALLOW_MESSAGES = false;
private static final boolean DEFAULT_ALLOW_REMINDERS = true;
private static final boolean DEFAULT_ALLOW_EVENTS = true;
private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false;
@@ -79,6 +82,8 @@ public class ZenModeConfig implements Parcelable {
private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers";
private static final String ALLOW_ATT_MESSAGES = "messages";
private static final String ALLOW_ATT_FROM = "from";
+ private static final String ALLOW_ATT_CALLS_FROM = "callsFrom";
+ private static final String ALLOW_ATT_MESSAGES_FROM = "messagesFrom";
private static final String ALLOW_ATT_REMINDERS = "reminders";
private static final String ALLOW_ATT_EVENTS = "events";
@@ -95,7 +100,7 @@ public class ZenModeConfig implements Parcelable {
private static final String MANUAL_TAG = "manual";
private static final String AUTOMATIC_TAG = "automatic";
- private static final String RULE_ATT_ID = "id";
+ private static final String RULE_ATT_ID = "ruleId";
private static final String RULE_ATT_ENABLED = "enabled";
private static final String RULE_ATT_SNOOZING = "snoozing";
private static final String RULE_ATT_NAME = "name";
@@ -103,12 +108,13 @@ public class ZenModeConfig implements Parcelable {
private static final String RULE_ATT_ZEN = "zen";
private static final String RULE_ATT_CONDITION_ID = "conditionId";
- public boolean allowCalls;
+ public boolean allowCalls = DEFAULT_ALLOW_CALLS;
public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS;
- public boolean allowMessages;
+ public boolean allowMessages = DEFAULT_ALLOW_MESSAGES;
public boolean allowReminders = DEFAULT_ALLOW_REMINDERS;
public boolean allowEvents = DEFAULT_ALLOW_EVENTS;
- public int allowFrom = SOURCE_ANYONE;
+ public int allowCallsFrom = DEFAULT_SOURCE;
+ public int allowMessagesFrom = DEFAULT_SOURCE;
public ZenRule manualRule;
public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>();
@@ -121,7 +127,8 @@ public class ZenModeConfig implements Parcelable {
allowMessages = source.readInt() == 1;
allowReminders = source.readInt() == 1;
allowEvents = source.readInt() == 1;
- allowFrom = source.readInt();
+ allowCallsFrom = source.readInt();
+ allowMessagesFrom = source.readInt();
manualRule = source.readParcelable(null);
final int len = source.readInt();
if (len > 0) {
@@ -142,7 +149,8 @@ public class ZenModeConfig implements Parcelable {
dest.writeInt(allowMessages ? 1 : 0);
dest.writeInt(allowReminders ? 1 : 0);
dest.writeInt(allowEvents ? 1 : 0);
- dest.writeInt(allowFrom);
+ dest.writeInt(allowCallsFrom);
+ dest.writeInt(allowMessagesFrom);
dest.writeParcelable(manualRule, 0);
if (!automaticRules.isEmpty()) {
final int len = automaticRules.size();
@@ -166,7 +174,8 @@ public class ZenModeConfig implements Parcelable {
.append("allowCalls=").append(allowCalls)
.append(",allowRepeatCallers=").append(allowRepeatCallers)
.append(",allowMessages=").append(allowMessages)
- .append(",allowFrom=").append(sourceToString(allowFrom))
+ .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom))
+ .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom))
.append(",allowReminders=").append(allowReminders)
.append(",allowEvents=").append(allowEvents)
.append(",automaticRules=").append(automaticRules)
@@ -201,6 +210,18 @@ public class ZenModeConfig implements Parcelable {
}
}
+ private static int[] generateMinuteBuckets() {
+ final int maxHrs = 12;
+ final int[] buckets = new int[maxHrs + 3];
+ buckets[0] = 15;
+ buckets[1] = 30;
+ buckets[2] = 45;
+ for (int i = 1; i <= maxHrs; i++) {
+ buckets[2 + i] = 60 * i;
+ }
+ return buckets;
+ }
+
public static String sourceToString(int source) {
switch (source) {
case SOURCE_ANYONE:
@@ -222,7 +243,8 @@ public class ZenModeConfig implements Parcelable {
return other.allowCalls == allowCalls
&& other.allowRepeatCallers == allowRepeatCallers
&& other.allowMessages == allowMessages
- && other.allowFrom == allowFrom
+ && other.allowCallsFrom == allowCallsFrom
+ && other.allowMessagesFrom == allowMessagesFrom
&& other.allowReminders == allowReminders
&& other.allowEvents == allowEvents
&& Objects.equals(other.automaticRules, automaticRules)
@@ -231,8 +253,8 @@ public class ZenModeConfig implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(allowCalls, allowRepeatCallers, allowMessages, allowFrom,
- allowReminders, allowEvents, automaticRules, manualRule);
+ return Objects.hash(allowCalls, allowRepeatCallers, allowMessages, allowCallsFrom,
+ allowMessagesFrom, allowReminders, allowEvents, automaticRules, manualRule);
}
private static String toDayList(int[] days) {
@@ -267,6 +289,15 @@ public class ZenModeConfig implements Parcelable {
}
}
+ private static long tryParseLong(String value, long defValue) {
+ if (TextUtils.isEmpty(value)) return defValue;
+ try {
+ return Long.valueOf(value);
+ } catch (NumberFormatException e) {
+ return defValue;
+ }
+ }
+
public static ZenModeConfig readXml(XmlPullParser parser, Migration migration)
throws XmlPullParserException, IOException {
int type = parser.getEventType();
@@ -293,15 +324,25 @@ public class ZenModeConfig implements Parcelable {
rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS,
DEFAULT_ALLOW_REMINDERS);
rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, DEFAULT_ALLOW_EVENTS);
- rt.allowFrom = safeInt(parser, ALLOW_ATT_FROM, SOURCE_ANYONE);
- if (rt.allowFrom < SOURCE_ANYONE || rt.allowFrom > MAX_SOURCE) {
- throw new IndexOutOfBoundsException("bad source in config:" + rt.allowFrom);
+ final int from = safeInt(parser, ALLOW_ATT_FROM, -1);
+ final int callsFrom = safeInt(parser, ALLOW_ATT_CALLS_FROM, -1);
+ final int messagesFrom = safeInt(parser, ALLOW_ATT_MESSAGES_FROM, -1);
+ if (isValidSource(callsFrom) && isValidSource(messagesFrom)) {
+ rt.allowCallsFrom = callsFrom;
+ rt.allowMessagesFrom = messagesFrom;
+ } else if (isValidSource(from)) {
+ Slog.i(TAG, "Migrating existing shared 'from': " + sourceToString(from));
+ rt.allowCallsFrom = from;
+ rt.allowMessagesFrom = from;
+ } else {
+ rt.allowCallsFrom = DEFAULT_SOURCE;
+ rt.allowMessagesFrom = DEFAULT_SOURCE;
}
} else if (MANUAL_TAG.equals(tag)) {
- rt.manualRule = readRuleXml(parser);
+ rt.manualRule = readRuleXml(parser, false /*conditionRequired*/);
} else if (AUTOMATIC_TAG.equals(tag)) {
final String id = parser.getAttributeValue(null, RULE_ATT_ID);
- final ZenRule automaticRule = readRuleXml(parser);
+ final ZenRule automaticRule = readRuleXml(parser, true /*conditionRequired*/);
if (id != null && automaticRule != null) {
rt.automaticRules.put(id, automaticRule);
}
@@ -321,7 +362,8 @@ public class ZenModeConfig implements Parcelable {
out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages));
out.attribute(null, ALLOW_ATT_REMINDERS, Boolean.toString(allowReminders));
out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents));
- out.attribute(null, ALLOW_ATT_FROM, Integer.toString(allowFrom));
+ out.attribute(null, ALLOW_ATT_CALLS_FROM, Integer.toString(allowCallsFrom));
+ out.attribute(null, ALLOW_ATT_MESSAGES_FROM, Integer.toString(allowMessagesFrom));
out.endTag(null, ALLOW_TAG);
if (manualRule != null) {
@@ -341,7 +383,7 @@ public class ZenModeConfig implements Parcelable {
out.endTag(null, ZEN_TAG);
}
- public static ZenRule readRuleXml(XmlPullParser parser) {
+ public static ZenRule readRuleXml(XmlPullParser parser, boolean conditionRequired) {
final ZenRule rt = new ZenRule();
rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true);
rt.snoozing = safeBoolean(parser, RULE_ATT_SNOOZING, false);
@@ -355,7 +397,7 @@ public class ZenModeConfig implements Parcelable {
rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID);
rt.component = safeComponentName(parser, RULE_ATT_COMPONENT);
rt.condition = readConditionXml(parser);
- return rt.condition != null ? rt : null;
+ return rt;
}
public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException {
@@ -411,6 +453,10 @@ public class ZenModeConfig implements Parcelable {
return val >= 0 && val < 60;
}
+ private static boolean isValidSource(int source) {
+ return source >= SOURCE_ANYONE && source <= MAX_SOURCE;
+ }
+
private static boolean safeBoolean(XmlPullParser parser, String att, boolean defValue) {
final String val = parser.getAttributeValue(null, att);
if (TextUtils.isEmpty(val)) return defValue;
@@ -473,7 +519,8 @@ public class ZenModeConfig implements Parcelable {
public Policy toNotificationPolicy() {
int priorityCategories = 0;
- int prioritySenders = Policy.PRIORITY_SENDERS_ANY;
+ int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS;
+ int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS;
if (allowCalls) {
priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS;
}
@@ -489,18 +536,27 @@ public class ZenModeConfig implements Parcelable {
if (allowRepeatCallers) {
priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS;
}
- switch (allowFrom) {
- case SOURCE_ANYONE:
- prioritySenders = Policy.PRIORITY_SENDERS_ANY;
- break;
- case SOURCE_CONTACT:
- prioritySenders = Policy.PRIORITY_SENDERS_CONTACTS;
- break;
- case SOURCE_STAR:
- prioritySenders = Policy.PRIORITY_SENDERS_STARRED;
- break;
+ priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders);
+ priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders);
+ return new Policy(priorityCategories, priorityCallSenders);
+ }
+
+ private static int sourceToPrioritySenders(int source, int def) {
+ switch (source) {
+ case SOURCE_ANYONE: return Policy.PRIORITY_SENDERS_ANY;
+ case SOURCE_CONTACT: return Policy.PRIORITY_SENDERS_CONTACTS;
+ case SOURCE_STAR: return Policy.PRIORITY_SENDERS_STARRED;
+ default: return def;
+ }
+ }
+
+ private static int prioritySendersToSource(int prioritySenders, int def) {
+ switch (prioritySenders) {
+ case Policy.PRIORITY_SENDERS_CONTACTS: return SOURCE_CONTACT;
+ case Policy.PRIORITY_SENDERS_STARRED: return SOURCE_STAR;
+ case Policy.PRIORITY_SENDERS_ANY: return SOURCE_ANYONE;
+ default: return def;
}
- return new Policy(priorityCategories, prioritySenders);
}
public void applyNotificationPolicy(Policy policy) {
@@ -511,17 +567,7 @@ public class ZenModeConfig implements Parcelable {
allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0;
allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS)
!= 0;
- switch (policy.prioritySenders) {
- case Policy.PRIORITY_SENDERS_CONTACTS:
- allowFrom = SOURCE_CONTACT;
- break;
- case Policy.PRIORITY_SENDERS_STARRED:
- allowFrom = SOURCE_STAR;
- break;
- default:
- allowFrom = SOURCE_ANYONE;
- break;
- }
+ allowCallsFrom = prioritySendersToSource(policy.prioritySenders, allowCallsFrom);
}
public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) {
@@ -556,10 +602,12 @@ public class ZenModeConfig implements Parcelable {
Condition.FLAG_RELEVANT_NOW);
}
- // For built-in conditions
+ // ==== Built-in system conditions ====
+
public static final String SYSTEM_AUTHORITY = "android";
- // Built-in countdown conditions, e.g. condition://android/countdown/1399917958951
+ // ==== Built-in system condition: countdown ====
+
public static final String COUNTDOWN_PATH = "countdown";
public static Uri toCountdownConditionId(long time) {
@@ -586,9 +634,43 @@ public class ZenModeConfig implements Parcelable {
return tryParseCountdownConditionId(conditionId) != 0;
}
- // built-in schedule conditions
+ // ==== Built-in system condition: schedule ====
+
public static final String SCHEDULE_PATH = "schedule";
+ public static Uri toScheduleConditionId(ScheduleInfo schedule) {
+ return new Uri.Builder().scheme(Condition.SCHEME)
+ .authority(SYSTEM_AUTHORITY)
+ .appendPath(SCHEDULE_PATH)
+ .appendQueryParameter("days", toDayList(schedule.days))
+ .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute)
+ .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute)
+ .build();
+ }
+
+ public static boolean isValidScheduleConditionId(Uri conditionId) {
+ return tryParseScheduleConditionId(conditionId) != null;
+ }
+
+ public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {
+ final boolean isSchedule = conditionId != null
+ && conditionId.getScheme().equals(Condition.SCHEME)
+ && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
+ && conditionId.getPathSegments().size() == 1
+ && conditionId.getPathSegments().get(0).equals(ZenModeConfig.SCHEDULE_PATH);
+ if (!isSchedule) return null;
+ final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start"));
+ final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end"));
+ if (start == null || end == null) return null;
+ final ScheduleInfo rt = new ScheduleInfo();
+ rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\.");
+ rt.startHour = start[0];
+ rt.startMinute = start[1];
+ rt.endHour = end[0];
+ rt.endMinute = end[1];
+ return rt;
+ }
+
public static class ScheduleInfo {
public int[] days;
public int startHour;
@@ -626,39 +708,67 @@ public class ZenModeConfig implements Parcelable {
}
}
- public static Uri toScheduleConditionId(ScheduleInfo schedule) {
+ // ==== Built-in system condition: event ====
+
+ public static final String EVENT_PATH = "event";
+
+ public static Uri toEventConditionId(EventInfo event) {
return new Uri.Builder().scheme(Condition.SCHEME)
.authority(SYSTEM_AUTHORITY)
- .appendPath(SCHEDULE_PATH)
- .appendQueryParameter("days", toDayList(schedule.days))
- .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute)
- .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute)
+ .appendPath(EVENT_PATH)
+ .appendQueryParameter("calendar", Long.toString(event.calendar))
+ .appendQueryParameter("reply", Integer.toString(event.reply))
.build();
}
- public static boolean isValidScheduleConditionId(Uri conditionId) {
- return tryParseScheduleConditionId(conditionId) != null;
+ public static boolean isValidEventConditionId(Uri conditionId) {
+ return tryParseEventConditionId(conditionId) != null;
}
- public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) {
- final boolean isSchedule = conditionId != null
+ public static EventInfo tryParseEventConditionId(Uri conditionId) {
+ final boolean isEvent = conditionId != null
&& conditionId.getScheme().equals(Condition.SCHEME)
&& conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY)
&& conditionId.getPathSegments().size() == 1
- && conditionId.getPathSegments().get(0).equals(ZenModeConfig.SCHEDULE_PATH);
- if (!isSchedule) return null;
- final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start"));
- final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end"));
- if (start == null || end == null) return null;
- final ScheduleInfo rt = new ScheduleInfo();
- rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\.");
- rt.startHour = start[0];
- rt.startMinute = start[1];
- rt.endHour = end[0];
- rt.endMinute = end[1];
+ && conditionId.getPathSegments().get(0).equals(EVENT_PATH);
+ if (!isEvent) return null;
+ final EventInfo rt = new EventInfo();
+ rt.calendar = tryParseLong(conditionId.getQueryParameter("calendar"), 0L);
+ rt.reply = tryParseInt(conditionId.getQueryParameter("reply"), 0);
return rt;
}
+ public static class EventInfo {
+ public static final int REPLY_ANY_EXCEPT_NO = 0;
+ public static final int REPLY_YES_OR_MAYBE = 1;
+ public static final int REPLY_YES = 2;
+
+ public long calendar; // CalendarContract.Calendars._ID, or 0 for any
+ public int reply;
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof EventInfo)) return false;
+ final EventInfo other = (EventInfo) o;
+ return calendar == other.calendar
+ && reply == other.reply;
+ }
+
+ public EventInfo copy() {
+ final EventInfo rt = new EventInfo();
+ rt.calendar = calendar;
+ rt.reply = reply;
+ return rt;
+ }
+ }
+
+ // ==== End built-in system conditions ====
+
private static int[] tryParseHourAndMinute(String value) {
if (TextUtils.isEmpty(value)) return null;
final int i = value.indexOf('.');
diff --git a/core/java/android/service/voice/IVoiceInteractionService.aidl b/core/java/android/service/voice/IVoiceInteractionService.aidl
index e8265a2..e3d68a6 100644
--- a/core/java/android/service/voice/IVoiceInteractionService.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionService.aidl
@@ -23,4 +23,5 @@ oneway interface IVoiceInteractionService {
void ready();
void soundModelsChanged();
void shutdown();
+ void launchVoiceAssistFromKeyguard();
}
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index fee0c75..8c89ddb 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -98,6 +98,10 @@ public class VoiceInteractionService extends Service {
@Override public void soundModelsChanged() {
mHandler.sendEmptyMessage(MSG_SOUND_MODELS_CHANGED);
}
+ @Override
+ public void launchVoiceAssistFromKeyguard() throws RemoteException {
+ mHandler.sendEmptyMessage(MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD);
+ }
};
MyHandler mHandler;
@@ -113,6 +117,7 @@ public class VoiceInteractionService extends Service {
static final int MSG_READY = 1;
static final int MSG_SHUTDOWN = 2;
static final int MSG_SOUND_MODELS_CHANGED = 3;
+ static final int MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD = 4;
class MyHandler extends Handler {
@Override
@@ -127,6 +132,9 @@ public class VoiceInteractionService extends Service {
case MSG_SOUND_MODELS_CHANGED:
onSoundModelsChangedInternal();
break;
+ case MSG_LAUNCH_VOICE_ASSIST_FROM_KEYGUARD:
+ onLaunchVoiceAssistFromKeyguard();
+ break;
default:
super.handleMessage(msg);
}
@@ -134,6 +142,19 @@ public class VoiceInteractionService extends Service {
}
/**
+ * Called when a user has activated an affordance to launch voice assist from the Keyguard.
+ *
+ * <p>This method will only be called if the VoiceInteractionService has set
+ * {@link android.R.attr#supportsLaunchVoiceAssistFromKeyguard} and the Keyguard is showing.</p>
+ *
+ * <p>A valid implementation must start a new activity that should use {@link
+ * android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED} to display
+ * on top of the lock screen.</p>
+ */
+ public void onLaunchVoiceAssistFromKeyguard() {
+ }
+
+ /**
* Check whether the given service component is the currently active
* VoiceInteractionService.
*/
diff --git a/core/java/android/service/voice/VoiceInteractionServiceInfo.java b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
index 4bc97c9..463eb5b 100644
--- a/core/java/android/service/voice/VoiceInteractionServiceInfo.java
+++ b/core/java/android/service/voice/VoiceInteractionServiceInfo.java
@@ -43,7 +43,8 @@ public class VoiceInteractionServiceInfo {
private String mSessionService;
private String mRecognitionService;
private String mSettingsActivity;
- private boolean mSupportsAssistGesture;
+ private boolean mSupportsAssist;
+ private boolean mSupportsLaunchFromKeyguard;
public VoiceInteractionServiceInfo(PackageManager pm, ComponentName comp)
throws PackageManager.NameNotFoundException {
@@ -95,8 +96,11 @@ public class VoiceInteractionServiceInfo {
com.android.internal.R.styleable.VoiceInteractionService_recognitionService);
mSettingsActivity = array.getString(
com.android.internal.R.styleable.VoiceInteractionService_settingsActivity);
- mSupportsAssistGesture = array.getBoolean(
- com.android.internal.R.styleable.VoiceInteractionService_supportsAssistGesture,
+ mSupportsAssist = array.getBoolean(
+ com.android.internal.R.styleable.VoiceInteractionService_supportsAssist,
+ false);
+ mSupportsLaunchFromKeyguard = array.getBoolean(com.android.internal.
+ R.styleable.VoiceInteractionService_supportsLaunchVoiceAssistFromKeyguard,
false);
array.recycle();
if (mSessionService == null) {
@@ -145,7 +149,11 @@ public class VoiceInteractionServiceInfo {
return mSettingsActivity;
}
- public boolean getSupportsAssistGesture() {
- return mSupportsAssistGesture;
+ public boolean getSupportsAssist() {
+ return mSupportsAssist;
+ }
+
+ public boolean getSupportsLaunchFromKeyguard() {
+ return mSupportsLaunchFromKeyguard;
}
}
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index e991d84..ce94315 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -403,4 +403,12 @@ public class RecognizerIntent {
*/
public static final String EXTRA_SUPPORTED_LANGUAGES =
"android.speech.extra.SUPPORTED_LANGUAGES";
+
+ /**
+ * Optional boolean, to be used with {@link #ACTION_RECOGNIZE_SPEECH},
+ * {@link #ACTION_VOICE_SEARCH_HANDS_FREE}, {@link #ACTION_WEB_SEARCH} to indicate whether to
+ * only use an offline speech recognition engine. The default is false, meaning that either
+ * network or offline recognition engines may be used.
+ */
+ public static final String EXTRA_PREFER_OFFLINE = "android.speech.extra.PREFER_OFFLINE";
}
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index 239b386..fc65f63 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -283,15 +283,14 @@ public class DynamicLayout extends Layout
if (reflowed == null) {
reflowed = new StaticLayout(null);
- b = StaticLayout.Builder.obtain(text, where, where + after, getWidth());
+ b = StaticLayout.Builder.obtain(text, where, where + after, getPaint(), getWidth());
}
b.setText(text, where, where + after)
.setPaint(getPaint())
.setWidth(getWidth())
.setTextDir(getTextDirectionHeuristic())
- .setSpacingMult(getSpacingMultiplier())
- .setSpacingAdd(getSpacingAdd())
+ .setLineSpacing(getSpacingAdd(), getSpacingMultiplier())
.setEllipsizedWidth(mEllipsizedWidth)
.setEllipsize(mEllipsizeAt)
.setBreakStrategy(mBreakStrategy);
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index 7bebbfb..a55a08c 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -278,7 +278,7 @@ public class Html {
if (style[j] instanceof TypefaceSpan) {
String s = ((TypefaceSpan) style[j]).getFamily();
- if (s.equals("monospace")) {
+ if ("monospace".equals(s)) {
out.append("<tt>");
}
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 67794b1..451abea 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -16,6 +16,7 @@
package android.text;
+import android.annotation.Nullable;
import android.graphics.Paint;
import android.text.style.LeadingMarginSpan;
import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
@@ -46,18 +47,31 @@ public class StaticLayout extends Layout {
static final String TAG = "StaticLayout";
/**
- * Builder for static layouts. It would be better if this were a public
- * API (as it would offer much greater flexibility for adding new options)
- * but for the time being it's just internal.
- *
- * @hide
+ * Builder for static layouts. The builder is a newer pattern for constructing
+ * StaticLayout objects and should be preferred over the constructors,
+ * particularly to access newer features. To build a static layout, first
+ * call {@link #obtain} with the required arguments (text, paint, and width),
+ * then call setters for optional parameters, and finally {@link #build}
+ * to build the StaticLayout object. Parameters not explicitly set will get
+ * default values.
*/
public final static class Builder {
private Builder() {
mNativePtr = nNewBuilder();
}
- public static Builder obtain(CharSequence source, int start, int end, int width) {
+ /**
+ * Obtain a builder for constructing StaticLayout objects
+ *
+ * @param source The text to be laid out, optionally with spans
+ * @param start The index of the start of the text
+ * @param end The index + 1 of the end of the text
+ * @param paint The base paint used for layout
+ * @param width The width in pixels
+ * @return a builder object used for constructing the StaticLayout
+ */
+ public static Builder obtain(CharSequence source, int start, int end, TextPaint paint,
+ int width) {
Builder b = sPool.acquire();
if (b == null) {
b = new Builder();
@@ -67,6 +81,7 @@ public class StaticLayout extends Layout {
b.mText = source;
b.mStart = start;
b.mEnd = end;
+ b.mPaint = paint;
b.mWidth = width;
b.mAlignment = Alignment.ALIGN_NORMAL;
b.mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
@@ -98,6 +113,18 @@ public class StaticLayout extends Layout {
return setText(source, 0, source.length());
}
+ /**
+ * Set the text. Only useful when re-using the builder, which is done for
+ * the internal implementation of {@link DynamicLayout} but not as part
+ * of normal {@link StaticLayout} usage.
+ *
+ * @param source The text to be laid out, optionally with spans
+ * @param start The index of the start of the text
+ * @param end The index + 1 of the end of the text
+ * @return this builder, useful for chaining
+ *
+ * @hide
+ */
public Builder setText(CharSequence source, int start, int end) {
mText = source;
mStart = start;
@@ -105,11 +132,27 @@ public class StaticLayout extends Layout {
return this;
}
+ /**
+ * Set the paint. Internal for reuse cases only.
+ *
+ * @param paint The base paint used for layout
+ * @return this builder, useful for chaining
+ *
+ * @hide
+ */
public Builder setPaint(TextPaint paint) {
mPaint = paint;
return this;
}
+ /**
+ * Set the width. Internal for reuse cases only.
+ *
+ * @param width The width in pixels
+ * @return this builder, useful for chaining
+ *
+ * @hide
+ */
public Builder setWidth(int width) {
mWidth = width;
if (mEllipsize == null) {
@@ -118,53 +161,126 @@ public class StaticLayout extends Layout {
return this;
}
+ /**
+ * Set the alignment. The default is {@link Layout.Alignment#ALIGN_NORMAL}.
+ *
+ * @param alignment Alignment for the resulting {@link StaticLayout}
+ * @return this builder, useful for chaining
+ */
public Builder setAlignment(Alignment alignment) {
mAlignment = alignment;
return this;
}
+ /**
+ * Set the text direction heuristic. The text direction heuristic is used to
+ * resolve text direction based per-paragraph based on the input text. The default is
+ * {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
+ *
+ * @param textDir text direction heuristic for resolving BiDi behavior.
+ * @return this builder, useful for chaining
+ */
public Builder setTextDir(TextDirectionHeuristic textDir) {
mTextDir = textDir;
return this;
}
- // TODO: combine the following, as they're almost always set together?
- public Builder setSpacingMult(float spacingMult) {
- mSpacingMult = spacingMult;
- return this;
- }
-
- public Builder setSpacingAdd(float spacingAdd) {
+ /**
+ * Set line spacing parameters. The default is 0.0 for {@code spacingAdd}
+ * and 1.0 for {@code spacingMult}.
+ *
+ * @param spacingAdd line spacing add
+ * @param spacingMult line spacing multiplier
+ * @return this builder, useful for chaining
+ * @see android.widget.TextView#setLineSpacing
+ */
+ public Builder setLineSpacing(float spacingAdd, float spacingMult) {
mSpacingAdd = spacingAdd;
+ mSpacingMult = spacingMult;
return this;
}
+ /**
+ * Set whether to include extra space beyond font ascent and descent (which is
+ * needed to avoid clipping in some languages, such as Arabic and Kannada). The
+ * default is {@code true}.
+ *
+ * @param includePad whether to include padding
+ * @return this builder, useful for chaining
+ * @see android.widget.TextView#setIncludeFontPadding
+ */
public Builder setIncludePad(boolean includePad) {
mIncludePad = includePad;
return this;
}
- // TODO: combine the following?
+ /**
+ * Set the width as used for ellipsizing purposes, if it differs from the
+ * normal layout width. The default is the {@code width}
+ * passed to {@link #obtain}.
+ *
+ * @param ellipsizedWidth width used for ellipsizing, in pixels
+ * @return this builder, useful for chaining
+ * @see android.widget.TextView#setEllipsize
+ */
public Builder setEllipsizedWidth(int ellipsizedWidth) {
mEllipsizedWidth = ellipsizedWidth;
return this;
}
- public Builder setEllipsize(TextUtils.TruncateAt ellipsize) {
+ /**
+ * Set ellipsizing on the layout. Causes words that are longer than the view
+ * is wide, or exceeding the number of lines (see #setMaxLines) in the case
+ * of {@link android.text.TextUtils.TruncateAt#END} or
+ * {@link android.text.TextUtils.TruncateAt#MARQUEE}, to be ellipsized instead
+ * of broken. The default is
+ * {@code null}, indicating no ellipsis is to be applied.
+ *
+ * @param ellipsize type of ellipsis behavior
+ * @return this builder, useful for chaining
+ * @see android.widget.TextView#setEllipsize
+ */
+ public Builder setEllipsize(@Nullable TextUtils.TruncateAt ellipsize) {
mEllipsize = ellipsize;
return this;
}
+ /**
+ * Set maximum number of lines. This is particularly useful in the case of
+ * ellipsizing, where it changes the layout of the last line. The default is
+ * unlimited.
+ *
+ * @param maxLines maximum number of lines in the layout
+ * @return this builder, useful for chaining
+ * @see android.widget.TextView#setMaxLines
+ */
public Builder setMaxLines(int maxLines) {
mMaxLines = maxLines;
return this;
}
+ /**
+ * Set break strategy, useful for selecting high quality or balanced paragraph
+ * layout options. The default is {@link Layout#BREAK_STRATEGY_SIMPLE}.
+ *
+ * @param breakStrategy break strategy for paragraph layout
+ * @return this builder, useful for chaining
+ * @see android.widget.TextView#setBreakStrategy
+ */
public Builder setBreakStrategy(@BreakStrategy int breakStrategy) {
mBreakStrategy = breakStrategy;
return this;
}
+ /**
+ * Set indents. Arguments are arrays holding an indent amount, one per line, measured in
+ * pixels. For lines past the last element in the array, the last element repeats.
+ *
+ * @param leftIndents array of indent values for left margin, in pixels
+ * @param rightIndents array of indent values for right margin, in pixels
+ * @return this builder, useful for chaining
+ * @see android.widget.TextView#setIndents
+ */
public Builder setIndents(int[] leftIndents, int[] rightIndents) {
int leftLen = leftIndents == null ? 0 : leftIndents.length;
int rightLen = rightIndents == null ? 0 : rightIndents.length;
@@ -218,6 +334,15 @@ public class StaticLayout extends Layout {
nAddReplacementRun(mNativePtr, start, end, width);
}
+ /**
+ * Build the {@link StaticLayout} after options have been set.
+ *
+ * <p>Note: the builder object must not be reused in any way after calling this
+ * method. Setting parameters after calling this method, or calling it a second
+ * time on the same builder object, will likely lead to unexpected results.
+ *
+ * @return the newly constructed {@link StaticLayout} object
+ */
public StaticLayout build() {
StaticLayout result = new StaticLayout(this);
Builder.recycle(this);
@@ -327,12 +452,10 @@ public class StaticLayout extends Layout {
: new Ellipsizer(source),
paint, outerwidth, align, textDir, spacingmult, spacingadd);
- Builder b = Builder.obtain(source, bufstart, bufend, outerwidth)
- .setPaint(paint)
+ Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
.setAlignment(align)
.setTextDir(textDir)
- .setSpacingMult(spacingmult)
- .setSpacingAdd(spacingadd)
+ .setLineSpacing(spacingadd, spacingmult)
.setIncludePad(includepad)
.setEllipsizedWidth(ellipsizedWidth)
.setEllipsize(ellipsize)
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index c942042..ebc2aac 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -792,6 +792,9 @@ public abstract class Transition implements Cloneable {
* views are ignored and only the ids are used).
*/
boolean isValidTarget(View target) {
+ if (target == null) {
+ return false;
+ }
int targetId = target.getId();
if (mTargetIdExcludes != null && mTargetIdExcludes.contains(targetId)) {
return false;
diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index a7d9503..cbf76bc 100644
--- a/core/java/android/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -211,10 +211,10 @@ public class TransitionInflater {
.asSubclass(expectedType);
if (c != null) {
constructor = c.getConstructor(sConstructorSignature);
+ constructor.setAccessible(true);
sConstructors.put(className, constructor);
}
}
-
return constructor.newInstance(mContext, attrs);
}
} catch (InstantiationException e) {
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index cd68fd1..ed7fd86 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -19,12 +19,17 @@ package android.transition;
import com.android.internal.R;
import android.animation.Animator;
+import android.animation.Animator.AnimatorListener;
+import android.animation.Animator.AnimatorPauseListener;
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+
/**
* This transition tracks changes to the visibility of target views in the
* start and end scenes. Visibility is determined not just by the
@@ -286,6 +291,12 @@ public abstract class Visibility extends Transition {
return null;
}
}
+ final boolean isForcedVisibility = mForcedStartVisibility != -1 ||
+ mForcedEndVisibility != -1;
+ if (isForcedVisibility) {
+ // Make sure that we reverse the effect of onDisappear's setTransitionAlpha(0)
+ endValues.view.setTransitionAlpha(1);
+ }
return onAppear(sceneRoot, endValues.view, startValues, endValues);
}
@@ -418,9 +429,9 @@ public abstract class Visibility extends Transition {
sceneRoot.getOverlay().remove(overlayView);
} else {
final View finalOverlayView = overlayView;
- animator.addListener(new AnimatorListenerAdapter() {
+ addListener(new TransitionListenerAdapter() {
@Override
- public void onAnimationEnd(Animator animation) {
+ public void onTransitionEnd(Transition transition) {
finalSceneRoot.getOverlay().remove(finalOverlayView);
}
});
@@ -438,40 +449,10 @@ public abstract class Visibility extends Transition {
}
Animator animator = onDisappear(sceneRoot, viewToKeep, startValues, endValues);
if (animator != null) {
- final View finalViewToKeep = viewToKeep;
- animator.addListener(new AnimatorListenerAdapter() {
- boolean mCanceled = false;
-
- @Override
- public void onAnimationPause(Animator animation) {
- if (!mCanceled && !isForcedVisibility) {
- finalViewToKeep.setVisibility(finalVisibility);
- }
- }
-
- @Override
- public void onAnimationResume(Animator animation) {
- if (!mCanceled && !isForcedVisibility) {
- finalViewToKeep.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public void onAnimationCancel(Animator animation) {
- mCanceled = true;
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (!mCanceled) {
- if (isForcedVisibility) {
- finalViewToKeep.setTransitionAlpha(0);
- } else {
- finalViewToKeep.setVisibility(finalVisibility);
- }
- }
- }
- });
+ DisappearListener disappearListener = new DisappearListener(viewToKeep,
+ finalVisibility, isForcedVisibility);
+ animator.addListener(disappearListener);
+ addListener(disappearListener);
} else if (!isForcedVisibility) {
viewToKeep.setVisibility(originalVisibility);
}
@@ -517,4 +498,68 @@ public abstract class Visibility extends Transition {
TransitionValues endValues) {
return null;
}
+
+ private static class DisappearListener
+ extends TransitionListenerAdapter implements AnimatorListener, AnimatorPauseListener {
+ private final boolean mIsForcedVisibility;
+ private final View mView;
+ private final int mFinalVisibility;
+
+ boolean mCanceled = false;
+
+ public DisappearListener(View view, int finalVisibility, boolean isForcedVisibility) {
+ this.mView = view;
+ this.mIsForcedVisibility = isForcedVisibility;
+ this.mFinalVisibility = finalVisibility;
+ }
+
+ @Override
+ public void onAnimationPause(Animator animation) {
+ if (!mCanceled && !mIsForcedVisibility) {
+ mView.setVisibility(mFinalVisibility);
+ }
+ }
+
+ @Override
+ public void onAnimationResume(Animator animation) {
+ if (!mCanceled && !mIsForcedVisibility) {
+ mView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ mCanceled = true;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ hideViewWhenNotCanceled();
+ }
+
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ hideViewWhenNotCanceled();
+ }
+
+ private void hideViewWhenNotCanceled() {
+ if (!mCanceled) {
+ if (mIsForcedVisibility) {
+ mView.setTransitionAlpha(0);
+ } else {
+ mView.setVisibility(mFinalVisibility);
+ }
+ }
+ }
+ }
}
diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java
index aefced8..558b8f5 100644
--- a/core/java/android/util/EventLog.java
+++ b/core/java/android/util/EventLog.java
@@ -74,6 +74,7 @@ public class EventLog {
private static final byte LONG_TYPE = 1;
private static final byte STRING_TYPE = 2;
private static final byte LIST_TYPE = 3;
+ private static final byte FLOAT_TYPE = 4;
/** @param data containing event, read from the system */
/*package*/ Event(byte[] data) {
@@ -106,7 +107,7 @@ public class EventLog {
return mBuffer.getInt(offset);
}
- /** @return one of Integer, Long, String, null, or Object[] of same. */
+ /** @return one of Integer, Long, Float, String, null, or Object[] of same. */
public synchronized Object getData() {
try {
int offset = mBuffer.getShort(HEADER_SIZE_OFFSET);
@@ -130,10 +131,13 @@ public class EventLog {
byte type = mBuffer.get();
switch (type) {
case INT_TYPE:
- return (Integer) mBuffer.getInt();
+ return mBuffer.getInt();
case LONG_TYPE:
- return (Long) mBuffer.getLong();
+ return mBuffer.getLong();
+
+ case FLOAT_TYPE:
+ return mBuffer.getFloat();
case STRING_TYPE:
try {
@@ -180,6 +184,14 @@ public class EventLog {
/**
* Record an event log message.
* @param tag The event type tag code
+ * @param value A value to log
+ * @return The number of bytes written
+ */
+ public static native int writeEvent(int tag, float value);
+
+ /**
+ * Record an event log message.
+ * @param tag The event type tag code
* @param str A value to log
* @return The number of bytes written
*/
diff --git a/core/java/android/view/DisplayListCanvas.java b/core/java/android/view/DisplayListCanvas.java
index eedbc70..bb761f0 100644
--- a/core/java/android/view/DisplayListCanvas.java
+++ b/core/java/android/view/DisplayListCanvas.java
@@ -93,12 +93,6 @@ public class DisplayListCanvas extends Canvas {
private static native long nCreateDisplayListCanvas();
- public static void setProperty(String name, String value) {
- nSetProperty(name, value);
- }
-
- private static native void nSetProperty(String name, String value);
-
///////////////////////////////////////////////////////////////////////////
// Canvas management
///////////////////////////////////////////////////////////////////////////
@@ -234,25 +228,13 @@ public class DisplayListCanvas extends Canvas {
* Draws the specified display list onto this canvas. The display list can only
* be drawn if {@link android.view.RenderNode#isValid()} returns true.
*
- * @param renderNode The RenderNode to replay.
+ * @param renderNode The RenderNode to draw.
*/
public void drawRenderNode(RenderNode renderNode) {
- drawRenderNode(renderNode, RenderNode.FLAG_CLIP_CHILDREN);
- }
-
- /**
- * Draws the specified display list onto this canvas.
- *
- * @param renderNode The RenderNode to replay.
- * @param flags Optional flags about drawing, see {@link RenderNode} for
- * the possible flags.
- */
- public void drawRenderNode(RenderNode renderNode, int flags) {
- nDrawRenderNode(mNativeCanvasWrapper, renderNode.getNativeDisplayList(), flags);
+ nDrawRenderNode(mNativeCanvasWrapper, renderNode.getNativeDisplayList());
}
- private static native void nDrawRenderNode(long renderer, long renderNode,
- int flags);
+ private static native void nDrawRenderNode(long renderer, long renderNode);
///////////////////////////////////////////////////////////////////////////
// Hardware layer
@@ -283,7 +265,7 @@ public class DisplayListCanvas extends Canvas {
Bitmap bitmap = patch.getBitmap();
throwIfCannotDraw(bitmap);
final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
- nDrawPatch(mNativeCanvasWrapper, bitmap.getSkBitmap(), patch.mNativeChunk,
+ nDrawPatch(mNativeCanvasWrapper, bitmap, patch.mNativeChunk,
dst.left, dst.top, dst.right, dst.bottom, nativePaint);
}
@@ -293,11 +275,11 @@ public class DisplayListCanvas extends Canvas {
Bitmap bitmap = patch.getBitmap();
throwIfCannotDraw(bitmap);
final long nativePaint = paint == null ? 0 : paint.getNativeInstance();
- nDrawPatch(mNativeCanvasWrapper, bitmap.getSkBitmap(), patch.mNativeChunk,
+ nDrawPatch(mNativeCanvasWrapper, bitmap, patch.mNativeChunk,
dst.left, dst.top, dst.right, dst.bottom, nativePaint);
}
- private static native void nDrawPatch(long renderer, long bitmap, long chunk,
+ private static native void nDrawPatch(long renderer, Bitmap bitmap, long chunk,
float left, float top, float right, float bottom, long paint);
public void drawCircle(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
diff --git a/core/java/android/view/GhostView.java b/core/java/android/view/GhostView.java
index d58e7c0..bc38e1a 100644
--- a/core/java/android/view/GhostView.java
+++ b/core/java/android/view/GhostView.java
@@ -41,7 +41,7 @@ public class GhostView extends View {
final ViewGroup parent = (ViewGroup) mView.getParent();
setGhostedVisibility(View.INVISIBLE);
parent.mRecreateDisplayList = true;
- parent.getDisplayList();
+ parent.updateDisplayListIfDirty();
}
@Override
@@ -49,7 +49,7 @@ public class GhostView extends View {
if (canvas instanceof DisplayListCanvas) {
DisplayListCanvas dlCanvas = (DisplayListCanvas) canvas;
mView.mRecreateDisplayList = true;
- RenderNode renderNode = mView.getDisplayList();
+ RenderNode renderNode = mView.updateDisplayListIfDirty();
if (renderNode.isValid()) {
dlCanvas.insertReorderBarrier(); // enable shadow for this rendernode
dlCanvas.drawRenderNode(renderNode);
@@ -84,7 +84,7 @@ public class GhostView extends View {
final ViewGroup parent = (ViewGroup) mView.getParent();
if (parent != null) {
parent.mRecreateDisplayList = true;
- parent.getDisplayList();
+ parent.updateDisplayListIfDirty();
}
}
}
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index cc090ad..6651b83 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -52,6 +52,11 @@ public class HapticFeedbackConstants {
public static final int CALENDAR_DATE = 5;
/**
+ * The user has touched the screen with a stylus and pressed the stylus button.
+ */
+ public static final int STYLUS_BUTTON_PRESS = 6;
+
+ /**
* This is a private constant. Feel free to renumber as desired.
* @hide
*/
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index 457d6ad..37312d0 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -590,6 +590,7 @@ public abstract class LayoutInflater {
}
}
constructor = clazz.getConstructor(mConstructorSignature);
+ constructor.setAccessible(true);
sConstructorMap.put(name, constructor);
} else {
// If we have a filter, apply it to cached constructor
@@ -615,7 +616,6 @@ public abstract class LayoutInflater {
Object[] args = mConstructorArgs;
args[1] = attrs;
- constructor.setAccessible(true);
final View view = constructor.newInstance(args);
if (view instanceof ViewStub) {
// Use the same context when inflating ViewStub later.
@@ -946,25 +946,21 @@ public abstract class LayoutInflater {
attrs, R.styleable.Include);
final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
final int visibility = a.getInt(R.styleable.Include_visibility, -1);
- final boolean hasWidth = a.hasValue(R.styleable.Include_layout_width);
- final boolean hasHeight = a.hasValue(R.styleable.Include_layout_height);
a.recycle();
- // We try to load the layout params set in the <include /> tag. If
- // they don't exist, we will rely on the layout params set in the
- // included XML file.
- // During a layoutparams generation, a runtime exception is thrown
- // if either layout_width or layout_height is missing. We catch
- // this exception and set localParams accordingly: true means we
- // successfully loaded layout params from the <include /> tag,
- // false means we need to rely on the included layout params.
+ // We try to load the layout params set in the <include /> tag.
+ // If the parent can't generate layout params (ex. missing width
+ // or height for the framework ViewGroups, though this is not
+ // necessarily true of all ViewGroups) then we expect it to throw
+ // a runtime exception.
+ // We catch this exception and set localParams accordingly: true
+ // means we successfully loaded layout params from the <include>
+ // tag, false means we need to rely on the included layout params.
ViewGroup.LayoutParams params = null;
- if (hasWidth && hasHeight) {
- try {
- params = group.generateLayoutParams(attrs);
- } catch (RuntimeException e) {
- // Ignore, just fail over to child attrs.
- }
+ try {
+ params = group.generateLayoutParams(attrs);
+ } catch (RuntimeException e) {
+ // Ignore, just fail over to child attrs.
}
if (params == null) {
params = group.generateLayoutParams(childAttrs);
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
index b49a59e..dc8cadf 100644
--- a/core/java/android/view/MenuInflater.java
+++ b/core/java/android/view/MenuInflater.java
@@ -544,6 +544,7 @@ public class MenuInflater {
try {
Class<?> clazz = mContext.getClassLoader().loadClass(className);
Constructor<?> constructor = clazz.getConstructor(constructorSignature);
+ constructor.setAccessible(true);
return (T) constructor.newInstance(arguments);
} catch (Exception e) {
Log.w(LOG_TAG, "Cannot instantiate class: " + className, e);
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 5e45c8f..5df596a 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -3180,6 +3180,18 @@ public final class MotionEvent extends InputEvent implements Parcelable {
return (getButtonState() & button) == button;
}
+ /**
+ * Checks if a stylus is being used and if the first stylus button is
+ * pressed.
+ *
+ * @return True if the tool is a stylus and if the first stylus button is
+ * pressed.
+ * @see #BUTTON_SECONDARY
+ */
+ public final boolean isStylusButtonPressed() {
+ return (isButtonPressed(BUTTON_SECONDARY) && getToolType(0) == TOOL_TYPE_STYLUS);
+ }
+
public static final Parcelable.Creator<MotionEvent> CREATOR
= new Parcelable.Creator<MotionEvent>() {
public MotionEvent createFromParcel(Parcel in) {
diff --git a/core/java/android/view/PhoneWindow.java b/core/java/android/view/PhoneWindow.java
index 794c8e7..a3e7a10 100644
--- a/core/java/android/view/PhoneWindow.java
+++ b/core/java/android/view/PhoneWindow.java
@@ -3599,7 +3599,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (!mForcedNavigationBarColor) {
mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000);
}
- if (a.getBoolean(R.styleable.Window_windowHasLightStatusBar, false)) {
+ if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) {
decor.setSystemUiVisibility(
decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index ad34f02..85b22fb 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -246,6 +246,9 @@ public class TextureView extends View {
mSurface = null;
mLayer = null;
+ // Make sure if/when new layer gets re-created, transform matrix will
+ // be re-applied.
+ mMatrixChanged = true;
mHadSurface = true;
}
}
@@ -284,11 +287,6 @@ public class TextureView extends View {
return LAYER_TYPE_HARDWARE;
}
- @Override
- boolean hasStaticLayer() {
- return true;
- }
-
/**
* Calling this method has no effect.
*/
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 87d5d9a..1fd7109 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -17,6 +17,7 @@
package android.view;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -269,7 +270,7 @@ public class ThreadedRenderer extends HardwareRenderer {
view.mRecreateDisplayList = (view.mPrivateFlags & View.PFLAG_INVALIDATED)
== View.PFLAG_INVALIDATED;
view.mPrivateFlags &= ~View.PFLAG_INVALIDATED;
- view.getDisplayList();
+ view.updateDisplayListIfDirty();
view.mRecreateDisplayList = false;
}
@@ -285,7 +286,7 @@ public class ThreadedRenderer extends HardwareRenderer {
callbacks.onHardwarePreDraw(canvas);
canvas.insertReorderBarrier();
- canvas.drawRenderNode(view.getDisplayList());
+ canvas.drawRenderNode(view.updateDisplayListIfDirty());
canvas.insertInorderBarrier();
callbacks.onHardwarePostDraw(canvas);
@@ -360,7 +361,7 @@ public class ThreadedRenderer extends HardwareRenderer {
@Override
boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) {
return nCopyLayerInto(mNativeProxy,
- layer.getDeferredLayerUpdater(), bitmap.getSkBitmap());
+ layer.getDeferredLayerUpdater(), bitmap);
}
@Override
@@ -412,6 +413,13 @@ public class ThreadedRenderer extends HardwareRenderer {
nTrimMemory(level);
}
+ public static void overrideProperty(@NonNull String name, @NonNull String value) {
+ if (name == null || value == null) {
+ throw new IllegalArgumentException("name and value must be non-null");
+ }
+ nOverrideProperty(name, value);
+ }
+
public static void dumpProfileData(byte[] data, FileDescriptor fd) {
nDumpProfileData(data, fd);
}
@@ -460,8 +468,6 @@ public class ThreadedRenderer extends HardwareRenderer {
if (buffer != null) {
long[] map = atlas.getMap();
if (map != null) {
- // TODO Remove after fixing b/15425820
- validateMap(context, map);
nSetAtlas(renderProxy, buffer, map);
}
// If IAssetAtlas is not the same class as the IBinder
@@ -476,32 +482,6 @@ public class ThreadedRenderer extends HardwareRenderer {
Log.w(LOG_TAG, "Could not acquire atlas", e);
}
}
-
- private static void validateMap(Context context, long[] map) {
- Log.d("Atlas", "Validating map...");
- HashSet<Long> preloadedPointers = new HashSet<Long>();
-
- // We only care about drawables that hold bitmaps
- final Resources resources = context.getResources();
- final LongSparseArray<Drawable.ConstantState> drawables = resources.getPreloadedDrawables();
-
- final int count = drawables.size();
- ArrayList<Bitmap> tmpList = new ArrayList<Bitmap>();
- for (int i = 0; i < count; i++) {
- drawables.valueAt(i).addAtlasableBitmaps(tmpList);
- for (int j = 0; j < tmpList.size(); j++) {
- preloadedPointers.add(tmpList.get(j).getSkBitmap());
- }
- tmpList.clear();
- }
-
- for (int i = 0; i < map.length; i += 4) {
- if (!preloadedPointers.contains(map[i])) {
- Log.w("Atlas", String.format("Pointer 0x%X, not in getPreloadedDrawables?", map[i]));
- map[i] = 0;
- }
- }
- }
}
static native void setupShadersDiskCache(String cacheFile);
@@ -531,13 +511,14 @@ public class ThreadedRenderer extends HardwareRenderer {
private static native long nCreateTextureLayer(long nativeProxy);
private static native void nBuildLayer(long nativeProxy, long node);
- private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap);
+ private static native boolean nCopyLayerInto(long nativeProxy, long layer, Bitmap bitmap);
private static native void nPushLayerUpdate(long nativeProxy, long layer);
private static native void nCancelLayerUpdate(long nativeProxy, long layer);
private static native void nDetachSurfaceTexture(long nativeProxy, long layer);
private static native void nDestroyHardwareResources(long nativeProxy);
private static native void nTrimMemory(int level);
+ private static native void nOverrideProperty(String name, String value);
private static native void nFence(long nativeProxy);
private static native void nStopDrawing(long nativeProxy);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7da2da4..f62e6a2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -28,8 +28,10 @@ import android.annotation.LayoutRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
+import android.annotation.UiThread;
import android.content.ClipData;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -699,6 +701,7 @@ import java.util.concurrent.atomic.AtomicInteger;
*
* @see android.view.ViewGroup
*/
+@UiThread
public class View implements Drawable.Callback, KeyEvent.Callback,
AccessibilityEventSource {
private static final boolean DBG = false;
@@ -986,6 +989,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
static final int DUPLICATE_PARENT_STATE = 0x00400000;
+ /**
+ * <p>
+ * Indicates this view can be stylus button pressed. When stylus button
+ * pressable, a View reacts to stylus button presses by notifiying
+ * the OnStylusButtonPressListener.
+ * </p>
+ * {@hide}
+ */
+ static final int STYLUS_BUTTON_PRESSABLE = 0x00800000;
+
+
/** @hide */
@IntDef({
SCROLLBARS_INSIDE_OVERLAY,
@@ -2381,10 +2395,143 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*/
static final int PFLAG3_NESTED_SCROLLING_ENABLED = 0x80;
+ /**
+ * Flag indicating that the bottom scroll indicator should be displayed
+ * when this view can scroll up.
+ */
+ static final int PFLAG3_SCROLL_INDICATOR_TOP = 0x0100;
+
+ /**
+ * Flag indicating that the bottom scroll indicator should be displayed
+ * when this view can scroll down.
+ */
+ static final int PFLAG3_SCROLL_INDICATOR_BOTTOM = 0x0200;
+
+ /**
+ * Flag indicating that the left scroll indicator should be displayed
+ * when this view can scroll left.
+ */
+ static final int PFLAG3_SCROLL_INDICATOR_LEFT = 0x0400;
+
+ /**
+ * Flag indicating that the right scroll indicator should be displayed
+ * when this view can scroll right.
+ */
+ static final int PFLAG3_SCROLL_INDICATOR_RIGHT = 0x0800;
+
+ /**
+ * Flag indicating that the start scroll indicator should be displayed
+ * when this view can scroll in the start direction.
+ */
+ static final int PFLAG3_SCROLL_INDICATOR_START = 0x1000;
+
+ /**
+ * Flag indicating that the end scroll indicator should be displayed
+ * when this view can scroll in the end direction.
+ */
+ static final int PFLAG3_SCROLL_INDICATOR_END = 0x2000;
+
/* End of masks for mPrivateFlags3 */
static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED;
+ static final int SCROLL_INDICATORS_NONE = 0x0000;
+
+ /**
+ * Mask for use with setFlags indicating bits used for indicating which
+ * scroll indicators are enabled.
+ */
+ static final int SCROLL_INDICATORS_PFLAG3_MASK = PFLAG3_SCROLL_INDICATOR_TOP
+ | PFLAG3_SCROLL_INDICATOR_BOTTOM | PFLAG3_SCROLL_INDICATOR_LEFT
+ | PFLAG3_SCROLL_INDICATOR_RIGHT | PFLAG3_SCROLL_INDICATOR_START
+ | PFLAG3_SCROLL_INDICATOR_END;
+
+ /**
+ * Left-shift required to translate between public scroll indicator flags
+ * and internal PFLAGS3 flags. When used as a right-shift, translates
+ * PFLAGS3 flags to public flags.
+ */
+ static final int SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT = 8;
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true,
+ value = {
+ SCROLL_INDICATOR_TOP,
+ SCROLL_INDICATOR_BOTTOM,
+ SCROLL_INDICATOR_LEFT,
+ SCROLL_INDICATOR_RIGHT,
+ SCROLL_INDICATOR_START,
+ SCROLL_INDICATOR_END,
+ })
+ public @interface ScrollIndicators {}
+
+ /**
+ * Scroll indicator direction for the top edge of the view.
+ *
+ * @see #setScrollIndicators(int)
+ * @see #setScrollIndicators(int, int)
+ * @see #getScrollIndicators()
+ */
+ public static final int SCROLL_INDICATOR_TOP =
+ PFLAG3_SCROLL_INDICATOR_TOP >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+
+ /**
+ * Scroll indicator direction for the bottom edge of the view.
+ *
+ * @see #setScrollIndicators(int)
+ * @see #setScrollIndicators(int, int)
+ * @see #getScrollIndicators()
+ */
+ public static final int SCROLL_INDICATOR_BOTTOM =
+ PFLAG3_SCROLL_INDICATOR_BOTTOM >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+
+ /**
+ * Scroll indicator direction for the left edge of the view.
+ *
+ * @see #setScrollIndicators(int)
+ * @see #setScrollIndicators(int, int)
+ * @see #getScrollIndicators()
+ */
+ public static final int SCROLL_INDICATOR_LEFT =
+ PFLAG3_SCROLL_INDICATOR_LEFT >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+
+ /**
+ * Scroll indicator direction for the right edge of the view.
+ *
+ * @see #setScrollIndicators(int)
+ * @see #setScrollIndicators(int, int)
+ * @see #getScrollIndicators()
+ */
+ public static final int SCROLL_INDICATOR_RIGHT =
+ PFLAG3_SCROLL_INDICATOR_RIGHT >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+
+ /**
+ * Scroll indicator direction for the starting edge of the view.
+ * <p>
+ * Resolved according to the view's layout direction, see
+ * {@link #getLayoutDirection()} for more information.
+ *
+ * @see #setScrollIndicators(int)
+ * @see #setScrollIndicators(int, int)
+ * @see #getScrollIndicators()
+ */
+ public static final int SCROLL_INDICATOR_START =
+ PFLAG3_SCROLL_INDICATOR_START >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+
+ /**
+ * Scroll indicator direction for the ending edge of the view.
+ * <p>
+ * Resolved according to the view's layout direction, see
+ * {@link #getLayoutDirection()} for more information.
+ *
+ * @see #setScrollIndicators(int)
+ * @see #setScrollIndicators(int, int)
+ * @see #getScrollIndicators()
+ */
+ public static final int SCROLL_INDICATOR_END =
+ PFLAG3_SCROLL_INDICATOR_END >> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+
/**
* <p>Indicates that we are allowing {@link android.view.ViewAssistStructure} to traverse
* into this view.<p>
@@ -2591,7 +2738,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* {@link android.view.WindowManager.LayoutParams#FLAG_TRANSLUCENT_STATUS
* FLAG_TRANSLUCENT_STATUS}.
*
- * @see android.R.attr#windowHasLightStatusBar
+ * @see android.R.attr#windowLightStatusBar
*/
public static final int SYSTEM_UI_FLAG_LIGHT_STATUS_BAR = 0x00002000;
@@ -3203,6 +3350,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@ViewDebug.ExportedProperty(deepExport = true, prefix = "fg_")
private ForegroundInfo mForegroundInfo;
+ private Drawable mScrollIndicatorDrawable;
+
/**
* RenderNode used for backgrounds.
* <p>
@@ -3269,6 +3418,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
protected OnLongClickListener mOnLongClickListener;
/**
+ * Listener used to dispatch stylus touch and button press events. This field should be made
+ * private, so it is hidden from the SDK.
+ * {@hide}
+ */
+ protected OnStylusButtonPressListener mOnStylusButtonPressListener;
+
+ /**
* Listener used to build the context menu.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
@@ -3359,6 +3515,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
private boolean mHasPerformedLongPress;
/**
+ * Whether the stylus button is currently pressed down. This is true when
+ * the stylus is touching the screen and the button has been pressed, this
+ * is false once the stylus has been lifted.
+ */
+ private boolean mInStylusButtonPress = false;
+
+ /**
* The minimum height of the view. We'll try our best to have the height
* of this view to at least this amount.
*/
@@ -3741,6 +3904,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY;
int overScrollMode = mOverScrollMode;
boolean initializeScrollbars = false;
+ boolean initializeScrollIndicators = false;
boolean startPaddingDefined = false;
boolean endPaddingDefined = false;
@@ -3874,6 +4038,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
viewFlagMasks |= LONG_CLICKABLE;
}
break;
+ case com.android.internal.R.styleable.View_stylusButtonPressable:
+ if (a.getBoolean(attr, false)) {
+ viewFlagValues |= STYLUS_BUTTON_PRESSABLE;
+ viewFlagMasks |= STYLUS_BUTTON_PRESSABLE;
+ }
+ break;
case com.android.internal.R.styleable.View_saveEnabled:
if (!a.getBoolean(attr, true)) {
viewFlagValues |= SAVE_DISABLED;
@@ -4016,37 +4186,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
final String handlerName = a.getString(attr);
if (handlerName != null) {
- setOnClickListener(new OnClickListener() {
- private Method mHandler;
-
- public void onClick(View v) {
- if (mHandler == null) {
- try {
- mHandler = getContext().getClass().getMethod(handlerName,
- View.class);
- } catch (NoSuchMethodException e) {
- int id = getId();
- String idText = id == NO_ID ? "" : " with id '"
- + getContext().getResources().getResourceEntryName(
- id) + "'";
- throw new IllegalStateException("Could not find a method " +
- handlerName + "(View) in the activity "
- + getContext().getClass() + " for onClick handler"
- + " on view " + View.this.getClass() + idText, e);
- }
- }
-
- try {
- mHandler.invoke(getContext(), View.this);
- } catch (IllegalAccessException e) {
- throw new IllegalStateException("Could not execute non "
- + "public method of the activity", e);
- } catch (InvocationTargetException e) {
- throw new IllegalStateException("Could not execute "
- + "method of the activity", e);
- }
- }
- });
+ setOnClickListener(new DeclaredOnClickListener(this, handlerName));
}
break;
case R.styleable.View_overScrollMode:
@@ -4131,6 +4271,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
mForegroundInfo.mInsidePadding = a.getBoolean(attr,
mForegroundInfo.mInsidePadding);
+ case R.styleable.View_scrollIndicators:
+ final int scrollIndicators =
+ a.getInt(attr, SCROLL_INDICATORS_NONE) & SCROLL_INDICATORS_PFLAG3_MASK;
+ if (scrollIndicators != 0) {
+ viewFlagValues |= scrollIndicators;
+ viewFlagMasks |= SCROLL_INDICATORS_PFLAG3_MASK;
+ initializeScrollIndicators = true;
+ }
break;
}
}
@@ -4207,6 +4355,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
initializeScrollbarsInternal(a);
}
+ if (initializeScrollIndicators) {
+ initializeScrollIndicatorsInternal();
+ }
+
a.recycle();
// Needs to be called after mViewFlags is set
@@ -4238,6 +4390,66 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * An implementation of OnClickListener that attempts to lazily load a
+ * named click handling method from a parent or ancestor context.
+ */
+ private static class DeclaredOnClickListener implements OnClickListener {
+ private final View mHostView;
+ private final String mMethodName;
+
+ private Method mMethod;
+
+ public DeclaredOnClickListener(@NonNull View hostView, @NonNull String methodName) {
+ mHostView = hostView;
+ mMethodName = methodName;
+ }
+
+ @Override
+ public void onClick(@NonNull View v) {
+ if (mMethod == null) {
+ mMethod = resolveMethod(mHostView.getContext(), mMethodName);
+ }
+
+ try {
+ mMethod.invoke(mHostView.getContext(), v);
+ } catch (IllegalAccessException e) {
+ throw new IllegalStateException(
+ "Could not execute non-public method for android:onClick", e);
+ } catch (InvocationTargetException e) {
+ throw new IllegalStateException(
+ "Could not execute method for android:onClick", e);
+ }
+ }
+
+ @NonNull
+ private Method resolveMethod(@Nullable Context context, @NonNull String name) {
+ while (context != null) {
+ try {
+ if (!context.isRestricted()) {
+ return context.getClass().getMethod(mMethodName, View.class);
+ }
+ } catch (NoSuchMethodException e) {
+ // Failed to find method, keep searching up the hierarchy.
+ }
+
+ if (context instanceof ContextWrapper) {
+ context = ((ContextWrapper) context).getBaseContext();
+ } else {
+ // Can't search up the hierarchy, null out and fail.
+ context = null;
+ }
+ }
+
+ final int id = mHostView.getId();
+ final String idText = id == NO_ID ? "" : " with id '"
+ + mHostView.getContext().getResources().getResourceEntryName(id) + "'";
+ throw new IllegalStateException("Could not find method " + mMethodName
+ + "(View) in a parent or ancestor Context for android:onClick "
+ + "attribute defined on view " + mHostView.getClass() + idText);
+ }
+ }
+
+ /**
* Non-public constructor for use in testing
*/
View() {
@@ -4309,6 +4521,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
out.append((mViewFlags&SCROLLBARS_VERTICAL) != 0 ? 'V' : '.');
out.append((mViewFlags&CLICKABLE) != 0 ? 'C' : '.');
out.append((mViewFlags&LONG_CLICKABLE) != 0 ? 'L' : '.');
+ out.append((mViewFlags & STYLUS_BUTTON_PRESSABLE) != 0 ? 'S' : '.');
out.append(' ');
out.append((mPrivateFlags&PFLAG_IS_ROOT_NAMESPACE) != 0 ? 'R' : '.');
out.append((mPrivateFlags&PFLAG_FOCUSED) != 0 ? 'F' : '.');
@@ -4617,6 +4830,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
resolvePadding();
}
+ private void initializeScrollIndicatorsInternal() {
+ // Some day maybe we'll break this into top/left/start/etc. and let the
+ // client control it. Until then, you can have any scroll indicator you
+ // want as long as it's a 1dp foreground-colored rectangle.
+ if (mScrollIndicatorDrawable == null) {
+ mScrollIndicatorDrawable = mContext.getDrawable(R.drawable.scroll_indicator_material);
+ }
+ }
+
/**
* <p>
* Initalizes the scrollability cache if necessary.
@@ -4656,6 +4878,118 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return mVerticalScrollbarPosition;
}
+ /**
+ * Sets the state of all scroll indicators.
+ * <p>
+ * See {@link #setScrollIndicators(int, int)} for usage information.
+ *
+ * @param indicators a bitmask of indicators that should be enabled, or
+ * {@code 0} to disable all indicators
+ * @see #setScrollIndicators(int, int)
+ * @see #getScrollIndicators()
+ * @attr ref android.R.styleable#View_scrollIndicators
+ */
+ public void setScrollIndicators(@ScrollIndicators int indicators) {
+ setScrollIndicators(indicators, SCROLL_INDICATORS_PFLAG3_MASK);
+ }
+
+ /**
+ * Sets the state of the scroll indicators specified by the mask. To change
+ * all scroll indicators at once, see {@link #setScrollIndicators(int)}.
+ * <p>
+ * When a scroll indicator is enabled, it will be displayed if the view
+ * can scroll in the direction of the indicator.
+ * <p>
+ * Multiple indicator types may be enabled or disabled by passing the
+ * logical OR of the desired types. If multiple types are specified, they
+ * will all be set to the same enabled state.
+ * <p>
+ * For example, to enable the top scroll indicatorExample: {@code setScrollIndicators
+ *
+ * @param indicators the indicator direction, or the logical OR of multiple
+ * indicator directions. One or more of:
+ * <ul>
+ * <li>{@link #SCROLL_INDICATOR_TOP}</li>
+ * <li>{@link #SCROLL_INDICATOR_BOTTOM}</li>
+ * <li>{@link #SCROLL_INDICATOR_LEFT}</li>
+ * <li>{@link #SCROLL_INDICATOR_RIGHT}</li>
+ * <li>{@link #SCROLL_INDICATOR_START}</li>
+ * <li>{@link #SCROLL_INDICATOR_END}</li>
+ * </ul>
+ * @see #setScrollIndicators(int)
+ * @see #getScrollIndicators()
+ * @attr ref android.R.styleable#View_scrollIndicators
+ */
+ public void setScrollIndicators(@ScrollIndicators int indicators, @ScrollIndicators int mask) {
+ // Shift and sanitize mask.
+ mask <<= SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+ mask &= SCROLL_INDICATORS_PFLAG3_MASK;
+
+ // Shift and mask indicators.
+ indicators <<= SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+ indicators &= mask;
+
+ // Merge with non-masked flags.
+ final int updatedFlags = indicators | (mPrivateFlags3 & ~mask);
+
+ if (mPrivateFlags3 != updatedFlags) {
+ mPrivateFlags3 = updatedFlags;
+
+ if (indicators != 0) {
+ initializeScrollIndicatorsInternal();
+ }
+ invalidate();
+ }
+ }
+
+ /**
+ * Returns a bitmask representing the enabled scroll indicators.
+ * <p>
+ * For example, if the top and left scroll indicators are enabled and all
+ * other indicators are disabled, the return value will be
+ * {@code View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_LEFT}.
+ * <p>
+ * To check whether the bottom scroll indicator is enabled, use the value
+ * of {@code (getScrollIndicators() & View.SCROLL_INDICATOR_BOTTOM) != 0}.
+ *
+ * @return a bitmask representing the enabled scroll indicators
+ */
+ @ScrollIndicators
+ public int getScrollIndicators() {
+ return (mPrivateFlags3 & SCROLL_INDICATORS_PFLAG3_MASK)
+ >>> SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+ }
+
+ /**
+ * Returns whether the specified scroll indicator is enabled.
+ * <p>
+ * Multiple indicator types may be queried by passing the logical OR of the
+ * desired types. If multiple types are specified, the return value
+ * represents whether they are all enabled.
+ *
+ * @param direction the indicator direction, or the logical OR of multiple
+ * indicator directions. One or more of:
+ * <ul>
+ * <li>{@link #SCROLL_INDICATOR_TOP}</li>
+ * <li>{@link #SCROLL_INDICATOR_BOTTOM}</li>
+ * <li>{@link #SCROLL_INDICATOR_LEFT}</li>
+ * <li>{@link #SCROLL_INDICATOR_RIGHT}</li>
+ * <li>{@link #SCROLL_INDICATOR_START}</li>
+ * <li>{@link #SCROLL_INDICATOR_END}</li>
+ * </ul>
+ * @return {@code true} if the specified indicator(s) are enabled,
+ * {@code false} otherwise
+ * @attr ref android.R.styleable#View_scrollIndicators
+ */
+ public boolean isScrollIndicatorEnabled(int direction) {
+ // Shift and sanitize input.
+ direction <<= SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT;
+ direction &= SCROLL_INDICATORS_PFLAG3_MASK;
+
+ // All of the flags must be set.
+ return (mPrivateFlags3 & direction) == direction;
+ }
+
ListenerInfo getListenerInfo() {
if (mListenerInfo != null) {
return mListenerInfo;
@@ -4804,6 +5138,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Register a callback to be invoked when this view is touched with a stylus and the button is
+ * pressed.
+ *
+ * @param l The callback that will run
+ * @see #setStylusButtonPressable(boolean)
+ */
+ public void setOnStylusButtonPressListener(@Nullable OnStylusButtonPressListener l) {
+ if (!isStylusButtonPressable()) {
+ setStylusButtonPressable(true);
+ }
+ getListenerInfo().mOnStylusButtonPressListener = l;
+ }
+
+ /**
* Register a callback to be invoked when the context menu for this view is
* being built. If this view is not long clickable, it becomes long clickable.
*
@@ -4881,6 +5229,46 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Call this view's OnStylusButtonPressListener, if it is defined.
+ *
+ * @return True if there was an assigned OnStylusButtonPressListener that consumed the event,
+ * false otherwise.
+ */
+ public boolean performStylusButtonPress() {
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_STYLUS_BUTTON_PRESSED);
+
+ boolean handled = false;
+ ListenerInfo li = mListenerInfo;
+ if (li != null && li.mOnStylusButtonPressListener != null) {
+ handled = li.mOnStylusButtonPressListener.onStylusButtonPress(View.this);
+ }
+ if (handled) {
+ performHapticFeedback(HapticFeedbackConstants.STYLUS_BUTTON_PRESS);
+ }
+ return handled;
+ }
+
+ /**
+ * Checks for a stylus button press and calls the listener.
+ *
+ * @param event The event.
+ * @return True if the event was consumed.
+ */
+ private boolean performStylusActionOnButtonPress(MotionEvent event) {
+ if (isStylusButtonPressable() && !mInStylusButtonPress
+ && !mHasPerformedLongPress && event.isStylusButtonPressed()) {
+ if (performStylusButtonPress()) {
+ mInStylusButtonPress = true;
+ setPressed(true, event.getX(), event.getY());
+ removeTapCallback();
+ removeLongPressCallback();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Performs button-related actions during a touch down event.
*
* @param event The event.
@@ -5670,6 +6058,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* <li>{@link AccessibilityNodeInfo#setFocused(boolean)},</li>
* <li>{@link AccessibilityNodeInfo#setLongClickable(boolean)},</li>
* <li>{@link AccessibilityNodeInfo#setSelected(boolean)},</li>
+ * <li>{@link AccessibilityNodeInfo#setStylusButtonPressable(boolean)}</li>
* </ul>
* <p>
* Subclasses should override this method, call the super implementation,
@@ -5821,6 +6210,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
structure.setChecked(true);
}
}
+ if (isStylusButtonPressable()) {
+ structure.setStylusButtonPressable(true);
+ }
structure.setClassName(getAccessibilityClassName().toString());
structure.setContentDescription(getContentDescription());
}
@@ -5878,6 +6270,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
structure.setChecked(true);
}
}
+ if (info.isStylusButtonPressable()) {
+ structure.setStylusButtonPressable(true);
+ }
CharSequence cname = info.getClassName();
structure.setClassName(cname != null ? cname.toString() : null);
structure.setContentDescription(info.getContentDescription());
@@ -6007,6 +6402,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
info.setAccessibilityFocused(isAccessibilityFocused());
info.setSelected(isSelected());
info.setLongClickable(isLongClickable());
+ info.setStylusButtonPressable(isStylusButtonPressable());
info.setLiveRegion(getAccessibilityLiveRegion());
// TODO: These make sense only if we are in an AdapterView but all
@@ -6037,6 +6433,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
info.addAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
}
+ if (isStylusButtonPressable() && isEnabled()) {
+ info.addAction(AccessibilityAction.ACTION_STYLUS_BUTTON_PRESS);
+ }
+
CharSequence text = getIterableTextForAccessibility();
if (text != null && text.length() > 0) {
info.setTextSelection(getAccessibilitySelectionStart(), getAccessibilitySelectionEnd());
@@ -7411,6 +7811,31 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Indicates whether this view reacts to stylus button press events or not.
+ *
+ * @return true if the view is stylus button pressable, false otherwise
+ * @see #setStylusButtonPressable(boolean)
+ * @attr ref android.R.styleable#View_stylusButtonPressable
+ */
+ public boolean isStylusButtonPressable() {
+ return (mViewFlags & STYLUS_BUTTON_PRESSABLE) == STYLUS_BUTTON_PRESSABLE;
+ }
+
+ /**
+ * Enables or disables stylus button press events for this view. When a view is stylus button
+ * pressable it reacts to the user touching the screen with a stylus and pressing the first
+ * stylus button. This event can launch the listener.
+ *
+ * @param stylusButtonPressable true to make the view react to a stylus button press, false
+ * otherwise
+ * @see #isStylusButtonPressable()
+ * @attr ref android.R.styleable#View_stylusButtonPressable
+ */
+ public void setStylusButtonPressable(boolean stylusButtonPressable) {
+ setFlags(stylusButtonPressable ? STYLUS_BUTTON_PRESSABLE : 0, STYLUS_BUTTON_PRESSABLE);
+ }
+
+ /**
* Sets the pressed state for this view and provides a touch coordinate for
* animation hinting.
*
@@ -7825,7 +8250,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public void addTouchables(ArrayList<View> views) {
final int viewFlags = mViewFlags;
- if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
+ if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE
+ || (viewFlags & STYLUS_BUTTON_PRESSABLE) == STYLUS_BUTTON_PRESSABLE)
&& (viewFlags & ENABLED_MASK) == ENABLED) {
views.add(this);
}
@@ -8440,7 +8866,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
if (isNestedScrollingEnabled()
&& (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
- || action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD)) {
+ || action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
+ || action == R.id.accessibilityActionScrollUp
+ || action == R.id.accessibilityActionScrollLeft
+ || action == R.id.accessibilityActionScrollDown
+ || action == R.id.accessibilityActionScrollRight)) {
if (dispatchNestedPrePerformAccessibilityAction(action, arguments)) {
return true;
}
@@ -8540,6 +8970,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return requestRectangleOnScreen(r, true);
}
} break;
+ case R.id.accessibilityActionStylusButtonPress: {
+ if (isStylusButtonPressable()) {
+ performStylusButtonPress();
+ return true;
+ }
+ } break;
}
return false;
}
@@ -9697,7 +10133,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
return (viewFlags & CLICKABLE) == CLICKABLE
- || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
+ || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE
+ || (viewFlags & STYLUS_BUTTON_PRESSABLE) == STYLUS_BUTTON_PRESSABLE;
}
/**
@@ -9780,15 +10217,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
final float x = event.getX();
final float y = event.getY();
final int viewFlags = mViewFlags;
+ final int action = event.getAction();
if ((viewFlags & ENABLED_MASK) == DISABLED) {
- if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
+ if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
setPressed(false);
}
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
- return (((viewFlags & CLICKABLE) == CLICKABLE ||
- (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
+ return (((viewFlags & CLICKABLE) == CLICKABLE
+ || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
+ || (viewFlags & STYLUS_BUTTON_PRESSABLE) == STYLUS_BUTTON_PRESSABLE);
}
if (mTouchDelegate != null) {
@@ -9798,9 +10237,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
if (((viewFlags & CLICKABLE) == CLICKABLE ||
- (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
- switch (event.getAction()) {
+ (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
+ (viewFlags & STYLUS_BUTTON_PRESSABLE) == STYLUS_BUTTON_PRESSABLE) {
+ switch (action) {
case MotionEvent.ACTION_UP:
+ if (mInStylusButtonPress) {
+ mInStylusButtonPress = false;
+ mHasPerformedLongPress = false;
+ }
boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
@@ -9854,6 +10298,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
case MotionEvent.ACTION_DOWN:
mHasPerformedLongPress = false;
+ mInStylusButtonPress = false;
+
+ if (performStylusActionOnButtonPress(event)) {
+ break;
+ }
if (performButtonActionOnTouchDown(event)) {
break;
@@ -9883,6 +10332,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setPressed(false);
removeTapCallback();
removeLongPressCallback();
+ if (mInStylusButtonPress) {
+ mInStylusButtonPress = false;
+ mHasPerformedLongPress = false;
+ }
break;
case MotionEvent.ACTION_MOVE:
@@ -9898,6 +10351,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
setPressed(false);
}
+ } else if (performStylusActionOnButtonPress(event)) {
+ // Check for stylus button press if we're within the view.
+ break;
}
break;
}
@@ -10185,7 +10641,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (accessibilityEnabled) {
if ((changed & FOCUSABLE_MASK) != 0 || (changed & VISIBILITY_MASK) != 0
- || (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0) {
+ || (changed & CLICKABLE) != 0 || (changed & LONG_CLICKABLE) != 0
+ || (changed & STYLUS_BUTTON_PRESSABLE) != 0) {
if (oldIncludeForAccessibility != includeForAccessibility()) {
notifySubtreeAccessibilityStateChangedIfNeeded();
} else {
@@ -10909,25 +11366,34 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * <p>Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is
- * completely transparent and 1 means the view is completely opaque.</p>
+ * Sets the opacity of the view to a value from 0 to 1, where 0 means the view is
+ * completely transparent and 1 means the view is completely opaque.
*
- * <p> Note that setting alpha to a translucent value (0 < alpha < 1) can have significant
- * performance implications, especially for large views. It is best to use the alpha property
- * sparingly and transiently, as in the case of fading animations.</p>
+ * <p class="note"><strong>Note:</strong> setting alpha to a translucent value (0 < alpha < 1)
+ * can have significant performance implications, especially for large views. It is best to use
+ * the alpha property sparingly and transiently, as in the case of fading animations.</p>
*
* <p>For a view with a frequently changing alpha, such as during a fading animation, it is
* strongly recommended for performance reasons to either override
- * {@link #hasOverlappingRendering()} to return false if appropriate, or setting a
- * {@link #setLayerType(int, android.graphics.Paint) layer type} on the view.</p>
+ * {@link #hasOverlappingRendering()} to return <code>false</code> if appropriate, or setting a
+ * {@link #setLayerType(int, android.graphics.Paint) layer type} on the view for the duration
+ * of the animation. On versions {@link android.os.Build.VERSION_CODES#MNC} and below,
+ * the default path for rendering an unlayered View with alpha could add multiple milliseconds
+ * of rendering cost, even for simple or small views. Starting with
+ * {@link android.os.Build.VERSION_CODES#MNC}, {@link #LAYER_TYPE_HARDWARE} is automatically
+ * applied to the view at the rendering level.</p>
*
* <p>If this view overrides {@link #onSetAlpha(int)} to return true, then this view is
* responsible for applying the opacity itself.</p>
*
- * <p>Note that if the view is backed by a
- * {@link #setLayerType(int, android.graphics.Paint) layer} and is associated with a
- * {@link #setLayerPaint(android.graphics.Paint) layer paint}, setting an alpha value less than
- * 1.0 will supersede the alpha of the layer paint.</p>
+ * <p>On versions {@link android.os.Build.VERSION_CODES#LOLLIPOP_MR1} and below, note that if
+ * the view is backed by a {@link #setLayerType(int, android.graphics.Paint) layer} and is
+ * associated with a {@link #setLayerPaint(android.graphics.Paint) layer paint}, setting an
+ * alpha value less than 1.0 will supersede the alpha of the layer paint.</p>
+ *
+ * <p>Starting with {@link android.os.Build.VERSION_CODES#MNC}, setting a translucent alpha
+ * value will clip a View to its bounds, unless the View returns <code>false</code> from
+ * {@link #hasOverlappingRendering}.</p>
*
* @param alpha The opacity of the view.
*
@@ -13247,6 +13713,75 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
+ void getScrollIndicatorBounds(@NonNull Rect out) {
+ out.left = mScrollX;
+ out.right = mScrollX + mRight - mLeft;
+ out.top = mScrollY;
+ out.bottom = mScrollY + mBottom - mTop;
+ }
+
+ private void onDrawScrollIndicators(Canvas c) {
+ if ((mPrivateFlags3 & SCROLL_INDICATORS_PFLAG3_MASK) == 0) {
+ // No scroll indicators enabled.
+ return;
+ }
+
+ final Drawable dr = mScrollIndicatorDrawable;
+ if (dr == null) {
+ // Scroll indicators aren't supported here.
+ return;
+ }
+
+ final int h = dr.getIntrinsicHeight();
+ final int w = dr.getIntrinsicWidth();
+ final Rect rect = mAttachInfo.mTmpInvalRect;
+ getScrollIndicatorBounds(rect);
+
+ if ((mPrivateFlags3 & PFLAG3_SCROLL_INDICATOR_TOP) != 0) {
+ final boolean canScrollUp = canScrollVertically(-1);
+ if (canScrollUp) {
+ dr.setBounds(rect.left, rect.top, rect.right, rect.top + h);
+ dr.draw(c);
+ }
+ }
+
+ if ((mPrivateFlags3 & PFLAG3_SCROLL_INDICATOR_BOTTOM) != 0) {
+ final boolean canScrollDown = canScrollVertically(1);
+ if (canScrollDown) {
+ dr.setBounds(rect.left, rect.bottom - h, rect.right, rect.bottom);
+ dr.draw(c);
+ }
+ }
+
+ final int leftRtl;
+ final int rightRtl;
+ if (getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+ leftRtl = PFLAG3_SCROLL_INDICATOR_END;
+ rightRtl = PFLAG3_SCROLL_INDICATOR_START;
+ } else {
+ leftRtl = PFLAG3_SCROLL_INDICATOR_START;
+ rightRtl = PFLAG3_SCROLL_INDICATOR_END;
+ }
+
+ final int leftMask = PFLAG3_SCROLL_INDICATOR_LEFT | leftRtl;
+ if ((mPrivateFlags3 & leftMask) != 0) {
+ final boolean canScrollLeft = canScrollHorizontally(-1);
+ if (canScrollLeft) {
+ dr.setBounds(rect.left, rect.top, rect.left + w, rect.bottom);
+ dr.draw(c);
+ }
+ }
+
+ final int rightMask = PFLAG3_SCROLL_INDICATOR_RIGHT | rightRtl;
+ if ((mPrivateFlags3 & rightMask) != 0) {
+ final boolean canScrollRight = canScrollHorizontally(1);
+ if (canScrollRight) {
+ dr.setBounds(rect.right - w, rect.top, rect.right, rect.bottom);
+ dr.draw(c);
+ }
+ }
+ }
+
/**
* <p>Request the drawing of the horizontal and the vertical scrollbar. The
* scrollbars are painted only if they have been awakened first.</p>
@@ -14342,15 +14877,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
- * Indicates whether this view has a static layer. A view with layer type
- * {@link #LAYER_TYPE_NONE} is a static layer. Other types of layers are
- * dynamic.
- */
- boolean hasStaticLayer() {
- return true;
- }
-
- /**
* Indicates what type of layer is currently associated with this view. By default
* a view does not have a layer, and the layer type is {@link #LAYER_TYPE_NONE}.
* Refer to the documentation of {@link #setLayerType(int, android.graphics.Paint)}
@@ -14529,11 +15055,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
return !(mAttachInfo == null || mAttachInfo.mHardwareRenderer == null);
}
- private void updateDisplayListIfDirty() {
+ /**
+ * Gets the RenderNode for the view, and updates its DisplayList (if needed and supported)
+ * @hide
+ */
+ @NonNull
+ public RenderNode updateDisplayListIfDirty() {
final RenderNode renderNode = mRenderNode;
if (!canHaveDisplayList()) {
// can't populate RenderNode, don't try
- return;
+ return renderNode;
}
if ((mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 0
@@ -14547,7 +15078,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
dispatchGetDisplayList();
- return; // no work needed
+ return renderNode; // no work needed
}
// If we got here, we're recreating it. Mark it as such to ensure that
@@ -14596,19 +15127,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags |= PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID;
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
}
- }
-
- /**
- * Returns a RenderNode with View draw content recorded, which can be
- * used to draw this view again without executing its draw method.
- *
- * @return A RenderNode ready to replay, or null if caching is not enabled.
- *
- * @hide
- */
- public RenderNode getDisplayList() {
- updateDisplayListIfDirty();
- return mRenderNode;
+ return renderNode;
}
private void resetDisplayList() {
@@ -15370,7 +15889,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (drawingWithRenderNode) {
// Delay getting the display list until animation-driven alpha values are
// set up and possibly passed on to the view
- renderNode = getDisplayList();
+ renderNode = updateDisplayListIfDirty();
if (!renderNode.isValid()) {
// Uncommon, but possible. If a view is removed from the hierarchy during the call
// to getDisplayList(), the display list will be marked invalid and we should not
@@ -15468,12 +15987,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (drawingWithRenderNode) {
renderNode.setAlpha(alpha * getAlpha() * getTransitionAlpha());
} else if (layerType == LAYER_TYPE_NONE) {
- int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
- if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0) {
- layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
- }
canvas.saveLayerAlpha(sx, sy, sx + getWidth(), sy + getHeight(),
- multipliedAlpha, layerFlags);
+ multipliedAlpha);
}
} else {
// Alpha is handled by the child directly, clobber the layer's alpha
@@ -15509,7 +16024,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (!drawingWithDrawingCache) {
if (drawingWithRenderNode) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
- ((DisplayListCanvas) canvas).drawRenderNode(renderNode, parentFlags);
+ ((DisplayListCanvas) canvas).drawRenderNode(renderNode);
} else {
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
@@ -17095,6 +17610,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @param canvas canvas to draw into
*/
public void onDrawForeground(Canvas canvas) {
+ onDrawScrollIndicators(canvas);
onDrawScrollBars(canvas);
final Drawable foreground = mForegroundInfo != null ? mForegroundInfo.mDrawable : null;
@@ -20764,6 +21280,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
/**
+ * Interface definition for a callback to be invoked when a view is touched with a stylus while
+ * the stylus button is pressed.
+ */
+ public interface OnStylusButtonPressListener {
+ /**
+ * Called when a view is touched with a stylus while the stylus button is pressed.
+ *
+ * @param v The view that was touched.
+ * @return true if the callback consumed the stylus button press, false otherwise.
+ */
+ boolean onStylusButtonPress(View v);
+ }
+
+ /**
* Interface definition for a callback to be invoked when the context menu
* for this view is being built.
*/
diff --git a/core/java/android/view/ViewAssistStructure.java b/core/java/android/view/ViewAssistStructure.java
index 346b8ec..fccfbb8 100644
--- a/core/java/android/view/ViewAssistStructure.java
+++ b/core/java/android/view/ViewAssistStructure.java
@@ -40,6 +40,8 @@ public abstract class ViewAssistStructure {
public abstract void setLongClickable(boolean state);
+ public abstract void setStylusButtonPressable(boolean state);
+
public abstract void setFocusable(boolean state);
public abstract void setFocused(boolean state);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 4324e75..babb4e9 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -18,6 +18,8 @@ package android.view;
import android.animation.LayoutTransition;
import android.annotation.IdRes;
+import android.annotation.NonNull;
+import android.annotation.UiThread;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -105,6 +107,7 @@ import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
* @attr ref android.R.styleable#ViewGroup_splitMotionEvents
* @attr ref android.R.styleable#ViewGroup_layoutMode
*/
+@UiThread
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
private static final String TAG = "ViewGroup";
@@ -3505,8 +3508,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
- if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) &&
- child.hasStaticLayer()) {
+ if (((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null)) {
recreateChildDisplayList(child);
}
}
@@ -3525,10 +3527,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
private void recreateChildDisplayList(View child) {
- child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED)
- == PFLAG_INVALIDATED;
+ child.mRecreateDisplayList = (child.mPrivateFlags & PFLAG_INVALIDATED) != 0;
child.mPrivateFlags &= ~PFLAG_INVALIDATED;
- child.getDisplayList();
+ child.updateDisplayListIfDirty();
child.mRecreateDisplayList = false;
}
@@ -3547,6 +3548,21 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
return child.draw(canvas, this, drawingTime);
}
+ @Override
+ void getScrollIndicatorBounds(@NonNull Rect out) {
+ super.getScrollIndicatorBounds(out);
+
+ // If we have padding and we're supposed to clip children to that
+ // padding, offset the scroll indicators to match our clip bounds.
+ final boolean clipToPadding = (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
+ if (clipToPadding) {
+ out.left += mPaddingLeft;
+ out.right -= mPaddingRight;
+ out.top += mPaddingTop;
+ out.bottom -= mPaddingBottom;
+ }
+ }
+
/**
* Returns whether this group's children are clipped to their bounds before drawing.
* The default value is true.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 4158340..fda6e63 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2322,10 +2322,8 @@ public final class ViewRootImpl implements ViewParent,
* @hide
*/
void outputDisplayList(View view) {
- RenderNode renderNode = view.getDisplayList();
- if (renderNode != null) {
- renderNode.output();
- }
+ RenderNode renderNode = view.updateDisplayListIfDirty();
+ renderNode.output();
}
/**
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 417e22c..b0dbeca 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -684,6 +684,11 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
public static final int TYPE_WINDOWS_CHANGED = 0x00400000;
/**
+ * Represents the event of a stylus button press on a {@link android.view.View}.
+ */
+ public static final int TYPE_VIEW_STYLUS_BUTTON_PRESSED = 0x00800000;
+
+ /**
* Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
* The type of change is not defined.
*/
@@ -731,6 +736,7 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
* @see #TYPE_TOUCH_INTERACTION_START
* @see #TYPE_TOUCH_INTERACTION_END
* @see #TYPE_WINDOWS_CHANGED
+ * @see #TYPE_VIEW_STYLUS_BUTTON_PRESSED
*/
public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
@@ -1396,6 +1402,14 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
builder.append("TYPE_WINDOWS_CHANGED");
eventTypeCount++;
} break;
+ case TYPE_VIEW_STYLUS_BUTTON_PRESSED: {
+ if (eventTypeCount > 0) {
+ builder.append(", ");
+ }
+ builder.append("TYPE_VIEW_STYLUS_BUTTON_PRESSED");
+ eventTypeCount++;
+ }
+ break;
}
}
if (eventTypeCount > 1) {
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 0736ed8..c785149 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -520,6 +520,8 @@ public class AccessibilityNodeInfo implements Parcelable {
private static final int BOOLEAN_PROPERTY_CONTENT_INVALID = 0x00010000;
+ private static final int BOOLEAN_PROPERTY_STYLUS_BUTTON_PRESSABLE = 0x00020000;
+
/**
* Bits that provide the id of a virtual descendant of a view.
*/
@@ -1930,6 +1932,30 @@ public class AccessibilityNodeInfo implements Parcelable {
}
/**
+ * Gets whether this node is stylus button pressable.
+ *
+ * @return True if the node is stylus button pressable.
+ */
+ public boolean isStylusButtonPressable() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_STYLUS_BUTTON_PRESSABLE);
+ }
+
+ /**
+ * Sets whether this node is stylus button pressable.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}. This class is made immutable
+ * before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param stylusButtonPressable True if the node is stylus button pressable.
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setStylusButtonPressable(boolean stylusButtonPressable) {
+ setBooleanProperty(BOOLEAN_PROPERTY_STYLUS_BUTTON_PRESSABLE, stylusButtonPressable);
+ }
+
+ /**
* Gets the node's live region mode.
* <p>
* A live region is a node that contains information that is important for
@@ -3117,6 +3143,7 @@ public class AccessibilityNodeInfo implements Parcelable {
builder.append("; selected: ").append(isSelected());
builder.append("; clickable: ").append(isClickable());
builder.append("; longClickable: ").append(isLongClickable());
+ builder.append("; stylusButtonPressable: ").append(isStylusButtonPressable());
builder.append("; enabled: ").append(isEnabled());
builder.append("; password: ").append(isPassword());
builder.append("; scrollable: ").append(isScrollable());
@@ -3472,6 +3499,36 @@ public class AccessibilityNodeInfo implements Parcelable {
public static final AccessibilityAction ACTION_SCROLL_TO_POSITION =
new AccessibilityAction(R.id.accessibilityActionScrollToPosition, null);
+ /**
+ * Action to scroll the node content up.
+ */
+ public static final AccessibilityAction ACTION_SCROLL_UP =
+ new AccessibilityAction(R.id.accessibilityActionScrollUp, null);
+
+ /**
+ * Action to scroll the node content left.
+ */
+ public static final AccessibilityAction ACTION_SCROLL_LEFT =
+ new AccessibilityAction(R.id.accessibilityActionScrollLeft, null);
+
+ /**
+ * Action to scroll the node content down.
+ */
+ public static final AccessibilityAction ACTION_SCROLL_DOWN =
+ new AccessibilityAction(R.id.accessibilityActionScrollDown, null);
+
+ /**
+ * Action to scroll the node content right.
+ */
+ public static final AccessibilityAction ACTION_SCROLL_RIGHT =
+ new AccessibilityAction(R.id.accessibilityActionScrollRight, null);
+
+ /**
+ * Action that stylus button presses the node.
+ */
+ public static final AccessibilityAction ACTION_STYLUS_BUTTON_PRESS =
+ new AccessibilityAction(R.id.accessibilityActionStylusButtonPress, null);
+
private static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<>();
static {
sStandardActions.add(ACTION_FOCUS);
@@ -3498,6 +3555,11 @@ public class AccessibilityNodeInfo implements Parcelable {
sStandardActions.add(ACTION_SET_TEXT);
sStandardActions.add(ACTION_SHOW_ON_SCREEN);
sStandardActions.add(ACTION_SCROLL_TO_POSITION);
+ sStandardActions.add(ACTION_SCROLL_UP);
+ sStandardActions.add(ACTION_SCROLL_LEFT);
+ sStandardActions.add(ACTION_SCROLL_DOWN);
+ sStandardActions.add(ACTION_SCROLL_RIGHT);
+ sStandardActions.add(ACTION_STYLUS_BUTTON_PRESS);
}
private final int mActionId;
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index c9d9a8c..c57a53a 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1506,10 +1506,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
if (isEnabled()) {
if (canScrollUp()) {
info.addAction(AccessibilityAction.ACTION_SCROLL_BACKWARD);
+ info.addAction(AccessibilityAction.ACTION_SCROLL_UP);
info.setScrollable(true);
}
if (canScrollDown()) {
info.addAction(AccessibilityAction.ACTION_SCROLL_FORWARD);
+ info.addAction(AccessibilityAction.ACTION_SCROLL_DOWN);
info.setScrollable(true);
}
}
@@ -1537,14 +1539,16 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
return true;
}
switch (action) {
- case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+ case R.id.accessibilityActionScrollDown: {
if (isEnabled() && getLastVisiblePosition() < getCount() - 1) {
final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom;
smoothScrollBy(viewportHeight, PositionScroller.SCROLL_DURATION);
return true;
}
} return false;
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+ case R.id.accessibilityActionScrollUp: {
if (isEnabled() && mFirstPosition > 0) {
final int viewportHeight = getHeight() - mListPadding.top - mListPadding.bottom;
smoothScrollBy(-viewportHeight, PositionScroller.SCROLL_DURATION);
@@ -3638,11 +3642,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
startNestedScroll(SCROLL_AXIS_VERTICAL);
- if (mFastScroll != null) {
- boolean intercepted = mFastScroll.onTouchEvent(ev);
- if (intercepted) {
- return true;
- }
+ if (mFastScroll != null && mFastScroll.onTouchEvent(ev)) {
+ return true;
}
initVelocityTrackerIfNotExists();
diff --git a/core/java/android/widget/CursorAdapter.java b/core/java/android/widget/CursorAdapter.java
index 30c74c0..d8ce60c 100644
--- a/core/java/android/widget/CursorAdapter.java
+++ b/core/java/android/widget/CursorAdapter.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.WorkerThread;
import android.content.Context;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -425,6 +426,7 @@ public abstract class CursorAdapter extends BaseAdapter implements Filterable,
* @see #getFilterQueryProvider()
* @see #setFilterQueryProvider(android.widget.FilterQueryProvider)
*/
+ @WorkerThread
public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
if (mFilterQueryProvider != null) {
return mFilterQueryProvider.runQuery(constraint);
diff --git a/core/java/android/widget/DayPickerPagerAdapter.java b/core/java/android/widget/DayPickerPagerAdapter.java
index 478fa00..d271af2 100644
--- a/core/java/android/widget/DayPickerPagerAdapter.java
+++ b/core/java/android/widget/DayPickerPagerAdapter.java
@@ -286,14 +286,10 @@ class DayPickerPagerAdapter extends PagerAdapter {
return null;
}
- private boolean isCalendarInRange(Calendar value) {
- return value.compareTo(mMinDate) >= 0 && value.compareTo(mMaxDate) <= 0;
- }
-
private final OnDayClickListener mOnDayClickListener = new OnDayClickListener() {
@Override
public void onDayClick(SimpleMonthView view, Calendar day) {
- if (day != null && isCalendarInRange(day)) {
+ if (day != null) {
setSelectedDay(day);
if (mOnDaySelectedListener != null) {
diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java
index 113e597..334afab 100644
--- a/core/java/android/widget/DayPickerView.java
+++ b/core/java/android/widget/DayPickerView.java
@@ -178,6 +178,13 @@ class DayPickerView extends ViewGroup {
});
}
+ private void updateButtonVisibility(int position) {
+ final boolean hasPrev = position > 0;
+ final boolean hasNext = position < (mAdapter.getCount() - 1);
+ mPrevButton.setVisibility(hasPrev ? View.VISIBLE : View.INVISIBLE);
+ mNextButton.setVisibility(hasNext ? View.VISIBLE : View.INVISIBLE);
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final ViewPager viewPager = mViewPager;
@@ -218,12 +225,6 @@ class DayPickerView extends ViewGroup {
final int height = bottom - top;
mViewPager.layout(0, 0, width, height);
- if (mViewPager.getChildCount() < 1) {
- leftButton.setVisibility(View.INVISIBLE);
- rightButton.setVisibility(View.INVISIBLE);
- return;
- }
-
final SimpleMonthView monthView = (SimpleMonthView) mViewPager.getChildAt(0);
final int monthHeight = monthView.getMonthHeight();
final int cellWidth = monthView.getCellWidth();
@@ -235,7 +236,6 @@ class DayPickerView extends ViewGroup {
final int leftIconTop = monthView.getPaddingTop() + (monthHeight - leftDH) / 2;
final int leftIconLeft = monthView.getPaddingLeft() + (cellWidth - leftDW) / 2;
leftButton.layout(leftIconLeft, leftIconTop, leftIconLeft + leftDW, leftIconTop + leftDH);
- leftButton.setVisibility(View.VISIBLE);
final int rightDW = rightButton.getMeasuredWidth();
final int rightDH = rightButton.getMeasuredHeight();
@@ -243,7 +243,6 @@ class DayPickerView extends ViewGroup {
final int rightIconRight = width - monthView.getPaddingRight() - (cellWidth - rightDW) / 2;
rightButton.layout(rightIconRight - rightDW, rightIconTop,
rightIconRight, rightIconTop + rightDH);
- rightButton.setVisibility(View.VISIBLE);
}
public void setDayOfWeekTextAppearance(int resId) {
@@ -399,10 +398,7 @@ class DayPickerView extends ViewGroup {
@Override
public void onPageSelected(int position) {
- mPrevButton.setVisibility(
- position > 0 ? View.VISIBLE : View.INVISIBLE);
- mNextButton.setVisibility(
- position < (mAdapter.getCount() - 1) ? View.VISIBLE : View.INVISIBLE);
+ updateButtonVisibility(position);
}
};
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index f1be434..8d35b83 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -62,7 +62,6 @@ import android.text.TextUtils;
import android.text.method.KeyListener;
import android.text.method.MetaKeyKeyListener;
import android.text.method.MovementMethod;
-import android.text.method.PasswordTransformationMethod;
import android.text.method.WordIterator;
import android.text.style.EasyEditSpan;
import android.text.style.SuggestionRangeSpan;
@@ -145,16 +144,16 @@ public class Editor {
InputContentType mInputContentType;
InputMethodState mInputMethodState;
- private static class TextDisplayList {
- RenderNode displayList;
+ private static class TextRenderNode {
+ RenderNode renderNode;
boolean isDirty;
- public TextDisplayList(String name) {
+ public TextRenderNode(String name) {
isDirty = true;
- displayList = RenderNode.create(name, null);
+ renderNode = RenderNode.create(name, null);
}
- boolean needsRecord() { return isDirty || !displayList.isValid(); }
+ boolean needsRecord() { return isDirty || !renderNode.isValid(); }
}
- TextDisplayList[] mTextDisplayLists;
+ TextRenderNode[] mTextRenderNodes;
boolean mFrozenWithFocus;
boolean mSelectionMoved;
@@ -319,6 +318,7 @@ public class Editor {
}
getPositionListener().addSubscriber(mCursorAnchorInfoNotifier, true);
+ resumeBlink();
}
void onDetachedFromWindow() {
@@ -328,9 +328,7 @@ public class Editor {
hideError();
}
- if (mBlink != null) {
- mBlink.removeCallbacks(mBlink);
- }
+ suspendBlink();
if (mInsertionPointCursorController != null) {
mInsertionPointCursorController.onDetached();
@@ -360,10 +358,10 @@ public class Editor {
}
private void destroyDisplayListsData() {
- if (mTextDisplayLists != null) {
- for (int i = 0; i < mTextDisplayLists.length; i++) {
- RenderNode displayList = mTextDisplayLists[i] != null
- ? mTextDisplayLists[i].displayList : null;
+ if (mTextRenderNodes != null) {
+ for (int i = 0; i < mTextRenderNodes.length; i++) {
+ RenderNode displayList = mTextRenderNodes[i] != null
+ ? mTextRenderNodes[i].renderNode : null;
if (displayList != null && displayList.isValid()) {
displayList.destroyDisplayListData();
}
@@ -579,7 +577,12 @@ public class Editor {
}
private void hideCursorControllers() {
- if (mSuggestionsPopupWindow != null && !mSuggestionsPopupWindow.isShowingUp()) {
+ // When mTextView is not ExtractEditText, we need to distinguish two kinds of focus-lost.
+ // One is the true focus lost where suggestions pop-up (if any) should be dismissed, and the
+ // other is an side effect of showing the suggestions pop-up itself. We use isShowingUp()
+ // to distinguish one from the other.
+ if (mSuggestionsPopupWindow != null && ((mTextView instanceof ExtractEditText) ||
+ !mSuggestionsPopupWindow.isShowingUp())) {
// Should be done before hide insertion point controller since it triggers a show of it
mSuggestionsPopupWindow.hide();
}
@@ -682,34 +685,6 @@ public class Editor {
}
}
- /**
- * Unlike {@link TextView#textCanBeSelected()}, this method is based on the <i>current</i> state
- * of the TextView. textCanBeSelected() has to be true (this is one of the conditions to have
- * a selection controller (see {@link #prepareCursorControllers()}), but this is not sufficient.
- */
- private boolean canSelectText() {
- return hasSelectionController() && mTextView.getText().length() != 0;
- }
-
- /**
- * It would be better to rely on the input type for everything. A password inputType should have
- * a password transformation. We should hence use isPasswordInputType instead of this method.
- *
- * We should:
- * - Call setInputType in setKeyListener instead of changing the input type directly (which
- * would install the correct transformation).
- * - Refuse the installation of a non-password transformation in setTransformation if the input
- * type is password.
- *
- * However, this is like this for legacy reasons and we cannot break existing apps. This method
- * is useful since it matches what the user can see (obfuscated text or not).
- *
- * @return true if the current transformation method is of the password type.
- */
- private boolean hasPasswordTransformationMethod() {
- return mTextView.getTransformationMethod() instanceof PasswordTransformationMethod;
- }
-
private int getWordStart(int offset) {
// FIXME - For this and similar methods we're not doing anything to check if there's
// a LocaleSpan in the text, this may be something we should try handling or checking for.
@@ -758,11 +733,11 @@ public class Editor {
* successfully performed.
*/
private boolean selectCurrentWord() {
- if (!canSelectText()) {
+ if (!mTextView.canSelectText()) {
return false;
}
- if (hasPasswordTransformationMethod()) {
+ if (mTextView.hasPasswordTransformationMethod()) {
// Always select all on a password field.
// Cut/copy menu entries are not available for passwords, but being able to select all
// is however useful to delete or paste to replace the entire content.
@@ -1426,12 +1401,11 @@ public class Editor {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
if (imm.isActive(mTextView)) {
- boolean reported = false;
if (ims.mContentChanged || ims.mSelectionModeChanged) {
// We are in extract mode and the content has changed
// in some way... just report complete new text to the
// input method.
- reported = reportExtractedText();
+ reportExtractedText();
}
}
}
@@ -1467,8 +1441,8 @@ public class Editor {
firstLine, lastLine);
if (layout instanceof DynamicLayout) {
- if (mTextDisplayLists == null) {
- mTextDisplayLists = ArrayUtils.emptyArray(TextDisplayList.class);
+ if (mTextRenderNodes == null) {
+ mTextRenderNodes = ArrayUtils.emptyArray(TextRenderNode.class);
}
DynamicLayout dynamicLayout = (DynamicLayout) layout;
@@ -1489,19 +1463,19 @@ public class Editor {
searchStartIndex);
// Note how dynamic layout's internal block indices get updated from Editor
blockIndices[i] = blockIndex;
- if (mTextDisplayLists[blockIndex] != null) {
- mTextDisplayLists[blockIndex].isDirty = true;
+ if (mTextRenderNodes[blockIndex] != null) {
+ mTextRenderNodes[blockIndex].isDirty = true;
}
searchStartIndex = blockIndex + 1;
}
- if (mTextDisplayLists[blockIndex] == null) {
- mTextDisplayLists[blockIndex] =
- new TextDisplayList("Text " + blockIndex);
+ if (mTextRenderNodes[blockIndex] == null) {
+ mTextRenderNodes[blockIndex] =
+ new TextRenderNode("Text " + blockIndex);
}
- final boolean blockDisplayListIsInvalid = mTextDisplayLists[blockIndex].needsRecord();
- RenderNode blockDisplayList = mTextDisplayLists[blockIndex].displayList;
+ final boolean blockDisplayListIsInvalid = mTextRenderNodes[blockIndex].needsRecord();
+ RenderNode blockDisplayList = mTextRenderNodes[blockIndex].renderNode;
if (i >= indexFirstChangedBlock || blockDisplayListIsInvalid) {
final int blockBeginLine = endOfPreviousBlock + 1;
final int top = layout.getLineTop(blockBeginLine);
@@ -1528,7 +1502,7 @@ public class Editor {
// brings this range of text back to the top left corner of the viewport
displayListCanvas.translate(-left, -top);
layout.drawText(displayListCanvas, blockBeginLine, blockEndLine);
- mTextDisplayLists[blockIndex].isDirty = false;
+ mTextRenderNodes[blockIndex].isDirty = false;
// No need to untranslate, previous context is popped after
// drawDisplayList
} finally {
@@ -1543,8 +1517,7 @@ public class Editor {
blockDisplayList.setLeftTopRightBottom(left, top, right, bottom);
}
- ((DisplayListCanvas) canvas).drawRenderNode(blockDisplayList,
- 0 /* no child clipping, our TextView parent enforces it */);
+ ((DisplayListCanvas) canvas).drawRenderNode(blockDisplayList);
endOfPreviousBlock = blockEndLine;
}
@@ -1558,7 +1531,7 @@ public class Editor {
private int getAvailableDisplayListIndex(int[] blockIndices, int numberOfBlocks,
int searchStartIndex) {
- int length = mTextDisplayLists.length;
+ int length = mTextRenderNodes.length;
for (int i = searchStartIndex; i < length; i++) {
boolean blockIndexFound = false;
for (int j = 0; j < numberOfBlocks; j++) {
@@ -1572,7 +1545,7 @@ public class Editor {
}
// No available index found, the pool has to grow
- mTextDisplayLists = GrowingArrayUtils.append(mTextDisplayLists, length, null);
+ mTextRenderNodes = GrowingArrayUtils.append(mTextRenderNodes, length, null);
return length;
}
@@ -1589,7 +1562,7 @@ public class Editor {
* Invalidates all the sub-display lists that overlap the specified character range
*/
void invalidateTextDisplayList(Layout layout, int start, int end) {
- if (mTextDisplayLists != null && layout instanceof DynamicLayout) {
+ if (mTextRenderNodes != null && layout instanceof DynamicLayout) {
final int firstLine = layout.getLineForOffset(start);
final int lastLine = layout.getLineForOffset(end);
@@ -1609,7 +1582,7 @@ public class Editor {
while (i < numberOfBlocks) {
final int blockIndex = blockIndices[i];
if (blockIndex != DynamicLayout.INVALID_BLOCK_INDEX) {
- mTextDisplayLists[blockIndex].isDirty = true;
+ mTextRenderNodes[blockIndex].isDirty = true;
}
if (blockEndLines[i] >= lastLine) break;
i++;
@@ -1618,9 +1591,9 @@ public class Editor {
}
void invalidateTextDisplayList() {
- if (mTextDisplayLists != null) {
- for (int i = 0; i < mTextDisplayLists.length; i++) {
- if (mTextDisplayLists[i] != null) mTextDisplayLists[i].isDirty = true;
+ if (mTextRenderNodes != null) {
+ for (int i = 0; i < mTextRenderNodes.length; i++) {
+ if (mTextRenderNodes[i] != null) mTextRenderNodes[i].isDirty = true;
}
}
}
@@ -1718,7 +1691,7 @@ public class Editor {
return false;
}
- if (!canSelectText() || !mTextView.requestFocus()) {
+ if (!mTextView.canSelectText() || !mTextView.requestFocus()) {
Log.w(TextView.LOG_TAG,
"TextView does not support text selection. Action mode cancelled.");
return false;
@@ -1949,10 +1922,6 @@ public class Editor {
mSuggestionsPopupWindow.show();
}
- boolean areSuggestionsShown() {
- return mSuggestionsPopupWindow != null && mSuggestionsPopupWindow.isShowing();
- }
-
void onScrollChanged() {
if (mPositionListener != null) {
mPositionListener.onScrollChanged();
@@ -3090,7 +3059,7 @@ public class Editor {
MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
- if (canSelectText() && !hasPasswordTransformationMethod()) {
+ if (mTextView.canSelectAllText()) {
menu.add(0, TextView.ID_SELECT_ALL, 0, com.android.internal.R.string.selectAll).
setAlphabeticShortcut('a').
setShowAsAction(
@@ -3563,7 +3532,7 @@ public class Editor {
return false;
}
- return isPositionVisible(mPositionX + mHotspotX, mPositionY);
+ return isPositionVisible(mPositionX + mHotspotX + getHorizontalOffset(), mPositionY);
}
public abstract int getCurrentCursorOffset();
@@ -3847,6 +3816,10 @@ public class Editor {
startSelectionActionModeWithoutSelection();
}
}
+ } else {
+ if (mSelectionActionMode != null) {
+ mSelectionActionMode.invalidateContentRect();
+ }
}
hideAfterDelay();
break;
@@ -4491,7 +4464,7 @@ public class Editor {
private class CorrectionHighlighter {
private final Path mPath = new Path();
- private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Paint mPaint = new Paint();
private int mStart, mEnd;
private long mFadingStartTime;
private RectF mTempRectF;
@@ -4646,8 +4619,6 @@ public class Editor {
}
static class InputMethodState {
- Rect mCursorRectInWindow = new Rect();
- float[] mTmpOffset = new float[2];
ExtractedTextRequest mExtractedTextRequest;
final ExtractedText mExtractedText = new ExtractedText();
int mBatchEditNesting;
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 552b274..f06f3c2 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -1389,7 +1389,8 @@ class FastScroller {
// to intercept events. If it does, we will receive a CANCEL
// event.
if (!mList.isInScrollingContainer()) {
- beginDrag();
+ // This will get dispatched to onTouchEvent(). Start
+ // dragging there.
return true;
}
@@ -1406,6 +1407,8 @@ class FastScroller {
final float pos = getPosFromMotionEvent(mInitialTouchY);
scrollTo(pos);
+ // This may get dispatched to onTouchEvent(), but it
+ // doesn't really matter since we'll already be in a drag.
return onTouchEvent(ev);
}
break;
@@ -1440,6 +1443,15 @@ class FastScroller {
}
switch (me.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ if (isPointInside(me.getX(), me.getY())) {
+ if (!mList.isInScrollingContainer()) {
+ beginDrag();
+ return true;
+ }
+ }
+ } break;
+
case MotionEvent.ACTION_UP: {
if (mPendingDrag >= 0) {
// Allow a tap to scroll.
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 324c2aa..0879c5d 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -40,6 +40,8 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AnimationUtils;
+import com.android.internal.R;
+
import java.util.List;
/**
@@ -768,7 +770,8 @@ public class HorizontalScrollView extends FrameLayout {
return true;
}
switch (action) {
- case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+ case R.id.accessibilityActionScrollRight: {
if (!isEnabled()) {
return false;
}
@@ -779,7 +782,8 @@ public class HorizontalScrollView extends FrameLayout {
return true;
}
} return false;
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+ case R.id.accessibilityActionScrollLeft: {
if (!isEnabled()) {
return false;
}
@@ -807,10 +811,12 @@ public class HorizontalScrollView extends FrameLayout {
if (scrollRange > 0) {
info.setScrollable(true);
if (isEnabled() && mScrollX > 0) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_LEFT);
}
if (isEnabled() && mScrollX < scrollRange) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_RIGHT);
}
}
}
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index 2375089..8d8b3a3 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -326,6 +326,16 @@ public class MediaController extends FrameLayout {
if (mFfwdButton != null && !mPlayer.canSeekForward()) {
mFfwdButton.setEnabled(false);
}
+ // TODO What we really should do is add a canSeek to the MediaPlayerControl interface;
+ // this scheme can break the case when applications want to allow seek through the
+ // progress bar but disable forward/backward buttons.
+ //
+ // However, currently the flags SEEK_BACKWARD_AVAILABLE, SEEK_FORWARD_AVAILABLE,
+ // and SEEK_AVAILABLE are all (un)set together; as such the aforementioned issue
+ // shouldn't arise in existing applications.
+ if (mProgress != null && !mPlayer.canSeekBackward() && !mPlayer.canSeekForward()) {
+ mProgress.setEnabled(false);
+ }
} catch (IncompatibleClassChangeError ex) {
// We were given an old version of the interface, that doesn't have
// the canPause/canSeekXYZ methods. This is OK, it just means we
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 10e4db3..5953a98 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -618,7 +618,15 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback
// remove based on both its position as well as it's current memory usage, as well
// as whether it was directly requested vs. whether it was preloaded by our caching
// mechanism.
- mIndexRemoteViews.remove(getFarthestPositionFrom(pruneFromPosition, visibleWindow));
+ int trimIndex = getFarthestPositionFrom(pruneFromPosition, visibleWindow);
+
+ // Need to check that this is a valid index, to cover the case where you have only
+ // a single view in the cache, but it's larger than the max memory limit
+ if (trimIndex < 0) {
+ break;
+ }
+
+ mIndexRemoteViews.remove(trimIndex);
}
// Update the metadata cache
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 2026169..98d61d3 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -819,7 +819,8 @@ public class ScrollView extends FrameLayout {
return false;
}
switch (action) {
- case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
+ case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
+ case R.id.accessibilityActionScrollDown: {
final int viewportHeight = getHeight() - mPaddingBottom - mPaddingTop;
final int targetScrollY = Math.min(mScrollY + viewportHeight, getScrollRange());
if (targetScrollY != mScrollY) {
@@ -827,7 +828,8 @@ public class ScrollView extends FrameLayout {
return true;
}
} return false;
- case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
+ case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
+ case R.id.accessibilityActionScrollUp: {
final int viewportHeight = getHeight() - mPaddingBottom - mPaddingTop;
final int targetScrollY = Math.max(mScrollY - viewportHeight, 0);
if (targetScrollY != mScrollY) {
@@ -853,10 +855,13 @@ public class ScrollView extends FrameLayout {
if (scrollRange > 0) {
info.setScrollable(true);
if (mScrollY > 0) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
+ info.addAction(
+ AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
}
if (mScrollY < scrollRange) {
- info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
+ info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
}
}
}
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index bbf120a..088adbb 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -18,6 +18,7 @@ package android.widget;
import static android.widget.SuggestionsAdapter.getColumnString;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.app.SearchManager;
import android.app.SearchableInfo;
@@ -120,6 +121,8 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
private final Intent mVoiceWebSearchIntent;
private final Intent mVoiceAppSearchIntent;
+ private final CharSequence mDefaultQueryHint;
+
private OnQueryTextListener mOnQueryChangeListener;
private OnCloseListener mOnCloseListener;
private OnFocusChangeListener mOnQueryTextFocusChangeListener;
@@ -329,10 +332,8 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
setMaxWidth(maxWidth);
}
- final CharSequence queryHint = a.getText(R.styleable.SearchView_queryHint);
- if (!TextUtils.isEmpty(queryHint)) {
- setQueryHint(queryHint);
- }
+ mDefaultQueryHint = a.getText(R.styleable.SearchView_defaultQueryHint);
+ mQueryHint = a.getText(R.styleable.SearchView_queryHint);
final int imeOptions = a.getInt(R.styleable.SearchView_imeOptions, -1);
if (imeOptions != -1) {
@@ -570,36 +571,48 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
}
/**
- * Sets the hint text to display in the query text field. This overrides any hint specified
- * in the SearchableInfo.
- *
- * @param hint the hint text to display
+ * Sets the hint text to display in the query text field. This overrides
+ * any hint specified in the {@link SearchableInfo}.
+ * <p>
+ * This value may be specified as an empty string to prevent any query hint
+ * from being displayed.
*
+ * @param hint the hint text to display or {@code null} to clear
* @attr ref android.R.styleable#SearchView_queryHint
*/
- public void setQueryHint(CharSequence hint) {
+ public void setQueryHint(@Nullable CharSequence hint) {
mQueryHint = hint;
updateQueryHint();
}
/**
- * Gets the hint text to display in the query text field.
- * @return the query hint text, if specified, null otherwise.
+ * Returns the hint text that will be displayed in the query text field.
+ * <p>
+ * The displayed query hint is chosen in the following order:
+ * <ol>
+ * <li>Non-null value set with {@link #setQueryHint(CharSequence)}
+ * <li>Value specified in XML using
+ * {@link android.R.styleable#SearchView_queryHint android:queryHint}
+ * <li>Valid string resource ID exposed by the {@link SearchableInfo} via
+ * {@link SearchableInfo#getHintId()}
+ * <li>Default hint provided by the theme against which the view was
+ * inflated
+ * </ol>
*
+ * @return the displayed query hint text, or {@code null} if none set
* @attr ref android.R.styleable#SearchView_queryHint
*/
+ @Nullable
public CharSequence getQueryHint() {
+ final CharSequence hint;
if (mQueryHint != null) {
- return mQueryHint;
- } else if (mSearchable != null) {
- CharSequence hint = null;
- int hintId = mSearchable.getHintId();
- if (hintId != 0) {
- hint = getContext().getString(hintId);
- }
- return hint;
+ hint = mQueryHint;
+ } else if (mSearchable != null && mSearchable.getHintId() != 0) {
+ hint = getContext().getText(mSearchable.getHintId());
+ } else {
+ hint = mDefaultQueryHint;
}
- return null;
+ return hint;
}
/**
@@ -1113,20 +1126,8 @@ public class SearchView extends LinearLayout implements CollapsibleActionView {
}
private void updateQueryHint() {
- if (mQueryHint != null) {
- mSearchSrcTextView.setHint(getDecoratedHint(mQueryHint));
- } else if (mSearchable != null) {
- CharSequence hint = null;
- int hintId = mSearchable.getHintId();
- if (hintId != 0) {
- hint = getContext().getString(hintId);
- }
- if (hint != null) {
- mSearchSrcTextView.setHint(getDecoratedHint(hint));
- }
- } else {
- mSearchSrcTextView.setHint(getDecoratedHint(""));
- }
+ final CharSequence hint = getQueryHint();
+ mSearchSrcTextView.setHint(getDecoratedHint(hint == null ? "" : hint));
}
/**
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 2778f0f..acf1df9 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -31,6 +31,7 @@ import android.text.TextPaint;
import android.text.format.DateFormat;
import android.util.AttributeSet;
import android.util.IntArray;
+import android.util.MathUtils;
import android.util.StateSet;
import android.view.MotionEvent;
import android.view.View;
@@ -422,7 +423,8 @@ class SimpleMonthView extends View {
int stateMask = 0;
- if (day >= mEnabledDayStart && day <= mEnabledDayEnd) {
+ final boolean isDayEnabled = isDayEnabled(day);
+ if (isDayEnabled) {
stateMask |= StateSet.VIEW_STATE_ENABLED;
}
@@ -435,8 +437,11 @@ class SimpleMonthView extends View {
} else if (mTouchedItem == day) {
stateMask |= StateSet.VIEW_STATE_PRESSED;
- // Adjust the circle to be centered on the row.
- canvas.drawCircle(colCenterRtl, rowCenter, mDaySelectorRadius, mDayHighlightPaint);
+ if (isDayEnabled) {
+ // Adjust the circle to be centered on the row.
+ canvas.drawCircle(colCenterRtl, rowCenter,
+ mDaySelectorRadius, mDayHighlightPaint);
+ }
}
final boolean isDayToday = mToday == day;
@@ -460,6 +465,14 @@ class SimpleMonthView extends View {
}
}
+ private boolean isDayEnabled(int day) {
+ return day >= mEnabledDayStart && day <= mEnabledDayEnd;
+ }
+
+ private boolean isValidDayOfMonth(int day) {
+ return day >= 1 && day <= mDaysInMonth;
+ }
+
private static boolean isValidDayOfWeek(int day) {
return day >= Calendar.SUNDAY && day <= Calendar.SATURDAY;
}
@@ -536,13 +549,6 @@ class SimpleMonthView extends View {
mWeekStart = mCalendar.getFirstDayOfWeek();
}
- if (enabledDayStart > 0 && enabledDayEnd < 32) {
- mEnabledDayStart = enabledDayStart;
- }
- if (enabledDayEnd > 0 && enabledDayEnd < 32 && enabledDayEnd >= enabledDayStart) {
- mEnabledDayEnd = enabledDayEnd;
- }
-
// Figure out what day today is.
final Calendar today = Calendar.getInstance();
mToday = -1;
@@ -554,6 +560,9 @@ class SimpleMonthView extends View {
}
}
+ mEnabledDayStart = MathUtils.constrain(enabledDayStart, 1, mDaysInMonth);
+ mEnabledDayEnd = MathUtils.constrain(enabledDayEnd, mEnabledDayStart, mDaysInMonth);
+
// Invalidate the old title.
mTitle = null;
@@ -694,7 +703,7 @@ class SimpleMonthView extends View {
final int col = (paddedXRtl * DAYS_IN_WEEK) / mPaddedWidth;
final int index = col + row * DAYS_IN_WEEK;
final int day = index + 1 - findDayOffset();
- if (day < 1 || day > mDaysInMonth) {
+ if (!isValidDayOfMonth(day)) {
return -1;
}
@@ -708,7 +717,7 @@ class SimpleMonthView extends View {
* @param outBounds the rect to populate with bounds
*/
private boolean getBoundsForDay(int id, Rect outBounds) {
- if (id < 1 || id > mDaysInMonth) {
+ if (!isValidDayOfMonth(id)) {
return false;
}
@@ -742,7 +751,7 @@ class SimpleMonthView extends View {
* @param day the day that was clicked
*/
private boolean onDayClicked(int day) {
- if (day < 0 || day > mDaysInMonth) {
+ if (!isValidDayOfMonth(day) || !isDayEnabled(day)) {
return false;
}
@@ -774,7 +783,7 @@ class SimpleMonthView extends View {
@Override
protected int getVirtualViewAt(float x, float y) {
final int day = getDayAtLocation((int) (x + 0.5f), (int) (y + 0.5f));
- if (day >= 0) {
+ if (day != -1) {
return day;
}
return ExploreByTouchHelper.INVALID_ID;
@@ -808,7 +817,13 @@ class SimpleMonthView extends View {
node.setText(getDayText(virtualViewId));
node.setContentDescription(getDayDescription(virtualViewId));
node.setBoundsInParent(mTempRect);
- node.addAction(AccessibilityAction.ACTION_CLICK);
+
+ final boolean isDayEnabled = isDayEnabled(virtualViewId);
+ if (isDayEnabled) {
+ node.addAction(AccessibilityAction.ACTION_CLICK);
+ }
+
+ node.setEnabled(isDayEnabled);
if (virtualViewId == mActivatedDay) {
// TODO: This should use activated once that's supported.
@@ -835,7 +850,7 @@ class SimpleMonthView extends View {
* @return a description of the virtual view
*/
private CharSequence getDayDescription(int id) {
- if (id >= 1 && id <= mDaysInMonth) {
+ if (isValidDayOfMonth(id)) {
mTempCalendar.set(mYear, mMonth, id);
return DateFormat.format(DATE_FORMAT, mTempCalendar.getTimeInMillis());
}
@@ -850,7 +865,7 @@ class SimpleMonthView extends View {
* @return the visible text of the virtual view
*/
private CharSequence getDayText(int id) {
- if (id >= 1 && id <= mDaysInMonth) {
+ if (isValidDayOfMonth(id)) {
return Integer.toString(id);
}
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index ae779fe..f94f97c 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -216,7 +216,7 @@ public class Switch extends CompoundButton {
public Switch(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
- mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
+ mTextPaint = new TextPaint();
final Resources res = getResources();
mTextPaint.density = res.getDisplayMetrics().density;
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3e8df08..449173f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -668,11 +668,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final Resources res = getResources();
final CompatibilityInfo compat = res.getCompatibilityInfo();
- mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
+ mTextPaint = new TextPaint();
mTextPaint.density = res.getDisplayMetrics().density;
mTextPaint.setCompatibilityScaling(compat.applicationScale);
- mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mHighlightPaint = new Paint();
mHighlightPaint.setCompatibilityScaling(compat.applicationScale);
mMovement = getDefaultMovementMethod();
@@ -4569,7 +4569,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*
* @return true if the current transformation method is of the password type.
*/
- private boolean hasPasswordTransformationMethod() {
+ boolean hasPasswordTransformationMethod() {
return mTransformation instanceof PasswordTransformationMethod;
}
@@ -6630,12 +6630,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
// TODO: code duplication with makeSingleLayout()
if (mHintLayout == null) {
StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0,
- mHint.length(), hintWidth)
- .setPaint(mTextPaint)
+ mHint.length(), mTextPaint, hintWidth)
.setAlignment(alignment)
.setTextDir(mTextDir)
- .setSpacingMult(mSpacingMult)
- .setSpacingAdd(mSpacingAdd)
+ .setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setBreakStrategy(mBreakStrategy);
if (mLeftIndents != null || mRightIndents != null) {
@@ -6721,12 +6719,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (result == null) {
StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
- 0, mTransformed.length(), wantWidth)
- .setPaint(mTextPaint)
+ 0, mTransformed.length(), mTextPaint, wantWidth)
.setAlignment(alignment)
.setTextDir(mTextDir)
- .setSpacingMult(mSpacingMult)
- .setSpacingAdd(mSpacingAdd)
+ .setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setBreakStrategy(mBreakStrategy);
if (mLeftIndents != null || mRightIndents != null) {
@@ -8583,7 +8579,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* a selection controller (see {@link Editor#prepareCursorControllers()}), but this is not
* sufficient.
*/
- private boolean canSelectText() {
+ boolean canSelectText() {
return mText.length() != 0 && mEditor != null && mEditor.hasSelectionController();
}
@@ -9199,6 +9195,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return false;
}
+ boolean canSelectAllText() {
+ return canSelectText() && !hasPasswordTransformationMethod();
+ }
+
boolean selectAllText() {
// Need to hide insert point cursor controller before settings selection, otherwise insert
// point cursor controller obtains cursor update event and update cursor with cancelling