diff options
Diffstat (limited to 'core/java')
71 files changed, 2071 insertions, 667 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index fc200550..3bf8e2e 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -700,6 +700,13 @@ public class ActivityManager { */ public int affiliatedTaskId; + /** + * Task affiliation color of the source task with the affiliated task id. + * + * @hide + */ + public int affiliatedTaskColor; + public RecentTaskInfo() { } @@ -732,6 +739,7 @@ public class ActivityManager { dest.writeLong(firstActiveTime); dest.writeLong(lastActiveTime); dest.writeInt(affiliatedTaskId); + dest.writeInt(affiliatedTaskColor); } public void readFromParcel(Parcel source) { @@ -747,6 +755,7 @@ public class ActivityManager { firstActiveTime = source.readLong(); lastActiveTime = source.readLong(); affiliatedTaskId = source.readInt(); + affiliatedTaskColor = source.readInt(); } public static final Creator<RecentTaskInfo> CREATOR diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 84b5516..a935dc0 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -28,7 +28,6 @@ import android.content.pm.ContainerEncryptionParams; import android.content.pm.FeatureInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageDeleteObserver; -import android.content.pm.IPackageDeleteObserver2; import android.content.pm.IPackageInstallObserver; import android.content.pm.IPackageManager; import android.content.pm.IPackageMoveObserver; @@ -53,7 +52,6 @@ import android.content.res.XmlResourceParser; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.os.Bundle; import android.os.IBinder; import android.os.Process; import android.os.RemoteException; @@ -1655,39 +1653,6 @@ final class ApplicationPackageManager extends PackageManager { new UserHandle(mContext.getUserId())); } - private static class LegacyPackageInstallObserver extends PackageInstallObserver { - private final IPackageInstallObserver mLegacy; - - public LegacyPackageInstallObserver(IPackageInstallObserver legacy) { - mLegacy = legacy; - } - - @Override - public void onPackageInstalled(String basePackageName, int returnCode, String msg, - Bundle extras) { - try { - mLegacy.packageInstalled(basePackageName, returnCode); - } catch (RemoteException ignored) { - } - } - } - - private static class LegacyPackageDeleteObserver extends PackageDeleteObserver { - private final IPackageDeleteObserver mLegacy; - - public LegacyPackageDeleteObserver(IPackageDeleteObserver legacy) { - mLegacy = legacy; - } - - @Override - public void onPackageDeleted(String basePackageName, int returnCode, String msg) { - try { - mLegacy.packageDeleted(basePackageName, returnCode); - } catch (RemoteException ignored) { - } - } - } - private final ContextImpl mContext; private final IPackageManager mPM; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 5684a7a..90b8b86 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -32,7 +32,6 @@ import android.media.AudioManager; import android.media.session.MediaSession; import android.net.Uri; import android.os.BadParcelableException; -import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -1900,8 +1899,7 @@ public class Notification implements Parcelable mPriority = PRIORITY_DEFAULT; mPeople = new ArrayList<String>(); - mColorUtil = context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.L ? - NotificationColorUtil.getInstance() : null; + mColorUtil = NotificationColorUtil.getInstance(); } /** diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index be8108c..e9297b9 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -16,6 +16,7 @@ package android.app.backup; +import android.annotation.SystemApi; import android.app.backup.RestoreSession; import android.app.backup.IBackupManager; import android.app.backup.IRestoreSession; @@ -114,7 +115,7 @@ public class BackupManager { try { sService.dataChanged(packageName); } catch (RemoteException e) { - Log.d(TAG, "dataChanged(pkg) couldn't connect"); + Log.e(TAG, "dataChanged(pkg) couldn't connect"); } } } @@ -150,7 +151,7 @@ public class BackupManager { result = session.restorePackage(mContext.getPackageName(), observer); } } catch (RemoteException e) { - Log.w(TAG, "restoreSelf() unable to contact service"); + Log.e(TAG, "restoreSelf() unable to contact service"); } finally { if (session != null) { session.endRestoreSession(); @@ -160,11 +161,14 @@ public class BackupManager { return result; } + // system APIs start here + /** * Begin the process of restoring data from backup. See the * {@link android.app.backup.RestoreSession} class for documentation on that process. * @hide */ + @SystemApi public RestoreSession beginRestoreSession() { RestoreSession session = null; checkServiceBinder(); @@ -176,9 +180,140 @@ public class BackupManager { session = new RestoreSession(mContext, binder); } } catch (RemoteException e) { - Log.w(TAG, "beginRestoreSession() couldn't connect"); + Log.e(TAG, "beginRestoreSession() couldn't connect"); } } return session; } + + /** + * Enable/disable the backup service entirely. When disabled, no backup + * or restore operations will take place. Data-changed notifications will + * still be observed and collected, however, so that changes made while the + * mechanism was disabled will still be backed up properly if it is enabled + * at some point in the future. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * + * @hide + */ + @SystemApi + public void setBackupEnabled(boolean isEnabled) { + checkServiceBinder(); + if (sService != null) { + try { + sService.setBackupEnabled(isEnabled); + } catch (RemoteException e) { + Log.e(TAG, "setBackupEnabled() couldn't connect"); + } + } + } + + /** + * Report whether the backup mechanism is currently enabled. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * + * @hide + */ + @SystemApi + public boolean isBackupEnabled() { + if (sService != null) { + try { + return sService.isBackupEnabled(); + } catch (RemoteException e) { + Log.e(TAG, "isBackupEnabled() couldn't connect"); + } + } + return false; + } + + /** + * Identify the currently selected transport. Callers must hold the + * android.permission.BACKUP permission to use this method. + * @return The name of the currently active backup transport. In case of + * failure or if no transport is currently active, this method returns {@code null}. + * + * @hide + */ + @SystemApi + public String getCurrentTransport() { + checkServiceBinder(); + if (sService != null) { + try { + return sService.getCurrentTransport(); + } catch (RemoteException e) { + Log.e(TAG, "getCurrentTransport() couldn't connect"); + } + } + return null; + } + + /** + * Request a list of all available backup transports' names. Callers must + * hold the android.permission.BACKUP permission to use this method. + * + * @hide + */ + @SystemApi + public String[] listAllTransports() { + checkServiceBinder(); + if (sService != null) { + try { + return sService.listAllTransports(); + } catch (RemoteException e) { + Log.e(TAG, "listAllTransports() couldn't connect"); + } + } + return null; + } + + /** + * Specify the current backup transport. Callers must hold the + * android.permission.BACKUP permission to use this method. + * + * @param transport The name of the transport to select. This should be one + * of the names returned by {@link #listAllTransports()}. + * @return The name of the previously selected transport. If the given transport + * name is not one of the currently available transports, no change is made to + * the current transport setting and the method returns null. + * + * @hide + */ + @SystemApi + public String selectBackupTransport(String transport) { + checkServiceBinder(); + if (sService != null) { + try { + return sService.selectBackupTransport(transport); + } catch (RemoteException e) { + Log.e(TAG, "selectBackupTransport() couldn't connect"); + } + } + return null; + } + + /** + * Schedule an immediate backup attempt for all pending key/value updates. This + * is primarily intended for transports to use when they detect a suitable + * opportunity for doing a backup pass. If there are no pending updates to + * be sent, no action will be taken. Even if some updates are pending, the + * transport will still be asked to confirm via the usual requestBackupTime() + * method. + * + * <p>Callers must hold the android.permission.BACKUP permission to use this method. + * + * @hide + */ + @SystemApi + public void backupNow() { + checkServiceBinder(); + if (sService != null) { + try { + sService.backupNow(); + } catch (RemoteException e) { + Log.e(TAG, "backupNow() couldn't connect"); + } + } + } } diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index 446c03e..6adc2e0 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -16,6 +16,7 @@ package android.app.backup; +import android.annotation.SystemApi; import android.content.Intent; import android.content.pm.PackageInfo; import android.os.IBinder; @@ -30,6 +31,7 @@ import com.android.internal.backup.IBackupTransport; * * @hide */ +@SystemApi public class BackupTransport { // Zero return always means things are okay. If returned from // getNextFullRestoreDataChunk(), it means that no data could be delivered at @@ -403,6 +405,25 @@ public class BackupTransport { return BackupTransport.TRANSPORT_ERROR; } + /** + * Tells the transport to cancel the currently-ongoing full backup operation. This + * will happen between {@link #performFullBackup()} and {@link #finishBackup()} + * if the OS needs to abort the backup operation for any reason, such as a crash in + * the application undergoing backup. + * + * <p>When it receives this call, the transport should discard any partial archive + * that it has stored so far. If possible it should also roll back to the previous + * known-good archive in its datastore. + * + * <p>If the transport receives this callback, it will <em>not</em> receive a + * call to {@link #finishBackup()}. It needs to tear down any ongoing backup state + * here. + */ + public void cancelFullBackup() { + throw new UnsupportedOperationException( + "Transport cancelFullBackup() not implemented"); + } + // ------------------------------------------------------------------------------------ // Full restore interfaces diff --git a/core/java/android/app/backup/RestoreDescription.java b/core/java/android/app/backup/RestoreDescription.java index 50ab0b4..611ff07 100644 --- a/core/java/android/app/backup/RestoreDescription.java +++ b/core/java/android/app/backup/RestoreDescription.java @@ -16,6 +16,7 @@ package android.app.backup; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -28,6 +29,7 @@ import android.os.Parcelable; * * @hide */ +@SystemApi public class RestoreDescription implements Parcelable { private final String mPackageName; private final int mDataType; diff --git a/core/java/android/app/backup/RestoreObserver.java b/core/java/android/app/backup/RestoreObserver.java index dbddb78..b4a23d0 100644 --- a/core/java/android/app/backup/RestoreObserver.java +++ b/core/java/android/app/backup/RestoreObserver.java @@ -17,6 +17,8 @@ package android.app.backup; import java.lang.String; + +import android.annotation.SystemApi; import android.app.backup.RestoreSet; /** @@ -36,6 +38,7 @@ public abstract class RestoreObserver { * * @hide */ + @SystemApi public void restoreSetsAvailable(RestoreSet[] result) { } diff --git a/core/java/android/app/backup/RestoreSession.java b/core/java/android/app/backup/RestoreSession.java index 7181c61..0a885b6 100644 --- a/core/java/android/app/backup/RestoreSession.java +++ b/core/java/android/app/backup/RestoreSession.java @@ -16,6 +16,7 @@ package android.app.backup; +import android.annotation.SystemApi; import android.app.backup.RestoreObserver; import android.app.backup.RestoreSet; import android.app.backup.IRestoreObserver; @@ -30,6 +31,7 @@ import android.util.Log; * Interface for managing a restore session. * @hide */ +@SystemApi public class RestoreSession { static final String TAG = "RestoreSession"; diff --git a/core/java/android/app/backup/RestoreSet.java b/core/java/android/app/backup/RestoreSet.java index 0431977..aacaf7c 100644 --- a/core/java/android/app/backup/RestoreSet.java +++ b/core/java/android/app/backup/RestoreSet.java @@ -16,6 +16,7 @@ package android.app.backup; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -25,6 +26,7 @@ import android.os.Parcelable; * * @hide */ +@SystemApi public class RestoreSet implements Parcelable { /** * Name of this restore set. May be user generated, may simply be the name diff --git a/core/java/android/app/trust/ITrustListener.aidl b/core/java/android/app/trust/ITrustListener.aidl index 45a066d..d80f58c 100644 --- a/core/java/android/app/trust/ITrustListener.aidl +++ b/core/java/android/app/trust/ITrustListener.aidl @@ -22,6 +22,6 @@ package android.app.trust; * {@hide} */ oneway interface ITrustListener { - void onTrustChanged(boolean enabled, int userId); + void onTrustChanged(boolean enabled, int userId, boolean initiatedByUser); void onTrustManagedChanged(boolean managed, int userId); }
\ No newline at end of file diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java index 796e3cc..3d262b1 100644 --- a/core/java/android/app/trust/TrustManager.java +++ b/core/java/android/app/trust/TrustManager.java @@ -34,6 +34,7 @@ public class TrustManager { private static final int MSG_TRUST_MANAGED_CHANGED = 2; private static final String TAG = "TrustManager"; + private static final String DATA_INITIATED_BY_USER = "initiatedByUser"; private final ITrustManager mService; private final ArrayMap<TrustListener, ITrustListener> mTrustListeners; @@ -95,14 +96,17 @@ public class TrustManager { try { ITrustListener.Stub iTrustListener = new ITrustListener.Stub() { @Override - public void onTrustChanged(boolean enabled, int userId) throws RemoteException { - mHandler.obtainMessage(MSG_TRUST_CHANGED, (enabled ? 1 : 0), userId, - trustListener).sendToTarget(); + public void onTrustChanged(boolean enabled, int userId, boolean initiatedByUser) { + Message m = mHandler.obtainMessage(MSG_TRUST_CHANGED, (enabled ? 1 : 0), userId, + trustListener); + if (initiatedByUser) { + m.getData().putBoolean(DATA_INITIATED_BY_USER, initiatedByUser); + } + m.sendToTarget(); } @Override - public void onTrustManagedChanged(boolean managed, int userId) - throws RemoteException { + public void onTrustManagedChanged(boolean managed, int userId) { mHandler.obtainMessage(MSG_TRUST_MANAGED_CHANGED, (managed ? 1 : 0), userId, trustListener).sendToTarget(); } @@ -139,7 +143,11 @@ public class TrustManager { public void handleMessage(Message msg) { switch(msg.what) { case MSG_TRUST_CHANGED: - ((TrustListener)msg.obj).onTrustChanged(msg.arg1 != 0, msg.arg2); + boolean initiatedByUser = msg.peekData() != null && + msg.peekData().getBoolean(DATA_INITIATED_BY_USER); + ((TrustListener)msg.obj).onTrustChanged( + msg.arg1 != 0, msg.arg2, initiatedByUser); + break; case MSG_TRUST_MANAGED_CHANGED: ((TrustListener)msg.obj).onTrustManagedChanged(msg.arg1 != 0, msg.arg2); @@ -153,8 +161,10 @@ public class TrustManager { * Reports that the trust state has changed. * @param enabled if true, the system believes the environment to be trusted. * @param userId the user, for which the trust changed. + * @param initiatedByUser indicates that the user has explicitly initiated an action that + * proves the user is about to use the device. */ - void onTrustChanged(boolean enabled, int userId); + void onTrustChanged(boolean enabled, int userId, boolean initiatedByUser); /** * Reports that whether trust is managed has changed diff --git a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java b/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java index 0eb9d21..da992f5 100644 --- a/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java +++ b/core/java/android/bluetooth/BluetoothGattCallbackWrapper.java @@ -124,8 +124,7 @@ public class BluetoothGattCallbackWrapper extends IBluetoothGattCallback.Stub { } @Override - public void onFoundOrLost(boolean onFound, String address, int rssi, byte[] advData) - throws RemoteException { + public void onFoundOrLost(boolean onFound, ScanResult scanResult) throws RemoteException { } } diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl index edf823e..7070bae 100644 --- a/core/java/android/bluetooth/IBluetoothGatt.aidl +++ b/core/java/android/bluetooth/IBluetoothGatt.aidl @@ -21,6 +21,7 @@ import android.bluetooth.le.AdvertiseSettings; import android.bluetooth.le.AdvertiseData; import android.bluetooth.le.ScanFilter; import android.bluetooth.le.ScanSettings; +import android.bluetooth.le.ResultStorageDescriptor; import android.os.ParcelUuid; import android.bluetooth.IBluetoothGattCallback; @@ -34,7 +35,8 @@ interface IBluetoothGatt { List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states); void startScan(in int appIf, in boolean isServer, in ScanSettings settings, - in List<ScanFilter> filters); + in List<ScanFilter> filters, + in List scanStorages); void stopScan(in int appIf, in boolean isServer); void flushPendingBatchResults(in int appIf, in boolean isServer); void startMultiAdvertising(in int appIf, diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallback.aidl index f14cce0..00b6b1b 100644 --- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl +++ b/core/java/android/bluetooth/IBluetoothGattCallback.aidl @@ -69,6 +69,5 @@ oneway interface IBluetoothGattCallback { in AdvertiseSettings advertiseSettings); void onConfigureMTU(in String address, in int mtu, in int status); void onConnectionCongested(in String address, in boolean congested); - void onFoundOrLost(in boolean onFound, in String address, in int rssi, - in byte[] advData); + void onFoundOrLost(in boolean onFound, in ScanResult scanResult); } diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java index 45e466f..7c3cbc6 100644 --- a/core/java/android/bluetooth/le/BluetoothLeScanner.java +++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java @@ -16,20 +16,19 @@ package android.bluetooth.le; +import android.annotation.SystemApi; import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallbackWrapper; import android.bluetooth.IBluetoothGatt; -import android.bluetooth.IBluetoothGattCallback; import android.bluetooth.IBluetoothManager; import android.os.Handler; import android.os.Looper; import android.os.ParcelUuid; import android.os.RemoteException; -import android.os.SystemClock; import android.util.Log; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -100,6 +99,11 @@ public final class BluetoothLeScanner { */ public void startScan(List<ScanFilter> filters, ScanSettings settings, final ScanCallback callback) { + startScan(filters, settings, callback, null); + } + + private void startScan(List<ScanFilter> filters, ScanSettings settings, + final ScanCallback callback, List<List<ResultStorageDescriptor>> resultStorages) { checkAdapterState(); if (settings == null || callback == null) { throw new IllegalArgumentException("settings or callback is null"); @@ -125,7 +129,7 @@ public final class BluetoothLeScanner { return; } BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters, - settings, callback); + settings, callback, resultStorages); try { UUID uuid = UUID.randomUUID(); gatt.registerClient(new ParcelUuid(uuid), wrapper); @@ -155,7 +159,8 @@ public final class BluetoothLeScanner { synchronized (mLeScanClients) { BleScanCallbackWrapper wrapper = mLeScanClients.remove(callback); if (wrapper == null) { - if (DBG) Log.d(TAG, "could not find callback wrapper"); + if (DBG) + Log.d(TAG, "could not find callback wrapper"); return; } wrapper.stopLeScan(); @@ -185,6 +190,25 @@ public final class BluetoothLeScanner { } /** + * Start truncated scan. + * + * @hide + */ + @SystemApi + public void startTruncatedScan(List<TruncatedFilter> truncatedFilters, ScanSettings settings, + final ScanCallback callback) { + int filterSize = truncatedFilters.size(); + List<ScanFilter> scanFilters = new ArrayList<ScanFilter>(filterSize); + List<List<ResultStorageDescriptor>> scanStorages = + new ArrayList<List<ResultStorageDescriptor>>(filterSize); + for (TruncatedFilter filter : truncatedFilters) { + scanFilters.add(filter.getFilter()); + scanStorages.add(filter.getStorageDescriptors()); + } + startScan(scanFilters, settings, callback, scanStorages); + } + + /** * Bluetooth GATT interface callbacks */ private static class BleScanCallbackWrapper extends BluetoothGattCallbackWrapper { @@ -194,6 +218,7 @@ public final class BluetoothLeScanner { private final List<ScanFilter> mFilters; private ScanSettings mSettings; private IBluetoothGatt mBluetoothGatt; + private List<List<ResultStorageDescriptor>> mResultStorages; // mLeHandle 0: not registered // -1: scan stopped @@ -202,12 +227,13 @@ public final class BluetoothLeScanner { public BleScanCallbackWrapper(IBluetoothGatt bluetoothGatt, List<ScanFilter> filters, ScanSettings settings, - ScanCallback scanCallback) { + ScanCallback scanCallback, List<List<ResultStorageDescriptor>> resultStorages) { mBluetoothGatt = bluetoothGatt; mFilters = filters; mSettings = settings; mScanCallback = scanCallback; mClientIf = 0; + mResultStorages = resultStorages; } public boolean scanStarted() { @@ -272,7 +298,8 @@ public final class BluetoothLeScanner { if (status == BluetoothGatt.GATT_SUCCESS) { mClientIf = clientIf; try { - mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters); + mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters, + mResultStorages); } catch (RemoteException e) { Log.e(TAG, "fail to start le scan: " + e); mClientIf = -1; @@ -322,26 +349,34 @@ public final class BluetoothLeScanner { } @Override - public void onFoundOrLost(boolean onFound, String address, int rssi, - byte[] advData) { + public void onFoundOrLost(final boolean onFound, final ScanResult scanResult) { if (DBG) { - Log.d(TAG, "onFoundOrLost() - Device=" + address); + Log.d(TAG, "onFoundOrLost() - onFound = " + onFound + + " " + scanResult.toString()); } - // ToDo: Fix issue with underlying reporting from chipset - BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice( - address); - long scanNanos = SystemClock.elapsedRealtimeNanos(); - ScanResult result = new ScanResult(device, ScanRecord.parseFromBytes(advData), rssi, - scanNanos); - if (onFound) { - mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, result); - } else { - mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, result); + + // Check null in case the scan has been stopped + synchronized (this) { + if (mClientIf <= 0) + return; } + Handler handler = new Handler(Looper.getMainLooper()); + handler.post(new Runnable() { + @Override + public void run() { + if (onFound) { + mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_FIRST_MATCH, + scanResult); + } else { + mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_MATCH_LOST, + scanResult); + } + } + }); } } - //TODO: move this api to a common util class. + // TODO: move this api to a common util class. private void checkAdapterState() { if (mBluetoothAdapter.getState() != mBluetoothAdapter.STATE_ON) { throw new IllegalStateException("BT Adapter is not turned ON"); diff --git a/core/java/android/bluetooth/le/ResultStorageDescriptor.aidl b/core/java/android/bluetooth/le/ResultStorageDescriptor.aidl new file mode 100644 index 0000000..f218a01 --- /dev/null +++ b/core/java/android/bluetooth/le/ResultStorageDescriptor.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth.le; + +/** + * {@hide} + */ + +parcelable ResultStorageDescriptor; diff --git a/core/java/android/bluetooth/le/ResultStorageDescriptor.java b/core/java/android/bluetooth/le/ResultStorageDescriptor.java new file mode 100644 index 0000000..748f97d --- /dev/null +++ b/core/java/android/bluetooth/le/ResultStorageDescriptor.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth.le; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Describes the way to store scan result. + * + * @hide + */ +@SystemApi +public final class ResultStorageDescriptor implements Parcelable { + private int mType; + private int mOffset; + private int mLength; + + public int getType() { + return mType; + } + + public int getOffset() { + return mOffset; + } + + public int getLength() { + return mLength; + } + + /** + * Constructor of {@link ResultStorageDescriptor} + * + * @param type Type of the data. + * @param offset Offset from start of the advertise packet payload. + * @param length Byte length of the data + */ + public ResultStorageDescriptor(int type, int offset, int length) { + mType = type; + mOffset = offset; + mLength = length; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeInt(mOffset); + dest.writeInt(mLength); + } + + private ResultStorageDescriptor(Parcel in) { + ReadFromParcel(in); + } + + private void ReadFromParcel(Parcel in) { + mType = in.readInt(); + mOffset = in.readInt(); + mLength = in.readInt(); + } + + public static final Parcelable.Creator<ResultStorageDescriptor> + CREATOR = new Creator<ResultStorageDescriptor>() { + @Override + public ResultStorageDescriptor createFromParcel(Parcel source) { + return new ResultStorageDescriptor(source); + } + + @Override + public ResultStorageDescriptor[] newArray(int size) { + return new ResultStorageDescriptor[size]; + } + }; +} diff --git a/core/java/android/bluetooth/le/TruncatedFilter.java b/core/java/android/bluetooth/le/TruncatedFilter.java new file mode 100644 index 0000000..6a6b3e3 --- /dev/null +++ b/core/java/android/bluetooth/le/TruncatedFilter.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth.le; + +import android.annotation.SystemApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.List; + +/** + * A special scan filter that lets the client decide how the scan record should be stored. + * + * @hide + */ +@SystemApi +public final class TruncatedFilter { + private final ScanFilter mFilter; + private final List<ResultStorageDescriptor> mStorageDescriptors; + + /** + * Constructor for {@link TruncatedFilter}. + * + * @param filter Scan filter of the truncated filter. + * @param storageDescriptors Describes how the scan should be stored. + */ + public TruncatedFilter(ScanFilter filter, List<ResultStorageDescriptor> storageDescriptors) { + mFilter = filter; + mStorageDescriptors = storageDescriptors; + } + + /** + * Returns the scan filter. + */ + public ScanFilter getFilter() { + return mFilter; + } + + /** + * Returns a list of descriptor for scan result storage. + */ + public List<ResultStorageDescriptor> getStorageDescriptors() { + return mStorageDescriptors; + } + + +} diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 87d14b9..0ca800f 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -329,7 +329,7 @@ public abstract class ContentResolver { try { String type = ActivityManagerNative.getDefault().getProviderMimeType( - url, UserHandle.myUserId()); + ContentProvider.getUriWithoutUserId(url), resolveUserId(url)); return type; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 7417208..5f046c5 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2616,6 +2616,7 @@ public abstract class Context { * * @see #getSystemService */ + @SystemApi public static final String BACKUP_SERVICE = "backup"; /** diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl index 176a81c..5223476 100644 --- a/core/java/android/content/pm/IPackageInstaller.aidl +++ b/core/java/android/content/pm/IPackageInstaller.aidl @@ -21,7 +21,6 @@ import android.content.pm.IPackageInstallerCallback; import android.content.pm.IPackageInstallerSession; import android.content.pm.InstallSessionInfo; import android.content.pm.InstallSessionParams; -import android.os.ParcelFileDescriptor; /** {@hide} */ interface IPackageInstaller { @@ -37,4 +36,6 @@ interface IPackageInstaller { void uninstall(String packageName, int flags, in IPackageDeleteObserver2 observer, int userId); void uninstallSplit(String packageName, String splitName, int flags, in IPackageDeleteObserver2 observer, int userId); + + void setPermissionsResult(int sessionId, boolean accepted); } diff --git a/core/java/android/content/pm/InstallSessionInfo.java b/core/java/android/content/pm/InstallSessionInfo.java index f263885..161bcde 100644 --- a/core/java/android/content/pm/InstallSessionInfo.java +++ b/core/java/android/content/pm/InstallSessionInfo.java @@ -16,7 +16,6 @@ package android.content.pm; -import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Intent; import android.graphics.Bitmap; @@ -33,8 +32,12 @@ public class InstallSessionInfo implements Parcelable { /** {@hide} */ public String installerPackageName; /** {@hide} */ + public String resolvedBaseCodePath; + /** {@hide} */ public float progress; /** {@hide} */ + public boolean sealed; + /** {@hide} */ public boolean open; /** {@hide} */ @@ -56,7 +59,9 @@ public class InstallSessionInfo implements Parcelable { public InstallSessionInfo(Parcel source) { sessionId = source.readInt(); installerPackageName = source.readString(); + resolvedBaseCodePath = source.readString(); progress = source.readFloat(); + sealed = source.readInt() != 0; open = source.readInt() != 0; mode = source.readInt(); @@ -149,7 +154,9 @@ public class InstallSessionInfo implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeInt(sessionId); dest.writeString(installerPackageName); + dest.writeString(resolvedBaseCodePath); dest.writeFloat(progress); + dest.writeInt(sealed ? 1 : 0); dest.writeInt(open ? 1 : 0); dest.writeInt(mode); diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 8c37e9e..268919c 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -210,20 +210,6 @@ public class LauncherApps { * Starts an activity in the specified profile. * * @param component The ComponentName of the activity to launch - * @param sourceBounds The Rect containing the source bounds of the clicked icon - * @param opts Options to pass to startActivity - * @param user The UserHandle of the profile - * @hide remove before ship - */ - public void startActivityForProfile(ComponentName component, Rect sourceBounds, - Bundle opts, UserHandle user) { - startActivityForProfile(component, user, sourceBounds, opts); - } - - /** - * Starts an activity in the specified profile. - * - * @param component The ComponentName of the activity to launch * @param user The UserHandle of the profile * @param sourceBounds The Rect containing the source bounds of the clicked icon * @param opts Options to pass to startActivity diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 01c080d..d70e22c 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -81,6 +81,10 @@ public class PackageInstaller { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_SESSION_DETAILS = "android.content.pm.action.SESSION_DETAILS"; + /** {@hide} */ + public static final String + ACTION_CONFIRM_PERMISSIONS = "android.content.pm.action.CONFIRM_PERMISSIONS"; + /** * An integer session ID. * @@ -88,6 +92,9 @@ public class PackageInstaller { */ public static final String EXTRA_SESSION_ID = "android.content.pm.extra.SESSION_ID"; + /** {@hide} */ + public static final String EXTRA_CALLBACK = "android.content.pm.extra.CALLBACK"; + private final PackageManager mPm; private final IPackageInstaller mInstaller; private final int mUserId; @@ -206,6 +213,15 @@ public class PackageInstaller { } } + /** {@hide} */ + public void setPermissionsResult(int sessionId, boolean accepted) { + try { + mInstaller.setPermissionsResult(sessionId, accepted); + } catch (RemoteException e) { + throw e.rethrowAsRuntimeException(); + } + } + /** * Events for observing session lifecycle. * <p> @@ -541,6 +557,26 @@ public class PackageInstaller { */ public static abstract class UninstallCallback { /** + * Generic unknown failure. The system will always try to provide a more + * specific failure reason, but in some rare cases this may be + * delivered. + */ + public static final int FAILURE_UNKNOWN = 0; + + /** + * This uninstall was blocked. The package may be required for core + * system operation, or the user may be restricted. Attempting to + * uninstall again will have the same result. + */ + public static final int FAILURE_BLOCKED = 1; + + /** + * This uninstall was actively aborted. For example, the user declined + * to uninstall. You may try to uninstall again. + */ + public static final int FAILURE_ABORTED = 2; + + /** * User action is required to proceed. You can start the given intent * activity to involve the user and continue. * <p> @@ -551,7 +587,7 @@ public class PackageInstaller { public abstract void onUserActionRequired(Intent intent); public abstract void onSuccess(); - public abstract void onFailure(String msg); + public abstract void onFailure(int failureReason, String msg, Bundle extras); } /** {@hide} */ @@ -572,8 +608,9 @@ public class PackageInstaller { if (returnCode == PackageManager.DELETE_SUCCEEDED) { target.onSuccess(); } else { + final int failureReason = PackageManager.deleteStatusToFailureReason(returnCode); msg = PackageManager.deleteStatusToString(returnCode) + ": " + msg; - target.onFailure(msg); + target.onFailure(failureReason, msg, null); } } } @@ -603,9 +640,8 @@ public class PackageInstaller { * permission, incompatible certificates, etc. The user may be able to * uninstall another app to fix the issue. * <p> - * The extras bundle may contain {@link #EXTRA_PACKAGE_NAME} if one - * specific package was identified as the cause of the conflict. If - * unknown, or multiple packages, the extra may be {@code null}. + * The extras bundle may contain {@link #EXTRA_PACKAGE_NAME} with the + * specific packages identified as the cause of the conflict. */ public static final int FAILURE_CONFLICT = 2; @@ -626,6 +662,15 @@ public class PackageInstaller { */ public static final int FAILURE_INCOMPATIBLE = 4; + /** + * This install session failed because it was actively aborted. For + * example, the user declined requested permissions, or a verifier + * rejected the session. + * + * @see PackageManager#VERIFICATION_REJECT + */ + public static final int FAILURE_ABORTED = 5; + public static final String EXTRA_PACKAGE_NAME = "android.content.pm.extra.PACKAGE_NAME"; /** diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 1e4ed31..b957a15 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SystemApi; +import android.app.PackageDeleteObserver; import android.app.PackageInstallObserver; import android.content.ComponentName; import android.content.Context; @@ -28,6 +29,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.PackageInstaller.CommitCallback; +import android.content.pm.PackageInstaller.UninstallCallback; import android.content.pm.PackageParser.PackageParserException; import android.content.res.Resources; import android.content.res.XmlResourceParser; @@ -35,6 +37,7 @@ import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.os.Environment; +import android.os.RemoteException; import android.os.UserHandle; import android.util.AndroidException; @@ -770,6 +773,9 @@ public abstract class PackageManager { */ public static final int NO_NATIVE_LIBRARIES = -114; + /** {@hide} */ + public static final int INSTALL_FAILED_ABORTED = -115; + /** * Flag parameter for {@link #deletePackage} to indicate that you don't want to delete the * package's data directory. @@ -842,7 +848,10 @@ public abstract class PackageManager { * * @hide */ - public static final int DELETE_FAILED_OWNER_BLOCKED= -4; + public static final int DELETE_FAILED_OWNER_BLOCKED = -4; + + /** {@hide} */ + public static final int DELETE_FAILED_ABORTED = -5; /** * Return code that is passed to the {@link IPackageMoveObserver} by @@ -3830,6 +3839,7 @@ public abstract class PackageManager { case INSTALL_FAILED_USER_RESTRICTED: return "INSTALL_FAILED_USER_RESTRICTED"; case INSTALL_FAILED_DUPLICATE_PERMISSION: return "INSTALL_FAILED_DUPLICATE_PERMISSION"; case INSTALL_FAILED_NO_MATCHING_ABIS: return "INSTALL_FAILED_NO_MATCHING_ABIS"; + case INSTALL_FAILED_ABORTED: return "INSTALL_FAILED_ABORTED"; default: return Integer.toString(status); } } @@ -3857,8 +3867,8 @@ public abstract class PackageManager { case INSTALL_FAILED_CONTAINER_ERROR: return CommitCallback.FAILURE_STORAGE; case INSTALL_FAILED_INVALID_INSTALL_LOCATION: return CommitCallback.FAILURE_STORAGE; case INSTALL_FAILED_MEDIA_UNAVAILABLE: return CommitCallback.FAILURE_STORAGE; - case INSTALL_FAILED_VERIFICATION_TIMEOUT: return CommitCallback.FAILURE_UNKNOWN; - case INSTALL_FAILED_VERIFICATION_FAILURE: return CommitCallback.FAILURE_UNKNOWN; + case INSTALL_FAILED_VERIFICATION_TIMEOUT: return CommitCallback.FAILURE_ABORTED; + case INSTALL_FAILED_VERIFICATION_FAILURE: return CommitCallback.FAILURE_ABORTED; case INSTALL_FAILED_PACKAGE_CHANGED: return CommitCallback.FAILURE_INVALID; case INSTALL_FAILED_UID_CHANGED: return CommitCallback.FAILURE_INVALID; case INSTALL_FAILED_VERSION_DOWNGRADE: return CommitCallback.FAILURE_INVALID; @@ -3876,6 +3886,7 @@ public abstract class PackageManager { case INSTALL_FAILED_USER_RESTRICTED: return CommitCallback.FAILURE_INCOMPATIBLE; case INSTALL_FAILED_DUPLICATE_PERMISSION: return CommitCallback.FAILURE_CONFLICT; case INSTALL_FAILED_NO_MATCHING_ABIS: return CommitCallback.FAILURE_INCOMPATIBLE; + case INSTALL_FAILED_ABORTED: return CommitCallback.FAILURE_ABORTED; default: return CommitCallback.FAILURE_UNKNOWN; } } @@ -3888,7 +3899,57 @@ public abstract class PackageManager { case DELETE_FAILED_DEVICE_POLICY_MANAGER: return "DELETE_FAILED_DEVICE_POLICY_MANAGER"; case DELETE_FAILED_USER_RESTRICTED: return "DELETE_FAILED_USER_RESTRICTED"; case DELETE_FAILED_OWNER_BLOCKED: return "DELETE_FAILED_OWNER_BLOCKED"; + case DELETE_FAILED_ABORTED: return "DELETE_FAILED_ABORTED"; default: return Integer.toString(status); } } + + /** {@hide} */ + public static int deleteStatusToFailureReason(int status) { + switch (status) { + case DELETE_FAILED_INTERNAL_ERROR: return UninstallCallback.FAILURE_UNKNOWN; + case DELETE_FAILED_DEVICE_POLICY_MANAGER: return UninstallCallback.FAILURE_BLOCKED; + case DELETE_FAILED_USER_RESTRICTED: return UninstallCallback.FAILURE_BLOCKED; + case DELETE_FAILED_OWNER_BLOCKED: return UninstallCallback.FAILURE_BLOCKED; + case DELETE_FAILED_ABORTED: return UninstallCallback.FAILURE_ABORTED; + default: return UninstallCallback.FAILURE_UNKNOWN; + } + } + + /** {@hide} */ + public static class LegacyPackageInstallObserver extends PackageInstallObserver { + private final IPackageInstallObserver mLegacy; + + public LegacyPackageInstallObserver(IPackageInstallObserver legacy) { + mLegacy = legacy; + } + + @Override + public void onPackageInstalled(String basePackageName, int returnCode, String msg, + Bundle extras) { + if (mLegacy == null) return; + try { + mLegacy.packageInstalled(basePackageName, returnCode); + } catch (RemoteException ignored) { + } + } + } + + /** {@hide} */ + public static class LegacyPackageDeleteObserver extends PackageDeleteObserver { + private final IPackageDeleteObserver mLegacy; + + public LegacyPackageDeleteObserver(IPackageDeleteObserver legacy) { + mLegacy = legacy; + } + + @Override + public void onPackageDeleted(String basePackageName, int returnCode, String msg) { + if (mLegacy == null) return; + try { + mLegacy.packageDeleted(basePackageName, returnCode); + } catch (RemoteException ignored) { + } + } + } } diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java index 900b41d..3c290f7 100644 --- a/core/java/android/content/res/ColorStateList.java +++ b/core/java/android/content/res/ColorStateList.java @@ -233,7 +233,7 @@ public class ColorStateList implements Parcelable { } if (alphaRes != 0) { - alpha = r.getFraction(alphaRes, 1, 1); + alpha = r.getFloat(alphaRes); } // Apply alpha modulation. diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 5face69..52d1c79 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -986,6 +986,34 @@ public class Resources { } /** + * Retrieve a floating-point value for a particular resource ID. + * + * @param id The desired resource identifier, as generated by the aapt + * tool. This integer encodes the package, type, and resource + * entry. The value 0 is an invalid identifier. + * + * @return Returns the floating-point value contained in the resource. + * + * @throws NotFoundException Throws NotFoundException if the given ID does + * not exist or is not a floating-point value. + * @hide Pending API council approval. + */ + public float getFloat(int id) { + synchronized (mAccessLock) { + TypedValue value = mTmpValue; + if (value == null) { + mTmpValue = value = new TypedValue(); + } + getValue(id, value, true); + if (value.type == TypedValue.TYPE_FLOAT) { + return value.getFloat(); + } + throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id) + " type #0x" + + Integer.toHexString(value.type) + " is not valid"); + } + } + + /** * Return an XmlResourceParser through which you can read a view layout * description for the given resource ID. This parser has limited * functionality -- in particular, you can't change its input, and only diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index c461511..6a9d565 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -130,15 +130,6 @@ public abstract class CameraDevice implements AutoCloseable { public abstract String getId(); /** - * <p>Set up a new output set of Surfaces for the camera device.</p> - * - * @deprecated Use {@link #createCaptureSession} instead - * @hide - */ - @Deprecated - public abstract void configureOutputs(List<Surface> outputs) throws CameraAccessException; - - /** * <p>Create a new camera capture session by providing the target output set of Surfaces to the * camera device.</p> * @@ -276,68 +267,6 @@ public abstract class CameraDevice implements AutoCloseable { throws CameraAccessException; /** - * <p>Submit a request for an image to be captured by this CameraDevice.</p> - * - * @deprecated Use {@link CameraCaptureSession#capture} instead - * @hide - */ - @Deprecated - public abstract int capture(CaptureRequest request, CaptureListener listener, Handler handler) - throws CameraAccessException; - - /** - * Submit a list of requests to be captured in sequence as a burst. - * - * @deprecated Use {@link CameraCaptureSession#captureBurst} instead - * @hide - */ - @Deprecated - public abstract int captureBurst(List<CaptureRequest> requests, CaptureListener listener, - Handler handler) throws CameraAccessException; - - /** - * Request endlessly repeating capture of images by this CameraDevice. - * - * @deprecated Use {@link CameraCaptureSession#setRepeatingRequest} instead - * @hide - */ - @Deprecated - public abstract int setRepeatingRequest(CaptureRequest request, CaptureListener listener, - Handler handler) throws CameraAccessException; - - /** - * <p>Request endlessly repeating capture of a sequence of images by this - * CameraDevice.</p> - * - * @deprecated Use {@link CameraCaptureSession#setRepeatingBurst} instead - * @hide - */ - @Deprecated - public abstract int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener, - Handler handler) throws CameraAccessException; - - /** - * <p>Cancel any ongoing repeating capture set by either - * {@link #setRepeatingRequest setRepeatingRequest} or - * {@link #setRepeatingBurst}. - * - * @deprecated Use {@link CameraCaptureSession#stopRepeating} instead - * @hide - */ - @Deprecated - public abstract void stopRepeating() throws CameraAccessException; - - /** - * Flush all captures currently pending and in-progress as fast as - * possible. - * - * @deprecated Use {@link CameraCaptureSession#abortCaptures} instead - * @hide - */ - @Deprecated - public abstract void flush() throws CameraAccessException; - - /** * Close the connection to this camera device as quickly as possible. * * <p>Immediately after this call, all calls to the camera device or active session interface @@ -356,96 +285,6 @@ public abstract class CameraDevice implements AutoCloseable { public abstract void close(); /** - * <p>A listener for tracking the progress of a {@link CaptureRequest} - * submitted to the camera device.</p> - * - * @deprecated Use {@link CameraCaptureSession.CaptureListener} instead - * @hide - */ - @Deprecated - public static abstract class CaptureListener { - - /** - * This constant is used to indicate that no images were captured for - * the request. - * - * @hide - */ - public static final int NO_FRAMES_CAPTURED = -1; - - /** - * This method is called when the camera device has started capturing - * the output image for the request, at the beginning of image exposure. - * - * @see android.media.MediaActionSound - */ - public void onCaptureStarted(CameraDevice camera, - CaptureRequest request, long timestamp) { - // default empty implementation - } - - /** - * This method is called when some results from an image capture are - * available. - * - * @hide - */ - public void onCapturePartial(CameraDevice camera, - CaptureRequest request, CaptureResult result) { - // default empty implementation - } - - /** - * This method is called when an image capture makes partial forward progress; some - * (but not all) results from an image capture are available. - * - */ - public void onCaptureProgressed(CameraDevice camera, - CaptureRequest request, CaptureResult partialResult) { - // default empty implementation - } - - /** - * This method is called when an image capture has fully completed and all the - * result metadata is available. - */ - public void onCaptureCompleted(CameraDevice camera, - CaptureRequest request, TotalCaptureResult result) { - // default empty implementation - } - - /** - * This method is called instead of {@link #onCaptureCompleted} when the - * camera device failed to produce a {@link CaptureResult} for the - * request. - */ - public void onCaptureFailed(CameraDevice camera, - CaptureRequest request, CaptureFailure failure) { - // default empty implementation - } - - /** - * This method is called independently of the others in CaptureListener, - * when a capture sequence finishes and all {@link CaptureResult} - * or {@link CaptureFailure} for it have been returned via this listener. - */ - public void onCaptureSequenceCompleted(CameraDevice camera, - int sequenceId, long frameNumber) { - // default empty implementation - } - - /** - * This method is called independently of the others in CaptureListener, - * when a capture sequence aborts before any {@link CaptureResult} - * or {@link CaptureFailure} for it have been returned via this listener. - */ - public void onCaptureSequenceAborted(CameraDevice camera, - int sequenceId) { - // default empty implementation - } - } - - /** * A listener for notifications about the state of a camera * device. * @@ -542,40 +381,6 @@ public abstract class CameraDevice implements AutoCloseable { public abstract void onOpened(CameraDevice camera); // Must implement /** - * The method called when a camera device has no outputs configured. - * - * @deprecated Use {@link #onOpened} instead. - * @hide - */ - @Deprecated - public void onUnconfigured(CameraDevice camera) { - // Default empty implementation - } - - /** - * The method called when a camera device begins processing - * {@link CaptureRequest capture requests}. - * - * @deprecated Use {@link CameraCaptureSession.StateListener#onActive} instead. - * @hide - */ - @Deprecated - public void onActive(CameraDevice camera) { - // Default empty implementation - } - - /** - * The method called when a camera device is busy. - * - * @deprecated Use {@link CameraCaptureSession.StateListener#onConfigured} instead. - * @hide - */ - @Deprecated - public void onBusy(CameraDevice camera) { - // Default empty implementation - } - - /** * The method called when a camera device has been closed with * {@link CameraDevice#close}. * @@ -591,18 +396,6 @@ public abstract class CameraDevice implements AutoCloseable { } /** - * The method called when a camera device has finished processing all - * submitted capture requests and has reached an idle state. - * - * @deprecated Use {@link CameraCaptureSession.StateListener#onReady} instead. - * @hide - */ - @Deprecated - public void onIdle(CameraDevice camera) { - // Default empty implementation - } - - /** * The method called when a camera device is no longer available for * use. * diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java index f829f5e..a15028c 100644 --- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java @@ -103,7 +103,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * Use the same handler as the device's StateListener for all the internal coming events * * This ensures total ordering between CameraDevice.StateListener and - * CameraDevice.CaptureListener events. + * CameraDeviceImpl.CaptureListener events. */ mSequenceDrainer = new TaskDrainer<>(mDeviceHandler, new SequenceDrainListener(), /*name*/"seq"); @@ -141,7 +141,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { checkNotClosed(); checkLegalToCapture(); - handler = checkHandler(handler); + handler = checkHandler(handler, listener); if (VERBOSE) { Log.v(TAG, "capture - request " + request + ", listener " + listener + " handler" + @@ -164,7 +164,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { checkNotClosed(); checkLegalToCapture(); - handler = checkHandler(handler); + handler = checkHandler(handler, listener); if (VERBOSE) { CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]); @@ -186,7 +186,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { checkNotClosed(); checkLegalToCapture(); - handler = checkHandler(handler); + handler = checkHandler(handler, listener); if (VERBOSE) { Log.v(TAG, "setRepeatingRequest - request " + request + ", listener " + listener + @@ -209,7 +209,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { checkNotClosed(); checkLegalToCapture(); - handler = checkHandler(handler); + handler = checkHandler(handler, listener); if (VERBOSE) { CaptureRequest[] requestArray = requests.toArray(new CaptureRequest[0]); @@ -261,9 +261,12 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped. * <p> * + * <p>After this call completes, the session will not call any further methods on the camera + * device.</p> + * * @see CameraCaptureSession#close */ - synchronized void replaceSessionClose(CameraCaptureSession other) { + synchronized void replaceSessionClose() { /* * In order for creating new sessions to be fast, the new session should be created * before the old session is closed. @@ -278,13 +281,17 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { if (VERBOSE) Log.v(TAG, "replaceSessionClose"); - // #close was already called explicitly, keep going the slow route - if (mClosed) { - if (VERBOSE) Log.v(TAG, "replaceSessionClose - close was already called"); - return; - } - + // Set up fast shutdown. Possible alternative paths: + // - This session is active, so close() below starts the shutdown drain + // - This session is mid-shutdown drain, and hasn't yet reached the idle drain listener. + // - This session is already closed and has executed the idle drain listener, and + // configureOutputs(null) has already been called. + // + // Do not call configureOutputs(null) going forward, since it would race with the + // configuration for the new session. If it was already called, then we don't care, since it + // won't get called again. mSkipUnconfigure = true; + close(); } @@ -347,7 +354,7 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { /** * Forward callbacks from - * CameraDevice.CaptureListener to the CameraCaptureSession.CaptureListener. + * CameraDeviceImpl.CaptureListener to the CameraCaptureSession.CaptureListener. * * <p>In particular, all calls are automatically split to go both to our own * internal listener, and to the user-specified listener (by transparently posting @@ -356,9 +363,9 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * <p>When a capture sequence finishes, update the pending checked sequences set.</p> */ @SuppressWarnings("deprecation") - private CameraDevice.CaptureListener createCaptureListenerProxy( + private CameraDeviceImpl.CaptureListener createCaptureListenerProxy( Handler handler, CaptureListener listener) { - CameraDevice.CaptureListener localListener = new CameraDevice.CaptureListener() { + CameraDeviceImpl.CaptureListener localListener = new CameraDeviceImpl.CaptureListener() { @Override public void onCaptureSequenceCompleted(CameraDevice camera, int sequenceId, long frameNumber) { @@ -379,27 +386,30 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * - then forward the call to a handler * - then finally invoke the destination method on the session listener object */ - Dispatchable<CaptureListener> userListenerSink; - if (listener == null) { // OK: API allows the user to not specify a listener - userListenerSink = new NullDispatcher<>(); - } else { - userListenerSink = new InvokeDispatcher<>(listener); + if (listener == null) { + // OK: API allows the user to not specify a listener, and the handler may + // also be null in that case. Collapse whole dispatch chain to only call the local + // listener + return localListener; } - InvokeDispatcher<CameraDevice.CaptureListener> localSink = + InvokeDispatcher<CameraDeviceImpl.CaptureListener> localSink = new InvokeDispatcher<>(localListener); + + InvokeDispatcher<CaptureListener> userListenerSink = + new InvokeDispatcher<>(listener); HandlerDispatcher<CaptureListener> handlerPassthrough = new HandlerDispatcher<>(userListenerSink, handler); - DuckTypingDispatcher<CameraDevice.CaptureListener, CaptureListener> duckToSession + DuckTypingDispatcher<CameraDeviceImpl.CaptureListener, CaptureListener> duckToSession = new DuckTypingDispatcher<>(handlerPassthrough, CaptureListener.class); - ArgumentReplacingDispatcher<CameraDevice.CaptureListener, CameraCaptureSessionImpl> - replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession, - /*argumentIndex*/0, this); + ArgumentReplacingDispatcher<CameraDeviceImpl.CaptureListener, CameraCaptureSessionImpl> + replaceDeviceWithSession = new ArgumentReplacingDispatcher<>(duckToSession, + /*argumentIndex*/0, this); - BroadcastDispatcher<CameraDevice.CaptureListener> broadcaster = - new BroadcastDispatcher<CameraDevice.CaptureListener>( - replaceDeviceWithSession, - localSink); + BroadcastDispatcher<CameraDeviceImpl.CaptureListener> broadcaster = + new BroadcastDispatcher<CameraDeviceImpl.CaptureListener>( + replaceDeviceWithSession, + localSink); return new ListenerProxies.DeviceCaptureListenerProxy(broadcaster); } @@ -415,10 +425,10 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * </ul> * </p> * */ - CameraDevice.StateListener getDeviceStateListener() { + CameraDeviceImpl.StateListenerKK getDeviceStateListener() { final CameraCaptureSession session = this; - return new CameraDevice.StateListener() { + return new CameraDeviceImpl.StateListenerKK() { private boolean mBusy = false; private boolean mActive = false; @@ -596,6 +606,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession { * * This operation is idempotent; a session will not be closed twice. */ + if (VERBOSE) Log.v(TAG, "Session drain complete, skip unconfigure: " + + mSkipUnconfigure); // Fast path: A new capture session has replaced this one; don't unconfigure. if (mSkipUnconfigure) { diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java index 18b1202..71eb0e9 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java @@ -21,8 +21,10 @@ import static android.hardware.camera2.CameraAccessException.CAMERA_IN_USE; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.ICameraDeviceCallbacks; import android.hardware.camera2.ICameraDeviceUser; import android.hardware.camera2.TotalCaptureResult; @@ -47,7 +49,7 @@ import java.util.TreeSet; /** * HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate */ -public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { +public class CameraDeviceImpl extends CameraDevice { private final String TAG; private final boolean DEBUG; @@ -62,7 +64,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks(); private final StateListener mDeviceListener; - private volatile StateListener mSessionStateListener; + private volatile StateListenerKK mSessionStateListener; private final Handler mDeviceHandler; private volatile boolean mClosing = false; @@ -103,7 +105,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { private final Runnable mCallOnOpened = new Runnable() { @Override public void run() { - StateListener sessionListener = null; + StateListenerKK sessionListener = null; synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed @@ -119,7 +121,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { private final Runnable mCallOnUnconfigured = new Runnable() { @Override public void run() { - StateListener sessionListener = null; + StateListenerKK sessionListener = null; synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed @@ -128,14 +130,13 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { if (sessionListener != null) { sessionListener.onUnconfigured(CameraDeviceImpl.this); } - mDeviceListener.onUnconfigured(CameraDeviceImpl.this); } }; private final Runnable mCallOnActive = new Runnable() { @Override public void run() { - StateListener sessionListener = null; + StateListenerKK sessionListener = null; synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed @@ -144,14 +145,13 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { if (sessionListener != null) { sessionListener.onActive(CameraDeviceImpl.this); } - mDeviceListener.onActive(CameraDeviceImpl.this); } }; private final Runnable mCallOnBusy = new Runnable() { @Override public void run() { - StateListener sessionListener = null; + StateListenerKK sessionListener = null; synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed @@ -160,7 +160,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { if (sessionListener != null) { sessionListener.onBusy(CameraDeviceImpl.this); } - mDeviceListener.onBusy(CameraDeviceImpl.this); } }; @@ -172,7 +171,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { if (mClosedOnce) { throw new AssertionError("Don't post #onClosed more than once"); } - StateListener sessionListener = null; + StateListenerKK sessionListener = null; synchronized(mInterfaceLock) { sessionListener = mSessionStateListener; } @@ -187,7 +186,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { private final Runnable mCallOnIdle = new Runnable() { @Override public void run() { - StateListener sessionListener = null; + StateListenerKK sessionListener = null; synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed @@ -196,14 +195,13 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { if (sessionListener != null) { sessionListener.onIdle(CameraDeviceImpl.this); } - mDeviceListener.onIdle(CameraDeviceImpl.this); } }; private final Runnable mCallOnDisconnected = new Runnable() { @Override public void run() { - StateListener sessionListener = null; + StateListenerKK sessionListener = null; synchronized(mInterfaceLock) { if (mRemoteDevice == null) return; // Camera already closed @@ -313,7 +311,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { return mCameraId; } - @Override public void configureOutputs(List<Surface> outputs) throws CameraAccessException { // Treat a null input the same an empty list if (outputs == null) { @@ -390,7 +387,11 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { checkIfCameraClosedOrInError(); - // TODO: we must be in UNCONFIGURED mode to begin with, or using another session + // Notify current session that it's going away, before starting camera operations + // After this call completes, the session is not allowed to call into CameraDeviceImpl + if (mCurrentSession != null) { + mCurrentSession.replaceSessionClose(); + } // TODO: dont block for this boolean configureSuccess = true; @@ -410,10 +411,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { new CameraCaptureSessionImpl(outputs, listener, handler, this, mDeviceHandler, configureSuccess); - if (mCurrentSession != null) { - mCurrentSession.replaceSessionClose(newSession); - } - // TODO: wait until current session closes, then create the new session mCurrentSession = newSession; @@ -425,6 +422,15 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { } } + /** + * For use by backwards-compatibility code only. + */ + public void setSessionListener(StateListenerKK sessionListener) { + synchronized(mInterfaceLock) { + mSessionStateListener = sessionListener; + } + } + @Override public CaptureRequest.Builder createCaptureRequest(int templateType) throws CameraAccessException { @@ -449,7 +455,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { } } - @Override public int capture(CaptureRequest request, CaptureListener listener, Handler handler) throws CameraAccessException { if (DEBUG) { @@ -460,7 +465,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { return submitCaptureRequest(requestList, listener, handler, /*streaming*/false); } - @Override public int captureBurst(List<CaptureRequest> requests, CaptureListener listener, Handler handler) throws CameraAccessException { if (requests == null || requests.isEmpty()) { @@ -543,9 +547,7 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { // Need a valid handler, or current thread needs to have a looper, if // listener is valid - if (listener != null) { - handler = checkHandler(handler); - } + handler = checkHandler(handler, listener); // Make sure that there all requests have at least 1 surface; all surfaces are non-null for (CaptureRequest request : requestList) { @@ -613,7 +615,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { } } - @Override public int setRepeatingRequest(CaptureRequest request, CaptureListener listener, Handler handler) throws CameraAccessException { List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); @@ -621,7 +622,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { return submitCaptureRequest(requestList, listener, handler, /*streaming*/true); } - @Override public int setRepeatingBurst(List<CaptureRequest> requests, CaptureListener listener, Handler handler) throws CameraAccessException { if (requests == null || requests.isEmpty()) { @@ -630,7 +630,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { return submitCaptureRequest(requests, listener, handler, /*streaming*/true); } - @Override public void stopRepeating() throws CameraAccessException { synchronized(mInterfaceLock) { @@ -681,7 +680,6 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { } } - @Override public void flush() throws CameraAccessException { synchronized(mInterfaceLock) { checkIfCameraClosedOrInError(); @@ -739,6 +737,133 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { } } + /** + * <p>A listener for tracking the progress of a {@link CaptureRequest} + * submitted to the camera device.</p> + * + */ + public static abstract class CaptureListener { + + /** + * This constant is used to indicate that no images were captured for + * the request. + * + * @hide + */ + public static final int NO_FRAMES_CAPTURED = -1; + + /** + * This method is called when the camera device has started capturing + * the output image for the request, at the beginning of image exposure. + * + * @see android.media.MediaActionSound + */ + public void onCaptureStarted(CameraDevice camera, + CaptureRequest request, long timestamp) { + // default empty implementation + } + + /** + * This method is called when some results from an image capture are + * available. + * + * @hide + */ + public void onCapturePartial(CameraDevice camera, + CaptureRequest request, CaptureResult result) { + // default empty implementation + } + + /** + * This method is called when an image capture makes partial forward progress; some + * (but not all) results from an image capture are available. + * + */ + public void onCaptureProgressed(CameraDevice camera, + CaptureRequest request, CaptureResult partialResult) { + // default empty implementation + } + + /** + * This method is called when an image capture has fully completed and all the + * result metadata is available. + */ + public void onCaptureCompleted(CameraDevice camera, + CaptureRequest request, TotalCaptureResult result) { + // default empty implementation + } + + /** + * This method is called instead of {@link #onCaptureCompleted} when the + * camera device failed to produce a {@link CaptureResult} for the + * request. + */ + public void onCaptureFailed(CameraDevice camera, + CaptureRequest request, CaptureFailure failure) { + // default empty implementation + } + + /** + * This method is called independently of the others in CaptureListener, + * when a capture sequence finishes and all {@link CaptureResult} + * or {@link CaptureFailure} for it have been returned via this listener. + */ + public void onCaptureSequenceCompleted(CameraDevice camera, + int sequenceId, long frameNumber) { + // default empty implementation + } + + /** + * This method is called independently of the others in CaptureListener, + * when a capture sequence aborts before any {@link CaptureResult} + * or {@link CaptureFailure} for it have been returned via this listener. + */ + public void onCaptureSequenceAborted(CameraDevice camera, + int sequenceId) { + // default empty implementation + } + } + + /** + * A listener for notifications about the state of a camera device, adding in the callbacks that + * were part of the earlier KK API design, but now only used internally. + */ + public static abstract class StateListenerKK extends StateListener { + /** + * The method called when a camera device has no outputs configured. + * + */ + public void onUnconfigured(CameraDevice camera) { + // Default empty implementation + } + + /** + * The method called when a camera device begins processing + * {@link CaptureRequest capture requests}. + * + */ + public void onActive(CameraDevice camera) { + // Default empty implementation + } + + /** + * The method called when a camera device is busy. + * + */ + public void onBusy(CameraDevice camera) { + // Default empty implementation + } + + /** + * The method called when a camera device has finished processing all + * submitted capture requests and has reached an idle state. + * + */ + public void onIdle(CameraDevice camera) { + // Default empty implementation + } + } + static class CaptureListenerHolder { private final boolean mRepeating; @@ -1155,6 +1280,18 @@ public class CameraDeviceImpl extends android.hardware.camera2.CameraDevice { return handler; } + /** + * Default handler management, conditional on there being a listener. + * + * <p>If the listener isn't null, check the handler, otherwise pass it through.</p> + */ + static <T> Handler checkHandler(Handler handler, T listener) { + if (listener != null) { + return checkHandler(handler); + } + return handler; + } + private void checkIfCameraClosedOrInError() throws CameraAccessException { if (mInError) { throw new CameraAccessException(CameraAccessException.CAMERA_ERROR, diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java index dc71a06..febb015 100644 --- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java +++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java @@ -67,6 +67,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; /** * Implementation of camera metadata marshal/unmarshal across Binder to @@ -227,6 +228,7 @@ public class CameraMetadataNative implements Parcelable { private static final String CELLID_PROCESS = "CELLID"; private static final String GPS_PROCESS = "GPS"; + private static final int FACE_LANDMARK_SIZE = 6; private static String translateLocationProviderToProcess(final String provider) { if (provider == null) { @@ -347,7 +349,7 @@ public class CameraMetadataNative implements Parcelable { // Check if key has been overridden to use a wrapper class on the java side. GetCommand g = sGetCommandMap.get(key); if (g != null) { - return (T) g.getValue(this, key); + return g.getValue(this, key); } return getBase(key); } @@ -587,9 +589,71 @@ public class CameraMetadataNative implements Parcelable { return availableFormats; } - private Face[] getFaces() { - final int FACE_LANDMARK_SIZE = 6; + private boolean setFaces(Face[] faces) { + if (faces == null) { + return false; + } + + int numFaces = faces.length; + + // Detect if all faces are SIMPLE or not; count # of valid faces + boolean fullMode = true; + for (Face face : faces) { + if (face == null) { + numFaces--; + Log.w(TAG, "setFaces - null face detected, skipping"); + continue; + } + + if (face.getId() == Face.ID_UNSUPPORTED) { + fullMode = false; + } + } + + Rect[] faceRectangles = new Rect[numFaces]; + byte[] faceScores = new byte[numFaces]; + int[] faceIds = null; + int[] faceLandmarks = null; + + if (fullMode) { + faceIds = new int[numFaces]; + faceLandmarks = new int[numFaces * FACE_LANDMARK_SIZE]; + } + + int i = 0; + for (Face face : faces) { + if (face == null) { + continue; + } + + faceRectangles[i] = face.getBounds(); + faceScores[i] = (byte)face.getScore(); + + if (fullMode) { + faceIds[i] = face.getId(); + int j = 0; + + faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getLeftEyePosition().x; + faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getLeftEyePosition().y; + faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getRightEyePosition().x; + faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getRightEyePosition().y; + faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getMouthPosition().x; + faceLandmarks[i * FACE_LANDMARK_SIZE + j++] = face.getMouthPosition().y; + } + + i++; + } + + set(CaptureResult.STATISTICS_FACE_RECTANGLES, faceRectangles); + set(CaptureResult.STATISTICS_FACE_IDS, faceIds); + set(CaptureResult.STATISTICS_FACE_LANDMARKS, faceLandmarks); + set(CaptureResult.STATISTICS_FACE_SCORES, faceScores); + + return true; + } + + private Face[] getFaces() { Integer faceDetectMode = get(CaptureResult.STATISTICS_FACE_DETECT_MODE); if (faceDetectMode == null) { Log.w(TAG, "Face detect mode metadata is null, assuming the mode is SIMPLE"); @@ -653,9 +717,12 @@ public class CameraMetadataNative implements Parcelable { if (faceScores[i] <= Face.SCORE_MAX && faceScores[i] >= Face.SCORE_MIN && faceIds[i] >= 0) { - Point leftEye = new Point(faceLandmarks[i*6], faceLandmarks[i*6+1]); - Point rightEye = new Point(faceLandmarks[i*6+2], faceLandmarks[i*6+3]); - Point mouth = new Point(faceLandmarks[i*6+4], faceLandmarks[i*6+5]); + Point leftEye = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE], + faceLandmarks[i*FACE_LANDMARK_SIZE+1]); + Point rightEye = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE+2], + faceLandmarks[i*FACE_LANDMARK_SIZE+3]); + Point mouth = new Point(faceLandmarks[i*FACE_LANDMARK_SIZE+4], + faceLandmarks[i*FACE_LANDMARK_SIZE+5]); Face face = new Face(faceRectangles[i], faceScores[i], faceIds[i], leftEye, rightEye, mouth); faceList.add(face); @@ -865,6 +932,13 @@ public class CameraMetadataNative implements Parcelable { metadata.setFaceRectangles((Rect[]) value); } }); + sSetCommandMap.put(CaptureResult.STATISTICS_FACES.getNativeKey(), + new SetCommand() { + @Override + public <T> void setValue(CameraMetadataNative metadata, T value) { + metadata.setFaces((Face[])value); + } + }); sSetCommandMap.put(CaptureRequest.TONEMAP_CURVE.getNativeKey(), new SetCommand() { @Override public <T> void setValue(CameraMetadataNative metadata, T value) { diff --git a/core/java/android/hardware/camera2/impl/ListenerProxies.java b/core/java/android/hardware/camera2/impl/ListenerProxies.java index ab9a4d5..f44f9ad 100644 --- a/core/java/android/hardware/camera2/impl/ListenerProxies.java +++ b/core/java/android/hardware/camera2/impl/ListenerProxies.java @@ -36,13 +36,13 @@ public class ListenerProxies { // TODO: replace with codegen - public static class DeviceStateListenerProxy extends CameraDevice.StateListener { - private final MethodNameInvoker<CameraDevice.StateListener> mProxy; + public static class DeviceStateListenerProxy extends CameraDeviceImpl.StateListenerKK { + private final MethodNameInvoker<CameraDeviceImpl.StateListenerKK> mProxy; public DeviceStateListenerProxy( - Dispatchable<CameraDevice.StateListener> dispatchTarget) { + Dispatchable<CameraDeviceImpl.StateListenerKK> dispatchTarget) { dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null"); - mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDevice.StateListener.class); + mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.StateListenerKK.class); } @Override @@ -87,13 +87,13 @@ public class ListenerProxies { } @SuppressWarnings("deprecation") - public static class DeviceCaptureListenerProxy extends CameraDevice.CaptureListener { - private final MethodNameInvoker<CameraDevice.CaptureListener> mProxy; + public static class DeviceCaptureListenerProxy extends CameraDeviceImpl.CaptureListener { + private final MethodNameInvoker<CameraDeviceImpl.CaptureListener> mProxy; public DeviceCaptureListenerProxy( - Dispatchable<CameraDevice.CaptureListener> dispatchTarget) { + Dispatchable<CameraDeviceImpl.CaptureListener> dispatchTarget) { dispatchTarget = checkNotNull(dispatchTarget, "dispatchTarget must not be null"); - mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDevice.CaptureListener.class); + mProxy = new MethodNameInvoker<>(dispatchTarget, CameraDeviceImpl.CaptureListener.class); } @Override diff --git a/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java new file mode 100644 index 0000000..1470b70 --- /dev/null +++ b/core/java/android/hardware/camera2/legacy/LegacyFaceDetectMapper.java @@ -0,0 +1,231 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.camera2.legacy; + +import android.graphics.Rect; +import android.hardware.Camera; +import android.hardware.Camera.FaceDetectionListener; +import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.legacy.ParameterUtils.ZoomData; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.params.Face; +import android.hardware.camera2.utils.ListUtils; +import android.hardware.camera2.utils.ParamsUtils; +import android.util.Log; +import android.util.Size; + +import com.android.internal.util.ArrayUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static android.hardware.camera2.CaptureRequest.*; +import static com.android.internal.util.Preconditions.*; + +/** + * Map legacy face detect callbacks into face detection results. + */ +@SuppressWarnings("deprecation") +public class LegacyFaceDetectMapper { + private static String TAG = "LegacyFaceDetectMapper"; + private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); + + private final Camera mCamera; + private final boolean mFaceDetectSupported; + private boolean mFaceDetectEnabled = false; + + private final Object mLock = new Object(); + private Camera.Face[] mFaces; + private Camera.Face[] mFacesPrev; + /** + * Instantiate a new face detect mapper. + * + * @param camera a non-{@code null} camera1 device + * @param characteristics a non-{@code null} camera characteristics for that camera1 + * + * @throws NullPointerException if any of the args were {@code null} + */ + public LegacyFaceDetectMapper(Camera camera, CameraCharacteristics characteristics) { + mCamera = checkNotNull(camera, "camera must not be null"); + checkNotNull(characteristics, "characteristics must not be null"); + + mFaceDetectSupported = ArrayUtils.contains( + characteristics.get( + CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES), + STATISTICS_FACE_DETECT_MODE_SIMPLE); + + if (!mFaceDetectSupported) { + return; + } + + mCamera.setFaceDetectionListener(new FaceDetectionListener() { + + @Override + public void onFaceDetection(Camera.Face[] faces, Camera camera) { + int lengthFaces = faces == null ? 0 : faces.length; + synchronized (mLock) { + if (mFaceDetectEnabled) { + mFaces = faces; + } else if (lengthFaces > 0) { + // stopFaceDetectMode could race against the requests, print a debug log + Log.d(TAG, + "onFaceDetection - Ignored some incoming faces since" + + "face detection was disabled"); + } + } + + if (VERBOSE) { + Log.v(TAG, "onFaceDetection - read " + lengthFaces + " faces"); + } + } + }); + } + + /** + * Process the face detect mode from the capture request into an api1 face detect toggle. + * + * <p>This method should be called after the parameters are {@link LegacyRequestMapper mapped} + * with the request.</p> + * + * <p>Callbacks are processed in the background, and the next call to {@link #mapResultTriggers} + * will have the latest faces detected as reflected by the camera1 callbacks.</p> + * + * <p>None of the arguments will be mutated.</p> + * + * @param captureRequest a non-{@code null} request + * @param parameters a non-{@code null} parameters corresponding to this request (read-only) + */ + public void processFaceDetectMode(CaptureRequest captureRequest, + Camera.Parameters parameters) { + checkNotNull(captureRequest, "captureRequest must not be null"); + + /* + * statistics.faceDetectMode + */ + int fdMode = ParamsUtils.getOrDefault(captureRequest, STATISTICS_FACE_DETECT_MODE, + STATISTICS_FACE_DETECT_MODE_OFF); + + if (fdMode != STATISTICS_FACE_DETECT_MODE_OFF && !mFaceDetectSupported) { + Log.w(TAG, + "processFaceDetectMode - Ignoring statistics.faceDetectMode; " + + "face detection is not available"); + return; + } + + // Print some warnings out in case the values were wrong + switch (fdMode) { + case STATISTICS_FACE_DETECT_MODE_OFF: + case STATISTICS_FACE_DETECT_MODE_SIMPLE: + break; + case STATISTICS_FACE_DETECT_MODE_FULL: + Log.w(TAG, + "processFaceDetectMode - statistics.faceDetectMode == FULL unsupported, " + + "downgrading to SIMPLE"); + break; + default: + Log.w(TAG, "processFaceDetectMode - ignoring unknown statistics.faceDetectMode = " + + fdMode); + return; + } + + boolean enableFaceDetect = fdMode != STATISTICS_FACE_DETECT_MODE_OFF; + synchronized (mLock) { + // Enable/disable face detection if it's changed since last time + if (enableFaceDetect != mFaceDetectEnabled) { + if (enableFaceDetect) { + mCamera.startFaceDetection(); + + if (VERBOSE) { + Log.v(TAG, "processFaceDetectMode - start face detection"); + } + } else { + mCamera.stopFaceDetection(); + + if (VERBOSE) { + Log.v(TAG, "processFaceDetectMode - stop face detection"); + } + + mFaces = null; + } + + mFaceDetectEnabled = enableFaceDetect; + } + } + } + + /** + * Update the {@code result} camera metadata map with the new value for the + * {@code statistics.faces} and {@code statistics.faceDetectMode}. + * + * <p>Face detect callbacks are processed in the background, and each call to + * {@link #mapResultFaces} will have the latest faces as reflected by the camera1 callbacks.</p> + * + * @param result a non-{@code null} result + * @param legacyRequest a non-{@code null} request (read-only) + */ + public void mapResultFaces(CameraMetadataNative result, LegacyRequest legacyRequest) { + checkNotNull(result, "result must not be null"); + checkNotNull(legacyRequest, "legacyRequest must not be null"); + + Camera.Face[] faces, previousFaces; + int fdMode; + synchronized (mLock) { + fdMode = mFaceDetectEnabled ? + STATISTICS_FACE_DETECT_MODE_SIMPLE : STATISTICS_FACE_DETECT_MODE_OFF; + + if (mFaceDetectEnabled) { + faces = mFaces; + } else { + faces = null; + } + + previousFaces = mFacesPrev; + mFacesPrev = faces; + } + + CameraCharacteristics characteristics = legacyRequest.characteristics; + CaptureRequest request = legacyRequest.captureRequest; + Size previewSize = legacyRequest.previewSize; + Camera.Parameters params = legacyRequest.parameters; + + Rect activeArray = characteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); + ZoomData zoomData = ParameterUtils.convertScalerCropRegion(activeArray, + request.get(CaptureRequest.SCALER_CROP_REGION), previewSize, params); + + List<Face> convertedFaces = new ArrayList<>(); + if (faces != null) { + for (Camera.Face face : faces) { + if (face != null) { + convertedFaces.add( + ParameterUtils.convertFaceFromLegacy(face, activeArray, zoomData)); + } else { + Log.w(TAG, "mapResultFaces - read NULL face from camera1 device"); + } + } + } + + if (VERBOSE && previousFaces != faces) { // Log only in verbose and IF the faces changed + Log.v(TAG, "mapResultFaces - changed to " + ListUtils.listToString(convertedFaces)); + } + + result.set(CaptureResult.STATISTICS_FACES, convertedFaces.toArray(new Face[0])); + result.set(CaptureResult.STATISTICS_FACE_DETECT_MODE, fdMode); + } +} diff --git a/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java b/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java index e576b43..d0a3a3f 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyFocusStateMapper.java @@ -247,7 +247,8 @@ public class LegacyFocusStateMapper { // No action necessary. The callbacks will handle transitions. break; default: - Log.w(TAG, "mapTriggers - ignoring unknown control.afTrigger = " + afTrigger); + Log.w(TAG, "processRequestTriggers - ignoring unknown control.afTrigger = " + + afTrigger); } } diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java index 711edf4..b05508b 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java @@ -204,6 +204,11 @@ public class LegacyMetadataMapper { mapSensor(m, p); /* + * statistics.* + */ + mapStatistics(m, p); + + /* * sync.* */ mapSync(m, p); @@ -487,6 +492,18 @@ public class LegacyMetadataMapper { private static void mapControlOther(CameraMetadataNative m, Camera.Parameters p) { /* + * android.control.availableVideoStabilizationModes + */ + { + int stabModes[] = p.isVideoStabilizationSupported() ? + new int[] { CONTROL_VIDEO_STABILIZATION_MODE_OFF, + CONTROL_VIDEO_STABILIZATION_MODE_ON } : + new int[] { CONTROL_VIDEO_STABILIZATION_MODE_OFF }; + + m.set(CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES, stabModes); + } + + /* * android.control.maxRegions */ final int AE = 0, AWB = 1, AF = 2; @@ -742,6 +759,31 @@ public class LegacyMetadataMapper { m.set(SENSOR_INFO_PIXEL_ARRAY_SIZE, largestJpegSize); } + private static void mapStatistics(CameraMetadataNative m, Parameters p) { + /* + * statistics.info.availableFaceDetectModes + */ + int[] fdModes; + + if (p.getMaxNumDetectedFaces() > 0) { + fdModes = new int[] { + STATISTICS_FACE_DETECT_MODE_OFF, + STATISTICS_FACE_DETECT_MODE_SIMPLE + // FULL is never-listed, since we have no way to query it statically + }; + } else { + fdModes = new int[] { + STATISTICS_FACE_DETECT_MODE_OFF + }; + } + m.set(STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES, fdModes); + + /* + * statistics.info.maxFaceCount + */ + m.set(STATISTICS_INFO_MAX_FACE_COUNT, p.getMaxNumDetectedFaces()); + } + private static void mapSync(CameraMetadataNative m, Parameters p) { /* * sync.maxLatency diff --git a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java index a6fe035..20f3fd2 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyRequestMapper.java @@ -150,10 +150,8 @@ public class LegacyRequestMapper { if (supported) { params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX], legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]); - params.setRecordingHint(false); } else { Log.w(TAG, "Unsupported FPS range set [" + legacyFps[0] + "," + legacyFps[1] + "]"); - params.setRecordingHint(true); } } @@ -248,6 +246,18 @@ public class LegacyRequestMapper { // TODO: Don't add control.awbLock to availableRequestKeys if it's not supported } + // control.videoStabilizationMode + { + Integer stabMode = getIfSupported(request, CONTROL_VIDEO_STABILIZATION_MODE, + /*defaultValue*/CONTROL_VIDEO_STABILIZATION_MODE_OFF, + params.isVideoStabilizationSupported(), + /*allowedValue*/CONTROL_VIDEO_STABILIZATION_MODE_OFF); + + if (stabMode != null) { + params.setVideoStabilization(stabMode == CONTROL_VIDEO_STABILIZATION_MODE_ON); + } + } + // lens.focusDistance { boolean infinityFocusSupported = diff --git a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java index 9eff943..a2487f4 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyResultMapper.java @@ -35,6 +35,9 @@ import java.util.ArrayList; import java.util.List; import static com.android.internal.util.Preconditions.*; +import static android.hardware.camera2.CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_OFF; +import static android.hardware.camera2.CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_ON; +import static android.hardware.camera2.CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE; import static android.hardware.camera2.CaptureResult.*; /** @@ -142,7 +145,6 @@ public class LegacyResultMapper { */ mapAwb(result, /*out*/params); - /* * control.mode */ @@ -171,7 +173,6 @@ public class LegacyResultMapper { } } - /* * control.effectMode */ @@ -187,6 +188,15 @@ public class LegacyResultMapper { } } + // control.videoStabilizationMode + { + int stabMode = + (params.isVideoStabilizationSupported() && params.getVideoStabilization()) ? + CONTROL_VIDEO_STABILIZATION_MODE_ON : + CONTROL_VIDEO_STABILIZATION_MODE_OFF; + result.set(CONTROL_VIDEO_STABILIZATION_MODE, stabMode); + } + /* * flash */ diff --git a/core/java/android/hardware/camera2/legacy/ParameterUtils.java b/core/java/android/hardware/camera2/legacy/ParameterUtils.java index efd12f2..385f844 100644 --- a/core/java/android/hardware/camera2/legacy/ParameterUtils.java +++ b/core/java/android/hardware/camera2/legacy/ParameterUtils.java @@ -43,6 +43,7 @@ import static com.android.internal.util.Preconditions.*; /** * Various utilities for dealing with camera API1 parameters. */ +@SuppressWarnings("deprecation") public class ParameterUtils { /** Upper/left minimal point of a normalized rectangle */ public static final int NORMALIZED_RECTANGLE_MIN = -1000; @@ -164,19 +165,23 @@ public class ParameterUtils { * <p>If the score is out of range of {@value Face#SCORE_MIN}, {@value Face#SCORE_MAX}, * the score is clipped first and a warning is printed to logcat.</p> * + * <p>If the id is negative, the id is changed to 0 and a warning is printed to + * logcat.</p> + * * <p>All other parameters are passed-through as-is.</p> * * @return a new face with the optional features set */ public Face toFace( int id, Point leftEyePosition, Point rightEyePosition, Point mouthPosition) { + int idSafe = clipLower(id, /*lo*/0, rect, "id"); int score = clip(weight, Face.SCORE_MIN, Face.SCORE_MAX, rect, "score"); - return new Face(rect, score, id, leftEyePosition, rightEyePosition, mouthPosition); + return new Face(rect, score, idSafe, leftEyePosition, rightEyePosition, mouthPosition); } /** @@ -861,6 +866,61 @@ public class ParameterUtils { /*usePreviewCrop*/true); } + /** + * Convert an api1 face into an active-array based api2 face. + * + * <p>Out-of-ranges scores and ids will be clipped to be within range (with a warning).</p> + * + * @param face a non-{@code null} api1 face + * @param activeArraySize active array size of the sensor (e.g. max jpeg size) + * @param zoomData the calculated zoom data corresponding to this request + * + * @return a non-{@code null} api2 face + * + * @throws NullPointerException if the {@code face} was {@code null} + */ + public static Face convertFaceFromLegacy(Camera.Face face, Rect activeArray, + ZoomData zoomData) { + checkNotNull(face, "face must not be null"); + + Face api2Face; + + Camera.Area fakeArea = new Camera.Area(face.rect, /*weight*/1); + + WeightedRectangle faceRect = + convertCameraAreaToActiveArrayRectangle(activeArray, zoomData, fakeArea); + + Point leftEye = face.leftEye, rightEye = face.rightEye, mouth = face.mouth; + if (leftEye != null && rightEye != null && mouth != null) { + leftEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData, + leftEye, /*usePreviewCrop*/true); + rightEye = convertCameraPointToActiveArrayPoint(activeArray, zoomData, + leftEye, /*usePreviewCrop*/true); + mouth = convertCameraPointToActiveArrayPoint(activeArray, zoomData, + leftEye, /*usePreviewCrop*/true); + + api2Face = faceRect.toFace(face.id, leftEye, rightEye, mouth); + } else { + api2Face = faceRect.toFace(); + } + + return api2Face; + } + + private static Point convertCameraPointToActiveArrayPoint( + Rect activeArray, ZoomData zoomData, Point point, boolean usePreviewCrop) { + Rect pointedRect = new Rect(point.x, point.y, point.x, point.y); + Camera.Area pointedArea = new Area(pointedRect, /*weight*/1); + + WeightedRectangle adjustedRect = + convertCameraAreaToActiveArrayRectangle(activeArray, + zoomData, pointedArea, usePreviewCrop); + + Point transformedPoint = new Point(adjustedRect.rect.left, adjustedRect.rect.top); + + return transformedPoint; + } + private static WeightedRectangle convertCameraAreaToActiveArrayRectangle( Rect activeArray, ZoomData zoomData, Camera.Area area, boolean usePreviewCrop) { Rect previewCrop = zoomData.previewCrop; diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java index c556c32..ec233da7 100644 --- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java +++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java @@ -39,7 +39,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.TimeUnit; import static com.android.internal.util.Preconditions.*; @@ -55,18 +54,23 @@ import static com.android.internal.util.Preconditions.*; * - An {@link CameraDeviceState} state machine that manages the callbacks for various operations. * </p> */ +@SuppressWarnings("deprecation") public class RequestThreadManager { private final String TAG; private final int mCameraId; private final RequestHandlerThread mRequestThread; private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG); + // For slightly more spammy messages that will get repeated every frame + private static final boolean VERBOSE = + Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.VERBOSE); private final Camera mCamera; private final CameraCharacteristics mCharacteristics; private final CameraDeviceState mDeviceState; private final CaptureCollector mCaptureCollector; private final LegacyFocusStateMapper mFocusStateMapper; + private final LegacyFaceDetectMapper mFaceDetectMapper; private static final int MSG_CONFIGURE_OUTPUTS = 1; private static final int MSG_SUBMIT_CAPTURE_REQUEST = 2; @@ -219,6 +223,9 @@ public class RequestThreadManager { }; private void stopPreview() { + if (VERBOSE) { + Log.v(TAG, "stopPreview - preview running? " + mPreviewRunning); + } if (mPreviewRunning) { mCamera.stopPreview(); mPreviewRunning = false; @@ -226,14 +233,18 @@ public class RequestThreadManager { } private void startPreview() { + if (VERBOSE) { + Log.v(TAG, "startPreview - preview running? " + mPreviewRunning); + } if (!mPreviewRunning) { + // XX: CameraClient:;startPreview is not getting called after a stop mCamera.startPreview(); mPreviewRunning = true; } } - private void doJpegCapture(RequestHolder request) throws IOException { - if (DEBUG) Log.d(TAG, "doJpegCapture"); + private void doJpegCapturePrepare(RequestHolder request) throws IOException { + if (DEBUG) Log.d(TAG, "doJpegCapturePrepare - preview running? " + mPreviewRunning); if (!mPreviewRunning) { if (DEBUG) Log.d(TAG, "doJpegCapture - create fake surface"); @@ -242,11 +253,20 @@ public class RequestThreadManager { mCamera.setPreviewTexture(mDummyTexture); startPreview(); } + } + + private void doJpegCapture(RequestHolder request) { + if (DEBUG) Log.d(TAG, "doJpegCapturePrepare"); + mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback); mPreviewRunning = false; } private void doPreviewCapture(RequestHolder request) throws IOException { + if (VERBOSE) { + Log.v(TAG, "doPreviewCapture - preview running? " + mPreviewRunning); + } + if (mPreviewRunning) { return; // Already running } @@ -264,7 +284,20 @@ public class RequestThreadManager { } private void configureOutputs(Collection<Surface> outputs) throws IOException { + if (DEBUG) { + String outputsStr = outputs == null ? "null" : (outputs.size() + " surfaces"); + + Log.d(TAG, "configureOutputs with " + outputsStr); + } + stopPreview(); + /* + * Try to release the previous preview's surface texture earlier if we end up + * using a different one; this also reduces the likelihood of getting into a deadlock + * when disconnecting from the old previous texture at a later time. + */ + mCamera.setPreviewTexture(/*surfaceTexture*/null); + if (mGLThreadManager != null) { mGLThreadManager.waitUntilStarted(); mGLThreadManager.ignoreNewFrames(); @@ -305,7 +338,6 @@ public class RequestThreadManager { } mParams.setPreviewFpsRange(bestRange[Camera.Parameters.PREVIEW_FPS_MIN_INDEX], bestRange[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]); - mParams.setRecordingHint(true); if (mPreviewOutputs.size() > 0) { List<Size> outputSizes = new ArrayList<>(outputs.size()); @@ -575,7 +607,6 @@ public class RequestThreadManager { Log.e(TAG, "Interrupted while waiting for requests to complete."); } mDeviceState.setIdle(); - stopPreview(); break; } else { // Queue another capture if we did not get the last burst. @@ -613,10 +644,6 @@ public class RequestThreadManager { } } - // Unconditionally process AF triggers, since they're non-idempotent - // - must be done after setting the most-up-to-date AF mode - mFocusStateMapper.processRequestTriggers(request, mParams); - try { boolean success = mCaptureCollector.queueRequest(holder, mLastRequest, JPEG_FRAME_TIMEOUT, TimeUnit.MILLISECONDS); @@ -624,6 +651,8 @@ public class RequestThreadManager { if (!success) { Log.e(TAG, "Timed out while queueing capture request."); } + // Starting the preview needs to happen before enabling + // face detection or auto focus if (holder.hasPreviewTargets()) { doPreviewCapture(holder); } @@ -635,12 +664,33 @@ public class RequestThreadManager { Log.e(TAG, "Timed out waiting for prior requests to complete."); } mReceivedJpeg.close(); + doJpegCapturePrepare(holder); + if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) { + // TODO: report error to CameraDevice + Log.e(TAG, "Hit timeout for jpeg callback!"); + } + } + + /* + * Do all the actions that require a preview to have been started + */ + + // Toggle face detection on/off + // - do this before AF to give AF a chance to use faces + mFaceDetectMapper.processFaceDetectMode(request, /*in*/mParams); + + // Unconditionally process AF triggers, since they're non-idempotent + // - must be done after setting the most-up-to-date AF mode + mFocusStateMapper.processRequestTriggers(request, mParams); + + if (holder.hasJpegTargets()) { doJpegCapture(holder); if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) { // TODO: report error to CameraDevice Log.e(TAG, "Hit timeout for jpeg callback!"); } } + } catch (IOException e) { // TODO: report error to CameraDevice throw new IOError(e); @@ -677,6 +727,8 @@ public class RequestThreadManager { mLastRequest, timestampMutable.value); // Update AF state mFocusStateMapper.mapResultTriggers(result); + // Update detected faces list + mFaceDetectMapper.mapResultFaces(result, mLastRequest); mDeviceState.setCaptureResult(holder, result); } @@ -731,6 +783,7 @@ public class RequestThreadManager { TAG = name; mDeviceState = checkNotNull(deviceState, "deviceState must not be null"); mFocusStateMapper = new LegacyFocusStateMapper(mCamera); + mFaceDetectMapper = new LegacyFaceDetectMapper(mCamera, mCharacteristics); mCaptureCollector = new CaptureCollector(MAX_IN_FLIGHT_REQUESTS, mDeviceState); mRequestThread = new RequestHandlerThread(name, mRequestHandlerCb); } diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java index 1efabb1..2e6b9ae 100644 --- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -813,6 +813,7 @@ public final class StreamConfigurationMap { switch (format) { case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: case HAL_PIXEL_FORMAT_BLOB: + case HAL_PIXEL_FORMAT_RAW_OPAQUE: return format; case ImageFormat.JPEG: throw new IllegalArgumentException( @@ -843,12 +844,6 @@ public final class StreamConfigurationMap { * @throws IllegalArgumentException if the format was not user-defined */ static int checkArgumentFormat(int format) { - // TODO: remove this hack , CTS shouldn't have been using internal constants - if (format == HAL_PIXEL_FORMAT_RAW_OPAQUE) { - Log.w(TAG, "RAW_OPAQUE is not yet a published format; allowing it anyway"); - return format; - } - if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { throw new IllegalArgumentException(String.format( "format 0x%x was not defined in either ImageFormat or PixelFormat", format)); diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index d4e6df5..51b7229 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -96,11 +96,9 @@ public final class DisplayManager { * windows on the display and the system may mirror the contents of other displays * onto it. * </p><p> - * Creating a public virtual display requires the - * {@link android.Manifest.permission#CAPTURE_VIDEO_OUTPUT} - * or {@link android.Manifest.permission#CAPTURE_SECURE_VIDEO_OUTPUT} permission. - * These permissions are reserved for use by system components and are not available to - * third-party applications. + * Creating a public virtual display that isn't restricted to own-content only implicitly + * creates an auto-mirroring display. See {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR} for + * restrictions on who is allowed to create an auto-mirroring display. * </p> * * <h3>Private virtual displays</h3> @@ -108,6 +106,8 @@ public final class DisplayManager { * When this flag is not set, the virtual display is private as defined by the * {@link Display#FLAG_PRIVATE} display flag. * </p> + * + * <p> * A private virtual display belongs to the application that created it. * Only the a owner of a private virtual display is allowed to place windows upon it. * The private virtual display also does not participate in display mirroring: it will @@ -115,10 +115,11 @@ public final class DisplayManager { * be mirrored elsewhere. More precisely, the only processes that are allowed to * enumerate or interact with the private display are those that have the same UID as the * application that originally created the private virtual display. - * </p> + * </p> * * @see #createVirtualDisplay * @see #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY + * @see #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR */ public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1 << 0; @@ -187,29 +188,51 @@ public final class DisplayManager { * will be blanked instead if it has no windows. * </p> * + * <p> + * This flag is mutually exclusive with {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. If both + * flags are specified then the own-content only behavior will be applied. + * </p> + * + * <p> + * This behavior of this flag is implied whenever neither {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC} + * nor {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR} have been set. This flag is only required to + * override the default behavior when creating a public display. + * </p> + * * @see #createVirtualDisplay */ public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 1 << 3; /** - * Virtual display flag: Indicates that the display is being created for - * the purpose of screen sharing. This implies - * VIRTUAL_DISPLAY_FLAG_PRIVATE. Other flags are not allowed (especially - * not VIRTUAL_DISPLAY_FLAG_PUBLIC or PRESENTATION). + * Virtual display flag: Allows content to be mirrored on private displays when no content is + * being shown. + * + * <p> + * This flag is mutually exclusive with {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}. + * If both flags are specified then the own-content only behavior will be applied. + * </p> * * <p> - * Requires screen share permission for use. + * The behavior of this flag is implied whenever {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC} is set + * and {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY} has not been set. This flag is only + * required to override the default behavior when creating a private display. * </p> * * <p> - * While a display of this type exists, the system will show some sort of - * notification to the user indicating that the screen is being shared. + * Creating an auto-mirroing virtual display requires the + * {@link android.Manifest.permission#CAPTURE_VIDEO_OUTPUT} + * or {@link android.Manifest.permission#CAPTURE_SECURE_VIDEO_OUTPUT} permission. + * These permissions are reserved for use by system components and are not available to + * third-party applications. + * + * Alternatively, an appropriate {@link MediaProjection} may be used to create an + * auto-mirroring virtual display. * </p> * * @see #createVirtualDisplay */ - public static final int VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE = 1 << 4; + public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 1 << 4; /** @hide */ public DisplayManager(Context context) { @@ -489,7 +512,7 @@ public final class DisplayManager { * @param flags A combination of virtual display flags: * {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}, {@link #VIRTUAL_DISPLAY_FLAG_PRESENTATION}, * {@link #VIRTUAL_DISPLAY_FLAG_SECURE}, {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, - * or {@link #VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE}. + * or {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. * @param callbacks Callbacks to call when the state of the {@link VirtualDisplay} changes * @param handler The handler on which the listener should be invoked, or null * if the listener should be invoked on the calling thread's looper. diff --git a/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl b/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl index f279668..2f6dbe7 100644 --- a/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl +++ b/core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl @@ -34,4 +34,12 @@ oneway interface IRecognitionStatusCallback { * @param status The error code that was seen. */ void onError(int status); + /** + * Called when the recognition is paused temporarily for some reason. + */ + void onRecognitionPaused(); + /** + * Called when the recognition is resumed after it was temporarily paused. + */ + void onRecognitionResumed(); }
\ No newline at end of file diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index 786439e..3f18519 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -16,6 +16,7 @@ package android.net; +import android.annotation.SystemApi; import android.app.DownloadManager; import android.app.backup.BackupManager; import android.content.Context; @@ -127,6 +128,16 @@ public class TrafficStats { } /** + * System API for backup-related support components to tag network traffic + * appropriately. + * @hide + */ + @SystemApi + public static void setThreadStatsTagBackup() { + setThreadStatsTag(TAG_SYSTEM_BACKUP); + } + + /** * Get the active tag used when accounting {@link Socket} traffic originating * from the current thread. Only one active tag per thread is supported. * {@link #tagSocket(Socket)}. @@ -160,11 +171,13 @@ public class TrafficStats { * * @hide */ + @SystemApi public static void setThreadStatsUid(int uid) { NetworkManagementSocketTagger.setThreadSocketStatsUid(uid); } /** {@hide} */ + @SystemApi public static void clearThreadStatsUid() { NetworkManagementSocketTagger.setThreadSocketStatsUid(-1); } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 545b19a..7d086e1 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -33,6 +33,7 @@ import android.util.Printer; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.TimeUtils; +import android.view.Display; import com.android.internal.os.BatterySipper; import com.android.internal.os.BatteryStatsHelper; @@ -158,7 +159,7 @@ public abstract class BatteryStats implements Parcelable { private static final long BYTES_PER_MB = 1048576; // 1024^2 private static final long BYTES_PER_GB = 1073741824; //1024^3 - + private static final String VERSION_DATA = "vers"; private static final String UID_DATA = "uid"; private static final String APK_DATA = "apk"; private static final String PROCESS_DATA = "pr"; @@ -1462,6 +1463,21 @@ public abstract class BatteryStats implements Parcelable { public abstract long getStartClockTime(); /** + * Return platform version tag that we were running in when the battery stats started. + */ + public abstract String getStartPlatformVersion(); + + /** + * Return platform version tag that we were running in when the battery stats ended. + */ + public abstract String getEndPlatformVersion(); + + /** + * Return the internal version code of the parcelled format. + */ + public abstract int getParcelVersion(); + + /** * Return whether we are currently running on battery. */ public abstract boolean getIsOnBattery(); @@ -1596,6 +1612,27 @@ public abstract class BatteryStats implements Parcelable { */ public abstract long computeBatteryTimeRemaining(long curTime); + // The part of a step duration that is the actual time. + public static final long STEP_LEVEL_TIME_MASK = 0x000000ffffffffffL; + + // Bits in a step duration that are the new battery level we are at. + public static final long STEP_LEVEL_LEVEL_MASK = 0x0000ff0000000000L; + public static final long STEP_LEVEL_LEVEL_SHIFT = 40; + + // Bits in a step duration that are the initial mode we were in at that step. + public static final long STEP_LEVEL_INITIAL_MODE_MASK = 0x00ff000000000000L; + public static final long STEP_LEVEL_INITIAL_MODE_SHIFT = 48; + + // Bits in a step duration that indicate which modes changed during that step. + public static final long STEP_LEVEL_MODIFIED_MODE_MASK = 0xff00000000000000L; + public static final long STEP_LEVEL_MODIFIED_MODE_SHIFT = 56; + + // Step duration mode: the screen is on, off, dozed, etc; value is Display.STATE_* - 1. + public static final int STEP_LEVEL_MODE_SCREEN_STATE = 0x03; + + // Step duration mode: power save is on. + public static final int STEP_LEVEL_MODE_POWER_SAVE = 0x04; + /** * Return the historical number of discharge steps we currently have. */ @@ -1986,7 +2023,8 @@ public abstract class BatteryStats implements Parcelable { } else { dumpLine(pw, 0 /* uid */, category, BATTERY_DISCHARGE_DATA, getLowDischargeAmountSinceCharge(), getHighDischargeAmountSinceCharge(), - getDischargeAmountScreenOn(), getDischargeAmountScreenOff()); + getDischargeAmountScreenOnSinceCharge(), + getDischargeAmountScreenOffSinceCharge()); } if (reqUid < 0) { @@ -3620,14 +3658,60 @@ public abstract class BatteryStats implements Parcelable { if (!checkin) { pw.println(header); } - String[] lineArgs = new String[1]; + String[] lineArgs = new String[4]; for (int i=0; i<count; i++) { + long duration = steps[i] & STEP_LEVEL_TIME_MASK; + int level = (int)((steps[i] & STEP_LEVEL_LEVEL_MASK) + >> STEP_LEVEL_LEVEL_SHIFT); + long initMode = (steps[i] & STEP_LEVEL_INITIAL_MODE_MASK) + >> STEP_LEVEL_INITIAL_MODE_SHIFT; + long modMode = (steps[i] & STEP_LEVEL_MODIFIED_MODE_MASK) + >> STEP_LEVEL_MODIFIED_MODE_SHIFT; if (checkin) { - lineArgs[0] = Long.toString(steps[i]); + lineArgs[0] = Long.toString(duration); + lineArgs[1] = Integer.toString(level); + if ((modMode&STEP_LEVEL_MODE_SCREEN_STATE) == 0) { + switch ((int)(initMode&STEP_LEVEL_MODE_SCREEN_STATE) + 1) { + case Display.STATE_OFF: lineArgs[2] = "s-"; break; + case Display.STATE_ON: lineArgs[2] = "s+"; break; + case Display.STATE_DOZE: lineArgs[2] = "sd"; break; + case Display.STATE_DOZE_SUSPEND: lineArgs[2] = "sds"; break; + default: lineArgs[1] = "?"; break; + } + } else { + lineArgs[2] = ""; + } + if ((modMode&STEP_LEVEL_MODE_POWER_SAVE) == 0) { + lineArgs[3] = (initMode&STEP_LEVEL_MODE_POWER_SAVE) != 0 ? "p+" : "p-"; + } else { + lineArgs[3] = ""; + } dumpLine(pw, 0 /* uid */, "i" /* category */, header, (Object[])lineArgs); } else { pw.print(" #"); pw.print(i); pw.print(": "); - TimeUtils.formatDuration(steps[i], pw); + TimeUtils.formatDuration(duration, pw); + pw.print(" to "); pw.print(level); + boolean haveModes = false; + if ((modMode&STEP_LEVEL_MODE_SCREEN_STATE) == 0) { + pw.print(" ("); + switch ((int)(initMode&STEP_LEVEL_MODE_SCREEN_STATE) + 1) { + case Display.STATE_OFF: pw.print("screen-off"); break; + case Display.STATE_ON: pw.print("screen-on"); break; + case Display.STATE_DOZE: pw.print("screen-doze"); break; + case Display.STATE_DOZE_SUSPEND: pw.print("screen-doze-suspend"); break; + default: lineArgs[1] = "screen-?"; break; + } + haveModes = true; + } + if ((modMode&STEP_LEVEL_MODE_POWER_SAVE) == 0) { + pw.print(haveModes ? ", " : " ("); + pw.print((initMode&STEP_LEVEL_MODE_POWER_SAVE) != 0 + ? "power-save-on" : "power-save-off"); + haveModes = true; + } + if (haveModes) { + pw.print(")"); + } pw.println(); } } @@ -3808,6 +3892,9 @@ public abstract class BatteryStats implements Parcelable { if (didPid) { pw.println(); } + } + + if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) { if (dumpDurationSteps(pw, "Discharge step durations:", getDischargeStepDurationsArray(), getNumDischargeStepDurations(), false)) { long timeRemaining = computeBatteryTimeRemaining(SystemClock.elapsedRealtime()); @@ -3828,9 +3915,6 @@ public abstract class BatteryStats implements Parcelable { } pw.println(); } - } - - if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) { pw.println("Statistics since last charge:"); pw.println(" System starts: " + getStartCount() + ", currently on battery: " + getIsOnBattery()); @@ -3847,7 +3931,10 @@ public abstract class BatteryStats implements Parcelable { public void dumpCheckinLocked(Context context, PrintWriter pw, List<ApplicationInfo> apps, int flags, long histStart) { prepareForDumpLocked(); - + + dumpLine(pw, 0 /* uid */, "i" /* category */, VERSION_DATA, + "10", getParcelVersion(), getStartPlatformVersion(), getEndPlatformVersion()); + long now = getHistoryBaseTime() + SystemClock.elapsedRealtime(); final boolean filtering = @@ -3908,7 +3995,7 @@ public abstract class BatteryStats implements Parcelable { } } } - if (!filtering) { + if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) { dumpDurationSteps(pw, DISCHARGE_STEP_DATA, getDischargeStepDurationsArray(), getNumDischargeStepDurations(), true); String[] lineArgs = new String[1]; @@ -3926,8 +4013,6 @@ public abstract class BatteryStats implements Parcelable { dumpLine(pw, 0 /* uid */, "i" /* category */, CHARGE_TIME_REMAIN_DATA, (Object[])lineArgs); } - } - if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) { dumpCheckinLocked(context, pw, STATS_SINCE_CHARGED, -1); } if (!filtering || (flags&DUMP_UNPLUGGED_ONLY) != 0) { diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index c43644a..713fcd8 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -18,7 +18,6 @@ package android.os; import android.os.Bundle; -import android.os.ParcelFileDescriptor; import android.content.pm.UserInfo; import android.content.RestrictionEntry; import android.graphics.Bitmap; diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index afbf983..5ce9d5c 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.SystemApi; import android.util.SparseArray; import java.io.PrintWriter; @@ -237,6 +238,16 @@ public final class UserHandle implements Parcelable { return getUserId(Process.myUid()); } + /** + * Returns true if this UserHandle refers to the owner user; false otherwise. + * @return true if this UserHandle refers to the owner user; false otherwise. + * @hide + */ + @SystemApi + public final boolean isOwner() { + return this.equals(OWNER); + } + /** @hide */ public UserHandle(int h) { mHandle = h; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index 04e6227..3087506 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -864,15 +864,6 @@ public class UserManager { } /** - * Kept during L development to simplify updating unbundled apps. - * TODO: Remove after 2014-08-04 - * @hide - */ - public String getBadgedLabelForUser(String label, UserHandle user) { - return (String) getBadgedLabelForUser((CharSequence) label, user); - } - - /** * If the target user is a managed profile of the calling user or the caller * is itself a managed profile, then this returns a drawable to use as a small * icon to include in a view to distinguish it from the original icon. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index de1df56..733ab32 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2375,6 +2375,16 @@ public final class Settings { public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing"; /** + * Whether automatic routing of system audio to USB audio peripheral is disabled. + * The value is boolean (1 or 0), where 1 means automatic routing is disabled, + * and 0 means automatic routing is enabled. + * + * @hide + */ + public static final String USB_AUDIO_AUTOMATIC_ROUTING_DISABLED = + "usb_audio_automatic_routing_disabled"; + + /** * Whether the audible DTMF tones are played by the dialer when dialing. The value is * boolean (1 or 0). */ diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index b0ff947..4860b44 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -161,6 +161,8 @@ public class AlwaysOnHotwordDetector { private static final int MSG_AVAILABILITY_CHANGED = 1; private static final int MSG_HOTWORD_DETECTED = 2; private static final int MSG_DETECTION_ERROR = 3; + private static final int MSG_DETECTION_PAUSE = 4; + private static final int MSG_DETECTION_RESUME = 5; private final String mText; private final String mLocale; @@ -180,21 +182,27 @@ public class AlwaysOnHotwordDetector { private int mAvailability = STATE_NOT_READY; /** - * Details of the audio that triggered the keyphrase. + * Additional payload for {@link Callback#onDetected}. */ - public static class TriggerAudio { + public static class EventPayload { /** - * Format of {@code data}. + * Indicates if {@code data} is the audio that triggered the keyphrase. */ - @NonNull + public final boolean isTriggerAudio; + /** + * Format of {@code data}. May be null if {@code isTriggerAudio} is false. + */ + @Nullable public final AudioFormat audioFormat; /** - * Raw audio data that triggered they keyphrase. + * Raw data associated with the event. + * This is the audio that triggered the keyphrase if {@code isTriggerAudio} is true. */ - @NonNull + @Nullable public final byte[] data; - private TriggerAudio(AudioFormat _audioFormat, byte[] _data) { + private EventPayload(boolean _isTriggerAudio, AudioFormat _audioFormat, byte[] _data) { + isTriggerAudio = _isTriggerAudio; audioFormat = _audioFormat; data = _data; } @@ -224,14 +232,27 @@ public class AlwaysOnHotwordDetector { * Clients should start a recognition again once they are done handling this * detection. * - * @param triggerAudio Optional trigger audio data, if it was requested during + * @param eventPayload Payload data for the detection event. + * This may contain the trigger audio, if requested when calling * {@link AlwaysOnHotwordDetector#startRecognition(int)}. */ - void onDetected(@Nullable TriggerAudio triggerAudio); + void onDetected(@NonNull EventPayload eventPayload); /** * Called when the detection fails due to an error. */ void onError(); + /** + * Called when the recognition is paused temporarily for some reason. + * This is an informational callback, and the clients shouldn't be doing anything here + * except showing an indication on their UI if they have to. + */ + void onRecognitionPaused(); + /** + * Called when the recognition is resumed after it was temporarily paused. + * This is an informational callback, and the clients shouldn't be doing anything here + * except showing an indication on their UI if they have to. + */ + void onRecognitionResumed(); } /** @@ -487,22 +508,13 @@ public class AlwaysOnHotwordDetector { @Override public void onDetected(KeyphraseRecognitionEvent event) { if (DBG) { - Slog.d(TAG, "OnDetected(" + event + ")"); + Slog.d(TAG, "onDetected(" + event + ")"); } else { Slog.i(TAG, "onDetected"); } - Message message = Message.obtain(mHandler, MSG_HOTWORD_DETECTED); - // FIXME: Check whether the event contains trigger data or not. - // FIXME: Read the audio format from the event. - if (event.data != null) { - AudioFormat audioFormat = new AudioFormat.Builder() - .setChannelMask(AudioFormat.CHANNEL_IN_MONO) - .setEncoding(AudioFormat.ENCODING_PCM_16BIT) - .setSampleRate(16000) - .build(); - message.obj = new TriggerAudio(audioFormat, event.data); - } - message.sendToTarget(); + Message.obtain(mHandler, MSG_HOTWORD_DETECTED, + new EventPayload(event.triggerInData, event.captureFormat, event.data)) + .sendToTarget(); } @Override @@ -510,6 +522,18 @@ public class AlwaysOnHotwordDetector { Slog.i(TAG, "onError: " + status); mHandler.sendEmptyMessage(MSG_DETECTION_ERROR); } + + @Override + public void onRecognitionPaused() { + Slog.i(TAG, "onRecognitionPaused"); + mHandler.sendEmptyMessage(MSG_DETECTION_PAUSE); + } + + @Override + public void onRecognitionResumed() { + Slog.i(TAG, "onRecognitionResumed"); + mHandler.sendEmptyMessage(MSG_DETECTION_RESUME); + } } class MyHandler extends Handler { @@ -527,11 +551,17 @@ public class AlwaysOnHotwordDetector { mExternalCallback.onAvailabilityChanged(msg.arg1); break; case MSG_HOTWORD_DETECTED: - mExternalCallback.onDetected((TriggerAudio) msg.obj); + mExternalCallback.onDetected((EventPayload) msg.obj); break; case MSG_DETECTION_ERROR: mExternalCallback.onError(); break; + case MSG_DETECTION_PAUSE: + mExternalCallback.onRecognitionPaused(); + break; + case MSG_DETECTION_RESUME: + mExternalCallback.onRecognitionResumed(); + break; default: super.handleMessage(msg); } diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index e3e328c..19d14bf 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -448,6 +448,7 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback { } void doDestroy() { + onDestroy(); if (mInitialized) { mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener( mInsetsComputer); diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 910f862..a410aa9 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -386,9 +386,6 @@ class GLES20Canvas extends HardwareCanvas { @Override public void scale(float sx, float sy) { - // TODO: remove - if (sx > 1000000 || sy > 1000000) throw new IllegalArgumentException("invalid scales passed " + sx + ", " + sy); - nScale(mRenderer, sx, sy); } diff --git a/core/java/android/view/RenderNode.java b/core/java/android/view/RenderNode.java index eee4973..099f153 100644 --- a/core/java/android/view/RenderNode.java +++ b/core/java/android/view/RenderNode.java @@ -598,9 +598,6 @@ public class RenderNode { * @see #getScaleX() */ public boolean setScaleX(float scaleX) { - if (scaleX > 1000000) { - throw new IllegalArgumentException("Invalid scale: " + scaleX); - } return nSetScaleX(mNativeRenderNode, scaleX); } @@ -622,9 +619,6 @@ public class RenderNode { * @see #getScaleY() */ public boolean setScaleY(float scaleY) { - if (scaleY > 1000000) { - throw new IllegalArgumentException("Invalid scale: " + scaleY); - } return nSetScaleY(mNativeRenderNode, scaleY); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 328d67c..8c2048d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -4649,16 +4649,18 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * otherwise is returned. */ public boolean performClick() { - sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); - - ListenerInfo li = mListenerInfo; + final boolean result; + final ListenerInfo li = mListenerInfo; if (li != null && li.mOnClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); li.mOnClickListener.onClick(this); - return true; + result = true; + } else { + result = false; } - return false; + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); + return result; } /** diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 1832cc3..571a8f0 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -266,7 +266,6 @@ public final class WindowInsets { * {@link View#fitSystemWindows(android.graphics.Rect)}.</p> * * @return true if the insets have been fully consumed. - * @hide Pending API */ public boolean isConsumed() { return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed && mStableInsetsConsumed; diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index d93ca2c..23894ee 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -175,7 +175,9 @@ public final class WebViewFactory { String[] nativePaths = null; try { nativePaths = getWebViewNativeLibraryPaths(); - } catch (PackageManager.NameNotFoundException e) { + } catch (Throwable t) { + // Log and discard errors at this stage as we must not crash the system server. + Log.e(LOGTAG, "error preparing webview native library", t); } prepareWebViewInSystemServer(nativePaths); } @@ -201,35 +203,37 @@ public final class WebViewFactory { String[] nativeLibs = null; try { nativeLibs = WebViewFactory.getWebViewNativeLibraryPaths(); - } catch (PackageManager.NameNotFoundException e) { - } - - if (nativeLibs != null) { - long newVmSize = 0L; - - for (String path : nativeLibs) { - if (DEBUG) Log.d(LOGTAG, "Checking file size of " + path); - if (path == null) continue; - File f = new File(path); - if (f.exists()) { - long length = f.length(); - if (length > newVmSize) { - newVmSize = length; + if (nativeLibs != null) { + long newVmSize = 0L; + + for (String path : nativeLibs) { + if (DEBUG) Log.d(LOGTAG, "Checking file size of " + path); + if (path == null) continue; + File f = new File(path); + if (f.exists()) { + long length = f.length(); + if (length > newVmSize) { + newVmSize = length; + } } } - } - if (DEBUG) { - Log.v(LOGTAG, "Based on library size, need " + newVmSize + - " bytes of address space."); + if (DEBUG) { + Log.v(LOGTAG, "Based on library size, need " + newVmSize + + " bytes of address space."); + } + // The required memory can be larger than the file on disk (due to .bss), and an + // upgraded version of the library will likely be larger, so always attempt to + // reserve twice as much as we think to allow for the library to grow during this + // boot cycle. + newVmSize = Math.max(2 * newVmSize, CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES); + Log.d(LOGTAG, "Setting new address space to " + newVmSize); + SystemProperties.set(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY, + Long.toString(newVmSize)); } - // The required memory can be larger than the file on disk (due to .bss), and an - // upgraded version of the library will likely be larger, so always attempt to reserve - // twice as much as we think to allow for the library to grow during this boot cycle. - newVmSize = Math.max(2 * newVmSize, CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES); - Log.d(LOGTAG, "Setting new address space to " + newVmSize); - SystemProperties.set(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY, - Long.toString(newVmSize)); + } catch (Throwable t) { + // Log and discard errors at this stage as we must not crash the system server. + Log.e(LOGTAG, "error preparing webview native library", t); } prepareWebViewInSystemServer(nativeLibs); } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index aa0b94f..60ef693 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -3857,13 +3857,17 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te firstChildTop == contentTop - mOverscrollDistance) || (mFirstPosition + childCount == mItemCount && lastChildBottom == contentBottom + mOverscrollDistance))) { - if (mFlingRunnable == null) { - mFlingRunnable = new FlingRunnable(); + if (!dispatchNestedPreFling(0, -initialVelocity)) { + if (mFlingRunnable == null) { + mFlingRunnable = new FlingRunnable(); + } + reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); + mFlingRunnable.start(-initialVelocity); + dispatchNestedFling(0, -initialVelocity, true); + } else { + mTouchMode = TOUCH_MODE_REST; + reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); } - reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); - - mFlingRunnable.start(-initialVelocity); - dispatchNestedFling(0, -initialVelocity, true); } else { mTouchMode = TOUCH_MODE_REST; reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE); @@ -3992,6 +3996,22 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te return super.onGenericMotionEvent(event); } + /** + * Initiate a fling with the given velocity. + * + * <p>Applications can use this method to manually initiate a fling as if the user + * initiated it via touch interaction.</p> + * + * @param velocityY Vertical velocity in pixels per second + */ + public void fling(int velocityY) { + if (mFlingRunnable == null) { + mFlingRunnable = new FlingRunnable(); + } + reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING); + mFlingRunnable.start(-velocityY); + } + @Override public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { return ((nestedScrollAxes & SCROLL_AXIS_VERTICAL) != 0); @@ -4029,7 +4049,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (mFlingRunnable == null) { mFlingRunnable = new FlingRunnable(); } - mFlingRunnable.start((int) velocityY); + if (!dispatchNestedPreFling(0, velocityY)) { + mFlingRunnable.start((int) velocityY); + } return true; } return dispatchNestedFling(velocityX, velocityY, consumed); diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index 74c66c8..d0a2eab 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -84,6 +84,9 @@ import libcore.icu.ICU; public class DatePicker extends FrameLayout { private static final String LOG_TAG = DatePicker.class.getSimpleName(); + private static final int MODE_SPINNER = 1; + private static final int MODE_CALENDAR = 2; + private final DatePickerDelegate mDelegate; /** @@ -120,24 +123,28 @@ public class DatePicker extends FrameLayout { final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DatePicker, defStyleAttr, defStyleRes); - final boolean legacyMode = a.getBoolean(R.styleable.DatePicker_legacyMode, true); + int mode = a.getInt(R.styleable.DatePicker_datePickerMode, MODE_SPINNER); a.recycle(); - if (legacyMode) { - mDelegate = createLegacyUIDelegate(context, attrs, defStyleAttr, defStyleRes); - } else { - mDelegate = createNewUIDelegate(context, attrs, defStyleAttr, defStyleRes); + switch (mode) { + case MODE_CALENDAR: + mDelegate = createCalendarUIDelegate(context, attrs, defStyleAttr, defStyleRes); + break; + case MODE_SPINNER: + default: + mDelegate = createSpinnerUIDelegate(context, attrs, defStyleAttr, defStyleRes); + break; } } - private DatePickerDelegate createLegacyUIDelegate(Context context, AttributeSet attrs, + private DatePickerDelegate createSpinnerUIDelegate(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - return new LegacyDatePickerDelegate(this, context, attrs, defStyleAttr, defStyleRes); + return new DatePickerSpinnerDelegate(this, context, attrs, defStyleAttr, defStyleRes); } - private DatePickerDelegate createNewUIDelegate(Context context, AttributeSet attrs, + private DatePickerDelegate createCalendarUIDelegate(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - return new android.widget.DatePickerDelegate(this, context, attrs, defStyleAttr, + return new DatePickerCalendarDelegate(this, context, attrs, defStyleAttr, defStyleRes); } @@ -454,7 +461,7 @@ public class DatePicker extends FrameLayout { /** * A delegate implementing the basic DatePicker */ - private static class LegacyDatePickerDelegate extends AbstractDatePickerDelegate { + private static class DatePickerSpinnerDelegate extends AbstractDatePickerDelegate { private static final String DATE_FORMAT = "MM/dd/yyyy"; @@ -500,7 +507,7 @@ public class DatePicker extends FrameLayout { private boolean mIsEnabled = DEFAULT_ENABLED_STATE; - LegacyDatePickerDelegate(DatePicker delegator, Context context, AttributeSet attrs, + DatePickerSpinnerDelegate(DatePicker delegator, Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(delegator, context); diff --git a/core/java/android/widget/DatePickerDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java index ddc565d..c0c76ac 100644 --- a/core/java/android/widget/DatePickerDelegate.java +++ b/core/java/android/widget/DatePickerCalendarDelegate.java @@ -52,7 +52,7 @@ import java.util.Locale; /** * A delegate for picking up a date (day / month / year). */ -class DatePickerDelegate extends DatePicker.AbstractDatePickerDelegate implements +class DatePickerCalendarDelegate extends DatePicker.AbstractDatePickerDelegate implements View.OnClickListener, DatePickerController { private static final int UNINITIALIZED = -1; @@ -113,7 +113,7 @@ class DatePickerDelegate extends DatePicker.AbstractDatePickerDelegate implement private HashSet<OnDateChangedListener> mListeners = new HashSet<OnDateChangedListener>(); - public DatePickerDelegate(DatePicker delegator, Context context, AttributeSet attrs, + public DatePickerCalendarDelegate(DatePicker delegator, Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(delegator, context); diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index 33cc66e..d263625 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -351,6 +351,7 @@ public class GridView extends AbsListView { final int selectedPosition = mSelectedPosition; View child = null; + final int nextChildDir = isLayoutRtl ? -1 : +1; for (int pos = startPos; pos < last; pos++) { // is this the selected item? boolean selected = pos == selectedPosition; @@ -359,9 +360,9 @@ public class GridView extends AbsListView { final int where = flow ? -1 : pos - startPos; child = makeAndAddView(pos, y, flow, nextLeft, selected, where); - nextLeft += (isLayoutRtl ? -1 : +1) * columnWidth; + nextLeft += nextChildDir * columnWidth; if (pos < last - 1) { - nextLeft += horizontalSpacing; + nextLeft += nextChildDir * horizontalSpacing; } if (selected && (hasFocus || inClick)) { diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java index 546cc5f..f1aaa4d 100644 --- a/core/java/android/widget/MediaController.java +++ b/core/java/android/widget/MediaController.java @@ -17,6 +17,7 @@ package android.widget; import android.content.Context; +import android.content.res.Resources; import android.graphics.PixelFormat; import android.media.AudioManager; import android.os.Handler; @@ -55,7 +56,7 @@ import java.util.Locale; * <p> * Functions like show() and hide() have no effect when MediaController * is created in an xml layout. - * + * * MediaController will hide and * show the buttons according to these rules: * <ul> @@ -70,32 +71,34 @@ import java.util.Locale; */ public class MediaController extends FrameLayout { - private MediaPlayerControl mPlayer; - private Context mContext; - private View mAnchor; - private View mRoot; - private WindowManager mWindowManager; - private Window mWindow; - private View mDecor; + private MediaPlayerControl mPlayer; + private Context mContext; + private View mAnchor; + private View mRoot; + private WindowManager mWindowManager; + private Window mWindow; + private View mDecor; private WindowManager.LayoutParams mDecorLayoutParams; - private ProgressBar mProgress; - private TextView mEndTime, mCurrentTime; - private boolean mShowing; - private boolean mDragging; - private static final int sDefaultTimeout = 3000; - private static final int FADE_OUT = 1; - private static final int SHOW_PROGRESS = 2; - private boolean mUseFastForward; - private boolean mFromXml; - private boolean mListenersSet; + private ProgressBar mProgress; + private TextView mEndTime, mCurrentTime; + private boolean mShowing; + private boolean mDragging; + private static final int sDefaultTimeout = 3000; + private static final int FADE_OUT = 1; + private static final int SHOW_PROGRESS = 2; + private boolean mUseFastForward; + private boolean mFromXml; + private boolean mListenersSet; private View.OnClickListener mNextListener, mPrevListener; - StringBuilder mFormatBuilder; - Formatter mFormatter; - private ImageButton mPauseButton; - private ImageButton mFfwdButton; - private ImageButton mRewButton; - private ImageButton mNextButton; - private ImageButton mPrevButton; + StringBuilder mFormatBuilder; + Formatter mFormatter; + private ImageButton mPauseButton; + private ImageButton mFfwdButton; + private ImageButton mRewButton; + private ImageButton mNextButton; + private ImageButton mPrevButton; + private CharSequence mPlayDescription; + private CharSequence mPauseDescription; public MediaController(Context context, AttributeSet attrs) { super(context, attrs); @@ -132,7 +135,7 @@ public class MediaController extends FrameLayout { mDecor.setOnTouchListener(mTouchListener); mWindow.setContentView(this); mWindow.setBackgroundDrawableResource(android.R.color.transparent); - + // While the media controller is up, the volume control keys should // affect the media stream type mWindow.setVolumeControlStream(AudioManager.STREAM_MUSIC); @@ -201,7 +204,7 @@ public class MediaController extends FrameLayout { return false; } }; - + public void setMediaPlayer(MediaPlayerControl player) { mPlayer = player; updatePausePlay(); @@ -249,6 +252,11 @@ public class MediaController extends FrameLayout { } private void initControllerView(View v) { + Resources res = mContext.getResources(); + mPlayDescription = res + .getText(com.android.internal.R.string.lockscreen_transport_play_description); + mPauseDescription = res + .getText(com.android.internal.R.string.lockscreen_transport_pause_description); mPauseButton = (ImageButton) v.findViewById(com.android.internal.R.id.pause); if (mPauseButton != null) { mPauseButton.requestFocus(); @@ -271,7 +279,7 @@ public class MediaController extends FrameLayout { } } - // By default these are hidden. They will be enabled when setPrevNextListeners() is called + // By default these are hidden. They will be enabled when setPrevNextListeners() is called mNextButton = (ImageButton) v.findViewById(com.android.internal.R.id.next); if (mNextButton != null && !mFromXml && !mListenersSet) { mNextButton.setVisibility(View.GONE); @@ -328,7 +336,7 @@ public class MediaController extends FrameLayout { // the buttons. } } - + /** * Show the controller on screen. It will go away * automatically after 'timeout' milliseconds of inactivity. @@ -347,7 +355,7 @@ public class MediaController extends FrameLayout { mShowing = true; } updatePausePlay(); - + // cause the progress bar to be updated even if mShowing // was already true. This happens, for example, if we're // paused with the progress bar showing the user hits play. @@ -359,7 +367,7 @@ public class MediaController extends FrameLayout { mHandler.sendMessageDelayed(msg, timeout); } } - + public boolean isShowing() { return mShowing; } @@ -525,8 +533,10 @@ public class MediaController extends FrameLayout { if (mPlayer.isPlaying()) { mPauseButton.setImageResource(com.android.internal.R.drawable.ic_media_pause); + mPauseButton.setContentDescription(mPauseDescription); } else { mPauseButton.setImageResource(com.android.internal.R.drawable.ic_media_play); + mPauseButton.setContentDescription(mPlayDescription); } } @@ -668,7 +678,7 @@ public class MediaController extends FrameLayout { if (mRoot != null) { installPrevNextListeners(); - + if (mNextButton != null && !mFromXml) { mNextButton.setVisibility(View.VISIBLE); } diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index fd04890..dd312a6 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -1577,9 +1577,11 @@ public class ScrollView extends FrameLayout { private void flingWithNestedDispatch(int velocityY) { final boolean canFling = (mScrollY > 0 || velocityY > 0) && (mScrollY < getScrollRange() || velocityY < 0); - dispatchNestedFling(0, velocityY, canFling); - if (canFling) { - fling(velocityY); + if (!dispatchNestedPreFling(0, velocityY)) { + dispatchNestedFling(0, velocityY, canFling); + if (canFling) { + fling(velocityY); + } } } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index a0f7baf..b162e54 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -17,6 +17,7 @@ package android.widget; import android.R; +import android.annotation.Nullable; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; @@ -222,6 +223,8 @@ import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; * @attr ref android.R.styleable#TextView_imeActionId * @attr ref android.R.styleable#TextView_editorExtras * @attr ref android.R.styleable#TextView_elegantTextHeight + * @attr ref android.R.styleable#TextView_letterSpacing + * @attr ref android.R.styleable#TextView_fontFeatureSettings */ @RemoteView public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { @@ -2702,7 +2705,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @see #setLetterSpacing(float) * @see Paint#setLetterSpacing - * @hide */ public float getLetterSpacing() { return mTextPaint.getLetterSpacing(); @@ -2716,7 +2718,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @see Paint#getLetterSpacing * * @attr ref android.R.styleable#TextView_letterSpacing - * @hide */ @android.view.RemotableViewMethod public void setLetterSpacing(float letterSpacing) { @@ -2736,8 +2737,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * * @see #setFontFeatureSettings(String) * @see Paint#setFontFeatureSettings - * @hide */ + @Nullable public String getFontFeatureSettings() { return mTextPaint.getFontFeatureSettings(); } @@ -2747,14 +2748,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * font-feature-settings attribute: * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings * + * @param fontFeatureSettings font feature settings represented as CSS compatible string * @see #getFontFeatureSettings() * @see Paint#getFontFeatureSettings * * @attr ref android.R.styleable#TextView_fontFeatureSettings - * @hide */ @android.view.RemotableViewMethod - public void setFontFeatureSettings(String fontFeatureSettings) { + public void setFontFeatureSettings(@Nullable String fontFeatureSettings) { if (fontFeatureSettings != mTextPaint.getFontFeatureSettings()) { mTextPaint.setFontFeatureSettings(fontFeatureSettings); diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java index 026a8ee..c488666 100644 --- a/core/java/android/widget/TimePicker.java +++ b/core/java/android/widget/TimePicker.java @@ -45,6 +45,9 @@ import java.util.Locale; */ @Widget public class TimePicker extends FrameLayout { + private static final int MODE_SPINNER = 1; + private static final int MODE_CLOCK = 2; + private final TimePickerDelegate mDelegate; /** @@ -77,15 +80,19 @@ public class TimePicker extends FrameLayout { final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.TimePicker, defStyleAttr, defStyleRes); - final boolean legacyMode = a.getBoolean(R.styleable.TimePicker_legacyMode, true); + int mode = a.getInt(R.styleable.TimePicker_timePickerMode, MODE_SPINNER); a.recycle(); - if (legacyMode) { - mDelegate = new LegacyTimePickerDelegate( - this, context, attrs, defStyleAttr, defStyleRes); - } else { - mDelegate = new android.widget.TimePickerDelegate( - this, context, attrs, defStyleAttr, defStyleRes); + switch (mode) { + case MODE_CLOCK: + mDelegate = new TimePickerSpinnerDelegate( + this, context, attrs, defStyleAttr, defStyleRes); + break; + case MODE_SPINNER: + default: + mDelegate = new TimePickerClockDelegate( + this, context, attrs, defStyleAttr, defStyleRes); + break; } } diff --git a/core/java/android/widget/LegacyTimePickerDelegate.java b/core/java/android/widget/TimePickerClockDelegate.java index 70db211..8102c4a 100644 --- a/core/java/android/widget/LegacyTimePickerDelegate.java +++ b/core/java/android/widget/TimePickerClockDelegate.java @@ -18,6 +18,7 @@ package android.widget; import android.content.Context; import android.content.res.Configuration; +import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Parcel; @@ -44,7 +45,7 @@ import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES; /** * A delegate implementing the basic TimePicker */ -class LegacyTimePickerDelegate extends TimePicker.AbstractTimePickerDelegate { +class TimePickerClockDelegate extends TimePicker.AbstractTimePickerDelegate { private static final boolean DEFAULT_ENABLED_STATE = true; @@ -100,8 +101,8 @@ class LegacyTimePickerDelegate extends TimePicker.AbstractTimePickerDelegate { } }; - public LegacyTimePickerDelegate(TimePicker delegator, Context context, AttributeSet attrs, - int defStyleAttr, int defStyleRes) { + public TimePickerClockDelegate(TimePicker delegator, Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { super(delegator, context); // process style attributes @@ -171,7 +172,10 @@ class LegacyTimePickerDelegate extends TimePicker.AbstractTimePickerDelegate { mMinuteSpinnerInput.setImeOptions(EditorInfo.IME_ACTION_NEXT); /* Get the localized am/pm strings and use them in the spinner */ - mAmPmStrings = new DateFormatSymbols().getAmPmStrings(); + final Resources res = context.getResources(); + final String amText = res.getString(R.string.time_picker_am_label); + final String pmText = res.getString(R.string.time_picker_pm_label); + mAmPmStrings = new String[] {amText, pmText}; // am/pm View amPmView = mDelegator.findViewById(R.id.amPm); diff --git a/core/java/android/widget/TimePickerDelegate.java b/core/java/android/widget/TimePickerSpinnerDelegate.java index c68619c..523965c 100644 --- a/core/java/android/widget/TimePickerDelegate.java +++ b/core/java/android/widget/TimePickerSpinnerDelegate.java @@ -51,7 +51,7 @@ import java.util.Locale; /** * A view for selecting the time of day, in either 24 hour or AM/PM mode. */ -class TimePickerDelegate extends TimePicker.AbstractTimePickerDelegate implements +class TimePickerSpinnerDelegate extends TimePicker.AbstractTimePickerDelegate implements RadialTimePickerView.OnValueSelectedListener { private static final String TAG = "TimePickerDelegate"; @@ -119,8 +119,8 @@ class TimePickerDelegate extends TimePicker.AbstractTimePickerDelegate implement private Calendar mTempCalendar; - public TimePickerDelegate(TimePicker delegator, Context context, AttributeSet attrs, - int defStyleAttr, int defStyleRes) { + public TimePickerSpinnerDelegate(TimePicker delegator, Context context, AttributeSet attrs, + int defStyleAttr, int defStyleRes) { super(delegator, context); // process style attributes @@ -134,6 +134,8 @@ class TimePickerDelegate extends TimePicker.AbstractTimePickerDelegate implement mSelectHours = res.getString(R.string.select_hours); mMinutePickerDescription = res.getString(R.string.minute_picker_description); mSelectMinutes = res.getString(R.string.select_minutes); + mAmText = res.getString(R.string.time_picker_am_label); + mPmText = res.getString(R.string.time_picker_pm_label); final int layoutResourceId = a.getResourceId(R.styleable.TimePicker_internalLayout, R.layout.time_picker_holo); @@ -182,10 +184,6 @@ class TimePickerDelegate extends TimePicker.AbstractTimePickerDelegate implement R.id.radial_picker); mDoneButton = (Button) mainView.findViewById(R.id.done_button); - String[] amPmTexts = new DateFormatSymbols().getAmPmStrings(); - mAmText = amPmTexts[0]; - mPmText = amPmTexts[1]; - setupListeners(); mAllowAutoAdvance = true; diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 5655506..40965f0 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -18,6 +18,7 @@ package com.android.internal.app; import com.android.internal.os.BatteryStatsImpl; +import android.os.ParcelFileDescriptor; import android.os.WorkSource; import android.telephony.DataConnectionRealTimeInfo; import android.telephony.SignalStrength; @@ -37,6 +38,8 @@ interface IBatteryStats { // Remaining methods are only used in Java. byte[] getStatistics(); + ParcelFileDescriptor getStatisticsStream(); + // Return the computed amount of time remaining on battery, in milliseconds. // Returns -1 if nothing could be computed. long computeBatteryTimeRemaining(); diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index 97e1102..d8dffe0 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -273,11 +273,15 @@ public class LocalTransport extends BackupTransport { @Override public int finishBackup() { - if (DEBUG) Log.v(TAG, "finishBackup()"); + if (DEBUG) Log.v(TAG, "finishBackup() of " + mFullTargetPackage); + return tearDownFullBackup(); + } + + // ------------------------------------------------------------------------------------ + // Full backup handling + + private int tearDownFullBackup() { if (mSocket != null) { - if (DEBUG) { - Log.v(TAG, "Concluding full backup of " + mFullTargetPackage); - } try { mFullBackupOutputStream.flush(); mFullBackupOutputStream.close(); @@ -286,7 +290,7 @@ public class LocalTransport extends BackupTransport { mSocket.close(); } catch (IOException e) { if (DEBUG) { - Log.w(TAG, "Exception caught in finishBackup()", e); + Log.w(TAG, "Exception caught in tearDownFullBackup()", e); } return TRANSPORT_ERROR; } finally { @@ -296,8 +300,9 @@ public class LocalTransport extends BackupTransport { return TRANSPORT_OK; } - // ------------------------------------------------------------------------------------ - // Full backup handling + private File tarballFile(String pkgName) { + return new File(mCurrentSetFullDir, pkgName); + } @Override public long requestFullBackupTime() { @@ -329,7 +334,7 @@ public class LocalTransport extends BackupTransport { mFullTargetPackage = targetPackage.packageName; FileOutputStream tarstream; try { - File tarball = new File(mCurrentSetFullDir, mFullTargetPackage); + File tarball = tarballFile(mFullTargetPackage); tarstream = new FileOutputStream(tarball); } catch (FileNotFoundException e) { return TRANSPORT_ERROR; @@ -368,6 +373,19 @@ public class LocalTransport extends BackupTransport { return TRANSPORT_OK; } + // For now we can't roll back, so just tear everything down. + @Override + public void cancelFullBackup() { + if (DEBUG) { + Log.i(TAG, "Canceling full backup of " + mFullTargetPackage); + } + File archive = tarballFile(mFullTargetPackage); + tearDownFullBackup(); + if (archive.exists()) { + archive.delete(); + } + } + // ------------------------------------------------------------------------------------ // Restore handling static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 }; diff --git a/core/java/com/android/internal/os/AtomicFile.java b/core/java/com/android/internal/os/AtomicFile.java index 445d10a..5a83f33 100644 --- a/core/java/com/android/internal/os/AtomicFile.java +++ b/core/java/com/android/internal/os/AtomicFile.java @@ -40,7 +40,7 @@ import java.io.IOException; * appropriate mutual exclusion invariants whenever it accesses the file. * </p> */ -public class AtomicFile { +public final class AtomicFile { private final File mBaseName; private final File mBackupName; @@ -129,7 +129,16 @@ public class AtomicFile { } catch (IOException e) { } } - + + public boolean exists() { + return mBaseName.exists() || mBackupName.exists(); + } + + public void delete() { + mBaseName.delete(); + mBackupName.delete(); + } + public FileInputStream openRead() throws FileNotFoundException { if (mBackupName.exists()) { mBaseName.delete(); diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index 6c9b4b8..63be2d4 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -30,19 +30,26 @@ import android.net.ConnectivityManager; import android.os.BatteryStats; import android.os.BatteryStats.Uid; import android.os.Bundle; +import android.os.MemoryFile; import android.os.Parcel; +import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.telephony.SignalStrength; +import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; import com.android.internal.app.IBatteryStats; import com.android.internal.os.BatterySipper.DrainType; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -55,7 +62,7 @@ import java.util.Map; * The caller must initialize this class as soon as activity object is ready to use (for example, in * onAttach() for Fragment), call create() in onCreate() and call destroy() in onDestroy(). */ -public class BatteryStatsHelper { +public final class BatteryStatsHelper { private static final boolean DEBUG = false; @@ -63,6 +70,7 @@ public class BatteryStatsHelper { private static BatteryStats sStatsXfer; private static Intent sBatteryBroadcastXfer; + private static ArrayMap<File, BatteryStats> sFileXfer = new ArrayMap<>(); final private Context mContext; final private boolean mCollectBatteryBroadcast; @@ -117,6 +125,68 @@ public class BatteryStatsHelper { mCollectBatteryBroadcast = collectBatteryBroadcast; } + public void storeStatsHistoryInFile(String fname) { + synchronized (sFileXfer) { + File path = makeFilePath(mContext, fname); + sFileXfer.put(path, this.getStats()); + FileOutputStream fout = null; + try { + fout = new FileOutputStream(path); + Parcel hist = Parcel.obtain(); + getStats().writeToParcelWithoutUids(hist, 0); + byte[] histData = hist.marshall(); + fout.write(histData); + } catch (IOException e) { + Log.w(TAG, "Unable to write history to file", e); + } finally { + if (fout != null) { + try { + fout.close(); + } catch (IOException e) { + } + } + } + } + } + + public static BatteryStats statsFromFile(Context context, String fname) { + synchronized (sFileXfer) { + File path = makeFilePath(context, fname); + BatteryStats stats = sFileXfer.get(path); + if (stats != null) { + return stats; + } + FileInputStream fin = null; + try { + fin = new FileInputStream(path); + byte[] data = readFully(fin); + Parcel parcel = Parcel.obtain(); + parcel.unmarshall(data, 0, data.length); + parcel.setDataPosition(0); + return com.android.internal.os.BatteryStatsImpl.CREATOR.createFromParcel(parcel); + } catch (IOException e) { + Log.w(TAG, "Unable to read history to file", e); + } finally { + if (fin != null) { + try { + fin.close(); + } catch (IOException e) { + } + } + } + } + return getStats(IBatteryStats.Stub.asInterface( + ServiceManager.getService(BatteryStats.SERVICE_NAME))); + } + + public static void dropFile(Context context, String fname) { + makeFilePath(context, fname).delete(); + } + + private static File makeFilePath(Context context, String fname) { + return new File(context.getFilesDir(), fname); + } + /** Clears the current stats and forces recreating for future use. */ public void clearStats() { mStats = null; @@ -853,25 +923,64 @@ public class BatteryStatsHelper { public long getChargeTimeRemaining() { return mChargeTimeRemaining; } + public static byte[] readFully(FileInputStream stream) throws java.io.IOException { + return readFully(stream, stream.available()); + } + + public static byte[] readFully(FileInputStream stream, int avail) throws java.io.IOException { + int pos = 0; + byte[] data = new byte[avail]; + while (true) { + int amt = stream.read(data, pos, data.length-pos); + //Log.i("foo", "Read " + amt + " bytes at " + pos + // + " of avail " + data.length); + if (amt <= 0) { + //Log.i("foo", "**** FINISHED READING: pos=" + pos + // + " len=" + data.length); + return data; + } + pos += amt; + avail = stream.available(); + if (avail > data.length-pos) { + byte[] newData = new byte[pos+avail]; + System.arraycopy(data, 0, newData, 0, pos); + data = newData; + } + } + } + private void load() { if (mBatteryInfo == null) { return; } - try { - byte[] data = mBatteryInfo.getStatistics(); - Parcel parcel = Parcel.obtain(); - parcel.unmarshall(data, 0, data.length); - parcel.setDataPosition(0); - BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR - .createFromParcel(parcel); - stats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED); - mStats = stats; - } catch (RemoteException e) { - Log.e(TAG, "RemoteException:", e); - } + mStats = getStats(mBatteryInfo); if (mCollectBatteryBroadcast) { mBatteryBroadcast = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); } } + + private static BatteryStatsImpl getStats(IBatteryStats service) { + try { + ParcelFileDescriptor pfd = service.getStatisticsStream(); + if (pfd != null) { + FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd); + try { + byte[] data = readFully(fis, MemoryFile.getSize(pfd.getFileDescriptor())); + Parcel parcel = Parcel.obtain(); + parcel.unmarshall(data, 0, data.length); + parcel.setDataPosition(0); + BatteryStatsImpl stats = com.android.internal.os.BatteryStatsImpl.CREATOR + .createFromParcel(parcel); + stats.distributeWorkLocked(BatteryStats.STATS_SINCE_CHARGED); + return stats; + } catch (IOException e) { + Log.w(TAG, "Unable to read statistics stream", e); + } + } + } catch (RemoteException e) { + Log.w(TAG, "RemoteException:", e); + } + return null; + } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index aad1156..ee0d14b 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -29,6 +29,7 @@ import android.net.wifi.WifiManager; import android.os.BadParcelableException; import android.os.BatteryManager; import android.os.BatteryStats; +import android.os.Build; import android.os.FileUtils; import android.os.Handler; import android.os.Looper; @@ -91,7 +92,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 112 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 113 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -109,6 +110,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static int sNumSpeedSteps; private final JournaledFile mFile; + public final AtomicFile mCheckinFile; static final int MSG_UPDATE_WAKELOCKS = 1; static final int MSG_REPORT_POWER_CHANGE = 2; @@ -142,7 +144,7 @@ public final class BatteryStatsImpl extends BatteryStats { } } - private final MyHandler mHandler; + public final MyHandler mHandler; private BatteryCallback mCallback; @@ -199,8 +201,8 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mRecordingHistory = false; int mNumHistoryItems; - static final int MAX_HISTORY_BUFFER = 128*1024; // 128KB - static final int MAX_MAX_HISTORY_BUFFER = 144*1024; // 144KB + static final int MAX_HISTORY_BUFFER = 256*1024; // 256KB + static final int MAX_MAX_HISTORY_BUFFER = 320*1024; // 320KB final Parcel mHistoryBuffer = Parcel.obtain(); final HistoryItem mHistoryLastWritten = new HistoryItem(); final HistoryItem mHistoryLastLastWritten = new HistoryItem(); @@ -232,6 +234,8 @@ public final class BatteryStatsImpl extends BatteryStats { int mStartCount; long mStartClockTime; + String mStartPlatformVersion; + String mEndPlatformVersion; long mUptime; long mUptimeStart; @@ -240,7 +244,7 @@ public final class BatteryStatsImpl extends BatteryStats { int mWakeLockNesting; boolean mWakeLockImportant; - boolean mRecordAllWakeLocks; + boolean mRecordAllHistory; boolean mNoAutoReset; int mScreenState = Display.STATE_UNKNOWN; @@ -340,7 +344,11 @@ public final class BatteryStatsImpl extends BatteryStats { int mDischargeAmountScreenOff; int mDischargeAmountScreenOffSinceCharge; - static final int MAX_LEVEL_STEPS = 100; + static final int MAX_LEVEL_STEPS = 200; + + int mInitStepMode = 0; + int mCurStepMode = 0; + int mModStepMode = 0; int mLastDischargeStepLevel; long mLastDischargeStepTime; @@ -429,10 +437,11 @@ public final class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") private String[] mWifiIfaces = new String[0]; - // For debugging public BatteryStatsImpl() { mFile = null; + mCheckinFile = null; mHandler = null; + clearHistoryLocked(); } public static interface TimeBaseObs { @@ -2353,6 +2362,9 @@ public final class BatteryStatsImpl extends BatteryStats { if (!mActiveEvents.updateState(HistoryItem.EVENT_PROC_START, name, uid, 0)) { return; } + if (!mRecordAllHistory) { + return; + } final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PROC_START, name, uid); @@ -2371,9 +2383,12 @@ public final class BatteryStatsImpl extends BatteryStats { } final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); - addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PROC_FINISH, name, uid); getUidStatsLocked(uid).updateProcessStateLocked(name, Uid.PROCESS_STATE_NONE, elapsedRealtime); + if (!mRecordAllHistory) { + return; + } + addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PROC_FINISH, name, uid); } public void noteSyncStartLocked(String name, int uid) { @@ -2427,11 +2442,41 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public void setRecordAllWakeLocksLocked(boolean enabled) { - mRecordAllWakeLocks = enabled; + public void setRecordAllHistoryLocked(boolean enabled) { + mRecordAllHistory = enabled; if (!enabled) { // Clear out any existing state. mActiveEvents.removeEvents(HistoryItem.EVENT_WAKE_LOCK); + // Record the currently running processes as stopping, now that we are no + // longer tracking them. + HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent( + HistoryItem.EVENT_PROC); + if (active != null) { + long mSecRealtime = SystemClock.elapsedRealtime(); + final long mSecUptime = SystemClock.uptimeMillis(); + for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) { + SparseIntArray uids = ent.getValue(); + for (int j=0; j<uids.size(); j++) { + addHistoryEventLocked(mSecRealtime, mSecUptime, + HistoryItem.EVENT_PROC_FINISH, ent.getKey(), uids.keyAt(j)); + } + } + } + } else { + // Record the currently running processes as starting, now that we are tracking them. + HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent( + HistoryItem.EVENT_PROC); + if (active != null) { + long mSecRealtime = SystemClock.elapsedRealtime(); + final long mSecUptime = SystemClock.uptimeMillis(); + for (HashMap.Entry<String, SparseIntArray> ent : active.entrySet()) { + SparseIntArray uids = ent.getValue(); + for (int j=0; j<uids.size(); j++) { + addHistoryEventLocked(mSecRealtime, mSecUptime, + HistoryItem.EVENT_PROC_START, ent.getKey(), uids.keyAt(j)); + } + } + } } } @@ -2452,7 +2497,7 @@ public final class BatteryStatsImpl extends BatteryStats { if (historyName == null) { historyName = name; } - if (mRecordAllWakeLocks) { + if (mRecordAllHistory) { if (mActiveEvents.updateState(HistoryItem.EVENT_WAKE_LOCK_START, historyName, uid, 0)) { addHistoryEventLocked(elapsedRealtime, uptime, @@ -2496,7 +2541,7 @@ public final class BatteryStatsImpl extends BatteryStats { uid = mapUid(uid); if (type == WAKE_TYPE_PARTIAL) { mWakeLockNesting--; - if (mRecordAllWakeLocks) { + if (mRecordAllHistory) { if (historyName == null) { historyName = name; } @@ -2788,6 +2833,16 @@ public final class BatteryStatsImpl extends BatteryStats { if (DEBUG) Slog.v(TAG, "Screen state: oldState=" + Display.stateToString(oldState) + ", newState=" + Display.stateToString(state)); + if (state != Display.STATE_UNKNOWN) { + int stepState = state-1; + if (stepState < 4) { + mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_SCREEN_STATE) ^ stepState; + mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_SCREEN_STATE) | stepState; + } else { + Slog.wtf(TAG, "Unexpected screen state: " + state); + } + } + if (state == Display.STATE_ON) { // Screen turning on. final long elapsedRealtime = SystemClock.elapsedRealtime(); @@ -2924,6 +2979,9 @@ public final class BatteryStatsImpl extends BatteryStats { public void noteLowPowerMode(boolean enabled) { if (mLowPowerModeEnabled != enabled) { + int stepState = enabled ? STEP_LEVEL_MODE_POWER_SAVE : 0; + mModStepMode |= (mCurStepMode&STEP_LEVEL_MODE_POWER_SAVE) ^ stepState; + mCurStepMode = (mCurStepMode&~STEP_LEVEL_MODE_POWER_SAVE) | stepState; final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mLowPowerModeEnabled = enabled; @@ -3871,6 +3929,18 @@ public final class BatteryStatsImpl extends BatteryStats { return mStartClockTime; } + @Override public String getStartPlatformVersion() { + return mStartPlatformVersion; + } + + @Override public String getEndPlatformVersion() { + return mEndPlatformVersion; + } + + @Override public int getParcelVersion() { + return VERSION; + } + @Override public boolean getIsOnBattery() { return mOnBattery; } @@ -4535,6 +4605,7 @@ public final class BatteryStatsImpl extends BatteryStats { proc.detach(); mProcessStats.removeAt(ip); } else { + proc.reset(); active = true; } } @@ -4791,7 +4862,7 @@ public final class BatteryStatsImpl extends BatteryStats { mProcessStats.clear(); for (int k = 0; k < numProcs; k++) { String processName = in.readString(); - Uid.Proc proc = new Proc(); + Uid.Proc proc = new Proc(processName); proc.readFromParcelLocked(in); mProcessStats.put(processName, proc); } @@ -5040,6 +5111,11 @@ public final class BatteryStatsImpl extends BatteryStats { */ public final class Proc extends BatteryStats.Uid.Proc implements TimeBaseObs { /** + * The name of this process. + */ + final String mName; + + /** * Remains true until removed from the stats. */ boolean mActive = true; @@ -5133,7 +5209,8 @@ public final class BatteryStatsImpl extends BatteryStats { ArrayList<ExcessivePower> mExcessivePower; - Proc() { + Proc(String name) { + mName = name; mOnBatteryTimeBase.add(this); mSpeedBins = new SamplingCounter[getCpuSpeedSteps()]; } @@ -5148,6 +5225,24 @@ public final class BatteryStatsImpl extends BatteryStats { public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) { } + void reset() { + mUserTime = mSystemTime = mForegroundTime = 0; + mStarts = 0; + mLoadedUserTime = mLoadedSystemTime = mLoadedForegroundTime = 0; + mLoadedStarts = 0; + mLastUserTime = mLastSystemTime = mLastForegroundTime = 0; + mLastStarts = 0; + mUnpluggedUserTime = mUnpluggedSystemTime = mUnpluggedForegroundTime = 0; + mUnpluggedStarts = 0; + for (int i = 0; i < mSpeedBins.length; i++) { + SamplingCounter c = mSpeedBins[i]; + if (c != null) { + c.reset(false); + } + } + mExcessivePower = null; + } + void detach() { mActive = false; mOnBatteryTimeBase.remove(this); @@ -5736,7 +5831,7 @@ public final class BatteryStatsImpl extends BatteryStats { public Proc getProcessStatsLocked(String name) { Proc ps = mProcessStats.get(name); if (ps == null) { - ps = new Proc(); + ps = new Proc(name); mProcessStats.put(name, ps); } @@ -6032,8 +6127,14 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public BatteryStatsImpl(String filename, Handler handler) { - mFile = new JournaledFile(new File(filename), new File(filename + ".tmp")); + public BatteryStatsImpl(File systemDir, Handler handler) { + if (systemDir != null) { + mFile = new JournaledFile(new File(systemDir, "batterystats.bin"), + new File(systemDir, "batterystats.bin.tmp")); + } else { + mFile = null; + } + mCheckinFile = new AtomicFile(new File(systemDir, "batterystats-checkin.bin")); mHandler = new MyHandler(handler.getLooper()); mStartCount++; mScreenOnTimer = new StopwatchTimer(null, -1, null, mOnBatteryTimeBase); @@ -6084,6 +6185,7 @@ public final class BatteryStatsImpl extends BatteryStats { long uptime = SystemClock.uptimeMillis() * 1000; long realtime = SystemClock.elapsedRealtime() * 1000; initTimes(uptime, realtime); + mStartPlatformVersion = mEndPlatformVersion = Build.ID; mDischargeStartLevel = 0; mDischargeUnplugLevel = 0; mDischargePlugLevel = -1; @@ -6095,6 +6197,7 @@ public final class BatteryStatsImpl extends BatteryStats { public BatteryStatsImpl(Parcel p) { mFile = null; + mCheckinFile = null; mHandler = null; clearHistoryLocked(); readFromParcel(p); @@ -6390,6 +6493,10 @@ public final class BatteryStatsImpl extends BatteryStats { private void initActiveHistoryEventsLocked(long elapsedRealtimeMs, long uptimeMs) { for (int i=0; i<HistoryItem.EVENT_COUNT; i++) { + if (!mRecordAllHistory && i == HistoryItem.EVENT_PROC) { + // Not recording process starts/stops. + continue; + } HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(i); if (active == null) { continue; @@ -6455,7 +6562,37 @@ public final class BatteryStatsImpl extends BatteryStats { boolean reset = false; if (!mNoAutoReset && (oldStatus == BatteryManager.BATTERY_STATUS_FULL || level >= 90 - || (mDischargeCurrentLevel < 20 && level >= 80))) { + || getLowDischargeAmountSinceCharge() >= 60) + || (getHighDischargeAmountSinceCharge() >= 60 + && mHistoryBuffer.dataSize() >= MAX_HISTORY_BUFFER)) { + // Before we write, collect a snapshot of the final aggregated + // stats to be reported in the next checkin. Only do this if we have + // a sufficient amount of data to make it interesting. + if (getLowDischargeAmountSinceCharge() >= 20) { + final Parcel parcel = Parcel.obtain(); + writeSummaryToParcel(parcel, true); + BackgroundThread.getHandler().post(new Runnable() { + @Override public void run() { + synchronized (mCheckinFile) { + FileOutputStream stream = null; + try { + stream = mCheckinFile.startWrite(); + stream.write(parcel.marshall()); + stream.flush(); + FileUtils.sync(stream); + stream.close(); + mCheckinFile.finishWrite(stream); + } catch (IOException e) { + Slog.w("BatteryStats", + "Error writing checkin battery statistics", e); + mCheckinFile.failWrite(stream); + } finally { + parcel.recycle(); + } + } + } + }); + } doWrite = true; resetAllStatsLocked(); mDischargeStartLevel = level; @@ -6465,6 +6602,8 @@ public final class BatteryStatsImpl extends BatteryStats { mLastDischargeStepLevel = level; mMinDischargeStepLevel = level; mLastDischargeStepTime = -1; + mInitStepMode = mCurStepMode; + mModStepMode = 0; pullPendingStateUpdatesLocked(); mHistoryCur.batteryLevel = (byte)level; mHistoryCur.states &= ~HistoryItem.STATE_BATTERY_PLUGGED_FLAG; @@ -6504,6 +6643,8 @@ public final class BatteryStatsImpl extends BatteryStats { mLastChargeStepLevel = level; mMaxChargeStepLevel = level; mLastChargeStepTime = -1; + mInitStepMode = mCurStepMode; + mModStepMode = 0; } if (doWrite || (mLastWriteTime + (60 * 1000)) < mSecRealtime) { if (mFile != null) { @@ -6529,14 +6670,17 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int BATTERY_PLUGGED_NONE = 0; private static int addLevelSteps(long[] steps, int stepCount, long lastStepTime, - int numStepLevels, long elapsedRealtime) { + int numStepLevels, long modeBits, long elapsedRealtime) { if (lastStepTime >= 0 && numStepLevels > 0) { long duration = elapsedRealtime - lastStepTime; for (int i=0; i<numStepLevels; i++) { System.arraycopy(steps, 0, steps, 1, steps.length-1); long thisDuration = duration / (numStepLevels-i); duration -= thisDuration; - steps[0] = thisDuration; + if (thisDuration > STEP_LEVEL_TIME_MASK) { + thisDuration = STEP_LEVEL_TIME_MASK; + } + steps[0] = thisDuration | modeBits; } stepCount += numStepLevels; if (stepCount > steps.length) { @@ -6623,23 +6767,30 @@ public final class BatteryStatsImpl extends BatteryStats { if (changed) { addHistoryRecordLocked(elapsedRealtime, uptime); } + long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT) + | (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT) + | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT); if (onBattery) { if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) { mNumDischargeStepDurations = addLevelSteps(mDischargeStepDurations, mNumDischargeStepDurations, mLastDischargeStepTime, - mLastDischargeStepLevel - level, elapsedRealtime); + mLastDischargeStepLevel - level, modeBits, elapsedRealtime); mLastDischargeStepLevel = level; mMinDischargeStepLevel = level; mLastDischargeStepTime = elapsedRealtime; + mInitStepMode = mCurStepMode; + mModStepMode = 0; } } else { if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) { mNumChargeStepDurations = addLevelSteps(mChargeStepDurations, mNumChargeStepDurations, mLastChargeStepTime, - level - mLastChargeStepLevel, elapsedRealtime); + level - mLastChargeStepLevel, modeBits, elapsedRealtime); mLastChargeStepLevel = level; mMaxChargeStepLevel = level; mLastChargeStepTime = elapsedRealtime; + mInitStepMode = mCurStepMode; + mModStepMode = 0; } } } @@ -6863,7 +7014,7 @@ public final class BatteryStatsImpl extends BatteryStats { } long total = 0; for (int i=0; i<numSteps; i++) { - total += steps[i]; + total += steps[i] & STEP_LEVEL_TIME_MASK; } return total / numSteps; /* @@ -6875,7 +7026,7 @@ public final class BatteryStatsImpl extends BatteryStats { long totalTime = 0; int num = 0; for (int j=0; j<numToAverage && (i+j)<numSteps; j++) { - totalTime += steps[i+j]; + totalTime += steps[i+j] & STEP_LEVEL_TIME_MASK; num++; } buckets[numBuckets] = totalTime / num; @@ -7213,7 +7364,7 @@ public final class BatteryStatsImpl extends BatteryStats { } Parcel out = Parcel.obtain(); - writeSummaryToParcel(out); + writeSummaryToParcel(out, true); mLastWriteTime = SystemClock.elapsedRealtime(); if (mPendingWrite != null) { @@ -7224,14 +7375,11 @@ public final class BatteryStatsImpl extends BatteryStats { if (sync) { commitPendingDataToDisk(); } else { - Thread thr = new Thread("BatteryStats-Write") { - @Override - public void run() { - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + BackgroundThread.getHandler().post(new Runnable() { + @Override public void run() { commitPendingDataToDisk(); } - }; - thr.start(); + }); } } @@ -7263,29 +7411,6 @@ public final class BatteryStatsImpl extends BatteryStats { } } - static byte[] readFully(FileInputStream stream) throws java.io.IOException { - int pos = 0; - int avail = stream.available(); - byte[] data = new byte[avail]; - while (true) { - int amt = stream.read(data, pos, data.length-pos); - //Log.i("foo", "Read " + amt + " bytes at " + pos - // + " of avail " + data.length); - if (amt <= 0) { - //Log.i("foo", "**** FINISHED READING: pos=" + pos - // + " len=" + data.length); - return data; - } - pos += amt; - avail = stream.available(); - if (avail > data.length-pos) { - byte[] newData = new byte[pos+avail]; - System.arraycopy(data, 0, newData, 0, pos); - data = newData; - } - } - } - public void readLocked() { if (mFile == null) { Slog.w("BatteryStats", "readLocked: no file associated with this instance"); @@ -7301,7 +7426,7 @@ public final class BatteryStatsImpl extends BatteryStats { } FileInputStream stream = new FileInputStream(file); - byte[] raw = readFully(stream); + byte[] raw = BatteryStatsHelper.readFully(stream); Parcel in = Parcel.obtain(); in.unmarshall(raw, 0, raw.length); in.setDataPosition(0); @@ -7312,6 +7437,8 @@ public final class BatteryStatsImpl extends BatteryStats { Slog.e("BatteryStats", "Error reading battery statistics", e); } + mEndPlatformVersion = Build.ID; + if (mHistoryBuffer.dataPosition() > 0) { mRecordingHistory = true; final long elapsedRealtime = SystemClock.elapsedRealtime(); @@ -7410,7 +7537,7 @@ public final class BatteryStatsImpl extends BatteryStats { } } - void writeHistory(Parcel out, boolean andOldHistory) { + void writeHistory(Parcel out, boolean inclData, boolean andOldHistory) { if (DEBUG_HISTORY) { StringBuilder sb = new StringBuilder(128); sb.append("****************** WRITING mHistoryBaseTime: "); @@ -7420,6 +7547,11 @@ public final class BatteryStatsImpl extends BatteryStats { Slog.i(TAG, sb.toString()); } out.writeLong(mHistoryBaseTime + mLastHistoryElapsedRealtime); + if (!inclData) { + out.writeInt(0); + out.writeInt(0); + return; + } out.writeInt(mHistoryTagPool.size()); for (HashMap.Entry<HistoryTag, Integer> ent : mHistoryTagPool.entrySet()) { HistoryTag tag = ent.getKey(); @@ -7449,7 +7581,7 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(-1); } - private void readSummaryFromParcel(Parcel in) { + public void readSummaryFromParcel(Parcel in) { final int version = in.readInt(); if (version != VERSION) { Slog.w("BatteryStats", "readFromParcel: version got " + version @@ -7463,6 +7595,8 @@ public final class BatteryStatsImpl extends BatteryStats { mUptime = in.readLong(); mRealtime = in.readLong(); mStartClockTime = in.readLong(); + mStartPlatformVersion = in.readString(); + mEndPlatformVersion = in.readString(); mOnBatteryTimeBase.readSummaryFromParcel(in); mOnBatteryScreenOffTimeBase.readSummaryFromParcel(in); mDischargeUnplugLevel = in.readInt(); @@ -7742,7 +7876,7 @@ public final class BatteryStatsImpl extends BatteryStats { * * @param out the Parcel to be written to. */ - public void writeSummaryToParcel(Parcel out) { + public void writeSummaryToParcel(Parcel out, boolean inclHistory) { pullPendingStateUpdatesLocked(); final long NOW_SYS = SystemClock.uptimeMillis() * 1000; @@ -7750,12 +7884,14 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(VERSION); - writeHistory(out, true); + writeHistory(out, inclHistory, true); out.writeInt(mStartCount); out.writeLong(computeUptime(NOW_SYS, STATS_SINCE_CHARGED)); out.writeLong(computeRealtime(NOWREAL_SYS, STATS_SINCE_CHARGED)); out.writeLong(mStartClockTime); + out.writeString(mStartPlatformVersion); + out.writeString(mEndPlatformVersion); mOnBatteryTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS); mOnBatteryScreenOffTimeBase.writeSummaryToParcel(out, NOW_SYS, NOWREAL_SYS); out.writeInt(mDischargeUnplugLevel); @@ -8043,6 +8179,8 @@ public final class BatteryStatsImpl extends BatteryStats { mStartCount = in.readInt(); mStartClockTime = in.readLong(); + mStartPlatformVersion = in.readString(); + mEndPlatformVersion = in.readString(); mUptime = in.readLong(); mUptimeStart = in.readLong(); mRealtime = in.readLong(); @@ -8194,10 +8332,12 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(MAGIC); - writeHistory(out, false); + writeHistory(out, true, false); out.writeInt(mStartCount); out.writeLong(mStartClockTime); + out.writeString(mStartPlatformVersion); + out.writeString(mEndPlatformVersion); out.writeLong(mUptime); out.writeLong(mUptimeStart); out.writeLong(mRealtime); diff --git a/core/java/com/android/internal/util/JournaledFile.java b/core/java/com/android/internal/util/JournaledFile.java index eeffc16..9f775d3 100644 --- a/core/java/com/android/internal/util/JournaledFile.java +++ b/core/java/com/android/internal/util/JournaledFile.java @@ -20,7 +20,7 @@ import java.io.File; import java.io.IOException; /** - * @Deprecated Use {@link com.android.internal.os.AtomicFile} instead. It would + * @deprecated Use {@link com.android.internal.os.AtomicFile} instead. It would * be nice to update all existing uses of this to switch to AtomicFile, but since * their on-file semantics are slightly different that would run the risk of losing * data if at the point of the platform upgrade to the new code it would need to |