diff options
65 files changed, 939 insertions, 543 deletions
diff --git a/api/current.txt b/api/current.txt index 68ecf96..e16294e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3341,6 +3341,7 @@ package android.app { method public boolean isImmersive(); method public boolean isTaskRoot(); method public boolean isVoiceInteraction(); + method public boolean isVoiceInteractionRoot(); method public final deprecated android.database.Cursor managedQuery(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String); method public boolean moveTaskToBack(boolean); method public boolean navigateUpTo(android.content.Intent); diff --git a/api/system-current.txt b/api/system-current.txt index 2e0613a..a784378 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3444,6 +3444,7 @@ package android.app { method public boolean isImmersive(); method public boolean isTaskRoot(); method public boolean isVoiceInteraction(); + method public boolean isVoiceInteractionRoot(); method public final deprecated android.database.Cursor managedQuery(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String); method public boolean moveTaskToBack(boolean); method public boolean navigateUpTo(android.content.Intent); diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java index 9c401c7..3e4a66d 100644 --- a/core/java/android/accounts/AbstractAccountAuthenticator.java +++ b/core/java/android/accounts/AbstractAccountAuthenticator.java @@ -138,9 +138,7 @@ public abstract class AbstractAccountAuthenticator { new AccountAuthenticatorResponse(response), accountType, authTokenType, features, options); if (Log.isLoggable(TAG, Log.VERBOSE)) { - if (result != null) { - result.keySet(); // force it to be unparcelled - } + result.keySet(); // force it to be unparcelled Log.v(TAG, "addAccount: result " + AccountManager.sanitizeResult(result)); } if (result != null) { @@ -162,9 +160,7 @@ public abstract class AbstractAccountAuthenticator { final Bundle result = AbstractAccountAuthenticator.this.confirmCredentials( new AccountAuthenticatorResponse(response), account, options); if (Log.isLoggable(TAG, Log.VERBOSE)) { - if (result != null) { - result.keySet(); // force it to be unparcelled - } + result.keySet(); // force it to be unparcelled Log.v(TAG, "confirmCredentials: result " + AccountManager.sanitizeResult(result)); } @@ -189,9 +185,7 @@ public abstract class AbstractAccountAuthenticator { result.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, AbstractAccountAuthenticator.this.getAuthTokenLabel(authTokenType)); if (Log.isLoggable(TAG, Log.VERBOSE)) { - if (result != null) { - result.keySet(); // force it to be unparcelled - } + result.keySet(); // force it to be unparcelled Log.v(TAG, "getAuthTokenLabel: result " + AccountManager.sanitizeResult(result)); } @@ -215,9 +209,7 @@ public abstract class AbstractAccountAuthenticator { new AccountAuthenticatorResponse(response), account, authTokenType, loginOptions); if (Log.isLoggable(TAG, Log.VERBOSE)) { - if (result != null) { - result.keySet(); // force it to be unparcelled - } + result.keySet(); // force it to be unparcelled Log.v(TAG, "getAuthToken: result " + AccountManager.sanitizeResult(result)); } if (result != null) { @@ -242,10 +234,7 @@ public abstract class AbstractAccountAuthenticator { new AccountAuthenticatorResponse(response), account, authTokenType, loginOptions); if (Log.isLoggable(TAG, Log.VERBOSE)) { - // Result may be null. - if (result != null) { - result.keySet(); // force it to be unparcelled - } + result.keySet(); // force it to be unparcelled Log.v(TAG, "updateCredentials: result " + AccountManager.sanitizeResult(result)); } @@ -501,7 +490,7 @@ public abstract class AbstractAccountAuthenticator { * <ul> * <li> {@link AccountManager#KEY_INTENT}, or * <li> {@link AccountManager#KEY_ACCOUNT_NAME} and {@link AccountManager#KEY_ACCOUNT_TYPE} of - * the account whose credentials were updated, or + * the account that was added, or * <li> {@link AccountManager#KEY_ERROR_CODE} and {@link AccountManager#KEY_ERROR_MESSAGE} to * indicate an error * </ul> diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 8c84b4d..9394d2c 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -333,7 +333,7 @@ public class AccountManager { try { return mService.getPassword(account); } catch (RemoteException e) { - // won't ever happen + // will never happen throw new RuntimeException(e); } } @@ -362,7 +362,7 @@ public class AccountManager { try { return mService.getUserData(account, key); } catch (RemoteException e) { - // won't ever happen + // will never happen throw new RuntimeException(e); } } @@ -415,10 +415,8 @@ public class AccountManager { * * <p>It is safe to call this method from the main thread. * - * <p>Clients of this method that have not been granted the - * {@link android.Manifest.permission#GET_ACCOUNTS} permission, - * will only see those accounts managed by AbstractAccountAuthenticators whose - * signature matches the client. + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#GET_ACCOUNTS}. * * @return An array of {@link Account}, one for each account. Empty * (never null) if no accounts have been added. @@ -440,10 +438,8 @@ public class AccountManager { * * <p>It is safe to call this method from the main thread. * - * <p>Clients of this method that have not been granted the - * {@link android.Manifest.permission#GET_ACCOUNTS} permission, - * will only see those accounts managed by AbstractAccountAuthenticators whose - * signature matches the client. + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#GET_ACCOUNTS}. * * @return An array of {@link Account}, one for each account. Empty * (never null) if no accounts have been added. @@ -470,7 +466,7 @@ public class AccountManager { try { return mService.getAccountsForPackage(packageName, uid); } catch (RemoteException re) { - // won't ever happen + // possible security exception throw new RuntimeException(re); } } @@ -487,7 +483,7 @@ public class AccountManager { try { return mService.getAccountsByTypeForPackage(type, packageName); } catch (RemoteException re) { - // won't ever happen + // possible security exception throw new RuntimeException(re); } } @@ -501,10 +497,9 @@ public class AccountManager { * * <p>It is safe to call this method from the main thread. * - * <p>Clients of this method that have not been granted the - * {@link android.Manifest.permission#GET_ACCOUNTS} permission, - * will only see those accounts managed by AbstractAccountAuthenticators whose - * signature matches the client. + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#GET_ACCOUNTS} or share a uid with the + * authenticator that owns the account type. * * <p><b>NOTE:</b> If targeting your app to work on API level 22 and before, * GET_ACCOUNTS permission is needed for those platforms, irrespective of uid @@ -590,8 +585,7 @@ public class AccountManager { * {@link AccountManagerFuture} must not be used on the main thread. * * <p>This method requires the caller to hold the permission - * {@link android.Manifest.permission#GET_ACCOUNTS} or be a signature - * match with the AbstractAccountAuthenticator that manages the account. + * {@link android.Manifest.permission#GET_ACCOUNTS}. * * @param account The {@link Account} to test * @param features An array of the account features to check @@ -634,10 +628,9 @@ public class AccountManager { * <p>This method may be called from any thread, but the returned * {@link AccountManagerFuture} must not be used on the main thread. * - * <p>Clients of this method that have not been granted the - * {@link android.Manifest.permission#GET_ACCOUNTS} permission, - * will only see those accounts managed by AbstractAccountAuthenticators whose - * signature matches the client. + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#GET_ACCOUNTS} or share a uid with the + * authenticator that owns the account type. * * @param type The type of accounts to return, must not be null * @param features An array of the account features to require, @@ -708,7 +701,7 @@ public class AccountManager { try { return mService.addAccountExplicitly(account, password, userdata); } catch (RemoteException e) { - // Can happen if there was a SecurityException was thrown. + // won't ever happen throw new RuntimeException(e); } } @@ -973,7 +966,7 @@ public class AccountManager { try { return mService.removeAccountExplicitly(account); } catch (RemoteException e) { - // May happen if the caller doesn't match the signature of the authenticator. + // won't ever happen throw new RuntimeException(e); } } @@ -1121,7 +1114,7 @@ public class AccountManager { try { mService.setUserData(account, key, value); } catch (RemoteException e) { - // Will happen if there is not signature match. + // won't ever happen throw new RuntimeException(e); } } @@ -1740,7 +1733,7 @@ public class AccountManager { * with these fields if an activity was supplied and the account * credentials were successfully updated: * <ul> - * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account + * <li> {@link #KEY_ACCOUNT_NAME} - the name of the account created * <li> {@link #KEY_ACCOUNT_TYPE} - the type of the account * </ul> * @@ -2508,13 +2501,11 @@ public class AccountManager { * listeners are added in an Activity or Service's {@link Activity#onCreate} * and removed in {@link Activity#onDestroy}. * - * <p>The listener will only be informed of accounts that would be returned - * to the caller via {@link #getAccounts()}. Typically this means that to - * get any accounts, the caller will need to be grated the GET_ACCOUNTS - * permission. - * * <p>It is safe to call this method from the main thread. * + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#GET_ACCOUNTS}. + * * @param listener The listener to send notifications to * @param handler {@link Handler} identifying the thread to use * for notifications, null for the main thread diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index bdea608..3c8af0d 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -1229,6 +1229,22 @@ public class Activity extends ContextThemeWrapper } /** + * Like {@link #isVoiceInteraction}, but only returns true if this is also the root + * of a voice interaction. That is, returns true if this activity was directly + * started by the voice interaction service as the initiation of a voice interaction. + * Otherwise, for example if it was started by another activity while under voice + * interaction, returns false. + */ + public boolean isVoiceInteractionRoot() { + try { + return mVoiceInteractor != null + && ActivityManagerNative.getDefault().isRootVoiceInteraction(mToken); + } catch (RemoteException e) { + } + return false; + } + + /** * Retrieve the active {@link VoiceInteractor} that the user is going through to * interact with this activity. */ diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index b758a7a..e144c29 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2582,6 +2582,15 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeInt(res ? 1 : 0); return true; } + + case IS_ROOT_VOICE_INTERACTION_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); + boolean res = isRootVoiceInteraction(token); + reply.writeNoException(); + reply.writeInt(res ? 1 : 0); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -5962,5 +5971,19 @@ class ActivityManagerProxy implements IActivityManager return res != 0; } + @Override + public boolean isRootVoiceInteraction(IBinder token) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(token); + mRemote.transact(IS_ROOT_VOICE_INTERACTION_TRANSACTION, data, reply, 0); + reply.readException(); + int res = reply.readInt(); + data.recycle(); + reply.recycle(); + return res != 0; + } + private IBinder mRemote; } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index dbe91f9..0adce5d 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -777,7 +777,9 @@ final class ApplicationPackageManager extends PackageManager { public List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) { try { - return mPM.queryContentProviders(processName, uid, flags); + ParceledListSlice<ProviderInfo> slice + = mPM.queryContentProviders(processName, uid, flags); + return slice != null ? slice.getList() : null; } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 9ebbb9b..90216af 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -515,6 +515,8 @@ public interface IActivityManager extends IInterface { public boolean setProcessMemoryTrimLevel(String process, int uid, int level) throws RemoteException; + public boolean isRootVoiceInteraction(IBinder token) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -861,4 +863,5 @@ public interface IActivityManager extends IInterface { int IS_SCREEN_CAPTURE_ALLOWED_ON_CURRENT_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+299; int SHOW_ASSIST_FROM_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+300; + int IS_ROOT_VOICE_INTERACTION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+301; } diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index c2d901d..69b8b95 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -1810,9 +1810,14 @@ public class Instrumentation { throw new SecurityException( "Starting under voice control not allowed for: " + intent); case ActivityManager.START_NOT_CURRENT_USER_ACTIVITY: - throw new SecurityException( - "Not allowed to start background user activity that shouldn't be" - + " displayed for all users."); + // Fail silently for this case so we don't break current apps. + // TODO(b/22929608): Instead of failing silently or throwing an exception, + // we should properly position the activity in the stack (i.e. behind all current + // user activity/task) and not change the positioning of stacks. + Log.e(TAG, + "Not allowed to start background user activity that shouldn't be displayed" + + " for all users. Failing silently..."); + break; default: throw new AndroidRuntimeException("Unknown error code " + res + " when starting " + intent); diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index aaaa745..ec443cd 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1782,14 +1782,6 @@ public class Intent implements Parcelable, Cloneable { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ALARM_CHANGED = "android.intent.action.ALARM_CHANGED"; /** - * Sync State Changed Action: This is broadcast when the sync starts or stops or when one has - * been failing for a long time. It is used by the SyncManager and the StatusBar service. - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_SYNC_STATE_CHANGED - = "android.intent.action.SYNC_STATE_CHANGED"; - /** * Broadcast Action: This is broadcast once, after the system has finished * booting. It can be used to perform application-specific initialization, * such as installing alarms. You must hold the diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 0336645..a5e9faf 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -203,7 +203,7 @@ interface IPackageManager { void querySyncProviders(inout List<String> outNames, inout List<ProviderInfo> outInfo); - List<ProviderInfo> queryContentProviders( + ParceledListSlice queryContentProviders( String processName, int uid, int flags); InstrumentationInfo getInstrumentationInfo( diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java index af1367c..c580083 100644 --- a/core/java/android/hardware/camera2/CameraMetadata.java +++ b/core/java/android/hardware/camera2/CameraMetadata.java @@ -173,6 +173,28 @@ public abstract class CameraMetadata<TKey> { } } + ArrayList<TKey> vendorKeys = CameraMetadataNative.getAllVendorKeys(keyClass); + + if (vendorKeys != null) { + for (TKey k : vendorKeys) { + String keyName; + if (k instanceof CaptureRequest.Key<?>) { + keyName = ((CaptureRequest.Key<?>) k).getName(); + } else if (k instanceof CaptureResult.Key<?>) { + keyName = ((CaptureResult.Key<?>) k).getName(); + } else if (k instanceof CameraCharacteristics.Key<?>) { + keyName = ((CameraCharacteristics.Key<?>) k).getName(); + } else { + continue; + } + + if (filterTags == null || Arrays.binarySearch(filterTags, + CameraMetadataNative.getTag(keyName)) >= 0) { + keyList.add(k); + } + } + } + return keyList; } diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index 7e50fd9..12a2910 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -1078,6 +1078,7 @@ public class CameraMetadataNative implements Parcelable { private native synchronized void nativeWriteValues(int tag, byte[] src); private native synchronized void nativeDump() throws IOException; // dump to ALOGD + private static native ArrayList nativeGetAllVendorKeys(Class keyClass); private static native int nativeGetTagFromKey(String keyName) throws IllegalArgumentException; private static native int nativeGetTypeFromTag(int tag) @@ -1113,6 +1114,19 @@ public class CameraMetadataNative implements Parcelable { return nativeIsEmpty(); } + + /** + * Return a list containing keys of the given key class for all defined vendor tags. + * + * @hide + */ + public static <K> ArrayList<K> getAllVendorKeys(Class<K> keyClass) { + if (keyClass == null) { + throw new NullPointerException(); + } + return (ArrayList<K>) nativeGetAllVendorKeys(keyClass); + } + /** * Convert a key string into the equivalent native tag. * diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index 80c7b1a..6e4c9de 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -97,9 +97,6 @@ interface IUsbManager */ void setUsbDataUnlocked(boolean unlock); - /* Returns true iff sensitive user data is exposed on the USB connection. */ - boolean isUsbDataUnlocked(); - /* Allow USB debugging from the attached host. If alwaysAllow is true, add the * the public key to list of host keys that the user has approved. */ diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java index c88f213..3b3ee52 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -520,21 +520,6 @@ public class UsbManager { } /** - * Returns {@code true} iff access to sensitive USB data is currently allowed when - * in device mode. - * - * {@hide} - */ - public boolean isUsbDataUnlocked() { - try { - return mService.isUsbDataUnlocked(); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in isUsbDataUnlocked", e); - } - return false; - } - - /** * Returns a list of physical USB ports on the device. * <p> * This list is guaranteed to contain all dual-role USB Type C ports but it might diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a836cc5..6f98788 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -8333,9 +8333,9 @@ public final class Settings { return true; case AppOpsManager.MODE_DEFAULT: // this is the default operating mode after an app's installation - if (!throwException) { - return context.checkCallingOrSelfPermission(permissionName) == - PackageManager.PERMISSION_GRANTED; + if(context.checkCallingOrSelfPermission(permissionName) == PackageManager + .PERMISSION_GRANTED) { + return true; } default: // this is for all other cases trickled down here... diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 928fe93..6e93a30 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -3159,6 +3159,17 @@ public final class ViewRootImpl implements ViewParent, return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent); } + private static void forceLayout(View view) { + view.forceLayout(); + if (view instanceof ViewGroup) { + ViewGroup group = (ViewGroup) view; + final int count = group.getChildCount(); + for (int i = 0; i < count; i++) { + forceLayout(group.getChildAt(i)); + } + } + } + private final static int MSG_INVALIDATE = 1; private final static int MSG_INVALIDATE_RECT = 2; private final static int MSG_DIE = 3; @@ -3297,6 +3308,10 @@ public final class ViewRootImpl implements ViewParent, mReportNextDraw = true; } + if (mView != null) { + forceLayout(mView); + } + requestLayout(); } break; @@ -3311,6 +3326,9 @@ public final class ViewRootImpl implements ViewParent, mWinFrame.top = t; mWinFrame.bottom = t + h; + if (mView != null) { + forceLayout(mView); + } requestLayout(); } break; diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 6af2e8b..d9faece 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -517,6 +517,7 @@ public class ChooserActivity extends ResolverActivity { private final ResolveInfo mBackupResolveInfo; private final ChooserTarget mChooserTarget; private Drawable mBadgeIcon = null; + private CharSequence mBadgeContentDescription; private Drawable mDisplayIcon; private final Intent mFillInIntent; private final int mFillInFlags; @@ -532,7 +533,9 @@ public class ChooserActivity extends ResolverActivity { if (ri != null) { final ActivityInfo ai = ri.activityInfo; if (ai != null && ai.applicationInfo != null) { - mBadgeIcon = getPackageManager().getApplicationIcon(ai.applicationInfo); + final PackageManager pm = getPackageManager(); + mBadgeIcon = pm.getApplicationIcon(ai.applicationInfo); + mBadgeContentDescription = pm.getApplicationLabel(ai.applicationInfo); } } } @@ -555,6 +558,7 @@ public class ChooserActivity extends ResolverActivity { mBackupResolveInfo = other.mBackupResolveInfo; mChooserTarget = other.mChooserTarget; mBadgeIcon = other.mBadgeIcon; + mBadgeContentDescription = other.mBadgeContentDescription; mDisplayIcon = other.mDisplayIcon; mFillInIntent = fillInIntent; mFillInFlags = flags; @@ -647,6 +651,11 @@ public class ChooserActivity extends ResolverActivity { } @Override + public CharSequence getBadgeContentDescription() { + return mBadgeContentDescription; + } + + @Override public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { return new ChooserTargetInfo(this, fillInIntent, flags); } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 89599e0..7dd3bed 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -932,6 +932,11 @@ public class ResolverActivity extends Activity { } @Override + public CharSequence getBadgeContentDescription() { + return null; + } + + @Override public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) { return new DisplayResolveInfo(this, fillInIntent, flags); } @@ -1072,6 +1077,11 @@ public class ResolverActivity extends Activity { public Drawable getBadgeIcon(); /** + * @return The content description for the badge icon + */ + public CharSequence getBadgeContentDescription(); + + /** * Clone this target with the given fill-in information. */ public TargetInfo cloneFilledIn(Intent fillInIntent, int flags); @@ -1542,6 +1552,7 @@ public class ResolverActivity extends Activity { final Drawable badge = info.getBadgeIcon(); if (badge != null) { holder.badge.setImageDrawable(badge); + holder.badge.setContentDescription(info.getBadgeContentDescription()); holder.badge.setVisibility(View.VISIBLE); } else { holder.badge.setVisibility(View.GONE); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index e7c58f4..4f42ed9 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -7915,6 +7915,8 @@ public final class BatteryStatsImpl extends BatteryStats { return; } + // Record whether we've seen a non-zero time (for debugging b/22716723). + boolean seenNonZeroTime = false; for (Map.Entry<String, KernelWakelockStats.Entry> ent : wakelockStats.entrySet()) { String name = ent.getKey(); KernelWakelockStats.Entry kws = ent.getValue(); @@ -7928,17 +7930,31 @@ public final class BatteryStatsImpl extends BatteryStats { kwlt.updateCurrentReportedCount(kws.mCount); kwlt.updateCurrentReportedTotalTime(kws.mTotalTime); kwlt.setUpdateVersion(kws.mVersion); + + if (kws.mVersion != wakelockStats.kernelWakelockVersion) + seenNonZeroTime |= kws.mTotalTime > 0; } + int numWakelocksSetStale = 0; if (wakelockStats.size() != mKernelWakelockStats.size()) { // Set timers to stale if they didn't appear in /proc/wakelocks this time. for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) { SamplingTimer st = ent.getValue(); if (st.getUpdateVersion() != wakelockStats.kernelWakelockVersion) { st.setStale(); + numWakelocksSetStale++; } } } + + if (!seenNonZeroTime) { + Slog.wtf(TAG, "All kernel wakelocks had time of zero"); + } + + if (numWakelocksSetStale == mKernelWakelockStats.size()) { + Slog.wtf(TAG, "All kernel wakelocks were set stale. new version=" + + wakelockStats.kernelWakelockVersion); + } } // We use an anonymous class to access these variables, diff --git a/core/java/com/android/internal/os/KernelWakelockReader.java b/core/java/com/android/internal/os/KernelWakelockReader.java index 0369c3f..6654ea5 100644 --- a/core/java/com/android/internal/os/KernelWakelockReader.java +++ b/core/java/com/android/internal/os/KernelWakelockReader.java @@ -75,6 +75,8 @@ public class KernelWakelockReader { is = new FileInputStream(sWakeupSourceFile); wakeup_sources = true; } catch (java.io.FileNotFoundException e2) { + Slog.wtf(TAG, "neither " + sWakelockFile + " nor " + + sWakeupSourceFile + " exists"); return null; } } @@ -82,6 +84,7 @@ public class KernelWakelockReader { len = is.read(buffer); is.close(); } catch (java.io.IOException e) { + Slog.wtf(TAG, "failed to read kernel wakelocks", e); return null; } @@ -171,6 +174,13 @@ public class KernelWakelockReader { numUpdatedWlNames++; } } + } else if (!parsed) { + try { + Slog.wtf(TAG, "Failed to parse proc line: " + + new String(wlBuffer, startIndex, endIndex - startIndex)); + } catch (Exception e) { + Slog.wtf(TAG, "Failed to parse proc line!"); + } } startIndex = endIndex; } diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index fc9a1a5..7679624 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -34,6 +34,7 @@ import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.animation.AnimationUtils; import android.widget.AbsListView; import android.widget.OverScroller; @@ -609,19 +610,37 @@ public class ResolverDrawerLayout extends ViewGroup { } @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); + public CharSequence getAccessibilityClassName() { + // Since we support scrolling, make this ViewGroup look like a + // ScrollView. This is kind of a hack until we have support for + // specifying auto-scroll behavior. + return android.widget.ScrollView.class.getName(); + } + + @Override + public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfoInternal(info); + if (isEnabled()) { if (mCollapseOffset != 0) { info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); info.setScrollable(true); } } + + // This view should never get accessibility focus, but it's interactive + // via nested scrolling, so we can't hide it completely. + info.removeAction(AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS); } @Override - public boolean performAccessibilityAction(int action, Bundle arguments) { - if (super.performAccessibilityAction(action, arguments)) { + public boolean performAccessibilityActionInternal(int action, Bundle arguments) { + if (action == AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS.getId()) { + // This view should never get accessibility focus. + return false; + } + + if (super.performAccessibilityActionInternal(action, arguments)) { return true; } @@ -629,6 +648,7 @@ public class ResolverDrawerLayout extends ViewGroup { smoothScrollTo(0, 0); return true; } + return false; } diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp index 670d3c0..fbe3ece 100755 --- a/core/jni/android/graphics/Bitmap.cpp +++ b/core/jni/android/graphics/Bitmap.cpp @@ -218,7 +218,6 @@ int Bitmap::getAshmemFd() const { } const SkImageInfo& Bitmap::info() const { - assertValid(); return mPixelRef->info(); } diff --git a/core/jni/android_hardware_camera2_CameraMetadata.cpp b/core/jni/android_hardware_camera2_CameraMetadata.cpp index 7c8769d..fb22689 100644 --- a/core/jni/android_hardware_camera2_CameraMetadata.cpp +++ b/core/jni/android_hardware_camera2_CameraMetadata.cpp @@ -23,7 +23,9 @@ #include <utils/Vector.h> #include <utils/SortedVector.h> #include <utils/KeyedVector.h> +#include <stdio.h> #include <string.h> +#include <vector> #include "jni.h" #include "JNIHelp.h" @@ -45,9 +47,30 @@ static const bool kIsDebug = false; // fully-qualified class name #define CAMERA_METADATA_CLASS_NAME "android/hardware/camera2/impl/CameraMetadataNative" +#define CHARACTERISTICS_KEY_CLASS_NAME "android/hardware/camera2/CameraCharacteristics$Key" +#define REQUEST_KEY_CLASS_NAME "android/hardware/camera2/CaptureRequest$Key" +#define RESULT_KEY_CLASS_NAME "android/hardware/camera2/CaptureResult$Key" using namespace android; +static struct metadata_java_key_offsets_t { + jclass mCharacteristicsKey; + jclass mResultKey; + jclass mRequestKey; + jmethodID mCharacteristicsConstr; + jmethodID mResultConstr; + jmethodID mRequestConstr; + jclass mByteArray; + jclass mInt32Array; + jclass mFloatArray; + jclass mInt64Array; + jclass mDoubleArray; + jclass mRationalArray; + jclass mArrayList; + jmethodID mArrayListConstr; + jmethodID mArrayListAdd; +} gMetadataOffsets; + struct fields_t { jfieldID metadata_ptr; }; @@ -141,6 +164,7 @@ struct Helpers { extern "C" { static void CameraMetadata_classInit(JNIEnv *env, jobject thiz); +static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType); static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName); static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag); static jint CameraMetadata_setupGlobalVendorTagDescriptor(JNIEnv *env, jobject thiz); @@ -510,6 +534,9 @@ static JNINativeMethod gCameraMetadataMethods[] = { { "nativeClassInit", "()V", (void *)CameraMetadata_classInit }, + { "nativeGetAllVendorKeys", + "(Ljava/lang/Class;)Ljava/util/ArrayList;", + (void *)CameraMetadata_getAllVendorKeys}, { "nativeGetTagFromKey", "(Ljava/lang/String;)I", (void *)CameraMetadata_getTagFromKey }, @@ -588,6 +615,44 @@ static int find_fields(JNIEnv *env, field *fields, int count) // Get all the required offsets in java class and register native functions int register_android_hardware_camera2_CameraMetadata(JNIEnv *env) { + + // Store global references to Key-related classes and methods used natively + jclass characteristicsKeyClazz = FindClassOrDie(env, CHARACTERISTICS_KEY_CLASS_NAME); + jclass requestKeyClazz = FindClassOrDie(env, REQUEST_KEY_CLASS_NAME); + jclass resultKeyClazz = FindClassOrDie(env, RESULT_KEY_CLASS_NAME); + gMetadataOffsets.mCharacteristicsKey = MakeGlobalRefOrDie(env, characteristicsKeyClazz); + gMetadataOffsets.mRequestKey = MakeGlobalRefOrDie(env, requestKeyClazz); + gMetadataOffsets.mResultKey = MakeGlobalRefOrDie(env, resultKeyClazz); + gMetadataOffsets.mCharacteristicsConstr = GetMethodIDOrDie(env, + gMetadataOffsets.mCharacteristicsKey, "<init>", + "(Ljava/lang/String;Ljava/lang/Class;)V"); + gMetadataOffsets.mRequestConstr = GetMethodIDOrDie(env, + gMetadataOffsets.mRequestKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V"); + gMetadataOffsets.mResultConstr = GetMethodIDOrDie(env, + gMetadataOffsets.mResultKey, "<init>", "(Ljava/lang/String;Ljava/lang/Class;)V"); + + // Store global references for primitive array types used by Keys + jclass byteClazz = FindClassOrDie(env, "[B"); + jclass int32Clazz = FindClassOrDie(env, "[I"); + jclass floatClazz = FindClassOrDie(env, "[F"); + jclass int64Clazz = FindClassOrDie(env, "[J"); + jclass doubleClazz = FindClassOrDie(env, "[D"); + jclass rationalClazz = FindClassOrDie(env, "[Landroid/util/Rational;"); + gMetadataOffsets.mByteArray = MakeGlobalRefOrDie(env, byteClazz); + gMetadataOffsets.mInt32Array = MakeGlobalRefOrDie(env, int32Clazz); + gMetadataOffsets.mFloatArray = MakeGlobalRefOrDie(env, floatClazz); + gMetadataOffsets.mInt64Array = MakeGlobalRefOrDie(env, int64Clazz); + gMetadataOffsets.mDoubleArray = MakeGlobalRefOrDie(env, doubleClazz); + gMetadataOffsets.mRationalArray = MakeGlobalRefOrDie(env, rationalClazz); + + // Store global references for ArrayList methods used + jclass arrayListClazz = FindClassOrDie(env, "java/util/ArrayList"); + gMetadataOffsets.mArrayList = MakeGlobalRefOrDie(env, arrayListClazz); + gMetadataOffsets.mArrayListConstr = GetMethodIDOrDie(env, gMetadataOffsets.mArrayList, + "<init>", "(I)V"); + gMetadataOffsets.mArrayListAdd = GetMethodIDOrDie(env, gMetadataOffsets.mArrayList, + "add", "(Ljava/lang/Object;)Z"); + // Register native functions return RegisterMethodsOrDie(env, CAMERA_METADATA_CLASS_NAME, @@ -596,6 +661,7 @@ int register_android_hardware_camera2_CameraMetadata(JNIEnv *env) } extern "C" { + static void CameraMetadata_classInit(JNIEnv *env, jobject thiz) { // XX: Why do this separately instead of doing it in the register function? ALOGV("%s", __FUNCTION__); @@ -612,6 +678,107 @@ static void CameraMetadata_classInit(JNIEnv *env, jobject thiz) { env->FindClass(CAMERA_METADATA_CLASS_NAME); } +static jobject CameraMetadata_getAllVendorKeys(JNIEnv* env, jobject thiz, jclass keyType) { + + // Get all vendor tags + sp<VendorTagDescriptor> vTags = VendorTagDescriptor::getGlobalVendorTagDescriptor(); + if (vTags.get() == nullptr) { + // No vendor tags. + return NULL; + } + + int count = vTags->getTagCount(); + if (count <= 0) { + // No vendor tags. + return NULL; + } + + std::vector<uint32_t> tagIds(count, /*initializer value*/0); + vTags->getTagArray(&tagIds[0]); + + // Which key class/constructor should we use? + jclass keyClazz; + jmethodID keyConstr; + if (env->IsSameObject(keyType, gMetadataOffsets.mCharacteristicsKey)) { + keyClazz = gMetadataOffsets.mCharacteristicsKey; + keyConstr = gMetadataOffsets.mCharacteristicsConstr; + } else if (env->IsSameObject(keyType, gMetadataOffsets.mResultKey)) { + keyClazz = gMetadataOffsets.mResultKey; + keyConstr = gMetadataOffsets.mResultConstr; + } else if (env->IsSameObject(keyType, gMetadataOffsets.mRequestKey)) { + keyClazz = gMetadataOffsets.mRequestKey; + keyConstr = gMetadataOffsets.mRequestConstr; + } else { + jniThrowException(env, "java/lang/IllegalArgumentException", + "Invalid key class given as argument."); + return NULL; + } + + // Allocate arrayList to return + jobject arrayList = env->NewObject(gMetadataOffsets.mArrayList, + gMetadataOffsets.mArrayListConstr, static_cast<jint>(count)); + if (env->ExceptionCheck()) { + return NULL; + } + + for (uint32_t id : tagIds) { + const char* section = vTags->getSectionName(id); + const char* tag = vTags->getTagName(id); + int type = vTags->getTagType(id); + + size_t totalLen = strlen(section) + strlen(tag) + 2; + std::vector<char> fullName(totalLen, 0); + snprintf(&fullName[0], totalLen, "%s.%s", section, tag); + + jstring name = env->NewStringUTF(&fullName[0]); + + if (env->ExceptionCheck()) { + return NULL; + } + + jclass valueClazz; + switch (type) { + case TYPE_BYTE: + valueClazz = gMetadataOffsets.mByteArray; + break; + case TYPE_INT32: + valueClazz = gMetadataOffsets.mInt32Array; + break; + case TYPE_FLOAT: + valueClazz = gMetadataOffsets.mFloatArray; + break; + case TYPE_INT64: + valueClazz = gMetadataOffsets.mInt64Array; + break; + case TYPE_DOUBLE: + valueClazz = gMetadataOffsets.mDoubleArray; + break; + case TYPE_RATIONAL: + valueClazz = gMetadataOffsets.mRationalArray; + break; + default: + jniThrowExceptionFmt(env, "java/lang/IllegalStateException", + "Invalid type %d given for key %s", type, &fullName[0]); + return NULL; + } + + jobject key = env->NewObject(keyClazz, keyConstr, name, valueClazz); + if (env->ExceptionCheck()) { + return NULL; + } + + env->CallBooleanMethod(arrayList, gMetadataOffsets.mArrayListAdd, key); + if (env->ExceptionCheck()) { + return NULL; + } + + env->DeleteLocalRef(name); + env->DeleteLocalRef(key); + } + + return arrayList; +} + static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName) { ScopedUtfChars keyScoped(env, keyName); diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml index 1cb39f0..7399fa9 100644 --- a/core/res/res/values/colors_material.xml +++ b/core/res/res/values/colors_material.xml @@ -41,9 +41,6 @@ <color name="switch_thumb_disabled_material_dark">#ff616161</color> <color name="switch_thumb_disabled_material_light">#ffbdbdbd</color> - <color name="link_text_material_light">@color/material_deep_teal_500</color> - <color name="link_text_material_dark">@color/material_deep_teal_200</color> - <!-- Text & foreground colors --> <eat-comment /> diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml index 9d3a7ef..e88a4fb 100644 --- a/core/res/res/values/themes_material.xml +++ b/core/res/res/values/themes_material.xml @@ -67,8 +67,8 @@ please see themes_device_defaults.xml. <item name="textColorHintInverse">@color/hint_foreground_material_light</item> <item name="textColorHighlight">@color/highlighted_text_material</item> <item name="textColorHighlightInverse">@color/highlighted_text_material</item> - <item name="textColorLink">@color/link_text_material_dark</item> - <item name="textColorLinkInverse">@color/link_text_material_light</item> + <item name="textColorLink">?attr/colorAccent</item> + <item name="textColorLinkInverse">?attr/colorAccent</item> <item name="textColorSearchUrl">@color/search_url_text_material_dark</item> <item name="textColorAlertDialogListItem">@color/primary_text_material_dark</item> @@ -422,8 +422,8 @@ please see themes_device_defaults.xml. <item name="textColorHintInverse">@color/hint_foreground_material_dark</item> <item name="textColorHighlight">@color/highlighted_text_material</item> <item name="textColorHighlightInverse">@color/highlighted_text_material</item> - <item name="textColorLink">@color/link_text_material_light</item> - <item name="textColorLinkInverse">@color/link_text_material_dark</item> + <item name="textColorLink">?attr/colorAccent</item> + <item name="textColorLinkInverse">?attr/colorAccent</item> <item name="textColorSearchUrl">@color/search_url_text_material_light</item> <item name="textColorAlertDialogListItem">@color/primary_text_material_light</item> @@ -793,8 +793,6 @@ please see themes_device_defaults.xml. <item name="textColorHintInverse">@color/hint_foreground_material_dark</item> <item name="textColorHighlight">@color/highlighted_text_material</item> <item name="textColorHighlightInverse">@color/highlighted_text_material</item> - <item name="textColorLink">@color/link_text_material_light</item> - <item name="textColorLinkInverse">@color/link_text_material_dark</item> <item name="textColorSearchUrl">@color/search_url_text_material_light</item> <item name="textColorAlertDialogListItem">@color/primary_text_material_light</item> @@ -827,8 +825,6 @@ please see themes_device_defaults.xml. <item name="textColorHintInverse">@color/hint_foreground_material_light</item> <item name="textColorHighlight">@color/highlighted_text_material</item> <item name="textColorHighlightInverse">@color/highlighted_text_material</item> - <item name="textColorLink">@color/link_text_material_dark</item> - <item name="textColorLinkInverse">@color/link_text_material_light</item> <item name="textColorSearchUrl">@color/search_url_text_material_dark</item> <item name="textColorAlertDialogListItem">@color/primary_text_material_dark</item> diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java index 8ad7c12..3e4d93b 100644 --- a/graphics/java/android/graphics/Bitmap.java +++ b/graphics/java/android/graphics/Bitmap.java @@ -23,6 +23,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.Trace; import android.util.DisplayMetrics; +import android.util.Log; import dalvik.system.VMRuntime; @@ -33,6 +34,8 @@ import java.nio.IntBuffer; import java.nio.ShortBuffer; public final class Bitmap implements Parcelable { + private static final String TAG = "Bitmap"; + /** * Indicates that the bitmap was created for an unknown pixel density. * @@ -159,6 +162,9 @@ public final class Bitmap implements Parcelable { * @see #DENSITY_NONE */ public int getDensity() { + if (mRecycled) { + Log.w(TAG, "Called getDensity() on a recycle()'d bitmap! This is undefined behavior!"); + } return mDensity; } @@ -330,7 +336,9 @@ public final class Bitmap implements Parcelable { * @return The current generation ID for this bitmap. */ public int getGenerationId() { - if (mRecycled) return 0; + if (mRecycled) { + Log.w(TAG, "Called getGenerationId() on a recycle()'d bitmap! This is undefined behavior!"); + } return nativeGenerationId(mFinalizer.mNativeBitmap); } @@ -1057,7 +1065,9 @@ public final class Bitmap implements Parcelable { * @see BitmapFactory.Options#inPremultiplied */ public final boolean isPremultiplied() { - if (mRecycled) return false; + if (mRecycled) { + Log.w(TAG, "Called isPremultiplied() on a recycle()'d bitmap! This is undefined behavior!"); + } return nativeIsPremultiplied(mFinalizer.mNativeBitmap); } @@ -1089,11 +1099,17 @@ public final class Bitmap implements Parcelable { /** Returns the bitmap's width */ public final int getWidth() { + if (mRecycled) { + Log.w(TAG, "Called getWidth() on a recycle()'d bitmap! This is undefined behavior!"); + } return mWidth; } /** Returns the bitmap's height */ public final int getHeight() { + if (mRecycled) { + Log.w(TAG, "Called getHeight() on a recycle()'d bitmap! This is undefined behavior!"); + } return mHeight; } @@ -1176,7 +1192,9 @@ public final class Bitmap implements Parcelable { * @return number of bytes between rows of the native bitmap pixels. */ public final int getRowBytes() { - if (mRecycled) return 0; + if (mRecycled) { + Log.w(TAG, "Called getRowBytes() on a recycle()'d bitmap! This is undefined behavior!"); + } return nativeRowBytes(mFinalizer.mNativeBitmap); } @@ -1220,7 +1238,9 @@ public final class Bitmap implements Parcelable { * that config, otherwise return null. */ public final Config getConfig() { - if (mRecycled) return Config.ARGB_8888; + if (mRecycled) { + Log.w(TAG, "Called getConfig() on a recycle()'d bitmap! This is undefined behavior!"); + } return Config.nativeToConfig(nativeConfig(mFinalizer.mNativeBitmap)); } @@ -1233,7 +1253,9 @@ public final class Bitmap implements Parcelable { * it will return true by default. */ public final boolean hasAlpha() { - if (mRecycled) return false; + if (mRecycled) { + Log.w(TAG, "Called hasAlpha() on a recycle()'d bitmap! This is undefined behavior!"); + } return nativeHasAlpha(mFinalizer.mNativeBitmap); } @@ -1270,7 +1292,9 @@ public final class Bitmap implements Parcelable { * @see #setHasMipMap(boolean) */ public final boolean hasMipMap() { - if (mRecycled) return false; + if (mRecycled) { + Log.w(TAG, "Called hasMipMap() on a recycle()'d bitmap! This is undefined behavior!"); + } return nativeHasMipMap(mFinalizer.mNativeBitmap); } diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp index 3ebd57b..1e39bfa 100644 --- a/libs/hwui/renderstate/RenderState.cpp +++ b/libs/hwui/renderstate/RenderState.cpp @@ -124,9 +124,15 @@ void RenderState::bindFramebuffer(GLuint fbo) { } void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) { - interruptForFunctorInvoke(); - (*functor)(mode, info); - resumeFromFunctorInvoke(); + if (mode == DrawGlInfo::kModeProcessNoContext) { + // If there's no context we don't need to interrupt as there's + // no gl state to save/restore + (*functor)(mode, info); + } else { + interruptForFunctorInvoke(); + (*functor)(mode, info); + resumeFromFunctorInvoke(); + } } void RenderState::interruptForFunctorInvoke() { diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index e472e93..6dfb6e8 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -229,10 +229,11 @@ void CanvasContext::draw() { SkRect dirty; mDamageAccumulator.finish(&dirty); - if (dirty.isEmpty() && Properties::skipEmptyFrames) { - mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); - return; - } + // TODO: Re-enable after figuring out cause of b/22592975 +// if (dirty.isEmpty() && Properties::skipEmptyFrames) { +// mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); +// return; +// } mCurrentFrameInfo->markIssueDrawCommandsStart(); diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 3c459d8..6c224e5 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.ImageFormat; import android.graphics.Rect; +import android.graphics.SurfaceTexture; import android.media.MediaCodecInfo.CodecCapabilities; import android.os.Bundle; import android.os.Handler; @@ -32,6 +33,7 @@ import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.nio.ReadOnlyBufferException; import java.util.Arrays; import java.util.HashMap; @@ -228,8 +230,9 @@ import java.util.Map; data and submit it as a single codec-config buffer. <p> Android uses the following codec-specific data buffers. These are also required to be set in - the track format for proper {@link MediaMuxer} track configuration. Each parameter set and - codec-specific-data must start with a start code of {@code "\x00\x00\x00\x01"}. + the track format for proper {@link MediaMuxer} track configuration. Each parameter set and the + codec-specific-data sections marked with (<sup>*</sup>) must start with a start code of + {@code "\x00\x00\x00\x01"}. <p> <style>td.NA { background: #ccc; } .mid > tr > td { vertical-align: middle; }</style> <table> @@ -237,28 +240,48 @@ import java.util.Map; <th>Format</th> <th>CSD buffer #0</th> <th>CSD buffer #1</th> + <th>CSD buffer #2</th> </thead> <tbody class=mid> <tr> <td>AAC</td> - <td>Decoder-specific information from ESDS</td> + <td>Decoder-specific information from ESDS<sup>*</sup></td> <td class=NA>Not Used</td> + <td class=NA>Not Used</td> + </tr> + <tr> + <td>VORBIS</td> + <td>Identification header</td> + <td>Setup header</td> + <td class=NA>Not Used</td> + </tr> + <tr> + <td>OPUS</td> + <td>Identification header</td> + <td>Pre-skip in nanosecs<br> + (unsigned 64-bit {@linkplain ByteOrder#nativeOrder native-order} integer.)<br> + This overrides the pre-skip value in the identification header.</td> + <td>Seek Pre-roll in nanosecs<br> + (unsigned 64-bit {@linkplain ByteOrder#nativeOrder native-order} integer.)</td> </tr> <tr> <td>MPEG-4</td> - <td>Decoder-specific information from ESDS</td> + <td>Decoder-specific information from ESDS<sup>*</sup></td> + <td class=NA>Not Used</td> <td class=NA>Not Used</td> </tr> <tr> <td>H.264 AVC</td> - <td>SPS (Sequence Parameter Sets)</td> - <td>PPS (Picture Parameter Sets)</td> + <td>SPS (Sequence Parameter Sets<sup>*</sup>)</td> + <td>PPS (Picture Parameter Sets<sup>*</sup>)</td> + <td class=NA>Not Used</td> </tr> <tr> <td>H.265 HEVC</td> - <td>VPS (Video Parameter Sets) +<br> - SPS (Sequence Parameter Sets) +<br> - PPS (Picture Parameter Sets)</td> + <td>VPS (Video Parameter Sets<sup>*</sup>) +<br> + SPS (Sequence Parameter Sets<sup>*</sup>) +<br> + PPS (Picture Parameter Sets<sup>*</sup>)</td> + <td class=NA>Not Used</td> <td class=NA>Not Used</td> </tr> </tbody> @@ -297,10 +320,10 @@ import java.util.Map; releaseOutputBuffer} methods to return the buffer to the codec. <p> While you are not required to resubmit/release buffers immediately to the codec, holding onto - input and/or output buffers may stall the codec, and this behavior is device dependent. E.g. it - is possible that a codec may hold off on generating output buffers until all outstanding buffers - have been released/resubmitted. Therefore, try to hold onto to available buffers as little as - possible. + input and/or output buffers may stall the codec, and this behavior is device dependent. + <strong>Specifically, it is possible that a codec may hold off on generating output buffers until + <em>all</em> outstanding buffers have been released/resubmitted.</strong> Therefore, try to + hold onto to available buffers as little as possible. <p> Depending on the API version, you can process data in three ways: <table> @@ -346,7 +369,7 @@ import java.util.Map; <p> MediaCodec is typically used like this in asynchronous mode: <pre class=prettyprint> - MediaCodec codec = MediaCodec.createCodecByName(name); + MediaCodec codec = MediaCodec.createByCodecName(name); MediaFormat mOutputFormat; // member variable codec.setCallback(new MediaCodec.Callback() { {@literal @Override} @@ -403,7 +426,7 @@ import java.util.Map; <p> MediaCodec is typically used like this in synchronous mode: <pre> - MediaCodec codec = MediaCodec.createCodecByName(name); + MediaCodec codec = MediaCodec.createByCodecName(name); codec.configure(format, …); MediaFormat outputFormat = codec.getOutputFormat(); // option B codec.start(); @@ -442,7 +465,7 @@ import java.util.Map; between the size of the arrays and the number of input and output buffers used by the system, although the array size provides an upper bound. <pre> - MediaCodec codec = MediaCodec.createCodecByName(name); + MediaCodec codec = MediaCodec.createByCodecName(name); codec.configure(format, …); codec.start(); ByteBuffer[] inputBuffers = codec.getInputBuffers(); @@ -643,10 +666,10 @@ import java.util.Map; class. For API version numbers, see {@link android.os.Build.VERSION_CODES}. <style> - .api > tr > th, td { text-align: center; padding: 4px 4px; } + .api > tr > th, .api > tr > td { text-align: center; padding: 4px 4px; } .api > tr > th { vertical-align: bottom; } .api > tr > td { vertical-align: middle; } - .sml > tr > th, td { text-align: center; padding: 2px 4px; } + .sml > tr > th, .sml > tr > td { text-align: center; padding: 2px 4px; } .fn { text-align: left; } .fn > code > a { font: 14px/19px Roboto Condensed, sans-serif; } .deg45 { @@ -1561,7 +1584,7 @@ final public class MediaCodec { private boolean mHasSurface = false; /** - * Instantiate a decoder supporting input data of the given mime type. + * Instantiate the preferred decoder supporting input data of the given mime type. * * The following is a partial list of defined mime types and their semantics: * <ul> @@ -1580,6 +1603,10 @@ final public class MediaCodec { * <li>"audio/g711-mlaw" - G.711 ulaw audio * </ul> * + * <strong>Note:</strong> It is preferred to use {@link MediaCodecList#findDecoderForFormat} + * and {@link #createByCodecName} to ensure that the resulting codec can handle a + * given format. + * * @param type The mime type of the input data. * @throws IOException if the codec cannot be created. * @throws IllegalArgumentException if type is not a valid mime type. @@ -1592,7 +1619,12 @@ final public class MediaCodec { } /** - * Instantiate an encoder supporting output data of the given mime type. + * Instantiate the preferred encoder supporting output data of the given mime type. + * + * <strong>Note:</strong> It is preferred to use {@link MediaCodecList#findEncoderForFormat} + * and {@link #createByCodecName} to ensure that the resulting codec can handle a + * given format. + * * @param type The desired mime type of the output data. * @throws IOException if the codec cannot be created. * @throws IllegalArgumentException if type is not a valid mime type. @@ -1661,6 +1693,8 @@ final public class MediaCodec { private native final void native_reset(); /** + * Free up resources used by the codec instance. + * * Make sure you call this when you're done to free up any opened * component instance instead of relying on the garbage collector * to do this for you at some point in the future. @@ -1881,17 +1915,25 @@ final public class MediaCodec { private native final void native_stop(); /** - * Flush both input and output ports of the component, all indices - * previously returned in calls to {@link #dequeueInputBuffer} and - * {@link #dequeueOutputBuffer} become invalid. + * Flush both input and output ports of the component. * <p> - * If codec is configured in asynchronous mode, call {@link #start} - * after {@code flush} has returned to resume codec operations. The - * codec will not request input buffers until this has happened. + * Upon return, all indices previously returned in calls to {@link #dequeueInputBuffer + * dequeueInputBuffer} and {@link #dequeueOutputBuffer dequeueOutputBuffer} — or obtained + * via {@link Callback#onInputBufferAvailable onInputBufferAvailable} or + * {@link Callback#onOutputBufferAvailable onOutputBufferAvailable} callbacks — become + * invalid, and all buffers are owned by the codec. * <p> - * If codec is configured in synchronous mode, codec will resume - * automatically if an input surface was created. Otherwise, it - * will resume when {@link #dequeueInputBuffer} is called. + * If the codec is configured in asynchronous mode, call {@link #start} + * after {@code flush} has returned to resume codec operations. The codec + * will not request input buffers until this has happened. + * <strong>Note, however, that there may still be outstanding {@code onOutputBufferAvailable} + * callbacks that were not handled prior to calling {@code flush}. + * The indices returned via these callbacks also become invalid upon calling {@code flush} and + * should be discarded.</strong> + * <p> + * If the codec is configured in synchronous mode, codec will resume + * automatically if it is configured with an input surface. Otherwise, it + * will resume when {@link #dequeueInputBuffer dequeueInputBuffer} is called. * * @throws IllegalStateException if not in the Executing state. * @throws MediaCodec.CodecException upon codec error. @@ -2082,6 +2124,15 @@ final public class MediaCodec { * To indicate that this is the final piece of input data (or rather that * no more input data follows unless the decoder is subsequently flushed) * specify the flag {@link #BUFFER_FLAG_END_OF_STREAM}. + * <p class=note> + * <strong>Note:</strong> Prior to {@link android.os.Build.VERSION_CODES#M}, + * {@code presentationTimeUs} was not propagated to the frame timestamp of (rendered) + * Surface output buffers, and the resulting frame timestamp was undefined. + * Use {@link #releaseOutputBuffer(int, long)} to ensure a specific frame timestamp is set. + * Similarly, since frame timestamps can be used by the destination surface for rendering + * synchronization, <strong>care must be taken to normalize presentationTimeUs so as to not be + * mistaken for a system time. (See {@linkplain #releaseOutputBuffer(int, long) + * SurfaceView specifics}).</strong> * * @param index The index of a client-owned input buffer previously returned * in a call to {@link #dequeueInputBuffer}. @@ -2089,7 +2140,10 @@ final public class MediaCodec { * @param size The number of bytes of valid input data. * @param presentationTimeUs The presentation timestamp in microseconds for this * buffer. This is normally the media time at which this - * buffer should be presented (rendered). + * buffer should be presented (rendered). When using an output + * surface, this will be propagated as the {@link + * SurfaceTexture#getTimestamp timestamp} for the frame (after + * conversion to nanoseconds). * @param flags A bitmask of flags * {@link #BUFFER_FLAG_CODEC_CONFIG} and {@link #BUFFER_FLAG_END_OF_STREAM}. * While not prohibited, most codecs do not use the @@ -2202,8 +2256,10 @@ final public class MediaCodec { }; /** - * Similar to {@link #queueInputBuffer} but submits a buffer that is + * Similar to {@link #queueInputBuffer queueInputBuffer} but submits a buffer that is * potentially encrypted. + * <strong>Check out further notes at {@link #queueInputBuffer queueInputBuffer}.</strong> + * * @param index The index of a client-owned input buffer previously returned * in a call to {@link #dequeueInputBuffer}. * @param offset The byte offset into the input buffer at which the data starts. @@ -2310,7 +2366,7 @@ final public class MediaCodec { /** * Dequeue an output buffer, block at most "timeoutUs" microseconds. * Returns the index of an output buffer that has been successfully - * decoded or one of the INFO_* constants below. + * decoded or one of the INFO_* constants. * @param info Will be filled with buffer meta data. * @param timeoutUs The timeout in microseconds, a negative timeout indicates "infinite". * @throws IllegalStateException if not in the Executing state, @@ -2338,9 +2394,11 @@ final public class MediaCodec { @NonNull BufferInfo info, long timeoutUs); /** - * If you are done with a buffer, use this call to return the buffer to - * the codec. If you previously specified a surface when configuring this - * video decoder you can optionally render the buffer. + * If you are done with a buffer, use this call to return the buffer to the codec + * or to render it on the output surface. If you configured the codec with an + * output surface, setting {@code render} to {@code true} will first send the buffer + * to that output surface. The surface will release the buffer back to the codec once + * it is no longer used/displayed. * * Once an output buffer is released to the codec, it MUST NOT * be used until it is later retrieved by {@link #getOutputBuffer} in response @@ -2674,6 +2732,8 @@ final public class MediaCodec { * <b>Note:</b> As of API 21, dequeued input buffers are * automatically {@link java.nio.Buffer#clear cleared}. * + * <em>Do not use this method if using an input surface.</em> + * * @throws IllegalStateException if not in the Executing state, * or codec is configured in asynchronous mode. * @throws MediaCodec.CodecException upon codec error. @@ -2703,6 +2763,8 @@ final public class MediaCodec { * buffers that are dequeued will be set to the valid data * range. * + * <em>Do not use this method if using an output surface.</em> + * * @throws IllegalStateException if not in the Executing state, * or codec is configured in asynchronous mode. * @throws MediaCodec.CodecException upon codec error. @@ -2988,6 +3050,10 @@ final public class MediaCodec { /** * Called when an output frame has rendered on the output surface. + * <p> + * <strong>Note:</strong> This callback is for informational purposes only: to get precise + * render timing samples, and can be significantly delayed and batched. Some frames may have + * been rendered even if there was no callback generated. * * @param codec the MediaCodec instance * @param presentationTimeUs the presentation time (media time) of the frame rendered. @@ -3004,10 +3070,14 @@ final public class MediaCodec { } /** - * Register a callback to be invoked when an output frame is rendered on the output surface. + * Registers a callback to be invoked when an output frame is rendered on the output surface. * <p> * This method can be called in any codec state, but will only have an effect in the * Executing state for codecs that render buffers to the output surface. + * <p> + * <strong>Note:</strong> This callback is for informational purposes only: to get precise + * render timing samples, and can be significantly delayed and batched. Some frames may have + * been rendered even if there was no callback generated. * * @param listener the callback that will be run * @param handler the callback will be run on the handler's thread. If {@code null}, diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 8243d40..4101935 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -525,6 +525,14 @@ public final class MediaCodecInfo { /** * Query whether codec supports a given {@link MediaFormat}. + * + * <p class=note> + * <strong>Note:</strong> On {@link android.os.Build.VERSION_CODES#LOLLIPOP}, + * {@code format} must not contain a {@linkplain MediaFormat#KEY_FRAME_RATE + * frame rate}. Use + * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code> + * to clear any existing frame rate setting in the format. + * * @param format media format with optional feature directives. * @throws IllegalArgumentException if format is not a valid media format. * @return whether the codec capabilities support the given format @@ -1230,8 +1238,22 @@ public final class MediaCodecInfo { * May return {@code null}, if the codec did not publish any measurement * data. * <p> - * This is a performance estimate, based on full-speed decoding - * and encoding measurements of common video sizes supported by the codec. + * This is a performance estimate provided by the device manufacturer + * based on full-speed decoding and encoding measurements in various configurations + * of common video sizes supported by the codec. As such it should only be used to + * compare individual codecs on the device. The value is not suitable for comparing + * different devices or even different android releases for the same device. + * <p> + * The returned range corresponds to the fastest frame rates achieved in the tested + * configurations. It is interpolated from the nearest frame size(s) tested. Codec + * performance is severely impacted by other activity on the device, and can vary + * significantly. + * <p class=note> + * Use this method in cases where only codec performance matters, e.g. to evaluate if + * a codec has any chance of meeting a performance target. Codecs are listed + * in {@link MediaCodecList} in the preferred order as defined by the device + * manufacturer. As such, applications should use the first suitable codec in the + * list to achieve the best balance between power use and performance. * * @param width the width of the video * @param height the height of the video diff --git a/media/java/android/media/MediaCodecList.java b/media/java/android/media/MediaCodecList.java index f44e048..cd7b3d3 100644 --- a/media/java/android/media/MediaCodecList.java +++ b/media/java/android/media/MediaCodecList.java @@ -190,6 +190,13 @@ final public class MediaCodecList { * Find a decoder supporting a given {@link MediaFormat} in the list * of media-codecs. * + * <p class=note> + * <strong>Note:</strong> On {@link android.os.Build.VERSION_CODES#LOLLIPOP}, + * {@code format} must not contain a {@linkplain MediaFormat#KEY_FRAME_RATE + * frame rate}. Use + * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code> + * to clear any existing frame rate setting in the format. + * * @param format A decoder media format with optional feature directives. * @throws IllegalArgumentException if format is not a valid media format. * @throws NullPointerException if format is null. @@ -204,6 +211,13 @@ final public class MediaCodecList { * Find an encoder supporting a given {@link MediaFormat} in the list * of media-codecs. * + * <p class=note> + * <strong>Note:</strong> On {@link android.os.Build.VERSION_CODES#LOLLIPOP}, + * {@code format} must not contain a {@linkplain MediaFormat#KEY_FRAME_RATE + * frame rate}. Use + * <code class=prettyprint>format.setString(MediaFormat.KEY_FRAME_RATE, null)</code> + * to clear any existing frame rate setting in the format. + * * @param format An encoder media format with optional feature directives. * @throws IllegalArgumentException if format is not a valid media format. * @throws NullPointerException if format is null. diff --git a/media/java/android/media/Ringtone.java b/media/java/android/media/Ringtone.java index 9e9d602..c2bcd93 100644 --- a/media/java/android/media/Ringtone.java +++ b/media/java/android/media/Ringtone.java @@ -52,7 +52,8 @@ public class Ringtone { MediaStore.Audio.Media.TITLE }; /** Selection that limits query results to just audio files */ - private static final String MEDIA_SELECTION = MediaColumns.MIME_TYPE + " LIKE 'audio/%'"; + private static final String MEDIA_SELECTION = MediaColumns.MIME_TYPE + " LIKE 'audio/%' OR " + + MediaColumns.MIME_TYPE + " IN ('application/ogg', 'application/x-flac')"; // keep references on active Ringtones until stopped or completion listener called. private static final ArrayList<Ringtone> sActiveRingtones = new ArrayList<Ringtone>(); diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java index 0c6837f..8f792de 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -53,15 +53,18 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout super(context, attrs); } + @Override public void setKeyguardCallback(KeyguardSecurityCallback callback) { mCallback = callback; } + @Override public void setLockPatternUtils(LockPatternUtils utils) { mLockPatternUtils = utils; mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled(); } + @Override public void reset() { // start fresh resetPasswordText(false /* animate */); @@ -95,6 +98,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout } } + @Override public void onEmergencyButtonClickedWhenInCall() { mCallback.reset(); } @@ -115,11 +119,11 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout mPendingLockCheck.cancel(false); } - if (entry.length() < MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { + if (entry.length() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { // to avoid accidental lockout, only count attempts that are long enough to be a // real password. This may require some tweaking. setPasswordEntryInputEnabled(true); - onPasswordChecked(entry, false, 0); + onPasswordChecked(false /* matched */, 0, false /* not valid - too short */); return; } @@ -132,24 +136,27 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout public void onChecked(boolean matched, int timeoutMs) { setPasswordEntryInputEnabled(true); mPendingLockCheck = null; - onPasswordChecked(entry, matched, timeoutMs); + onPasswordChecked(matched, timeoutMs, true /* isValidPassword */); } }); } - private void onPasswordChecked(String entry, boolean matched, int timeoutMs) { + private void onPasswordChecked(boolean matched, int timeoutMs, boolean isValidPassword) { if (matched) { mCallback.reportUnlockAttempt(true, 0); mCallback.dismiss(true); } else { - mCallback.reportUnlockAttempt(false, timeoutMs); - int attempts = KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(); - if (timeoutMs > 0) { - long deadline = mLockPatternUtils.setLockoutAttemptDeadline( - KeyguardUpdateMonitor.getCurrentUser(), timeoutMs); - handleAttemptLockout(deadline); + if (isValidPassword) { + mCallback.reportUnlockAttempt(false, timeoutMs); + if (timeoutMs > 0) { + long deadline = mLockPatternUtils.setLockoutAttemptDeadline( + KeyguardUpdateMonitor.getCurrentUser(), timeoutMs); + handleAttemptLockout(deadline); + } + } + if (timeoutMs == 0) { + mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true); } - mSecurityMessageDisplay.setMessage(getWrongPasswordStringId(), true); } resetPasswordText(true /* animate */); } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java index b000e26..4bd1a2e 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java @@ -82,6 +82,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit * Useful for clearing out the wrong pattern after a delay */ private Runnable mCancelPatternRunnable = new Runnable() { + @Override public void run() { mLockPatternView.clearPattern(); } @@ -117,10 +118,12 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit R.dimen.disappear_y_translation); } + @Override public void setKeyguardCallback(KeyguardSecurityCallback callback) { mCallback = callback; } + @Override public void setLockPatternUtils(LockPatternUtils utils) { mLockPatternUtils = utils; } @@ -153,6 +156,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } } + @Override public void onEmergencyButtonClickedWhenInCall() { mCallback.reset(); } @@ -174,6 +178,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit return result; } + @Override public void reset() { // reset lock pattern mLockPatternView.enableInput(); @@ -207,18 +212,22 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit private class UnlockPatternListener implements LockPatternView.OnPatternListener { + @Override public void onPatternStart() { mLockPatternView.removeCallbacks(mCancelPatternRunnable); mSecurityMessageDisplay.setMessage("", false); } + @Override public void onPatternCleared() { } + @Override public void onPatternCellAdded(List<LockPatternView.Cell> pattern) { mCallback.userActivity(); } + @Override public void onPatternDetected(final List<LockPatternView.Cell> pattern) { mLockPatternView.disableInput(); if (mPendingLockCheck != null) { @@ -227,7 +236,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) { mLockPatternView.enableInput(); - onPatternChecked(pattern, false, 0); + onPatternChecked(false, 0, false /* not valid - too short */); return; } @@ -240,29 +249,30 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit public void onChecked(boolean matched, int timeoutMs) { mLockPatternView.enableInput(); mPendingLockCheck = null; - onPatternChecked(pattern, matched, timeoutMs); + onPatternChecked(matched, timeoutMs, true); } }); + if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { + mCallback.userActivity(); + } } - private void onPatternChecked(List<LockPatternView.Cell> pattern, boolean matched, - int timeoutMs) { + private void onPatternChecked(boolean matched, int timeoutMs, boolean isValidPattern) { if (matched) { mCallback.reportUnlockAttempt(true, 0); mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct); mCallback.dismiss(true); } else { - if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { - mCallback.userActivity(); - } mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong); - mCallback.reportUnlockAttempt(false, timeoutMs); - int attempts = mKeyguardUpdateMonitor.getFailedUnlockAttempts(); - if (timeoutMs > 0) { - long deadline = mLockPatternUtils.setLockoutAttemptDeadline( - KeyguardUpdateMonitor.getCurrentUser(), timeoutMs); - handleAttemptLockout(deadline); - } else { + if (isValidPattern) { + mCallback.reportUnlockAttempt(false, timeoutMs); + if (timeoutMs > 0) { + long deadline = mLockPatternUtils.setLockoutAttemptDeadline( + KeyguardUpdateMonitor.getCurrentUser(), timeoutMs); + handleAttemptLockout(deadline); + } + } + if (timeoutMs == 0) { mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern, true); mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 249eaa5..b0429ef 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -808,7 +808,7 @@ public final class CachedBluetoothDevice implements Comparable<CachedBluetoothDe // The pairing dialog now warns of phone-book access for paired devices. // No separate prompt is displayed after pairing. if (getPhonebookPermissionChoice() == CachedBluetoothDevice.ACCESS_UNKNOWN) { - setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_REJECTED); + setPhonebookPermissionChoice(CachedBluetoothDevice.ACCESS_ALLOWED); } } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 3e9b122..73971ad 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -42,6 +42,9 @@ import android.os.Build; import android.os.Bundle; import android.os.DropBoxManager; import android.os.Environment; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.SystemProperties; @@ -204,9 +207,6 @@ public class SettingsProvider extends ContentProvider { // We have to call in the user manager with no lock held, private volatile UserManager mUserManager; - // We have to call in the app ops manager with no lock held, - private volatile AppOpsManager mAppOpsManager; - // We have to call in the package manager with no lock held, private volatile PackageManager mPackageManager; @@ -214,7 +214,6 @@ public class SettingsProvider extends ContentProvider { public boolean onCreate() { synchronized (mLock) { mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); - mAppOpsManager = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); mPackageManager = getContext().getPackageManager(); mSettingsRegistry = new SettingsRegistry(); } @@ -532,7 +531,7 @@ public class SettingsProvider extends ContentProvider { } while (cursor.moveToNext()); } - private static final String toDumpString(String s) { + private static String toDumpString(String s) { if (s != null) { return s; } @@ -1158,18 +1157,6 @@ public class SettingsProvider extends ContentProvider { getCallingPackage()); } - private void sendNotify(Uri uri, int userId) { - final long identity = Binder.clearCallingIdentity(); - try { - getContext().getContentResolver().notifyChange(uri, null, true, userId); - if (DEBUG) { - Slog.v(LOG_TAG, "Notifying for " + userId + ": " + uri); - } - } finally { - Binder.restoreCallingIdentity(identity); - } - } - private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( int targetSdkVersion, String name) { // If the app targets Lollipop MR1 or older SDK we warn, otherwise crash. @@ -1390,8 +1377,11 @@ public class SettingsProvider extends ContentProvider { private final BackupManager mBackupManager; + private final Handler mHandler; + public SettingsRegistry() { mBackupManager = new BackupManager(getContext()); + mHandler = new MyHandler(getContext().getMainLooper()); migrateAllLegacySettingsIfNeeded(); } @@ -1733,7 +1723,7 @@ public class SettingsProvider extends ContentProvider { // Inform the backup manager about a data change if (backedUpDataChanged) { - mBackupManager.dataChanged(); + mHandler.obtainMessage(MyHandler.MSG_NOTIFY_DATA_CHANGED).sendToTarget(); } // Now send the notification through the content framework. @@ -1741,7 +1731,9 @@ public class SettingsProvider extends ContentProvider { final int userId = getUserIdFromKey(key); Uri uri = getNotificationUriFor(key, name); - sendNotify(uri, userId); + mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED, + userId, 0, uri).sendToTarget(); + if (isSecureSettingsKey(key)) { maybeNotifyProfiles(userId, uri, name, sSecureCloneToManagedSettings); } else if (isSystemSettingsKey(key)) { @@ -1758,7 +1750,8 @@ public class SettingsProvider extends ContentProvider { UserInfo profile = profiles.get(i); // the notification for userId has already been sent. if (profile.id != userId) { - sendNotify(uri, profile.id); + mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED, + profile.id, 0, uri).sendToTarget(); } } } @@ -1834,6 +1827,33 @@ public class SettingsProvider extends ContentProvider { } } + private final class MyHandler extends Handler { + private static final int MSG_NOTIFY_URI_CHANGED = 1; + private static final int MSG_NOTIFY_DATA_CHANGED = 2; + + public MyHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_NOTIFY_URI_CHANGED: { + final int userId = msg.arg1; + Uri uri = (Uri) msg.obj; + getContext().getContentResolver().notifyChange(uri, null, true, userId); + if (DEBUG) { + Slog.v(LOG_TAG, "Notifying for " + userId + ": " + uri); + } + } break; + + case MSG_NOTIFY_DATA_CHANGED: { + mBackupManager.dataChanged(); + } break; + } + } + } + private final class UpgradeController { private static final int SETTINGS_VERSION = 122; @@ -1963,9 +1983,11 @@ public class SettingsProvider extends ContentProvider { currentVersion = 120; } - // Before 121, we used a different string encoding logic. We just bump the version - // here; SettingsState knows how to handle pre-version 120 files. - currentVersion = 121; + if (currentVersion == 120) { + // Before 121, we used a different string encoding logic. We just bump the + // version here; SettingsState knows how to handle pre-version 120 files. + currentVersion = 121; + } if (currentVersion == 121) { // Version 122: allow OEMs to set a default payment component in resources. diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java index 5d74604..21cbef2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java @@ -79,6 +79,8 @@ public class FlashlightTile extends QSTile<QSTile.BooleanState> implements return; } state.value = value; + } else { + state.value = mFlashlightController.isEnabled(); } final AnimationIcon icon = state.value ? mEnable : mDisable; icon.setAllowAnimation(arg instanceof UserBoolean && ((UserBoolean) arg).userInitiated); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java index cd1914c..29a8f67 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/FlashlightController.java @@ -93,6 +93,10 @@ public class FlashlightController { } } + public synchronized boolean isEnabled() { + return mFlashlightEnabled; + } + public synchronized boolean isAvailable() { return mTorchAvailable; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java index 13e9b16..ed1dca3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java @@ -51,7 +51,7 @@ public class MobileSignalController extends SignalController< @VisibleForTesting final PhoneStateListener mPhoneStateListener; // Save entire info for logging, we only use the id. - private final SubscriptionInfo mSubscriptionInfo; + final SubscriptionInfo mSubscriptionInfo; // @VisibleForDemoMode final SparseArray<MobileIconGroup> mNetworkToIconLookup; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 57dfff5..2996808 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -66,6 +66,11 @@ public class NetworkControllerImpl extends BroadcastReceiver // additional diagnostics, but not logspew static final boolean CHATTY = Log.isLoggable(TAG + "Chat", Log.DEBUG); + private static final int EMERGENCY_NO_CONTROLLERS = 0; + private static final int EMERGENCY_FIRST_CONTROLLER = 100; + private static final int EMERGENCY_VOICE_CONTROLLER = 200; + private static final int EMERGENCY_NO_SUB = 300; + private final Context mContext; private final TelephonyManager mPhone; private final WifiManager mWifiManager; @@ -118,6 +123,9 @@ public class NetworkControllerImpl extends BroadcastReceiver // Handler that all callbacks are made on. private final CallbackHandler mCallbackHandler; + private int mEmergencySource; + private boolean mIsEmergency; + @VisibleForTesting ServiceState mLastServiceState; @@ -267,6 +275,7 @@ public class NetworkControllerImpl extends BroadcastReceiver if (mMobileSignalControllers.size() == 0) { // When there are no active subscriptions, determine emengency state from last // broadcast. + mEmergencySource = EMERGENCY_NO_CONTROLLERS; return mLastServiceState != null && mLastServiceState.isEmergencyOnly(); } int voiceSubId = mSubDefaults.getDefaultVoiceSubId(); @@ -274,16 +283,20 @@ public class NetworkControllerImpl extends BroadcastReceiver for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { if (!mobileSignalController.getState().isEmergency) { + mEmergencySource = EMERGENCY_FIRST_CONTROLLER + + mobileSignalController.mSubscriptionInfo.getSubscriptionId(); if (DEBUG) Log.d(TAG, "Found emergency " + mobileSignalController.mTag); return false; } } } if (mMobileSignalControllers.containsKey(voiceSubId)) { + mEmergencySource = EMERGENCY_VOICE_CONTROLLER + voiceSubId; if (DEBUG) Log.d(TAG, "Getting emergency from " + voiceSubId); return mMobileSignalControllers.get(voiceSubId).getState().isEmergency; } if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId); + mEmergencySource = EMERGENCY_NO_SUB + voiceSubId; // Something is wrong, better assume we can't make calls... return true; } @@ -293,7 +306,8 @@ public class NetworkControllerImpl extends BroadcastReceiver * so we should recheck and send out the state to listeners. */ void recalculateEmergency() { - mCallbackHandler.setEmergencyCallsOnly(isEmergencyOnly()); + mIsEmergency = isEmergencyOnly(); + mCallbackHandler.setEmergencyCallsOnly(mIsEmergency); } public void addSignalCallback(SignalCallback cb) { @@ -606,6 +620,10 @@ public class NetworkControllerImpl extends BroadcastReceiver pw.println(mLocale); pw.print(" mLastServiceState="); pw.println(mLastServiceState); + pw.print(" mIsEmergency="); + pw.println(mIsEmergency); + pw.print(" mEmergencySource="); + pw.println(emergencyToString(mEmergencySource)); for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) { mobileSignalController.dump(pw); @@ -617,6 +635,19 @@ public class NetworkControllerImpl extends BroadcastReceiver mAccessPoints.dump(pw); } + private static final String emergencyToString(int emergencySource) { + if (emergencySource > EMERGENCY_NO_SUB) { + return "NO_SUB(" + (emergencySource - EMERGENCY_NO_SUB) + ")"; + } else if (emergencySource > EMERGENCY_VOICE_CONTROLLER) { + return "VOICE_CONTROLLER(" + (emergencySource - EMERGENCY_VOICE_CONTROLLER) + ")"; + } else if (emergencySource > EMERGENCY_FIRST_CONTROLLER) { + return "FIRST_CONTROLLER(" + (emergencySource - EMERGENCY_FIRST_CONTROLLER) + ")"; + } else if (emergencySource == EMERGENCY_NO_CONTROLLERS) { + return "NO_CONTROLLERS"; + } + return "UNKNOWN_SOURCE"; + } + private boolean mDemoMode; private boolean mDemoInetCondition; private WifiSignalController.WifiState mDemoWifiState; diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index ec02789..2a3492b 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -1718,11 +1718,15 @@ public class AppOpsService extends IAppOpsService.Stub { } private static String[] getPackagesForUid(int uid) { + String[] packageNames = null; try { - return AppGlobals.getPackageManager().getPackagesForUid(uid); + packageNames= AppGlobals.getPackageManager().getPackagesForUid(uid); } catch (RemoteException e) { /* ignore - local call */ } - return EmptyArray.STRING; + if (packageNames == null) { + return EmptyArray.STRING; + } + return packageNames; } } diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 10a4cd1..50bd544 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -1506,6 +1506,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Unbind mContext.unbindService(mConnection); } + mBluetoothGatt = null; } SystemClock.sleep(100); @@ -1811,6 +1812,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { //Unbind mContext.unbindService(mConnection); } + mBluetoothGatt = null; } mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 6ab2fd7..53e8d14 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -559,6 +559,7 @@ class MountService extends IMountService.Stub private static final int H_FSTRIM = 4; private static final int H_VOLUME_MOUNT = 5; private static final int H_VOLUME_BROADCAST = 6; + private static final int H_INTERNAL_BROADCAST = 7; class MountServiceHandler extends Handler { public MountServiceHandler(Looper looper) { @@ -655,6 +656,13 @@ class MountService extends IMountService.Stub } break; } + case H_INTERNAL_BROADCAST: { + // Internal broadcasts aimed at system components, not for + // third-party apps. + final Intent intent = (Intent) msg.obj; + mContext.sendBroadcastAsUser(intent, UserHandle.ALL, + android.Manifest.permission.WRITE_MEDIA_STORAGE); + } } } } @@ -1126,8 +1134,7 @@ class MountService extends IMountService.Stub intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.id); intent.putExtra(DiskInfo.EXTRA_VOLUME_COUNT, volumeCount); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, - android.Manifest.permission.WRITE_MEDIA_STORAGE); + mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget(); final CountDownLatch latch = mDiskScanLatches.remove(disk.id); if (latch != null) { @@ -1239,8 +1246,7 @@ class MountService extends IMountService.Stub intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState); intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL, - android.Manifest.permission.WRITE_MEDIA_STORAGE); + mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget(); } final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState); diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 83e8db0..32fd56a 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -527,14 +527,14 @@ public class AccountManagerService + ", pid " + Binder.getCallingPid()); } if (account == null) throw new IllegalArgumentException("account is null"); - int userId = UserHandle.getCallingUserId(); - if (!isAccountManagedByCaller(account.type, callingUid, userId)) { + if (!isAccountManagedByCaller(account.type, callingUid)) { String msg = String.format( "uid %s cannot get secrets for accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } + int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -627,14 +627,14 @@ public class AccountManagerService } if (account == null) throw new IllegalArgumentException("account is null"); if (key == null) throw new IllegalArgumentException("key is null"); - int userId = UserHandle.getCallingUserId(); - if (!isAccountManagedByCaller(account.type, callingUid, userId)) { + if (!isAccountManagedByCaller(account.type, callingUid)) { String msg = String.format( "uid %s cannot get user data for accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } + int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -664,32 +664,22 @@ public class AccountManagerService final long identityToken = clearCallingIdentity(); try { - return getAuthenticatorTypesInternal(userId); - + Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>> + authenticatorCollection = mAuthenticatorCache.getAllServices(userId); + AuthenticatorDescription[] types = + new AuthenticatorDescription[authenticatorCollection.size()]; + int i = 0; + for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator + : authenticatorCollection) { + types[i] = authenticator.type; + i++; + } + return types; } finally { restoreCallingIdentity(identityToken); } } - /** - * Should only be called inside of a clearCallingIdentity block. - */ - private AuthenticatorDescription[] getAuthenticatorTypesInternal(int userId) { - Collection<AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription>> - authenticatorCollection = mAuthenticatorCache.getAllServices(userId); - AuthenticatorDescription[] types = - new AuthenticatorDescription[authenticatorCollection.size()]; - int i = 0; - for (AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticator - : authenticatorCollection) { - types[i] = authenticator.type; - i++; - } - return types; - } - - - private boolean isCrossUser(int callingUid, int userId) { return (userId != UserHandle.getCallingUserId() && callingUid != Process.myUid() @@ -707,8 +697,7 @@ public class AccountManagerService + ", pid " + Binder.getCallingPid()); } if (account == null) throw new IllegalArgumentException("account is null"); - int userId = UserHandle.getCallingUserId(); - if (!isAccountManagedByCaller(account.type, callingUid, userId)) { + if (!isAccountManagedByCaller(account.type, callingUid)) { String msg = String.format( "uid %s cannot explicitly add accounts of type: %s", callingUid, @@ -724,10 +713,12 @@ public class AccountManagerService */ // fails if the account already exists + int uid = getCallingUid(); + int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); - return addAccountInternal(accounts, account, password, extras, false, callingUid); + return addAccountInternal(accounts, account, password, extras, false, uid); } finally { restoreCallingIdentity(identityToken); } @@ -803,26 +794,25 @@ public class AccountManagerService if (account == null) { throw new IllegalArgumentException("account is null"); } - int userId = UserHandle.getCallingUserId(); - if (!isAccountManagedByCaller(account.type, callingUid, userId)) { + if (!isAccountManagedByCaller(account.type, callingUid)) { String msg = String.format( "uid %s cannot notify authentication for accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } - + int userId = Binder.getCallingUserHandle().getIdentifier(); if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) { return false; } - + int user = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { - UserAccounts accounts = getUserAccounts(userId); - return updateLastAuthenticatedTime(account); + UserAccounts accounts = getUserAccounts(user); } finally { restoreCallingIdentity(identityToken); } + return updateLastAuthenticatedTime(account); } private boolean updateLastAuthenticatedTime(Account account) { @@ -995,9 +985,8 @@ public class AccountManagerService if (response == null) throw new IllegalArgumentException("response is null"); if (account == null) throw new IllegalArgumentException("account is null"); if (features == null) throw new IllegalArgumentException("features is null"); + checkReadAccountsPermitted(callingUid, account.type); int userId = UserHandle.getCallingUserId(); - checkReadAccountsPermitted(callingUid, account.type, userId); - long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -1073,14 +1062,14 @@ public class AccountManagerService + ", pid " + Binder.getCallingPid()); } if (accountToRename == null) throw new IllegalArgumentException("account is null"); - int userId = UserHandle.getCallingUserId(); - if (!isAccountManagedByCaller(accountToRename.type, callingUid, userId)) { + if (!isAccountManagedByCaller(accountToRename.type, callingUid)) { String msg = String.format( "uid %s cannot rename accounts of type: %s", callingUid, accountToRename.type); throw new SecurityException(msg); } + int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -1222,15 +1211,14 @@ public class AccountManagerService * authenticator. This will let users remove accounts (via Settings in the system) but not * arbitrary applications (like competing authenticators). */ - UserHandle user = new UserHandle(userId); - if (!isAccountManagedByCaller(account.type, callingUid, user.getIdentifier()) - && !isSystemUid(callingUid)) { + if (!isAccountManagedByCaller(account.type, callingUid) && !isSystemUid(callingUid)) { String msg = String.format( "uid %s cannot remove accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } + if (!canUserModifyAccounts(userId)) { try { response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED, @@ -1247,7 +1235,10 @@ public class AccountManagerService } return; } + + UserHandle user = new UserHandle(userId); long identityToken = clearCallingIdentity(); + UserAccounts accounts = getUserAccounts(userId); cancelNotification(getSigninRequiredNotificationId(accounts, account), user); synchronized(accounts.credentialsPermissionNotificationIds) { @@ -1277,7 +1268,6 @@ public class AccountManagerService + ", caller's uid " + callingUid + ", pid " + Binder.getCallingPid()); } - int userId = Binder.getCallingUserHandle().getIdentifier(); if (account == null) { /* * Null accounts should result in returning false, as per @@ -1285,18 +1275,22 @@ public class AccountManagerService */ Log.e(TAG, "account is null"); return false; - } else if (!isAccountManagedByCaller(account.type, callingUid, userId)) { + } else if (!isAccountManagedByCaller(account.type, callingUid)) { String msg = String.format( "uid %s cannot explicitly add accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } + UserAccounts accounts = getUserAccountsForCaller(); + int userId = Binder.getCallingUserHandle().getIdentifier(); if (!canUserModifyAccounts(userId) || !canUserModifyAccountsForType(userId, account.type)) { return false; } + logRecord(accounts, DebugDbHelper.ACTION_CALLED_ACCOUNT_REMOVE, TABLE_ACCOUNTS); + long identityToken = clearCallingIdentity(); try { return removeAccountInternal(accounts, account); @@ -1530,14 +1524,14 @@ public class AccountManagerService } if (account == null) throw new IllegalArgumentException("account is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); - int userId = UserHandle.getCallingUserId(); - if (!isAccountManagedByCaller(account.type, callingUid, userId)) { + if (!isAccountManagedByCaller(account.type, callingUid)) { String msg = String.format( "uid %s cannot peek the authtokens associated with accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } + int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -1558,14 +1552,14 @@ public class AccountManagerService } if (account == null) throw new IllegalArgumentException("account is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); - int userId = UserHandle.getCallingUserId(); - if (!isAccountManagedByCaller(account.type, callingUid, userId)) { + if (!isAccountManagedByCaller(account.type, callingUid)) { String msg = String.format( "uid %s cannot set auth tokens associated with accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } + int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -1584,14 +1578,14 @@ public class AccountManagerService + ", pid " + Binder.getCallingPid()); } if (account == null) throw new IllegalArgumentException("account is null"); - int userId = UserHandle.getCallingUserId(); - if (!isAccountManagedByCaller(account.type, callingUid, userId)) { + if (!isAccountManagedByCaller(account.type, callingUid)) { String msg = String.format( "uid %s cannot set secrets for accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } + int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -1648,14 +1642,14 @@ public class AccountManagerService + ", pid " + Binder.getCallingPid()); } if (account == null) throw new IllegalArgumentException("account is null"); - int userId = UserHandle.getCallingUserId(); - if (!isAccountManagedByCaller(account.type, callingUid, userId)) { + if (!isAccountManagedByCaller(account.type, callingUid)) { String msg = String.format( "uid %s cannot clear passwords for accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } + int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -1676,14 +1670,14 @@ public class AccountManagerService } if (key == null) throw new IllegalArgumentException("key is null"); if (account == null) throw new IllegalArgumentException("account is null"); - int userId = UserHandle.getCallingUserId(); - if (!isAccountManagedByCaller(account.type, callingUid, userId)) { + if (!isAccountManagedByCaller(account.type, callingUid)) { String msg = String.format( "uid %s cannot set user data for accounts of type: %s", callingUid, account.type); throw new SecurityException(msg); } + int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -1846,8 +1840,8 @@ public class AccountManagerService // skip the check if customTokens final int callerUid = Binder.getCallingUid(); - final boolean permissionGranted = - customTokens || permissionIsGranted(account, authTokenType, callerUid, userId); + final boolean permissionGranted = customTokens || + permissionIsGranted(account, authTokenType, callerUid); // Get the calling package. We will use it for the purpose of caching. final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME); @@ -2369,14 +2363,14 @@ public class AccountManagerService } if (response == null) throw new IllegalArgumentException("response is null"); if (accountType == null) throw new IllegalArgumentException("accountType is null"); - int userId = UserHandle.getCallingUserId(); - if (!isAccountManagedByCaller(accountType, callingUid, userId) && !isSystemUid(callingUid)) { + if (!isAccountManagedByCaller(accountType, callingUid) && !isSystemUid(callingUid)) { String msg = String.format( "uid %s cannot edit authenticator properites for account type: %s", callingUid, accountType); throw new SecurityException(msg); } + int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts accounts = getUserAccounts(userId); @@ -2499,22 +2493,20 @@ public class AccountManagerService } /** - * Returns the accounts visible to the client within the context of a specific user + * Returns the accounts for a specific user * @hide */ public Account[] getAccounts(int userId) { int callingUid = Binder.getCallingUid(); - List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId); - if (visibleAccountTypes.isEmpty()) { + if (!isReadAccountsPermitted(callingUid, null)) { return new Account[0]; } long identityToken = clearCallingIdentity(); try { - return getAccountsInternal( - userId, - callingUid, - null, // packageName - visibleAccountTypes); + UserAccounts accounts = getUserAccounts(userId); + synchronized (accounts.cacheLock) { + return getAccountsFromCacheLocked(accounts, null, callingUid, null); + } } finally { restoreCallingIdentity(identityToken); } @@ -2596,52 +2588,22 @@ public class AccountManagerService callingUid = packageUid; } - List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId); - if (visibleAccountTypes.isEmpty() - || (type != null && !visibleAccountTypes.contains(type))) { + // Authenticators should be able to see their own accounts regardless of permissions. + if (TextUtils.isEmpty(type) && !isReadAccountsPermitted(callingUid, type)) { return new Account[0]; - } else if (visibleAccountTypes.contains(type)) { - // Prune the list down to just the requested type. - visibleAccountTypes = new ArrayList<>(); - visibleAccountTypes.add(type); - } // else aggregate all the visible accounts (it won't matter if the list is empty). + } long identityToken = clearCallingIdentity(); try { - return getAccountsInternal( - userId, - callingUid, - callingPackage, - visibleAccountTypes); + UserAccounts accounts = getUserAccounts(userId); + synchronized (accounts.cacheLock) { + return getAccountsFromCacheLocked(accounts, type, callingUid, callingPackage); + } } finally { restoreCallingIdentity(identityToken); } } - private Account[] getAccountsInternal( - int userId, - int callingUid, - String callingPackage, - List<String> visibleAccountTypes) { - UserAccounts accounts = getUserAccounts(userId); - synchronized (accounts.cacheLock) { - UserAccounts userAccounts = getUserAccounts(userId); - ArrayList<Account> visibleAccounts = new ArrayList<>(); - for (String visibleType : visibleAccountTypes) { - Account[] accountsForType = getAccountsFromCacheLocked( - userAccounts, visibleType, callingUid, callingPackage); - if (accountsForType != null) { - visibleAccounts.addAll(Arrays.asList(accountsForType)); - } - } - Account[] result = new Account[visibleAccounts.size()]; - for (int i = 0; i < visibleAccounts.size(); i++) { - result[i] = visibleAccounts.get(i); - } - return result; - } - } - @Override public boolean addSharedAccountAsUser(Account account, int userId) { userId = handleIncomingUser(userId); @@ -2777,12 +2739,8 @@ public class AccountManagerService } if (response == null) throw new IllegalArgumentException("response is null"); if (type == null) throw new IllegalArgumentException("accountType is null"); - int userId = UserHandle.getCallingUserId(); - - List<String> visibleAccountTypes = getTypesVisibleToCaller(callingUid, userId); - if (!visibleAccountTypes.contains(type)) { + if (!isReadAccountsPermitted(callingUid, type)) { Bundle result = new Bundle(); - // Need to return just the accounts that are from matching signatures. result.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]); try { response.onResult(result); @@ -2791,6 +2749,7 @@ public class AccountManagerService } return; } + int userId = UserHandle.getCallingUserId(); long identityToken = clearCallingIdentity(); try { UserAccounts userAccounts = getUserAccounts(userId); @@ -2804,11 +2763,7 @@ public class AccountManagerService onResult(response, result); return; } - new GetAccountsByTypeAndFeatureSession( - userAccounts, - response, - type, - features, + new GetAccountsByTypeAndFeatureSession(userAccounts, response, type, features, callingUid).bind(); } finally { restoreCallingIdentity(identityToken); @@ -3741,11 +3696,10 @@ public class AccountManagerService return false; } - private boolean permissionIsGranted( - Account account, String authTokenType, int callerUid, int userId) { + private boolean permissionIsGranted(Account account, String authTokenType, int callerUid) { final boolean isPrivileged = isPrivileged(callerUid); final boolean fromAuthenticator = account != null - && isAccountManagedByCaller(account.type, callerUid, userId); + && isAccountManagedByCaller(account.type, callerUid); final boolean hasExplicitGrants = account != null && hasExplicitlyGrantedPermission(account, authTokenType, callerUid); if (Log.isLoggable(TAG, Log.VERBOSE)) { @@ -3757,45 +3711,23 @@ public class AccountManagerService return fromAuthenticator || hasExplicitGrants || isPrivileged; } - private boolean isAccountVisibleToCaller(String accountType, int callingUid, int userId) { + private boolean isAccountManagedByCaller(String accountType, int callingUid) { if (accountType == null) { return false; - } else { - return getTypesVisibleToCaller(callingUid, userId).contains(accountType); } - } - - private boolean isAccountManagedByCaller(String accountType, int callingUid, int userId) { - if (accountType == null) { - return false; - } else { - return getTypesManagedByCaller(callingUid, userId).contains(accountType); - } - } - - private List<String> getTypesVisibleToCaller(int callingUid, int userId) { - boolean isPermitted = - isPermitted(callingUid, Manifest.permission.GET_ACCOUNTS, - Manifest.permission.GET_ACCOUNTS_PRIVILEGED); - Log.i(TAG, String.format("getTypesVisibleToCaller: isPermitted? %s", isPermitted)); - return getTypesForCaller(callingUid, userId, isPermitted); - } - - private List<String> getTypesManagedByCaller(int callingUid, int userId) { - return getTypesForCaller(callingUid, userId, false); - } - - private List<String> getTypesForCaller( - int callingUid, int userId, boolean isOtherwisePermitted) { - List<String> managedAccountTypes = new ArrayList<>(); + final int callingUserId = UserHandle.getUserId(callingUid); for (RegisteredServicesCache.ServiceInfo<AuthenticatorDescription> serviceInfo : - mAuthenticatorCache.getAllServices(userId)) { - final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid); - if (isOtherwisePermitted || sigChk == PackageManager.SIGNATURE_MATCH) { - managedAccountTypes.add(serviceInfo.type.type); + mAuthenticatorCache.getAllServices(callingUserId)) { + if (serviceInfo.type.type.equals(accountType)) { + /* + * We can't simply compare uids because uids can be recycled before the + * authenticator cache is updated. + */ + final int sigChk = mPackageManager.checkSignatures(serviceInfo.uid, callingUid); + return sigChk == PackageManager.SIGNATURE_MATCH; } } - return managedAccountTypes; + return false; } private boolean isAccountPresentForCaller(String accountName, String accountType) { @@ -3860,12 +3792,28 @@ public class AccountManagerService return false; } + private boolean isReadAccountsPermitted(int callingUid, String accountType) { + /* + * Settings app (which is in the same uid as AcocuntManagerService), apps with the + * GET_ACCOUNTS permission or authenticators that own the account type should be able to + * access accounts of the specified account. + */ + boolean isPermitted = + isPermitted(callingUid, Manifest.permission.GET_ACCOUNTS, + Manifest.permission.GET_ACCOUNTS_PRIVILEGED); + boolean isAccountManagedByCaller = isAccountManagedByCaller(accountType, callingUid); + Log.w(TAG, String.format( + "isReadAccountPermitted: isPermitted: %s, isAM: %s", + isPermitted, + isAccountManagedByCaller)); + return isPermitted || isAccountManagedByCaller; + } + /** Succeeds if any of the specified permissions are granted. */ private void checkReadAccountsPermitted( int callingUid, - String accountType, - int userId) { - if (!isAccountVisibleToCaller(accountType, callingUid, userId)) { + String accountType) { + if (!isReadAccountsPermitted(callingUid, accountType)) { String msg = String.format( "caller uid %s cannot access %s accounts", callingUid, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 783dea5..b8d32c3 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -6552,6 +6552,17 @@ public final class ActivityManagerService extends ActivityManagerNative } @Override + public boolean isRootVoiceInteraction(IBinder token) { + synchronized(this) { + ActivityRecord r = ActivityRecord.isInStackLocked(token); + if (r == null) { + return false; + } + return r.rootVoiceInteraction; + } + } + + @Override public IIntentSender getIntentSender(int type, String packageName, IBinder token, String resultWho, int requestCode, Intent[] intents, String[] resolvedTypes, @@ -9154,9 +9165,10 @@ public final class ActivityManagerService extends ActivityManagerNative private final List<ProviderInfo> generateApplicationProvidersLocked(ProcessRecord app) { List<ProviderInfo> providers = null; try { - providers = AppGlobals.getPackageManager(). + ParceledListSlice<ProviderInfo> slice = AppGlobals.getPackageManager(). queryContentProviders(app.processName, app.uid, STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS); + providers = slice != null ? slice.getList() : null; } catch (RemoteException ex) { } if (DEBUG_MU) Slog.v(TAG_MU, diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 0957eb5..3de2009 100755 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -107,6 +107,7 @@ final class ActivityRecord { boolean fullscreen; // covers the full screen? final boolean noDisplay; // activity is not displayed? final boolean componentSpecified; // did caller specifiy an explicit component? + final boolean rootVoiceInteraction; // was this the root activity of a voice interaction? static final int APPLICATION_ACTIVITY_TYPE = 0; static final int HOME_ACTIVITY_TYPE = 1; @@ -207,6 +208,9 @@ final class ActivityRecord { pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded); pw.print(" componentSpecified="); pw.print(componentSpecified); pw.print(" mActivityType="); pw.println(mActivityType); + if (rootVoiceInteraction) { + pw.print(prefix); pw.print("rootVoiceInteraction="); pw.println(rootVoiceInteraction); + } pw.print(prefix); pw.print("compat="); pw.print(compat); pw.print(" labelRes=0x"); pw.print(Integer.toHexString(labelRes)); pw.print(" icon=0x"); pw.print(Integer.toHexString(icon)); @@ -432,7 +436,8 @@ final class ActivityRecord { int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType, ActivityInfo aInfo, Configuration _configuration, ActivityRecord _resultTo, String _resultWho, int _reqCode, - boolean _componentSpecified, ActivityStackSupervisor supervisor, + boolean _componentSpecified, boolean _rootVoiceInteraction, + ActivityStackSupervisor supervisor, ActivityContainer container, Bundle options) { service = _service; appToken = new Token(this, service); @@ -444,6 +449,7 @@ final class ActivityRecord { shortComponentName = _intent.getComponent().flattenToShortString(); resolvedType = _resolvedType; componentSpecified = _componentSpecified; + rootVoiceInteraction = _rootVoiceInteraction; configuration = _configuration; stackConfigOverride = (container != null) ? container.mStack.mOverrideConfig : Configuration.EMPTY; @@ -1257,7 +1263,7 @@ final class ActivityRecord { } final ActivityRecord r = new ActivityRecord(service, /*caller*/null, launchedFromUid, launchedFromPackage, intent, resolvedType, aInfo, service.getConfiguration(), - null, null, 0, componentSpecified, stackSupervisor, null, null); + null, null, 0, componentSpecified, false, stackSupervisor, null, null); r.persistentState = persistentState; r.taskDescription = taskDescription; diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 0be2f6f..71fd49b 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1506,6 +1506,7 @@ public final class ActivityStackSupervisor implements DisplayListener { if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && sourceRecord.info.applicationInfo.uid != aInfo.applicationInfo.uid) { try { + intent.addCategory(Intent.CATEGORY_VOICE); if (!AppGlobals.getPackageManager().activitySupportsIntent( intent.getComponent(), intent, resolvedType)) { err = ActivityManager.START_NOT_VOICE_COMPATIBLE; @@ -1626,7 +1627,7 @@ public final class ActivityStackSupervisor implements DisplayListener { ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage, intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho, - requestCode, componentSpecified, this, container, options); + requestCode, componentSpecified, voiceSession != null, this, container, options); if (outActivity != null) { outActivity[0] = r; } @@ -3721,19 +3722,19 @@ public final class ActivityStackSupervisor implements DisplayListener { @Override public void onDisplayAdded(int displayId) { - Slog.v(TAG, "Display added displayId=" + displayId); + if (DEBUG_STACK) Slog.v(TAG, "Display added displayId=" + displayId); mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_ADDED, displayId, 0)); } @Override public void onDisplayRemoved(int displayId) { - Slog.v(TAG, "Display removed displayId=" + displayId); + if (DEBUG_STACK) Slog.v(TAG, "Display removed displayId=" + displayId); mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_REMOVED, displayId, 0)); } @Override public void onDisplayChanged(int displayId) { - Slog.v(TAG, "Display changed displayId=" + displayId); + if (DEBUG_STACK) Slog.v(TAG, "Display changed displayId=" + displayId); mHandler.sendMessage(mHandler.obtainMessage(HANDLE_DISPLAY_CHANGED, displayId, 0)); } diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java index a7e6471..1fbfd9f 100644 --- a/services/core/java/com/android/server/am/BroadcastRecord.java +++ b/services/core/java/com/android/server/am/BroadcastRecord.java @@ -240,8 +240,12 @@ final class BroadcastRecord extends Binder { } didSomething = true; receivers.remove(i); + if (i < nextReceiver) { + nextReceiver--; + } } } + nextReceiver = Math.min(nextReceiver, receivers.size()); return didSomething; } diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java index 6ee1650..b216114 100644 --- a/services/core/java/com/android/server/am/RecentTasks.java +++ b/services/core/java/com/android/server/am/RecentTasks.java @@ -435,7 +435,8 @@ class RecentTasks extends ArrayList<TaskRecord> { */ int trimForTaskLocked(TaskRecord task, boolean doTrim) { int recentsCount = size(); - final boolean document = task.intent != null && task.intent.isDocument(); + final Intent intent = task.intent; + final boolean document = intent != null && intent.isDocument(); int maxRecents = task.maxRecents - 1; for (int i = 0; i < recentsCount; i++) { final TaskRecord tr = get(i); @@ -446,12 +447,13 @@ class RecentTasks extends ArrayList<TaskRecord> { if (i > MAX_RECENT_BITMAPS) { tr.freeLastThumbnail(); } + final Intent trIntent = tr.intent; final boolean sameAffinity = task.affinity != null && task.affinity.equals(tr.affinity); - final boolean trIsDocument = tr.intent != null && tr.intent.isDocument(); + final boolean sameIntent = (intent != null && intent.filterEquals(trIntent)); + final boolean trIsDocument = trIntent != null && trIntent.isDocument(); final boolean bothDocuments = document && trIsDocument; - if (!sameAffinity && !bothDocuments) { - // Not the same affinity and not documents. Move along... + if (!sameAffinity && !sameIntent && !bothDocuments) { continue; } diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java index 658f6f8..c998c2c 100644 --- a/services/core/java/com/android/server/content/SyncManager.java +++ b/services/core/java/com/android/server/content/SyncManager.java @@ -297,7 +297,6 @@ public class SyncManager { private final UserManager mUserManager; private static final long SYNC_ALARM_TIMEOUT_MIN = 30 * 1000; // 30 seconds - private static final long SYNC_ALARM_TIMEOUT_MAX = 2 * 60 * 60 * 1000; // two hours private List<UserInfo> getAllUsers() { return mUserManager.getUsers(); @@ -1478,9 +1477,9 @@ public class SyncManager { final long now = SystemClock.elapsedRealtime(); pw.print("now: "); pw.print(now); pw.println(" (" + formatTime(System.currentTimeMillis()) + ")"); - pw.print("offset: "); pw.print(DateUtils.formatElapsedTime(mSyncRandomOffsetMillis/1000)); + pw.print("offset: "); pw.print(DateUtils.formatElapsedTime(mSyncRandomOffsetMillis / 1000)); pw.println(" (HH:MM:SS)"); - pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now/1000)); + pw.print("uptime: "); pw.print(DateUtils.formatElapsedTime(now / 1000)); pw.println(" (HH:MM:SS)"); pw.print("time spent syncing: "); pw.print(DateUtils.formatElapsedTime( @@ -1497,11 +1496,6 @@ public class SyncManager { pw.println("no alarm is scheduled (there had better not be any pending syncs)"); } - pw.print("notification info: "); - final StringBuilder sb = new StringBuilder(); - mSyncHandler.mSyncNotificationInfo.toString(sb); - pw.println(sb.toString()); - pw.println(); pw.println("Active Syncs: " + mActiveSyncContexts.size()); final PackageManager pm = mContext.getPackageManager(); @@ -1514,8 +1508,8 @@ public class SyncManager { pw.println(); } + final StringBuilder sb = new StringBuilder(); synchronized (mSyncQueue) { - sb.setLength(0); mSyncQueue.dump(sb); // Dump Pending Operations. getSyncStorageEngine().dumpPendingOperations(sb); @@ -2349,7 +2343,6 @@ public class SyncManager { } } finally { - manageSyncNotificationLocked(); manageSyncAlarmLocked(earliestFuturePollTime, nextPendingSyncTime); mSyncTimeTracker.update(); mSyncManagerWakeLock.release(); @@ -3169,67 +3162,6 @@ public class SyncManager { throw new IllegalStateException("we are not in an error state, " + syncResult); } - private void manageSyncNotificationLocked() { - boolean shouldCancel; - boolean shouldInstall; - - if (mActiveSyncContexts.isEmpty()) { - mSyncNotificationInfo.startTime = null; - - // we aren't syncing. if the notification is active then remember that we need - // to cancel it and then clear out the info - shouldCancel = mSyncNotificationInfo.isActive; - shouldInstall = false; - } else { - // we are syncing - final long now = SystemClock.elapsedRealtime(); - if (mSyncNotificationInfo.startTime == null) { - mSyncNotificationInfo.startTime = now; - } - - // there are three cases: - // - the notification is up: do nothing - // - the notification is not up but it isn't time yet: don't install - // - the notification is not up and it is time: need to install - - if (mSyncNotificationInfo.isActive) { - shouldInstall = shouldCancel = false; - } else { - // it isn't currently up, so there is nothing to cancel - shouldCancel = false; - - final boolean timeToShowNotification = - now > mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY; - if (timeToShowNotification) { - shouldInstall = true; - } else { - // show the notification immediately if this is a manual sync - shouldInstall = false; - for (ActiveSyncContext activeSyncContext : mActiveSyncContexts) { - final boolean manualSync = activeSyncContext.mSyncOperation.extras - .getBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, false); - if (manualSync) { - shouldInstall = true; - break; - } - } - } - } - } - - if (shouldCancel && !shouldInstall) { - mNeedSyncActiveNotification = false; - sendSyncStateIntent(); - mSyncNotificationInfo.isActive = false; - } - - if (shouldInstall) { - mNeedSyncActiveNotification = true; - sendSyncStateIntent(); - mSyncNotificationInfo.isActive = true; - } - } - private void manageSyncAlarmLocked(long nextPeriodicEventElapsedTime, long nextPendingEventElapsedTime) { // in each of these cases the sync loop will be kicked, which will cause this @@ -3238,13 +3170,6 @@ public class SyncManager { if (mStorageIsLow) return; if (mDeviceIsIdle) return; - // When the status bar notification should be raised - final long notificationTime = - (!mSyncHandler.mSyncNotificationInfo.isActive - && mSyncHandler.mSyncNotificationInfo.startTime != null) - ? mSyncHandler.mSyncNotificationInfo.startTime + SYNC_NOTIFICATION_DELAY - : Long.MAX_VALUE; - // When we should consider canceling an active sync long earliestTimeoutTime = Long.MAX_VALUE; for (ActiveSyncContext currentSyncContext : mActiveSyncContexts) { @@ -3260,24 +3185,14 @@ public class SyncManager { } if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: notificationTime is " + notificationTime); - } - - if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "manageSyncAlarm: earliestTimeoutTime is " + earliestTimeoutTime); - } - - if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "manageSyncAlarm: nextPeriodicEventElapsedTime is " + nextPeriodicEventElapsedTime); - } - if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "manageSyncAlarm: nextPendingEventElapsedTime is " + nextPendingEventElapsedTime); } - long alarmTime = Math.min(notificationTime, earliestTimeoutTime); - alarmTime = Math.min(alarmTime, nextPeriodicEventElapsedTime); + long alarmTime = Math.min(earliestTimeoutTime, nextPeriodicEventElapsedTime); alarmTime = Math.min(alarmTime, nextPendingEventElapsedTime); // Bound the alarm time. @@ -3288,24 +3203,16 @@ public class SyncManager { + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN)); } alarmTime = now + SYNC_ALARM_TIMEOUT_MIN; - } else if (alarmTime > now + SYNC_ALARM_TIMEOUT_MAX) { - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "manageSyncAlarm: the alarmTime is too large, " - + alarmTime + ", setting to " + (now + SYNC_ALARM_TIMEOUT_MIN)); - } - alarmTime = now + SYNC_ALARM_TIMEOUT_MAX; } - // determine if we need to set or cancel the alarm + // Determine if we need to set or cancel the alarm boolean shouldSet = false; boolean shouldCancel = false; final boolean alarmIsActive = (mAlarmScheduleTime != null) && (now < mAlarmScheduleTime); - final boolean needAlarm = alarmTime != Long.MAX_VALUE; - if (needAlarm) { - // Need the alarm if - // - it's currently not set - // - if the alarm is set in the past. - if (!alarmIsActive || alarmTime < mAlarmScheduleTime) { + + if (alarmTime != Long.MAX_VALUE) { + // Need the alarm if it isn't set or has changed. + if (!alarmIsActive || alarmTime != mAlarmScheduleTime) { shouldSet = true; } } else { @@ -3329,14 +3236,6 @@ public class SyncManager { } } - private void sendSyncStateIntent() { - Intent syncStateIntent = new Intent(Intent.ACTION_SYNC_STATE_CHANGED); - syncStateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - syncStateIntent.putExtra("active", mNeedSyncActiveNotification); - syncStateIntent.putExtra("failing", false); - mContext.sendBroadcastAsUser(syncStateIntent, UserHandle.OWNER); - } - private void installHandleTooManyDeletesNotification(Account account, String authority, long numDeletes, int userId) { if (mNotificationMgr == null) return; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 7a6895f..9426b76 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1842,7 +1842,7 @@ public class NotificationManagerService extends SystemService { } pw.println(':'); int N; - final boolean zenOnly = filter != null && filter.zen; + final boolean zenOnly = filter.filtered && filter.zen; if (!zenOnly) { synchronized (mToastQueue) { @@ -1864,13 +1864,13 @@ public class NotificationManagerService extends SystemService { pw.println(" Notification List:"); for (int i=0; i<N; i++) { final NotificationRecord nr = mNotificationList.get(i); - if (filter != null && !filter.matches(nr.sbn)) continue; + if (filter.filtered && !filter.matches(nr.sbn)) continue; nr.dump(pw, " ", getContext(), filter.redact); } pw.println(" "); } - if (filter == null) { + if (!filter.filtered) { N = mLights.size(); if (N > 0) { pw.println(" Lights List:"); @@ -1911,7 +1911,7 @@ public class NotificationManagerService extends SystemService { mUsageStats.dump(pw, " ", filter); } - if (filter == null || zenOnly) { + if (!filter.filtered || zenOnly) { pw.println("\n Zen Mode:"); pw.print(" mInterruptionFilter="); pw.println(mInterruptionFilter); mZenModeHelper.dump(pw, " "); diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java index 669b8e5..3227ef8 100644 --- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java @@ -696,7 +696,7 @@ final class DefaultPermissionGrantPolicy { List<PackageParser.Package> syncAdapterPackages = new ArrayList<>(); Intent homeIntent = new Intent(Intent.ACTION_MAIN); - homeIntent.addCategory(Intent.CATEGORY_HOME); + homeIntent.addCategory(Intent.CATEGORY_LAUNCHER); for (String syncAdapterPackageName : syncAdapterPackageNames) { homeIntent.setPackage(syncAdapterPackageName); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 26304ab..6fe8b8a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -5435,7 +5435,7 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public List<ProviderInfo> queryContentProviders(String processName, + public ParceledListSlice<ProviderInfo> queryContentProviders(String processName, int uid, int flags) { ArrayList<ProviderInfo> finalList = null; // reader @@ -5467,9 +5467,10 @@ public class PackageManagerService extends IPackageManager.Stub { if (finalList != null) { Collections.sort(finalList, mProviderInitOrderSorter); + return new ParceledListSlice<ProviderInfo>(finalList); } - return finalList; + return null; } @Override diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 1787b91..09e15a8 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -49,10 +49,8 @@ import com.android.internal.util.IndentingPrintWriter; import com.android.server.FgThread; import java.io.File; -import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; -import java.io.PrintWriter; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -316,6 +314,9 @@ public class UsbDeviceManager { // Restore default functions. mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE); + if (UsbManager.USB_FUNCTION_NONE.equals(mCurrentFunctions)) { + mCurrentFunctions = UsbManager.USB_FUNCTION_MTP; + } mCurrentFunctionsApplied = mCurrentFunctions.equals( SystemProperties.get(USB_STATE_PROPERTY)); mAdbEnabled = UsbManager.containsFunction(getDefaultFunctions(), @@ -400,6 +401,14 @@ public class UsbDeviceManager { return waitForState(config); } + private void setUsbDataUnlocked(boolean enable) { + if (DEBUG) Slog.d(TAG, "setUsbDataUnlocked: " + enable); + mUsbDataUnlocked = enable; + updateUsbNotification(); + updateUsbStateBroadcast(); + setEnabledFunctions(mCurrentFunctions, true); + } + private void setAdbEnabled(boolean enable) { if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable); if (enable != mAdbEnabled) { @@ -471,7 +480,6 @@ public class UsbDeviceManager { } functions = applyAdbFunction(functions); functions = applyOemOverrideFunction(functions); - functions = applyUserRestrictions(functions); if (!mCurrentFunctions.equals(functions) || !mCurrentFunctionsApplied || forceRestart) { @@ -502,13 +510,9 @@ public class UsbDeviceManager { return functions; } - private String applyUserRestrictions(String functions) { + private boolean isUsbTransferAllowed() { UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - if (userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER)) { - functions = UsbManager.removeFunction(functions, UsbManager.USB_FUNCTION_MTP); - functions = UsbManager.removeFunction(functions, UsbManager.USB_FUNCTION_PTP); - } - return functions; + return !userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER); } private void updateCurrentAccessory() { @@ -555,7 +559,7 @@ public class UsbDeviceManager { | Intent.FLAG_RECEIVER_FOREGROUND); intent.putExtra(UsbManager.USB_CONNECTED, mConnected); intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured); - intent.putExtra(UsbManager.USB_DATA_UNLOCKED, mUsbDataUnlocked); + intent.putExtra(UsbManager.USB_DATA_UNLOCKED, isUsbTransferAllowed() && mUsbDataUnlocked); if (mCurrentFunctions != null) { String[] functions = mCurrentFunctions.split(","); @@ -659,10 +663,7 @@ public class UsbDeviceManager { setEnabledFunctions(mCurrentFunctions, false); break; case MSG_SET_USB_DATA_UNLOCKED: - mUsbDataUnlocked = (msg.arg1 == 1); - updateUsbNotification(); - updateUsbStateBroadcast(); - setEnabledFunctions(mCurrentFunctions, true); + setUsbDataUnlocked(msg.arg1 == 1); break; case MSG_SYSTEM_READY: updateUsbNotification(); @@ -807,8 +808,12 @@ public class UsbDeviceManager { } private String getDefaultFunctions() { - return SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, - UsbManager.USB_FUNCTION_ADB); + String func = SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, + UsbManager.USB_FUNCTION_NONE); + if (UsbManager.USB_FUNCTION_NONE.equals(func)) { + func = UsbManager.USB_FUNCTION_MTP; + } + return func; } public void dump(IndentingPrintWriter pw) { @@ -817,6 +822,7 @@ public class UsbDeviceManager { pw.println(" mCurrentFunctionsApplied: " + mCurrentFunctionsApplied); pw.println(" mConnected: " + mConnected); pw.println(" mConfigured: " + mConfigured); + pw.println(" mUsbDataUnlocked: " + mUsbDataUnlocked); pw.println(" mCurrentAccessory: " + mCurrentAccessory); try { pw.println(" Kernel state: " @@ -864,11 +870,6 @@ public class UsbDeviceManager { mHandler.sendMessage(MSG_SET_USB_DATA_UNLOCKED, unlocked); } - public boolean isUsbDataUnlocked() { - if (DEBUG) Slog.d(TAG, "isUsbDataUnlocked() -> " + mHandler.mUsbDataUnlocked); - return mHandler.mUsbDataUnlocked; - } - private void readOemUsbOverrideConfig() { String[] configList = mContext.getResources().getStringArray( com.android.internal.R.array.config_oemUsbModeOverride); diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java index f93a2ef..edd9201 100644 --- a/services/usb/java/com/android/server/usb/UsbService.java +++ b/services/usb/java/com/android/server/usb/UsbService.java @@ -322,23 +322,10 @@ public class UsbService extends IUsbManager.Stub { @Override public void setUsbDataUnlocked(boolean unlocked) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); - // If attempt to change USB function while file transfer is restricted, ensure that - // usb data is always locked, and return. - UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); - if (userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER)) { - if (mDeviceManager != null) mDeviceManager.setUsbDataUnlocked(false); - return; - } mDeviceManager.setUsbDataUnlocked(unlocked); } @Override - public boolean isUsbDataUnlocked() { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); - return mDeviceManager.isUsbDataUnlocked(); - } - - @Override public void allowUsbDebugging(boolean alwaysAllow, String publicKey) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); mDeviceManager.allowUsbDebugging(alwaysAllow, publicKey); diff --git a/tests/VoiceInteraction/res/layout/test_interaction.xml b/tests/VoiceInteraction/res/layout/test_interaction.xml index d1a7ad5..277117e 100644 --- a/tests/VoiceInteraction/res/layout/test_interaction.xml +++ b/tests/VoiceInteraction/res/layout/test_interaction.xml @@ -39,6 +39,19 @@ android:layout_marginTop="16dp" android:orientation="horizontal"> + <Button android:id="@+id/airplane" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/launchAirplane" + /> + + </LinearLayout> + + <LinearLayout android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="16dp" + android:orientation="horizontal"> + <Button android:id="@+id/complete" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/tests/VoiceInteraction/res/values/strings.xml b/tests/VoiceInteraction/res/values/strings.xml index 29ffe21..c665c23 100644 --- a/tests/VoiceInteraction/res/values/strings.xml +++ b/tests/VoiceInteraction/res/values/strings.xml @@ -21,6 +21,7 @@ <string name="tree">Tree</string> <string name="text">Text</string> <string name="asyncStructure">(Async structure goes here)</string> + <string name="launchAirplane">Launch airplane mode</string> <string name="confirm">Confirm</string> <string name="abort">Abort</string> <string name="complete">Complete</string> diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java index e10d89f..ada0e21 100644 --- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java +++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/TestInteractionActivity.java @@ -21,6 +21,7 @@ import android.app.VoiceInteractor; import android.content.ComponentName; import android.content.Intent; import android.os.Bundle; +import android.provider.Settings; import android.service.voice.VoiceInteractionService; import android.util.Log; import android.view.View; @@ -39,6 +40,7 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis VoiceInteractor mInteractor; VoiceInteractor.Request mCurrentRequest = null; TextView mLog; + Button mAirplaneButton; Button mAbortButton; Button mCompleteButton; Button mCommandButton; @@ -65,6 +67,8 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis setContentView(R.layout.test_interaction); mLog = (TextView)findViewById(R.id.log); + mAirplaneButton = (Button)findViewById(R.id.airplane); + mAirplaneButton.setOnClickListener(this); mAbortButton = (Button)findViewById(R.id.abort); mAbortButton.setOnClickListener(this); mCompleteButton = (Button)findViewById(R.id.complete); @@ -122,7 +126,12 @@ public class TestInteractionActivity extends Activity implements View.OnClickLis @Override public void onClick(View v) { - if (v == mAbortButton) { + if (v == mAirplaneButton) { + Intent intent = new Intent(Settings.ACTION_VOICE_CONTROL_AIRPLANE_MODE); + intent.addCategory(Intent.CATEGORY_VOICE); + intent.putExtra(Settings.EXTRA_AIRPLANE_MODE_ENABLED, true); + startActivity(intent); + } else if (v == mAbortButton) { VoiceInteractor.AbortVoiceRequest req = new TestAbortVoice(); mInteractor.submitRequest(req, REQUEST_ABORT); } else if (v == mCompleteButton) { diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java index 1105c7b..a503e50 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java @@ -673,7 +673,7 @@ public final class Matrix_Delegate { return; } - System.arraycopy(d.mValues, 0, d.mValues, 0, MATRIX_SIZE); + System.arraycopy(d.mValues, 0, values, 0, MATRIX_SIZE); } @LayoutlibDelegate diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java index 34d0985..3c9a062 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java @@ -36,7 +36,6 @@ import java.awt.geom.PathIterator; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; -import java.util.ArrayList; /** * Delegate implementing the native methods of android.graphics.Path @@ -504,13 +503,13 @@ public final class Path_Delegate { switch (type) { case PathIterator.SEG_MOVETO: case PathIterator.SEG_LINETO: - store(coords, tmp, 1, isFirstPoint); + store(tmp, coords, 1, isFirstPoint); break; case PathIterator.SEG_QUADTO: - store(coords, tmp, 2, isFirstPoint); + store(tmp, coords, 2, isFirstPoint); break; case PathIterator.SEG_CUBICTO: - store(coords, tmp, 3, isFirstPoint); + store(tmp, coords, 3, isFirstPoint); break; case PathIterator.SEG_CLOSE: // No points returned. @@ -528,14 +527,14 @@ public final class Path_Delegate { private static void store(float[] src, float[] dst, int count, boolean isFirst) { if (isFirst) { - dst[0] = 0; - dst[1] = src[0]; - dst[2] = src[1]; + dst[0] = 0; // fraction + dst[1] = src[0]; // abscissa + dst[2] = src[1]; // ordinate } if (count > 1 || !isFirst) { dst[3] = 1; - dst[4] = src[2 * count]; - dst[5] = src[2 * count + 1]; + dst[4] = src[2 * count - 2]; + dst[5] = src[2 * count - 1]; } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java index 8c7ea8a..c72c979 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/Layout.java @@ -83,7 +83,7 @@ class Layout extends RelativeLayout { // Theme attributes used for configuring appearance of the system decor. private static final String ATTR_WINDOW_FLOATING = "windowIsFloating"; private static final String ATTR_WINDOW_BACKGROUND = "windowBackground"; - private static final String ATTR_WINDOW_FULL_SCREEN = "windowFullScreen"; + private static final String ATTR_WINDOW_FULL_SCREEN = "windowFullscreen"; private static final String ATTR_NAV_BAR_HEIGHT = "navigation_bar_height"; private static final String ATTR_NAV_BAR_WIDTH = "navigation_bar_width"; private static final String ATTR_STATUS_BAR_HEIGHT = "status_bar_height"; @@ -329,9 +329,12 @@ class Layout extends RelativeLayout { mWindowIsFloating = getBooleanThemeValue(mResources, ATTR_WINDOW_FLOATING, true, true); findBackground(); - findStatusBar(); - findActionBar(); - findNavBar(); + + if (!mParams.isForceNoDecor()) { + findStatusBar(); + findActionBar(); + findNavBar(); + } } private void findBackground() { diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png Binary files differindex 9a13568..336f9d8 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/array_check.png diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png Binary files differindex 92eb3e1..0c16215 100644 --- a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png +++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java index ee448ca..9ebeebd 100644 --- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java +++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java @@ -329,8 +329,8 @@ public class Main { .setNavigation(Navigation.NONAV); SessionParams params = getSessionParams(parser, customConfigGenerator, - layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", RenderingMode.V_SCROLL, - 22); + layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false, + RenderingMode.V_SCROLL, 22); renderAndVerify(params, "expand_vert_layout.png"); @@ -342,8 +342,8 @@ public class Main { parser = new LayoutPullParser(APP_TEST_RES + "/layout/" + "expand_horz_layout.xml"); params = getSessionParams(parser, customConfigGenerator, - layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", RenderingMode - .H_SCROLL, 22); + layoutLibCallback, "Theme.Material.NoActionBar.Fullscreen", false, + RenderingMode.H_SCROLL, 22); renderAndVerify(params, "expand_horz_layout.png"); } @@ -390,7 +390,7 @@ public class Main { // TODO: Set up action bar handler properly to test menu rendering. // Create session params. SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5, - layoutLibCallback, "Theme.Material.Light.DarkActionBar", RenderingMode.NORMAL, 22); + layoutLibCallback, "AppTheme", true, RenderingMode.NORMAL, 22); renderAndVerify(params, goldenFileName); } @@ -399,12 +399,12 @@ public class Main { */ private SessionParams getSessionParams(LayoutPullParser layoutParser, ConfigGenerator configGenerator, LayoutLibTestCallback layoutLibCallback, - String themeName, RenderingMode renderingMode, int targetSdk) { + String themeName, boolean isProjectTheme, RenderingMode renderingMode, int targetSdk) { FolderConfiguration config = configGenerator.getFolderConfig(); ResourceResolver resourceResolver = ResourceResolver.create(sProjectResources.getConfiguredResources(config), sFrameworkRepo.getConfiguredResources(config), - themeName, false); + themeName, isProjectTheme); return new SessionParams( layoutParser, |