diff options
36 files changed, 1038 insertions, 1821 deletions
diff --git a/Android.mk b/Android.mk index 917d57924623..216d8a2d45f6 100644 --- a/Android.mk +++ b/Android.mk @@ -94,8 +94,6 @@ LOCAL_SRC_FILES += \ core/java/android/app/backup/IFullBackupRestoreObserver.aidl \ core/java/android/app/backup/IRestoreObserver.aidl \ core/java/android/app/backup/IRestoreSession.aidl \ - core/java/android/app/maintenance/IIdleCallback.aidl \ - core/java/android/app/maintenance/IIdleService.aidl \ core/java/android/bluetooth/IBluetooth.aidl \ core/java/android/bluetooth/IBluetoothA2dp.aidl \ core/java/android/bluetooth/IBluetoothA2dpSink.aidl \ diff --git a/CleanSpec.mk b/CleanSpec.mk index 5d927928a203..1968a789da0b 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -34,9 +34,9 @@ # made today requires touching the same file, just copy the old # touch step and add it to the end of the list. # -# ************************************************ -# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST -# ************************************************ +# ***************************************************************** +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER +# ***************************************************************** # For example: #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates) @@ -194,10 +194,15 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framew $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/wearable) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/tv/ITv*) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates) - -# ************************************************ -# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST -# ************************************************ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/task) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/task) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/TaskManager) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/maintenance) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/maintenance) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes/android/app/maintenance) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes/android/app/maintenance) +$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/maintenance) + +# ****************************************************************** +# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER +# ****************************************************************** diff --git a/api/current.txt b/api/current.txt index 5f51f377aecc..cf19d542117e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -26036,29 +26036,61 @@ package android.service.dreams { package android.service.fingerprint { public class FingerprintManager { - ctor public FingerprintManager(android.content.Context); + ctor public FingerprintManager(android.content.Context, android.service.fingerprint.IFingerprintService); method public void enroll(long); + method public void enrollCancel(); method public boolean enrolledAndEnabled(); method public void remove(int); method public void startListening(android.service.fingerprint.FingerprintManagerReceiver); method public void stopListening(); + field public static final int FINGERPRINT_ACQUIRED = 1; // 0x1 + field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0 + field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 4; // 0x4 + field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2 + field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1 + field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 16; // 0x10 + field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 8; // 0x8 field public static final int FINGERPRINT_ERROR = -1; // 0xffffffff - field public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2; // 0x2 field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1 field public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10; // 0xfffffff6 field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4 field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3 - field public static final int FINGERPRINT_SCANNED = 1; // 0x1 - field public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2; // 0x2 + field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2 + field public static final int FINGERPRINT_PROCESSED = 2; // 0x2 + field public static final int FINGERPRINT_TEMPLATE_ENROLLING = 3; // 0x3 field public static final int FINGERPRINT_TEMPLATE_REMOVED = 4; // 0x4 } public class FingerprintManagerReceiver { ctor public FingerprintManagerReceiver(); + method public void onAcquired(int); method public void onEnrollResult(int, int); method public void onError(int); + method public void onProcessed(int); method public void onRemoved(int); - method public void onScanned(int, int); + } + + public class FingerprintUtils { + ctor public FingerprintUtils(); + method public static void addFingerprintIdForUser(int, android.content.ContentResolver, int); + method public static int[] getFingerprintIdsForUser(android.content.ContentResolver, int); + method public static boolean removeFingerprintIdForUser(int, android.content.ContentResolver, int); + } + + public abstract interface IFingerprintService implements android.os.IInterface { + method public abstract void enroll(android.os.IBinder, long, int) throws android.os.RemoteException; + method public abstract void enrollCancel(android.os.IBinder, int) throws android.os.RemoteException; + method public abstract void remove(android.os.IBinder, int, int) throws android.os.RemoteException; + method public abstract void startListening(android.os.IBinder, android.service.fingerprint.IFingerprintServiceReceiver, int) throws android.os.RemoteException; + method public abstract void stopListening(android.os.IBinder, int) throws android.os.RemoteException; + } + + public abstract interface IFingerprintServiceReceiver implements android.os.IInterface { + method public abstract void onAcquired(int) throws android.os.RemoteException; + method public abstract void onEnrollResult(int, int) throws android.os.RemoteException; + method public abstract void onError(int) throws android.os.RemoteException; + method public abstract void onProcessed(int) throws android.os.RemoteException; + method public abstract void onRemoved(int) throws android.os.RemoteException; } } diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index ab3bb4966624..a42bd3b92730 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -115,9 +115,8 @@ import android.os.storage.IMountService; import android.os.storage.StorageManager; import android.print.IPrintManager; import android.print.PrintManager; +import android.service.fingerprint.IFingerprintService; import android.service.fingerprint.FingerprintManager; -import android.service.fingerprint.FingerprintManagerReceiver; -import android.service.fingerprint.FingerprintService; import android.telecomm.TelecommManager; import android.telephony.TelephonyManager; import android.content.ClipboardManager; @@ -466,11 +465,6 @@ class ContextImpl extends Context { return new KeyguardManager(); }}); - registerService(FINGERPRINT_SERVICE, new ServiceFetcher() { - public Object createService(ContextImpl ctx) { - return new FingerprintManager(ctx); - }}); - registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext()); @@ -690,6 +684,7 @@ class ContextImpl extends Context { return new MediaSessionManager(ctx); } }); + registerService(TRUST_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { IBinder b = ServiceManager.getService(TRUST_SERVICE); @@ -697,6 +692,14 @@ class ContextImpl extends Context { } }); + registerService(FINGERPRINT_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(FINGERPRINT_SERVICE); + IFingerprintService service = IFingerprintService.Stub.asInterface(b); + return new FingerprintManager(ctx.getOuterContext(), service); + } + }); + registerService(TV_INPUT_SERVICE, new ServiceFetcher() { public Object createService(ContextImpl ctx) { IBinder iBinder = ServiceManager.getService(TV_INPUT_SERVICE); diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index 56a55fb4ae28..706ef043dccb 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -228,19 +228,35 @@ public class BackupTransport { } /** - * Get the package name of the next application with data in the backup store. + * Get the package name of the next application with data in the backup store, plus + * a description of the structure of the restored archive: either TYPE_KEY_VALUE for + * an original-API key/value dataset, or TYPE_FULL_STREAM for a tarball-type archive stream. * - * @return The name of one of the packages supplied to {@link #startRestore}, - * or "" (the empty string) if no more backup data is available, - * or null if an error occurred (the restore should be aborted and rescheduled). + * <p>If the package name in the returned RestoreDescription object is the singleton + * {@link RestoreDescription#NO_MORE_PACKAGES}, it indicates that no further data is available + * in the current restore session: all packages described in startRestore() have been + * processed. + * + * <p>If this method returns {@code null}, it means that a transport-level error has + * occurred and the entire restore operation should be abandoned. + * + * @return A RestoreDescription object containing the name of one of the packages + * supplied to {@link #startRestore} plus an indicator of the data type of that + * restore data; or {@link RestoreDescription#NO_MORE_PACKAGES} to indicate that + * no more packages can be restored in this session; or {@code null} to indicate + * a transport-level error. */ - public String nextRestorePackage() { + public RestoreDescription nextRestorePackage() { return null; } /** - * Get the data for the application returned by {@link #nextRestorePackage}. - * @param data An open, writable file into which the backup data should be stored. + * Get the data for the application returned by {@link #nextRestorePackage}, if that + * method reported {@link RestoreDescription#TYPE_KEY_VALUE} as its delivery type. + * If the package has only TYPE_FULL_STREAM data, then this method will return an + * error. + * + * @param data An open, writable file into which the key/value backup data should be stored. * @return the same error codes as {@link #startRestore}. */ public int getRestoreData(ParcelFileDescriptor outFd) { @@ -332,32 +348,11 @@ public class BackupTransport { // Full restore interfaces /** - * Ask the transport to set up to perform a full data restore of the given packages. + * Ask the transport to provide data for the "current" package being restored. This + * is the package that was just reported by {@link #nextRestorePackage()} as having + * {@link RestoreDescription#TYPE_FULL_STREAM} data. * - * @param token A backup token as returned by {@link #getAvailableRestoreSets} - * or {@link #getCurrentRestoreSet}. - * @param targetPackage The names of the packages whose data is being requested. - * @return TRANSPORT_OK to indicate that the OS may proceed with requesting - * restore data; TRANSPORT_ERROR to indicate a fatal error condition that precludes - * performing any restore at this time. - */ - public int prepareFullRestore(long token, String[] targetPackages) { - return BackupTransport.TRANSPORT_OK; - } - - /** - * Ask the transport what package's full data will be restored next. When all apps' - * data has been delivered, the transport should return {@code null} here. - * @return The package name of the next application whose data will be restored, or - * {@code null} if all available package has been delivered. - */ - public String getNextFullRestorePackage() { - return null; - } - - /** - * Ask the transport to provide data for the "current" package being restored. The - * transport then writes some data to the socket supplied to this call, and returns + * The transport writes some data to the socket supplied to this call, and returns * the number of bytes written. The system will then read that many bytes and * stream them to the application's agent for restore, then will call this method again * to receive the next chunk of the archive. This sequence will be repeated until the @@ -369,10 +364,14 @@ public class BackupTransport { * {@link #getNextFullRestorePackage()} to begin the restore process for the next * application, and the sequence begins again. * + * <p>The transport should always close this socket when returning from this method. + * Do not cache this socket across multiple calls or you may leak file descriptors. + * * @param socket The file descriptor that the transport will use for delivering the - * streamed archive. + * streamed archive. The transport must close this socket in all cases when returning + * from this method. * @return 0 when no more data for the current package is available. A positive value - * indicates the presence of that much data to be delivered to the app. A negative + * indicates the presence of that many bytes to be delivered to the app. Any negative * return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR}, * indicating a fatal error condition that precludes further restore operations * on the current dataset. @@ -380,6 +379,24 @@ public class BackupTransport { public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) { return 0; } + + /** + * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM} + * data for restore, it will invoke this method to tell the transport that it should + * abandon the data download for the current package. The OS will then either call + * {@link #nextRestorePackage()} again to move on to restoring the next package in the + * set being iterated over, or will call {@link #finishRestore()} to shut down the restore + * operation. + * + * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the + * current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious + * transport-level failure. If the transport reports an error here, the entire restore + * operation will immediately be finished with no further attempts to restore app data. + */ + public int abortFullRestore() { + return BackupTransport.TRANSPORT_OK; + } + /** * Bridge between the actual IBackupTransport implementation and the stable API. If the * binder interface needs to change, we use this layer to translate so that we can @@ -450,7 +467,7 @@ public class BackupTransport { } @Override - public String nextRestorePackage() throws RemoteException { + public RestoreDescription nextRestorePackage() throws RemoteException { return BackupTransport.this.nextRestorePackage(); } @@ -478,5 +495,15 @@ public class BackupTransport { public int sendBackupData(int numBytes) throws RemoteException { return BackupTransport.this.sendBackupData(numBytes); } + + @Override + public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) { + return BackupTransport.this.getNextFullRestoreDataChunk(socket); + } + + @Override + public int abortFullRestore() { + return BackupTransport.this.abortFullRestore(); + } } } diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java b/core/java/android/app/backup/RestoreDescription.aidl index b2ba21b542b2..9cbea786fda9 100644 --- a/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java +++ b/core/java/android/app/backup/RestoreDescription.aidl @@ -14,23 +14,6 @@ * limitations under the License. */ -package com.android.idleservicetest; +package android.app.backup; -import android.app.maintenance.IdleService; -import android.util.Log; - -public class TimeoutTestService extends IdleService { - private static final String TAG = "TimeoutTestService"; - - @Override - public boolean onIdleStart() { - Log.i(TAG, "onIdleStart() but anticipating time-slice timeout"); - return true; - } - - @Override - public void onIdleStop() { - Log.i(TAG, "onIdleStop() so we're done"); - } - -} +parcelable RestoreDescription; diff --git a/core/java/android/app/backup/RestoreDescription.java b/core/java/android/app/backup/RestoreDescription.java new file mode 100644 index 000000000000..0fb43557252a --- /dev/null +++ b/core/java/android/app/backup/RestoreDescription.java @@ -0,0 +1,101 @@ +/* + * 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.app.backup; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Description of the available restore data for a given package. Returned by a + * BackupTransport in response to a request about the next available restorable + * package. + * + * @see BackupTransport#nextRestorePackage() + * + * @hide + */ +public class RestoreDescription implements Parcelable { + private final String mPackageName; + private final int mDataType; + + private static final String NO_MORE_PACKAGES_SENTINEL = ""; + + /** + * Return this constant RestoreDescription from BackupTransport.nextRestorePackage() + * to indicate that no more package data is available in the current restore operation. + */ + public static final RestoreDescription NO_MORE_PACKAGES = + new RestoreDescription(NO_MORE_PACKAGES_SENTINEL, 0); + + // --------------------------------------- + // Data type identifiers + + /** This package's restore data is an original-style key/value dataset */ + public static final int TYPE_KEY_VALUE = 1; + + /** This package's restore data is a tarball-type full data stream */ + public static final int TYPE_FULL_STREAM = 2; + + // --------------------------------------- + // API + + public RestoreDescription(String packageName, int dataType) { + mPackageName = packageName; + mDataType = dataType; + } + + public String getPackageName() { + return mPackageName; + } + + public int getDataType() { + return mDataType; + } + + // --------------------------------------- + // Parcelable implementation - not used by transport + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeString(mPackageName); + out.writeInt(mDataType); + } + + public static final Parcelable.Creator<RestoreDescription> CREATOR + = new Parcelable.Creator<RestoreDescription>() { + public RestoreDescription createFromParcel(Parcel in) { + final RestoreDescription unparceled = new RestoreDescription(in); + return (NO_MORE_PACKAGES_SENTINEL.equals(unparceled.mPackageName)) + ? NO_MORE_PACKAGES + : unparceled; + } + + public RestoreDescription[] newArray(int size) { + return new RestoreDescription[size]; + } + }; + + private RestoreDescription(Parcel in) { + mPackageName = in.readString(); + mDataType = in.readInt(); + } +} diff --git a/core/java/android/app/maintenance/IIdleCallback.aidl b/core/java/android/app/maintenance/IIdleCallback.aidl deleted file mode 100644 index 582dede9574c..000000000000 --- a/core/java/android/app/maintenance/IIdleCallback.aidl +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright 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.app.maintenance; - -import android.app.maintenance.IIdleService; - -/** - * The server side of the idle maintenance IPC protocols. The app-side implementation - * invokes on this interface to indicate completion of the (asynchronous) instructions - * issued by the server. - * - * In all cases, the 'who' parameter is the caller's service binder, used to track - * which idle service instance is reporting. - * - * {@hide} - */ -interface IIdleCallback { - /** - * Acknowledge receipt and processing of the asynchronous "start idle work" incall. - * 'result' is true if the app wants some time to perform ongoing background - * idle-time work; or false if the app declares that it does not need any time - * for such work. - */ - void acknowledgeStart(int token, boolean result); - - /** - * Acknowledge receipt and processing of the asynchronous "stop idle work" incall. - */ - void acknowledgeStop(int token); - - /* - * Tell the idle service manager that we're done with our idle maintenance, so that - * it can go on to the next one and stop attributing wakelock time to us etc. - * - * @param opToken The identifier passed in the startIdleMaintenance() call that - * indicated the beginning of this service's idle timeslice. - */ - void idleFinished(int token); -} diff --git a/core/java/android/app/maintenance/IIdleService.aidl b/core/java/android/app/maintenance/IIdleService.aidl deleted file mode 100644 index 54abccdc1b4f..000000000000 --- a/core/java/android/app/maintenance/IIdleService.aidl +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright 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.app.maintenance; - -import android.app.maintenance.IIdleCallback; - -/** - * Interface that the framework uses to communicate with application code - * that implements an idle-time "maintenance" service. End user code does - * not implement this interface directly; instead, the app's idle service - * implementation will extend android.app.maintenance.IdleService. - * {@hide} - */ -oneway interface IIdleService { - /** - * Begin your idle-time work. - */ - void startIdleMaintenance(IIdleCallback callbackBinder, int token); - void stopIdleMaintenance(IIdleCallback callbackBinder, int token); -} diff --git a/core/java/android/app/maintenance/IdleService.java b/core/java/android/app/maintenance/IdleService.java deleted file mode 100644 index 2331b81aba1b..000000000000 --- a/core/java/android/app/maintenance/IdleService.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * 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.app.maintenance; - -import android.annotation.SdkConstant; -import android.annotation.SdkConstant.SdkConstantType; -import android.app.Service; -import android.content.Intent; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.RemoteException; -import android.util.Log; -import android.util.Slog; - -/** - * Idle maintenance API. Full docs TBW (to be written). - */ -public abstract class IdleService extends Service { - private static final String TAG = "IdleService"; - - static final int MSG_START = 1; - static final int MSG_STOP = 2; - static final int MSG_FINISH = 3; - - IdleHandler mHandler; - IIdleCallback mCallbackBinder; - int mToken; - final Object mHandlerLock = new Object(); - - void ensureHandler() { - synchronized (mHandlerLock) { - if (mHandler == null) { - mHandler = new IdleHandler(getMainLooper()); - } - } - } - - /** - * TBW: the idle service should supply an intent-filter handling this intent - * <p> - * <p class="note">The application must also protect the idle service with the - * {@code "android.permission.BIND_IDLE_SERVICE"} permission to ensure that other - * applications cannot maliciously bind to it. If an idle service's manifest - * declaration does not require that permission, it will never be invoked. - * </p> - */ - @SdkConstant(SdkConstantType.SERVICE_ACTION) - public static final String SERVICE_INTERFACE = - "android.service.idle.IdleService"; - - /** - * Idle services must be protected with this permission: - * - * <pre class="prettyprint"> - * <service android:name="MyIdleService" - * android:permission="android.permission.BIND_IDLE_SERVICE" > - * ... - * </service> - * </pre> - * - * <p>If an idle service is declared in the manifest but not protected with this - * permission, that service will be ignored by the OS. - */ - public static final String PERMISSION_BIND = - "android.permission.BIND_IDLE_SERVICE"; - - // Trampoline: the callbacks are always run on the main thread - IIdleService mBinder = new IIdleService.Stub() { - @Override - public void startIdleMaintenance(IIdleCallback callbackBinder, int token) - throws RemoteException { - ensureHandler(); - Message msg = mHandler.obtainMessage(MSG_START, token, 0, callbackBinder); - mHandler.sendMessage(msg); - } - - @Override - public void stopIdleMaintenance(IIdleCallback callbackBinder, int token) - throws RemoteException { - ensureHandler(); - Message msg = mHandler.obtainMessage(MSG_STOP, token, 0, callbackBinder); - mHandler.sendMessage(msg); - } - }; - - /** - * Your application may begin doing "idle" maintenance work in the background. - * <p> - * Your application may continue to run in the background until it receives a call - * to {@link #onIdleStop()}, at which point you <i>must</i> cease doing work. The - * OS will hold a wakelock on your application's behalf from the time this method is - * called until after the following call to {@link #onIdleStop()} returns. - * </p> - * <p> - * Returning {@code false} from this method indicates that you have no ongoing work - * to do at present. The OS will respond by immediately calling {@link #onIdleStop()} - * and returning your application to its normal stopped state. Returning {@code true} - * indicates that the application is indeed performing ongoing work, so the OS will - * let your application run in this state until it's no longer appropriate. - * </p> - * <p> - * You will always receive a matching call to {@link #onIdleStop()} even if your - * application returns {@code false} from this method. - * - * @return {@code true} to indicate that the application wishes to perform some ongoing - * background work; {@code false} to indicate that it does not need to perform such - * work at present. - */ - public abstract boolean onIdleStart(); - - /** - * Your app's maintenance opportunity is over. Once the application returns from - * this method, the wakelock held by the OS on its behalf will be released. - */ - public abstract void onIdleStop(); - - /** - * Tell the OS that you have finished your idle work. Calling this more than once, - * or calling it when you have not received an {@link #onIdleStart()} callback, is - * an error. - * - * <p>It is safe to call {@link #finishIdle()} from any thread. - */ - public final void finishIdle() { - ensureHandler(); - mHandler.sendEmptyMessage(MSG_FINISH); - } - - class IdleHandler extends Handler { - IdleHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_START: { - // Call the concrete onIdleStart(), reporting its return value back to - // the OS. If onIdleStart() throws, report it as a 'false' return but - // rethrow the exception at the offending app. - boolean result = false; - IIdleCallback callbackBinder = (IIdleCallback) msg.obj; - mCallbackBinder = callbackBinder; - final int token = mToken = msg.arg1; - try { - result = IdleService.this.onIdleStart(); - } catch (Exception e) { - Log.e(TAG, "Unable to start idle workload", e); - throw new RuntimeException(e); - } finally { - // don't bother if the service already called finishIdle() - if (mCallbackBinder != null) { - try { - callbackBinder.acknowledgeStart(token, result); - } catch (RemoteException re) { - Log.e(TAG, "System unreachable to start idle workload"); - } - } - } - break; - } - - case MSG_STOP: { - // Structured just like MSG_START for the stop-idle bookend call. - IIdleCallback callbackBinder = (IIdleCallback) msg.obj; - final int token = msg.arg1; - try { - IdleService.this.onIdleStop(); - } catch (Exception e) { - Log.e(TAG, "Unable to stop idle workload", e); - throw new RuntimeException(e); - } finally { - if (mCallbackBinder != null) { - try { - callbackBinder.acknowledgeStop(token); - } catch (RemoteException re) { - Log.e(TAG, "System unreachable to stop idle workload"); - } - } - } - break; - } - - case MSG_FINISH: { - if (mCallbackBinder != null) { - try { - mCallbackBinder.idleFinished(mToken); - } catch (RemoteException e) { - Log.e(TAG, "System unreachable to finish idling"); - } finally { - mCallbackBinder = null; - } - } else { - Log.e(TAG, "finishIdle() called but the idle service is not started"); - } - break; - } - - default: { - Slog.w(TAG, "Unknown message " + msg.what); - } - } - } - } - - /** @hide */ - @Override - public final IBinder onBind(Intent intent) { - return mBinder.asBinder(); - } - -} diff --git a/core/java/android/app/maintenance/package.html b/core/java/android/app/maintenance/package.html deleted file mode 100644 index 1c9bf9dad835..000000000000 --- a/core/java/android/app/maintenance/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<html> -<body> - {@hide} -</body> -</html> diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index ba66e6542e95..1e7d7f1d4830 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -7963,8 +7963,7 @@ public final class ContactsContract { actualContext = ((ContextWrapper) actualContext).getBaseContext(); } final int intentFlags = (actualContext instanceof Activity) - ? Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET - : Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK; + ? 0 : Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK; // Launch pivot dialog through intent for now final Intent intent = new Intent(ACTION_QUICK_CONTACT).addFlags(intentFlags); diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java index 2fcec52d2e65..b6137d11b2cc 100644 --- a/core/java/android/service/fingerprint/FingerprintManager.java +++ b/core/java/android/service/fingerprint/FingerprintManager.java @@ -22,12 +22,14 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import android.util.Log; +import android.util.Slog; /** * A class that coordinates access to the fingerprint hardware. @@ -36,31 +38,40 @@ import android.util.Log; public class FingerprintManager { private static final String TAG = "FingerprintManager"; private static final boolean DEBUG = true; - private static final String FINGERPRINT_SERVICE_PACKAGE = "com.android.service.fingerprint"; - private static final String FINGERPRINT_SERVICE_CLASS = - "com.android.service.fingerprint.FingerprintService"; private static final int MSG_ENROLL_RESULT = 100; - private static final int MSG_SCANNED = 101; - private static final int MSG_ERROR = 102; - private static final int MSG_REMOVED = 103; + private static final int MSG_ACQUIRED = 101; + private static final int MSG_PROCESSED = 102; + private static final int MSG_ERROR = 103; + private static final int MSG_REMOVED = 104; + // Errors generated by layers above HAL public static final int FINGERPRINT_ERROR_NO_RECEIVER = -10; - public static final int FINGERPRINT_ERROR = -1; // One of the error messages below. - // Progress messages. - public static final int FINGERPRINT_SCANNED = 1; - public static final int FINGERPRINT_TEMPLATE_ENROLLING = 2; + // Message types. Must agree with HAL (fingerprint.h) + public static final int FINGERPRINT_ERROR = -1; + public static final int FINGERPRINT_ACQUIRED = 1; + public static final int FINGERPRINT_PROCESSED = 2; + public static final int FINGERPRINT_TEMPLATE_ENROLLING = 3; public static final int FINGERPRINT_TEMPLATE_REMOVED = 4; - // Error messages. Must agree with fingerprint HAL definitions. + // Error messages. Must agree with HAL (fingerprint.h) public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; - public static final int FINGERPRINT_ERROR_BAD_CAPTURE = 2; + public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; public static final int FINGERPRINT_ERROR_TIMEOUT = 3; public static final int FINGERPRINT_ERROR_NO_SPACE = 4; + // FINGERPRINT_ACQUIRED messages. Must agree with HAL (fingerprint.h) + public static final int FINGERPRINT_ACQUIRED_GOOD = 0; + public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; + public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; + public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 4; + public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 8; + public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 16; + private IFingerprintService mService; private FingerprintManagerReceiver mClientReceiver; private Context mContext; + private IBinder mToken = new Binder(); private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { @@ -69,8 +80,11 @@ public class FingerprintManager { case MSG_ENROLL_RESULT: mClientReceiver.onEnrollResult(msg.arg1, msg.arg2); break; - case MSG_SCANNED: - mClientReceiver.onScanned(msg.arg1, msg.arg2); + case MSG_ACQUIRED: + mClientReceiver.onAcquired(msg.arg1); + break; + case MSG_PROCESSED: + mClientReceiver.onProcessed(msg.arg1); break; case MSG_ERROR: mClientReceiver.onError(msg.arg1); @@ -82,45 +96,26 @@ public class FingerprintManager { } }; - public FingerprintManager(Context context) { + public FingerprintManager(Context context, IFingerprintService service) { mContext = context; - // Connect to service... - Intent intent = new Intent(); - intent.setClassName(FINGERPRINT_SERVICE_PACKAGE, FINGERPRINT_SERVICE_CLASS); - if (!context.bindServiceAsUser(intent, mFingerprintConnection, - Context.BIND_AUTO_CREATE, UserHandle.CURRENT_OR_SELF)) { - if (DEBUG) Log.v(TAG, "Can't bind to " + FINGERPRINT_SERVICE_CLASS); + mService = service; + if (mService == null) { + Slog.v(TAG, "FingerprintManagerService was null"); } } - private final ServiceConnection mFingerprintConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - if (DEBUG) Log.v(TAG, "Connected to FingerprintService"); - mService = IFingerprintService.Stub.asInterface(service); - try { - mService.startListening(mServiceReceiver, getCurrentUserId()); - } catch (RemoteException e) { - if (DEBUG) Log.v(TAG, "Failed to set callback", e); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - if (DEBUG) Log.v(TAG, "Disconnected from FingerprintService"); - mService = null; - } - }; - private IFingerprintServiceReceiver mServiceReceiver = new IFingerprintServiceReceiver.Stub() { public void onEnrollResult(int fingerprintId, int remaining) { mHandler.obtainMessage(MSG_ENROLL_RESULT, fingerprintId, remaining).sendToTarget(); } - public void onScanned(int fingerprintId, int confidence) { - mHandler.obtainMessage(MSG_SCANNED, fingerprintId, confidence) - .sendToTarget();; + public void onAcquired(int acquireInfo) { + mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0).sendToTarget(); + } + + public void onProcessed(int fingerprintId) { + mHandler.obtainMessage(MSG_PROCESSED, fingerprintId, 0).sendToTarget(); } public void onError(int error) { @@ -151,12 +146,14 @@ public class FingerprintManager { */ public void enroll(long timeout) { if (mServiceReceiver == null) { - throw new IllegalStateException("enroll: Call registerCallback() first"); + sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0); + return; } if (mService != null) try { - mService.enroll(timeout, getCurrentUserId()); + mService.enroll(mToken, timeout, getCurrentUserId()); } catch (RemoteException e) { Log.v(TAG, "Remote exception while enrolling: ", e); + sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0); } } @@ -166,10 +163,19 @@ public class FingerprintManager { * @param fingerprintId */ public void remove(int fingerprintId) { - if (mService != null) try { - mService.remove(fingerprintId, getCurrentUserId()); - } catch (RemoteException e) { - Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e); + if (mServiceReceiver == null) { + sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0); + return; + } + if (mService != null) { + try { + mService.remove(mToken, fingerprintId, getCurrentUserId()); + } catch (RemoteException e) { + Log.v(TAG, "Remote exception during remove of fingerprintId: " + fingerprintId, e); + } + } else { + Log.w(TAG, "remove(): Service not connected!"); + sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0); } } @@ -181,10 +187,13 @@ public class FingerprintManager { mClientReceiver = receiver; if (mService != null) { try { - mService.startListening(mServiceReceiver, getCurrentUserId()); + mService.startListening(mToken, mServiceReceiver, getCurrentUserId()); } catch (RemoteException e) { Log.v(TAG, "Remote exception in startListening(): ", e); } + } else { + Log.w(TAG, "startListening(): Service not connected!"); + sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0); } } @@ -201,15 +210,38 @@ public class FingerprintManager { * Stops the client from listening to fingerprint events. */ public void stopListening() { - mClientReceiver = null; if (mService != null) { try { - mService.stopListening(getCurrentUserId()); + mService.stopListening(mToken, getCurrentUserId()); + mClientReceiver = null; } catch (RemoteException e) { Log.v(TAG, "Remote exception in stopListening(): ", e); } } else { Log.w(TAG, "stopListening(): Service not connected!"); + sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0); + } + } + + public void enrollCancel() { + if (mServiceReceiver == null) { + sendError(FINGERPRINT_ERROR_NO_RECEIVER, 0, 0); + return; } + if (mService != null) { + try { + mService.enrollCancel(mToken, getCurrentUserId()); + mClientReceiver = null; + } catch (RemoteException e) { + Log.v(TAG, "Remote exception in enrollCancel(): ", e); + sendError(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0, 0); + } + } else { + Log.w(TAG, "enrollCancel(): Service not connected!"); + } + } + + private void sendError(int msg, int arg1, int arg2) { + mHandler.obtainMessage(msg, arg1, arg2); } }
\ No newline at end of file diff --git a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java index 34f16558acca..e5193f572b80 100644 --- a/core/java/android/service/fingerprint/FingerprintManagerReceiver.java +++ b/core/java/android/service/fingerprint/FingerprintManagerReceiver.java @@ -30,18 +30,32 @@ public class FingerprintManagerReceiver { public void onEnrollResult(int fingerprintId, int remaining) { } /** - * Fingerprint scan detected. Most clients will use this function to detect a fingerprint + * Fingerprint touch detected, but not processed yet. Clients will use this message to + * determine a good or bad scan before the fingerprint is processed. This is meant for the + * client to provide feedback about the scan or alert the user that recognition is to follow. * - * @param fingerprintId is the finger the hardware has detected. - * @param confidence from 0 (no confidence) to 65535 (high confidence). Fingerprint 0 has - * special meaning - the finger wasn't recognized. + * @param acquiredInfo one of: + * {@link FingerprintManager#FINGERPRINT_ACQUIRED_GOOD}, + * {@link FingerprintManager#FINGERPRINT_ACQUIRED_PARTIAL}, + * {@link FingerprintManager#FINGERPRINT_ACQUIRED_INSUFFICIENT}, + * {@link FingerprintManager#FINGERPRINT_ACQUIRED_IMAGER_DIRTY}, + * {@link FingerprintManager#FINGERPRINT_ACQUIRED_TOO_SLOW}, + * {@link FingerprintManager#FINGERPRINT_ACQUIRED_TOO_FAST} */ - public void onScanned(int fingerprintId, int confidence) { } + public void onAcquired(int acquiredInfo) { } + + /** + * Fingerprint has been detected and processed. A non-zero return indicates a valid + * fingerprint was detected. + * + * @param fingerprintId the finger id, or 0 if not recognized. + */ + public void onProcessed(int fingerprintId) { } /** * An error was detected during scan or enrollment. One of * {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE}, - * {@link FingerprintManager#FINGERPRINT_ERROR_BAD_CAPTURE} or + * {@link FingerprintManager#FINGERPRINT_ERROR_UNABLE_TO_PROCESS} or * {@link FingerprintManager#FINGERPRINT_ERROR_TIMEOUT} * {@link FingerprintManager#FINGERPRINT_ERROR_NO_SPACE} * diff --git a/core/java/android/service/fingerprint/FingerprintService.java b/core/java/android/service/fingerprint/FingerprintService.java deleted file mode 100644 index c7fa7cdadd08..000000000000 --- a/core/java/android/service/fingerprint/FingerprintService.java +++ /dev/null @@ -1,219 +0,0 @@ -/** - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.service.fingerprint; - -import android.app.Service; -import android.content.ContentResolver; -import android.content.Intent; -import android.os.Handler; -import android.os.IBinder; -import android.os.RemoteException; -import android.provider.Settings; -import android.util.Slog; - -import java.io.PrintWriter; -import java.util.HashMap; - -/** - * A service to manage multiple clients that want to access the fingerprint HAL API. - * The service is responsible for maintaining a list of clients and dispatching all - * fingerprint -related events. - * - * @hide - */ -public class FingerprintService extends Service { - private final String TAG = FingerprintService.class.getSimpleName() + - "[" + getClass().getSimpleName() + "]"; - private static final boolean DEBUG = true; - HashMap<IFingerprintServiceReceiver, ClientData> mClients = - new HashMap<IFingerprintServiceReceiver, ClientData>(); - - private static final int MSG_NOTIFY = 10; - - Handler mHandler = new Handler() { - public void handleMessage(android.os.Message msg) { - switch (msg.what) { - case MSG_NOTIFY: - handleNotify(msg.arg1, msg.arg2, (Integer) msg.obj); - break; - - default: - Slog.w(TAG, "Unknown message:" + msg.what); - } - } - }; - - private static final int STATE_IDLE = 0; - private static final int STATE_LISTENING = 1; - private static final int STATE_ENROLLING = 2; - private static final int STATE_DELETING = 3; - private static final long MS_PER_SEC = 1000; - - private static final class ClientData { - public IFingerprintServiceReceiver receiver; - int state; - int userId; - } - - @Override - public final IBinder onBind(Intent intent) { - if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent); - return new FingerprintServiceWrapper(); - } - - // JNI methods to communicate from FingerprintManagerService to HAL - native int nativeEnroll(int timeout); - native int nativeRemove(int fingerprintId); - - // JNI methods for communicating from HAL to clients - void notify(int msg, int arg1, int arg2) { - mHandler.obtainMessage(MSG_NOTIFY, msg, arg1, arg2).sendToTarget(); - } - - void handleNotify(int msg, int arg1, int arg2) { - for (int i = 0; i < mClients.size(); i++) { - ClientData clientData = mClients.get(i); - switch (msg) { - case FingerprintManager.FINGERPRINT_ERROR: { - if (clientData.state != STATE_IDLE) { - // FINGERPRINT_ERROR_HW_UNAVAILABLE - // FINGERPRINT_ERROR_BAD_CAPTURE - // FINGERPRINT_ERROR_TIMEOUT - // FINGERPRINT_ERROR_NO_SPACE - final int error = arg1; - clientData.state = STATE_IDLE; - if (clientData.receiver != null) { - try { - clientData.receiver.onError(error); - } catch (RemoteException e) { - Slog.e(TAG, "can't send message to client. Did it die?", e); - } - } - } - } - break; - case FingerprintManager.FINGERPRINT_SCANNED: { - final int fingerId = arg1; - final int confidence = arg2; - if (clientData.state == STATE_LISTENING && clientData.receiver != null) { - try { - clientData.receiver.onScanned(fingerId, confidence); - } catch (RemoteException e) { - Slog.e(TAG, "can't send message to client. Did it die?", e); - } - } - break; - } - case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: { - if (clientData.state == STATE_ENROLLING) { - final int fingerId = arg1; - final int remaining = arg2; - if (remaining == 0) { - FingerprintUtils.addFingerprintIdForUser(fingerId, - getContentResolver(), clientData.userId); - clientData.state = STATE_IDLE; // Nothing left to do - } - if (clientData.receiver != null) { - try { - clientData.receiver.onEnrollResult(fingerId, remaining); - } catch (RemoteException e) { - Slog.e(TAG, "can't send message to client. Did it die?", e); - } - } - } - break; - } - case FingerprintManager.FINGERPRINT_TEMPLATE_REMOVED: { - int fingerId = arg1; - if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL"); - if (clientData.state == STATE_DELETING) { - FingerprintUtils.removeFingerprintIdForUser(fingerId, getContentResolver(), - clientData.userId); - if (clientData.receiver != null) { - try { - clientData.receiver.onRemoved(fingerId); - } catch (RemoteException e) { - Slog.e(TAG, "can't send message to client. Did it die?", e); - } - } - } - } - break; - } - } - } - - int enroll(IFingerprintServiceReceiver receiver, long timeout, int userId) { - ClientData clientData = mClients.get(receiver); - if (clientData != null) { - if (clientData.userId != userId) throw new IllegalStateException("Bad user"); - clientData.state = STATE_ENROLLING; - return nativeEnroll((int) (timeout / MS_PER_SEC)); - } - return -1; - } - - int remove(IFingerprintServiceReceiver receiver, int fingerId, int userId) { - ClientData clientData = mClients.get(receiver); - if (clientData != null) { - if (clientData.userId != userId) throw new IllegalStateException("Bad user"); - clientData.state = STATE_DELETING; - // The fingerprint id will be removed when we get confirmation from the HAL - return nativeRemove(fingerId); - } - return -1; - } - - void startListening(IFingerprintServiceReceiver receiver, int userId) { - ClientData clientData = new ClientData(); - clientData.state = STATE_LISTENING; - clientData.receiver = receiver; - clientData.userId = userId; - mClients.put(receiver, clientData); - } - - void stopListening(IFingerprintServiceReceiver receiver, int userId) { - ClientData clientData = mClients.get(receiver); - if (clientData != null) { - clientData.state = STATE_IDLE; - clientData.userId = -1; - clientData.receiver = null; - } - mClients.remove(receiver); - } - - private final class FingerprintServiceWrapper extends IFingerprintService.Stub { - IFingerprintServiceReceiver mReceiver; - public int enroll(long timeout, int userId) { - return mReceiver != null ? FingerprintService.this.enroll(mReceiver, timeout, userId) - : FingerprintManager.FINGERPRINT_ERROR_NO_RECEIVER; - } - - public int remove(int fingerprintId, int userId) { - return FingerprintService.this.remove(mReceiver, fingerprintId, userId); - } - - public void startListening(IFingerprintServiceReceiver receiver, int userId) { - mReceiver = receiver; - FingerprintService.this.startListening(receiver, userId); - } - - public void stopListening(int userId) { - FingerprintService.this.stopListening(mReceiver, userId); - } - } -} diff --git a/core/java/android/service/fingerprint/FingerprintUtils.java b/core/java/android/service/fingerprint/FingerprintUtils.java index 81a2aac5867b..f4b5526928c9 100644 --- a/core/java/android/service/fingerprint/FingerprintUtils.java +++ b/core/java/android/service/fingerprint/FingerprintUtils.java @@ -18,10 +18,12 @@ package android.service.fingerprint; import android.content.ContentResolver; import android.provider.Settings; +import android.text.TextUtils; import android.util.Log; import java.util.Arrays; +public class FingerprintUtils { private static final boolean DEBUG = true; private static final String TAG = "FingerprintUtils"; @@ -30,13 +32,16 @@ class FingerprintUtils { String fingerIdsRaw = Settings.Secure.getStringForUser(res, Settings.Secure.USER_FINGERPRINT_IDS, userId); - String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", "); - int result[] = new int[fingerStringIds.length]; - for (int i = 0; i < result.length; i++) { - try { - result[i] = Integer.decode(fingerStringIds[i]); - } catch (NumberFormatException e) { - if (DEBUG) Log.d(TAG, "Error when parsing finger id " + fingerStringIds[i]); + int result[] = {}; + if (!TextUtils.isEmpty(fingerIdsRaw)) { + String[] fingerStringIds = fingerIdsRaw.replace("[","").replace("]","").split(", "); + result = new int[fingerStringIds.length]; + for (int i = 0; i < result.length; i++) { + try { + result[i] = Integer.decode(fingerStringIds[i]); + } catch (NumberFormatException e) { + if (DEBUG) Log.d(TAG, "Error when parsing finger id " + fingerStringIds[i]); + } } } return result; diff --git a/core/java/android/service/fingerprint/IFingerprintService.aidl b/core/java/android/service/fingerprint/IFingerprintService.aidl index e92c20cd0339..43d5e9a48d13 100644 --- a/core/java/android/service/fingerprint/IFingerprintService.aidl +++ b/core/java/android/service/fingerprint/IFingerprintService.aidl @@ -22,17 +22,20 @@ import android.service.fingerprint.IFingerprintServiceReceiver; * Communication channel from client to the fingerprint service. * @hide */ -interface IFingerprintService { - // Returns 0 if successfully started, -1 otherwise - int enroll(long timeout, int userId); +oneway interface IFingerprintService { + // Any errors resulting from this call will be returned to the listener + void enroll(IBinder token, long timeout, int userId); + + // Any errors resulting from this call will be returned to the listener + void enrollCancel(IBinder token, int userId); - // Returns 0 if fingerprintId's template can be removed, -1 otherwise - int remove(int fingerprintId, int userId); + // Any errors resulting from this call will be returned to the listener + void remove(IBinder token, int fingerprintId, int userId); // Start listening for fingerprint events. This has the side effect of starting // the hardware if not already started. - oneway void startListening(IFingerprintServiceReceiver receiver, int userId); + void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId); // Stops listening for fingerprints - oneway void stopListening(int userId); + void stopListening(IBinder token, int userId); } diff --git a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl index 4826b59b650c..af4128f1122d 100644 --- a/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl +++ b/core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl @@ -24,7 +24,8 @@ import android.os.UserHandle; */ oneway interface IFingerprintServiceReceiver { void onEnrollResult(int fingerprintId, int remaining); - void onScanned(int fingerprintId, int confidence); + void onAcquired(int acquiredInfo); + void onProcessed(int fingerprintId); void onError(int error); void onRemoved(int fingerprintId); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index cafb044e4351..9156216dd619 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -13447,8 +13447,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, // Destroy any previous software drawing cache if needed if (mLayerType == LAYER_TYPE_SOFTWARE) { destroyDrawingCache(); - invalidateParentCaches(); - invalidate(true); } mLayerType = layerType; @@ -13456,13 +13454,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mLayerPaint = layerDisabled ? null : (paint == null ? new Paint() : paint); mRenderNode.setLayerPaint(mLayerPaint); - if (mLayerType == LAYER_TYPE_SOFTWARE) { - // LAYER_TYPE_SOFTWARE is handled by View:draw(), so need to invalidate() - invalidateParentCaches(); - invalidate(true); - } else { - invalidateViewProperty(false, false); - } + // draw() behaves differently if we are on a layer, so we need to + // invalidate() here + invalidateParentCaches(); + invalidate(true); } /** diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index 960fa49e2154..643225d7f031 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -16,6 +16,7 @@ package com.android.internal.backup; +import android.app.backup.RestoreDescription; import android.app.backup.RestoreSet; import android.content.Intent; import android.content.pm.PackageInfo; @@ -168,12 +169,25 @@ interface IBackupTransport { int startRestore(long token, in PackageInfo[] packages); /** - * Get the package name of the next application with data in the backup store. - * @return The name of one of the packages supplied to {@link #startRestore}, - * or "" (the empty string) if no more backup data is available, - * or null if an error occurred (the restore should be aborted and rescheduled). + * Get the package name of the next application with data in the backup store, plus + * a description of the structure of the restored archive: either TYPE_KEY_VALUE for + * an original-API key/value dataset, or TYPE_FULL_STREAM for a tarball-type archive stream. + * + * <p>If the package name in the returned RestoreDescription object is the singleton + * {@link RestoreDescription#NO_MORE_PACKAGES}, it indicates that no further data is available + * in the current restore session: all packages described in startRestore() have been + * processed. + * + * <p>If this method returns {@code null}, it means that a transport-level error has + * occurred and the entire restore operation should be abandoned. + * + * @return A RestoreDescription object containing the name of one of the packages + * supplied to {@link #startRestore} plus an indicator of the data type of that + * restore data; or {@link RestoreDescription#NO_MORE_PACKAGES} to indicate that + * no more packages can be restored in this session; or {@code null} to indicate + * a transport-level error. */ - String nextRestorePackage(); + RestoreDescription nextRestorePackage(); /** * Get the data for the application returned by {@link #nextRestorePackage}. @@ -188,7 +202,58 @@ interface IBackupTransport { */ void finishRestore(); + // full backup stuff + long requestFullBackupTime(); int performFullBackup(in PackageInfo targetPackage, in ParcelFileDescriptor socket); int sendBackupData(int numBytes); + + // full restore stuff + + /** + * Ask the transport to provide data for the "current" package being restored. This + * is the package that was just reported by {@link #nextRestorePackage()} as having + * {@link RestoreDescription#TYPE_FULL_STREAM} data. + * + * The transport writes some data to the socket supplied to this call, and returns + * the number of bytes written. The system will then read that many bytes and + * stream them to the application's agent for restore, then will call this method again + * to receive the next chunk of the archive. This sequence will be repeated until the + * transport returns zero indicating that all of the package's data has been delivered + * (or returns a negative value indicating some sort of hard error condition at the + * transport level). + * + * <p>After this method returns zero, the system will then call + * {@link #getNextFullRestorePackage()} to begin the restore process for the next + * application, and the sequence begins again. + * + * <p>The transport should always close this socket when returning from this method. + * Do not cache this socket across multiple calls or you may leak file descriptors. + * + * @param socket The file descriptor that the transport will use for delivering the + * streamed archive. The transport must close this socket in all cases when returning + * from this method. + * @return 0 when no more data for the current package is available. A positive value + * indicates the presence of that many bytes to be delivered to the app. Any negative + * return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR}, + * indicating a fatal error condition that precludes further restore operations + * on the current dataset. + */ + int getNextFullRestoreDataChunk(in ParcelFileDescriptor socket); + + /** + * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM} + * data for restore, it will invoke this method to tell the transport that it should + * abandon the data download for the current package. The OS will then either call + * {@link #nextRestorePackage()} again to move on to restoring the next package in the + * set being iterated over, or will call {@link #finishRestore()} to shut down the restore + * operation. + * + * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the + * current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious + * transport-level failure. If the transport reports an error here, the entire restore + * operation will immediately be finished with no further attempts to restore app data. + */ + int abortFullRestore(); + } diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index c9d621dcd2d8..ff0ee65c911d 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -19,6 +19,7 @@ package com.android.internal.backup; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.app.backup.BackupTransport; +import android.app.backup.RestoreDescription; import android.app.backup.RestoreSet; import android.content.ComponentName; import android.content.Context; @@ -63,18 +64,24 @@ public class LocalTransport extends BackupTransport { private static final String TRANSPORT_DESTINATION_STRING = "Backing up to debug-only private cache"; + private static final String INCREMENTAL_DIR = "_delta"; + private static final String FULL_DATA_DIR = "_full"; + // The currently-active restore set always has the same (nonzero!) token private static final long CURRENT_SET_TOKEN = 1; private Context mContext; private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup"); private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN)); - private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, "_delta"); - private File mCurrentSetFullDir = new File(mCurrentSetDir, "_full"); + private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, INCREMENTAL_DIR); + private File mCurrentSetFullDir = new File(mCurrentSetDir, FULL_DATA_DIR); private PackageInfo[] mRestorePackages = null; private int mRestorePackage = -1; // Index into mRestorePackages - private File mRestoreDataDir; + private int mRestoreType; + private File mRestoreSetDir; + private File mRestoreSetIncrementalDir; + private File mRestoreSetFullDir; private long mRestoreToken; // Additional bookkeeping for full backup @@ -361,30 +368,55 @@ public class LocalTransport extends BackupTransport { mRestorePackages = packages; mRestorePackage = -1; mRestoreToken = token; - mRestoreDataDir = new File(mDataDir, Long.toString(token)); + mRestoreSetDir = new File(mDataDir, Long.toString(token)); + mRestoreSetIncrementalDir = new File(mRestoreSetDir, INCREMENTAL_DIR); + mRestoreSetFullDir = new File(mRestoreSetDir, FULL_DATA_DIR); return BackupTransport.TRANSPORT_OK; } - public String nextRestorePackage() { + @Override + public RestoreDescription nextRestorePackage() { if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); + + boolean found = false; while (++mRestorePackage < mRestorePackages.length) { String name = mRestorePackages[mRestorePackage].packageName; + + // If we have key/value data for this package, deliver that // skip packages where we have a data dir but no actual contents - String[] contents = (new File(mRestoreDataDir, name)).list(); + String[] contents = (new File(mRestoreSetIncrementalDir, name)).list(); if (contents != null && contents.length > 0) { - if (DEBUG) Log.v(TAG, " nextRestorePackage() = " + name); - return name; + if (DEBUG) Log.v(TAG, " nextRestorePackage(TYPE_KEY_VALUE) = " + name); + mRestoreType = RestoreDescription.TYPE_KEY_VALUE; + found = true; + } + + if (!found) { + // No key/value data; check for [non-empty] full data + File maybeFullData = new File(mRestoreSetFullDir, name); + if (maybeFullData.length() > 0) { + if (DEBUG) Log.v(TAG, " nextRestorePackage(TYPE_FULL_STREAM) = " + name); + mRestoreType = RestoreDescription.TYPE_FULL_STREAM; + found = true; + } + } + + if (found) { + return new RestoreDescription(name, mRestoreType); } } if (DEBUG) Log.v(TAG, " no more packages to restore"); - return ""; + return RestoreDescription.NO_MORE_PACKAGES; } public int getRestoreData(ParcelFileDescriptor outFd) { if (mRestorePackages == null) throw new IllegalStateException("startRestore not called"); if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called"); - File packageDir = new File(mRestoreDataDir, mRestorePackages[mRestorePackage].packageName); + if (mRestoreType != RestoreDescription.TYPE_KEY_VALUE) { + throw new IllegalStateException("getRestoreData(fd) for non-key/value dataset"); + } + File packageDir = new File(mRestoreSetDir, mRestorePackages[mRestorePackage].packageName); // The restore set is the concatenation of the individual record blobs, // each of which is a file in the package's directory. We return the @@ -463,8 +495,8 @@ public class LocalTransport extends BackupTransport { // Full restore handling public int prepareFullRestore(long token, String[] targetPackages) { - mRestoreDataDir = new File(mDataDir, Long.toString(token)); - mFullRestoreSetDir = new File(mRestoreDataDir, "_full"); + mRestoreSetDir = new File(mDataDir, Long.toString(token)); + mFullRestoreSetDir = new File(mRestoreSetDir, FULL_DATA_DIR); mFullRestorePackages = new HashSet<String>(); if (mFullRestoreSetDir.exists()) { List<String> pkgs = Arrays.asList(mFullRestoreSetDir.list()); diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index f2b9bac92a9f..a7a1faad41cb 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -159,6 +159,7 @@ extern int register_android_net_TrafficStats(JNIEnv* env); extern int register_android_text_AndroidCharacter(JNIEnv *env); extern int register_android_text_AndroidBidi(JNIEnv *env); extern int register_android_opengl_classes(JNIEnv *env); +extern int register_android_server_fingerprint_FingerprintService(JNIEnv* env); extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env); extern int register_android_server_Watchdog(JNIEnv* env); extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env); @@ -1328,6 +1329,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_media_ToneGenerator), REG_JNI(register_android_opengl_classes), + REG_JNI(register_android_server_fingerprint_FingerprintService), REG_JNI(register_android_server_NetworkManagementSocketTagger), REG_JNI(register_android_server_Watchdog), REG_JNI(register_android_ddm_DdmHandleNativeHeap), diff --git a/core/jni/android_server_FingerprintManager.cpp b/core/jni/android_server_FingerprintManager.cpp index f8a1fd9a4893..ad36843c764b 100644 --- a/core/jni/android_server_FingerprintManager.cpp +++ b/core/jni/android_server_FingerprintManager.cpp @@ -20,54 +20,195 @@ #include <android_runtime/AndroidRuntime.h> #include <android_runtime/Log.h> +#include <hardware/hardware.h> +#include <hardware/fingerprint.h> #include <utils/Log.h> +#define FIND_CLASS(var, className) \ + var = env->FindClass(className); \ + LOG_FATAL_IF(! var, "Unable to find class " className); \ + var = jclass(env->NewGlobalRef(var)); + +#define GET_STATIC_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find static method" methodName); + +#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ + var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find method" methodName); + +#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ + var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ + LOG_FATAL_IF(! var, "Unable to find field " fieldName); + namespace android { +static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(1, 0); + +static const char* FINGERPRINT_SERVICE = "com/android/server/fingerprint/FingerprintService"; static struct { jclass clazz; jmethodID notify; -} gFingerprintManagerClassInfo; + jobject callbackObject; +} gFingerprintServiceClassInfo; + +static struct { + fingerprint_module_t const* module; + fingerprint_device_t *device; +} gContext; + +// TODO: remove after driver update to use new HAL +fingerprint_msg_type_t hackTilFpDriverUpdate(fingerprint_msg_type_t t) { + switch(static_cast<int>(t)) { + case 1: return FINGERPRINT_PROCESSED; + case 2: return FINGERPRINT_TEMPLATE_ENROLLING; + default: return t; + } +} + +// Called by the HAL to notify us of fingerprint events +static void hal_notify_callback(fingerprint_msg_t msg) { + uint32_t arg1 = 0; + uint32_t arg2 = 0; + uint32_t arg3 = 0; // TODO + msg.type = hackTilFpDriverUpdate(msg.type); + switch (msg.type) { + case FINGERPRINT_ERROR: + arg1 = msg.data.error; + break; + case FINGERPRINT_ACQUIRED: + arg1 = msg.data.acquired.acquired_info; + break; + case FINGERPRINT_PROCESSED: + arg1 = msg.data.processed.id; + break; + case FINGERPRINT_TEMPLATE_ENROLLING: + arg1 = msg.data.enroll.id; + arg2 = msg.data.enroll.samples_remaining; + arg3 = msg.data.enroll.data_collected_bmp; + break; + case FINGERPRINT_TEMPLATE_REMOVED: + arg1 = msg.data.removed.id; + break; + default: + ALOGE("fingerprint: invalid msg: %d", msg.type); + return; + } + //ALOG(LOG_VERBOSE, LOG_TAG, "hal_notify(msg=%d, arg1=%d, arg2=%d)\n", msg.type, arg1, arg2); + + // TODO: fix gross hack to attach JNI to calling thread + JNIEnv* env = AndroidRuntime::getJNIEnv(); + if (env == NULL) { + JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL}; + JavaVM* vm = AndroidRuntime::getJavaVM(); + int result = vm->AttachCurrentThread(&env, (void*) &args); + if (result != JNI_OK) { + ALOGE("Can't call JNI method: attach failed: %#x", result); + return; + } + } + env->CallVoidMethod(gFingerprintServiceClassInfo.callbackObject, + gFingerprintServiceClassInfo.notify, msg.type, arg1, arg2); +} + +static void nativeInit(JNIEnv *env, jobject clazz, jobject callbackObj) { + ALOG(LOG_VERBOSE, LOG_TAG, "nativeInit()\n"); + FIND_CLASS(gFingerprintServiceClassInfo.clazz, FINGERPRINT_SERVICE); + GET_METHOD_ID(gFingerprintServiceClassInfo.notify, gFingerprintServiceClassInfo.clazz, + "notify", "(III)V"); + gFingerprintServiceClassInfo.callbackObject = env->NewGlobalRef(callbackObj); +} static jint nativeEnroll(JNIEnv* env, jobject clazz, jint timeout) { - return -1; // TODO + ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnroll()\n"); + int ret = gContext.device->enroll(gContext.device, timeout); + return reinterpret_cast<jint>(ret); +} + +static jint nativeEnrollCancel(JNIEnv* env, jobject clazz) { + ALOG(LOG_VERBOSE, LOG_TAG, "nativeEnrollCancel()\n"); + int ret = gContext.device->enroll_cancel(gContext.device); + return reinterpret_cast<jint>(ret); } static jint nativeRemove(JNIEnv* env, jobject clazz, jint fingerprintId) { - return -1; // TODO + ALOG(LOG_VERBOSE, LOG_TAG, "nativeRemove(%d)\n", fingerprintId); + int ret = gContext.device->remove(gContext.device, fingerprintId); + return reinterpret_cast<jint>(ret); +} + +static jint nativeOpenHal(JNIEnv* env, jobject clazz) { + ALOG(LOG_VERBOSE, LOG_TAG, "nativeOpenHal()\n"); + int err; + const hw_module_t *hw_module = NULL; + if (0 != (err = hw_get_module(FINGERPRINT_HARDWARE_MODULE_ID, &hw_module))) { + ALOGE("Can't open fingerprint HW Module, error: %d", err); + return 0; + } + if (NULL == hw_module) { + ALOGE("No valid fingerprint module"); + return 0; + } + + gContext.module = reinterpret_cast<const fingerprint_module_t*>(hw_module); + + if (gContext.module->common.methods->open == NULL) { + ALOGE("No valid open method"); + return 0; + } + + hw_device_t *device = NULL; + + if (0 != (err = gContext.module->common.methods->open(hw_module, NULL, &device))) { + ALOGE("Can't open fingerprint methods, error: %d", err); + return 0; + } + + if (kVersion != device->version) { + ALOGE("Wrong fp version. Expected %d, got %d", kVersion, device->version); + // return 0; // FIXME + } + + gContext.device = reinterpret_cast<fingerprint_device_t*>(device); + err = gContext.device->set_notify(gContext.device, hal_notify_callback); + if (err < 0) { + ALOGE("Failed in call to set_notify(), err=%d", err); + return 0; + } + + // Sanity check - remove + if (gContext.device->notify != hal_notify_callback) { + ALOGE("NOTIFY not set properly: %p != %p", gContext.device->notify, hal_notify_callback); + } + + ALOG(LOG_VERBOSE, LOG_TAG, "fingerprint HAL successfully initialized"); + return reinterpret_cast<jlong>(gContext.device); +} + +static jint nativeCloseHal(JNIEnv* env, jobject clazz) { + return -ENOSYS; // TODO } // ---------------------------------------------------------------------------- +// TODO: clean up void methods static const JNINativeMethod g_methods[] = { { "nativeEnroll", "(I)I", (void*)nativeEnroll }, + { "nativeEnrollCancel", "()I", (void*)nativeEnroll }, { "nativeRemove", "(I)I", (void*)nativeRemove }, + { "nativeOpenHal", "()I", (void*)nativeOpenHal }, + { "nativeCloseHal", "()I", (void*)nativeCloseHal }, + { "nativeInit", "(Lcom/android/server/fingerprint/FingerprintService;)V", (void*)nativeInit } }; -#define FIND_CLASS(var, className) \ - var = env->FindClass(className); \ - LOG_FATAL_IF(! var, "Unable to find class " className); \ - var = jclass(env->NewGlobalRef(var)); - -#define GET_STATIC_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ - var = env->GetStaticMethodID(clazz, methodName, fieldDescriptor); \ - LOG_FATAL_IF(! var, "Unable to find static method" methodName); - -#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ - var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ - LOG_FATAL_IF(! var, "Unable to find method" methodName); - -#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ - var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ - LOG_FATAL_IF(! var, "Unable to find field " fieldName); - -int register_android_server_FingerprintManager(JNIEnv* env) { - FIND_CLASS(gFingerprintManagerClassInfo.clazz, - "android/service/fingerprint/FingerprintManager"); - GET_METHOD_ID(gFingerprintManagerClassInfo.notify, gFingerprintManagerClassInfo.clazz, - "notify", "(III)V"); - return AndroidRuntime::registerNativeMethods( - env, "com/android/service/fingerprint/FingerprintManager", g_methods, NELEM(g_methods)); +int register_android_server_fingerprint_FingerprintService(JNIEnv* env) { + FIND_CLASS(gFingerprintServiceClassInfo.clazz, FINGERPRINT_SERVICE); + GET_METHOD_ID(gFingerprintServiceClassInfo.notify, gFingerprintServiceClassInfo.clazz, "notify", + "(III)V"); + int result = AndroidRuntime::registerNativeMethods( + env, FINGERPRINT_SERVICE, g_methods, NELEM(g_methods)); + ALOG(LOG_VERBOSE, LOG_TAG, "FingerprintManager JNI ready.\n"); + return result; } } // namespace android diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index edca10148144..c08c5e2ef61c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2928,6 +2928,12 @@ android:permission="android.permission.BIND_JOB_SERVICE" > </service> + <service + android:name="com.android.server.pm.BackgroundDexOptService" + android:exported="true" + android:permission="android.permission.BIND_JOB_SERVICE"> + </service> + </application> </manifest> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java index 4cbb06b5ef9b..ccbaed346e08 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java @@ -969,7 +969,7 @@ public class NotificationStackScrollLayout extends ViewGroup * @param animate Should an animation be performed. */ public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) { - setOverScrollAmount(numPixels * getRubberBandFactor(), onTop, animate, true); + setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true); } /** @@ -1005,7 +1005,7 @@ public class NotificationStackScrollLayout extends ViewGroup if (animate) { mStateAnimator.animateOverScrollToAmount(amount, onTop); } else { - setOverScrolledPixels(amount / getRubberBandFactor(), onTop); + setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop); mAmbientState.setOverScrollAmount(amount, onTop); if (onTop) { notifyOverscrollTopListener(amount); @@ -1227,13 +1227,14 @@ public class NotificationStackScrollLayout extends ViewGroup mOwnScrollY -= (int) topAmount; mDontReportNextOverScroll = true; setOverScrollAmount(0, true, false); - mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor() + mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */) * mOverflingDistance + topAmount; } else if (velocityY > 0 && bottomAmount > 0) { mOwnScrollY += bottomAmount; setOverScrollAmount(0, false, false); - mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor() - * mOverflingDistance + bottomAmount; + mMaxOverScroll = Math.abs(velocityY) / 1000f + * getRubberBandFactor(false /* onTop */) * mOverflingDistance + + bottomAmount; } else { // it will be set once we reach the boundary mMaxOverScroll = 0.0f; @@ -1275,7 +1276,10 @@ public class NotificationStackScrollLayout extends ViewGroup return Math.max(desiredPadding, mIntrinsicPadding); } - private float getRubberBandFactor() { + private float getRubberBandFactor(boolean onTop) { + if (!onTop) { + return RUBBER_BAND_FACTOR_NORMAL; + } if (mExpandedInThisMotion) { return RUBBER_BAND_FACTOR_AFTER_EXPAND; } else if (mIsExpansionChanging) { diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index cef68301853e..3b1e88a11d2f 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -28,6 +28,7 @@ import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.app.backup.BackupTransport; import android.app.backup.FullBackup; +import android.app.backup.RestoreDescription; import android.app.backup.RestoreSet; import android.app.backup.IBackupManager; import android.app.backup.IFullBackupRestoreObserver; @@ -5177,7 +5178,9 @@ public class BackupManagerService extends IBackupManager.Stub { void restorePmMetadata() { try { - String packageName = mTransport.nextRestorePackage(); + RestoreDescription desc = mTransport.nextRestorePackage(); + // TODO: handle full-data stream restore payloads + String packageName = desc.getPackageName(); if (packageName == null) { Slog.e(TAG, "Error getting first restore package"); EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE); @@ -5245,7 +5248,9 @@ public class BackupManagerService extends IBackupManager.Stub { void restoreNextAgent() { try { - String packageName = mTransport.nextRestorePackage(); + final RestoreDescription desc = mTransport.nextRestorePackage(); + // TODO: handle full-data stream restore payloads + String packageName = desc.getPackageName(); if (packageName == null) { Slog.e(TAG, "Error getting next restore package"); diff --git a/services/core/java/com/android/server/IdleMaintenanceService.java b/services/core/java/com/android/server/IdleMaintenanceService.java deleted file mode 100644 index acc6abe89b33..000000000000 --- a/services/core/java/com/android/server/IdleMaintenanceService.java +++ /dev/null @@ -1,818 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import android.app.AlarmManager; -import android.app.PendingIntent; -import android.app.maintenance.IIdleCallback; -import android.app.maintenance.IIdleService; -import android.app.maintenance.IdleService; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.ServiceConnection; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.os.Handler; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; -import android.os.IBinder; -import android.os.Looper; -import android.os.Message; -import android.os.Process; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.UserHandle; -import android.os.WorkSource; -import android.util.ArrayMap; -import android.util.Log; -import android.util.Slog; -import android.util.SparseArray; - -import java.util.LinkedList; -import java.util.List; -import java.util.Random; - -/** - * This service observes the device state and when applicable sends - * broadcasts at the beginning and at the end of a period during which - * observers can perform idle maintenance tasks. Typical use of the - * idle maintenance is to perform somehow expensive tasks that can be - * postponed to a moment when they will not degrade user experience. - * - * The current implementation is very simple. The start of a maintenance - * window is announced if: the screen is off or showing a dream AND the - * battery level is more than twenty percent AND at least one hour passed - * activity). - * - * The end of a maintenance window is announced only if: a start was - * announced AND the screen turned on or a dream was stopped. - * - * Method naming note: - * Methods whose name ends with "Tm" must only be called from the main thread. - */ -public class IdleMaintenanceService extends BroadcastReceiver { - - private static final boolean DEBUG = false; - - private static final String TAG = IdleMaintenanceService.class.getSimpleName(); - - private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1; - - private static final long MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS = 24 * 60 * 60 * 1000; // 1 day - - private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING = 30; // percent - - private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING = 80; // percent - - private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 20; // percent - - private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 71 * 60 * 1000; // 71 min - - private static final long MAX_IDLE_MAINTENANCE_DURATION = 71 * 60 * 1000; // 71 min - - private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE = - "com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE"; - - private static final String ACTION_FORCE_IDLE_MAINTENANCE = - "com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE"; - - static final int MSG_OP_COMPLETE = 1; - static final int MSG_IDLE_FINISHED = 2; - static final int MSG_TIMEOUT = 3; - - // when a timeout happened, what were we expecting? - static final int VERB_BINDING = 1; - static final int VERB_IDLING = 2; - static final int VERB_ENDING = 3; - - // What are our relevant timeouts / allocated slices? - static final long OP_TIMEOUT = 8 * 1000; // 8 seconds to bind or ack the start - static final long IDLE_TIMESLICE = 10 * 60 * 1000; // ten minutes for each idler - - private final AlarmManager mAlarmService; - private final BatteryService mBatteryService; - private final PendingIntent mUpdateIdleMaintenanceStatePendingIntent; - private final Context mContext; - private final WakeLock mWakeLock; - private final WorkSource mSystemWorkSource = new WorkSource(Process.myUid()); - - private long mLastIdleMaintenanceStartTimeMillis; - private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID; - private boolean mIdleMaintenanceStarted; - - final IdleCallback mCallback; - final Handler mHandler; - - final Random mTokenGenerator = new Random(); - - int makeToken() { - int token; - do { - token = mTokenGenerator.nextInt(Integer.MAX_VALUE); - } while (token == 0); - return token; - } - - class ActiveTask { - public IdleServiceInfo who; - public int verb; - public int token; - - ActiveTask(IdleServiceInfo target, int action) { - who = target; - verb = action; - token = makeToken(); - } - - @Override - public String toString() { - return "ActiveTask{" + Integer.toHexString(this.hashCode()) - + " : verb=" + verb - + " : token=" + token - + " : "+ who + "}"; - } - } - - // What operations are in flight? - final SparseArray<ActiveTask> mPendingOperations = new SparseArray<ActiveTask>(); - - // Idle service queue management - class IdleServiceInfo { - public final ComponentName componentName; - public final int uid; - public IIdleService service; - - IdleServiceInfo(ResolveInfo info, ComponentName cname) { - componentName = cname; // derived from 'info' but this avoids an extra object - uid = info.serviceInfo.applicationInfo.uid; - service = null; - } - - @Override - public int hashCode() { - return componentName.hashCode(); - } - - @Override - public String toString() { - return "IdleServiceInfo{" + componentName - + " / " + (service == null ? "null" : service.asBinder()) + "}"; - } - } - - final ArrayMap<ComponentName, IdleServiceInfo> mIdleServices = - new ArrayMap<ComponentName, IdleServiceInfo>(); - final LinkedList<IdleServiceInfo> mIdleServiceQueue = new LinkedList<IdleServiceInfo>(); - IdleServiceInfo mCurrentIdler; // set when we've committed to launching an idler - IdleServiceInfo mLastIdler; // end of queue when idling begins - - void reportNoTimeout(int token, boolean result) { - final Message msg = mHandler.obtainMessage(MSG_OP_COMPLETE, result ? 1 : 0, token); - mHandler.sendMessage(msg); - } - - // Binder acknowledgment trampoline - class IdleCallback extends IIdleCallback.Stub { - @Override - public void acknowledgeStart(int token, boolean result) throws RemoteException { - reportNoTimeout(token, result); - } - - @Override - public void acknowledgeStop(int token) throws RemoteException { - reportNoTimeout(token, false); - } - - @Override - public void idleFinished(int token) throws RemoteException { - if (DEBUG) { - Slog.v(TAG, "idleFinished: " + token); - } - final Message msg = mHandler.obtainMessage(MSG_IDLE_FINISHED, 0, token); - mHandler.sendMessage(msg); - } - } - - // Stuff that we run on a Handler - class IdleHandler extends Handler { - public IdleHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - final int token = msg.arg2; - - switch (msg.what) { - case MSG_OP_COMPLETE: { - if (DEBUG) { - Slog.i(TAG, "MSG_OP_COMPLETE of " + token); - } - ActiveTask task = mPendingOperations.get(token); - if (task != null) { - mPendingOperations.remove(token); - removeMessages(MSG_TIMEOUT); - - handleOpCompleteTm(task, msg.arg1); - } else { - // Can happen in a race between timeout and actual - // (belated) completion of a "begin idling" or similar - // operation. In that state we've already processed the - // timeout, so we intentionally no-op here. - if (DEBUG) { - Slog.w(TAG, "Belated op-complete of " + token); - } - } - break; - } - - case MSG_IDLE_FINISHED: { - if (DEBUG) { - Slog.i(TAG, "MSG_IDLE_FINISHED of " + token); - } - ActiveTask task = mPendingOperations.get(token); - if (task != null) { - if (DEBUG) { - Slog.i(TAG, "... removing task " + token); - } - mPendingOperations.remove(token); - removeMessages(MSG_TIMEOUT); - - handleIdleFinishedTm(task); - } else { - // Can happen "legitimately" from an app explicitly calling - // idleFinished() after already having been told that its slice - // has ended. - if (DEBUG) { - Slog.w(TAG, "Belated idle-finished of " + token); - } - } - break; - } - - case MSG_TIMEOUT: { - if (DEBUG) { - Slog.i(TAG, "MSG_TIMEOUT of " + token); - } - ActiveTask task = mPendingOperations.get(token); - if (task != null) { - mPendingOperations.remove(token); - removeMessages(MSG_OP_COMPLETE); - - handleTimeoutTm(task); - } else { - // This one should not happen; we flushed timeout messages - // whenever we entered a state after which we have established - // that they are not appropriate. - Slog.w(TAG, "Unexpected timeout of " + token); - } - break; - } - - default: - Slog.w(TAG, "Unknown message: " + msg.what); - } - } - } - - void handleTimeoutTm(ActiveTask task) { - switch (task.verb) { - case VERB_BINDING: { - // We were trying to bind to this service, but it wedged or otherwise - // failed to respond in time. Let it stay in the queue for the next - // time around, but just give up on it for now and go on to the next. - startNextIdleServiceTm(); - break; - } - case VERB_IDLING: { - // The service has reached the end of its designated idle timeslice. - // This is not considered an error. - if (DEBUG) { - Slog.i(TAG, "Idler reached end of timeslice: " + task.who); - } - sendEndIdleTm(task.who); - break; - } - case VERB_ENDING: { - if (mCurrentIdler == task.who) { - if (DEBUG) { - Slog.i(TAG, "Task timed out when ending; unbind needed"); - } - handleIdleFinishedTm(task); - } else { - if (DEBUG) { - Slog.w(TAG, "Ending timeout for non-current idle service!"); - } - } - break; - } - default: { - Slog.w(TAG, "Unknown timeout state " + task.verb); - break; - } - } - } - - void handleOpCompleteTm(ActiveTask task, int result) { - if (DEBUG) { - Slog.i(TAG, "handleOpComplete : task=" + task + " result=" + result); - } - if (task.verb == VERB_IDLING) { - // If the service was told to begin idling and responded positively, then - // it has begun idling and will eventually either explicitly finish, or - // reach the end of its allotted timeslice. It's running free now, so we - // just schedule the idle-expiration timeout under the token it's already been - // given and let it keep going. - if (result != 0) { - scheduleOpTimeoutTm(task); - } else { - // The idle service has indicated that it does not, in fact, - // need to run at present, so we immediately indicate that it's - // to finish idling, and go on to the next idler. - if (DEBUG) { - Slog.i(TAG, "Idler declined idling; moving along"); - } - sendEndIdleTm(task.who); - } - } else { - // In the idling case, the task will be cleared either as the result of a timeout - // or of an explicit idleFinished(). For all other operations (binding, ending) we - // are done with the task as such, so we remove it from our bookkeeping. - if (DEBUG) { - Slog.i(TAG, "Clearing task " + task); - } - mPendingOperations.remove(task.token); - if (task.verb == VERB_ENDING) { - // The last bit of handshaking around idle cessation for this target - handleIdleFinishedTm(task); - } - } - } - - void handleIdleFinishedTm(ActiveTask task) { - final IdleServiceInfo who = task.who; - if (who == mCurrentIdler) { - if (DEBUG) { - Slog.i(TAG, "Current idler has finished: " + who); - Slog.i(TAG, "Attributing wakelock to system work source"); - } - mContext.unbindService(mConnection); - startNextIdleServiceTm(); - } else { - Slog.w(TAG, "finish from non-current idle service? " + who); - } - } - - void updateIdleServiceQueueTm() { - if (DEBUG) { - Slog.i(TAG, "Updating idle service queue"); - } - PackageManager pm = mContext.getPackageManager(); - Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE); - List<ResolveInfo> services = pm.queryIntentServices(idleIntent, 0); - for (ResolveInfo info : services) { - if (info.serviceInfo != null) { - if (IdleService.PERMISSION_BIND.equals(info.serviceInfo.permission)) { - final ComponentName componentName = new ComponentName( - info.serviceInfo.packageName, - info.serviceInfo.name); - if (DEBUG) { - Slog.i(TAG, " - " + componentName); - } - if (!mIdleServices.containsKey(componentName)) { - if (DEBUG) { - Slog.i(TAG, " + not known; adding"); - } - IdleServiceInfo serviceInfo = new IdleServiceInfo(info, componentName); - mIdleServices.put(componentName, serviceInfo); - mIdleServiceQueue.add(serviceInfo); - } - } else { - if (DEBUG) { - Slog.i(TAG, "Idle service " + info.serviceInfo - + " does not have required permission; ignoring"); - } - } - } - } - } - - void startNextIdleServiceTm() { - mWakeLock.setWorkSource(mSystemWorkSource); - - if (mLastIdler == null) { - // we've run the queue; nothing more to do until the next idle interval. - if (DEBUG) { - Slog.i(TAG, "Queue already drained; nothing more to do"); - } - return; - } - - if (DEBUG) { - Slog.i(TAG, "startNextIdleService : last=" + mLastIdler + " cur=" + mCurrentIdler); - if (mIdleServiceQueue.size() > 0) { - int i = 0; - Slog.i(TAG, "Queue (" + mIdleServiceQueue.size() + "):"); - for (IdleServiceInfo info : mIdleServiceQueue) { - Slog.i(TAG, " " + i + " : " + info); - i++; - } - } - } - if (mCurrentIdler != mLastIdler) { - if (mIdleServiceQueue.size() > 0) { - IdleServiceInfo target = mIdleServiceQueue.pop(); - if (DEBUG) { - Slog.i(TAG, "starting next idle service " + target); - } - Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE); - idleIntent.setComponent(target.componentName); - mCurrentIdler = target; - ActiveTask task = new ActiveTask(target, VERB_BINDING); - scheduleOpTimeoutTm(task); - boolean bindOk = mContext.bindServiceAsUser(idleIntent, mConnection, - Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY, UserHandle.OWNER); - if (!bindOk) { - if (DEBUG) { - Slog.w(TAG, "bindService() to " + target.componentName - + " failed"); - } - } else { - mIdleServiceQueue.add(target); // at the end for next time - if (DEBUG) { Slog.i(TAG, "Attributing wakelock to target uid " + target.uid); } - mWakeLock.setWorkSource(new WorkSource(target.uid)); - } - } else { - // Queue is empty but mLastIdler is non-null -- eeep. Clear *everything* - // and wind up until the next time around. - Slog.e(TAG, "Queue unexpectedly empty; resetting. last=" - + mLastIdler + " cur=" + mCurrentIdler); - mHandler.removeMessages(MSG_TIMEOUT); - mPendingOperations.clear(); - stopIdleMaintenanceTm(); - } - } else { - // we've reached the place we started, so mark the queue as drained - if (DEBUG) { - Slog.i(TAG, "Reached end of queue."); - } - stopIdleMaintenanceTm(); - } - } - - void sendStartIdleTm(IdleServiceInfo who) { - ActiveTask task = new ActiveTask(who, VERB_IDLING); - scheduleOpTimeoutTm(task); - try { - who.service.startIdleMaintenance(mCallback, task.token); - } catch (RemoteException e) { - // We bound to it, but now we can't reach it. Bail and go on to the - // next service. - mContext.unbindService(mConnection); - if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); } - mHandler.removeMessages(MSG_TIMEOUT); - startNextIdleServiceTm(); - } - } - - void sendEndIdleTm(IdleServiceInfo who) { - ActiveTask task = new ActiveTask(who, VERB_ENDING); - scheduleOpTimeoutTm(task); - if (DEBUG) { - Slog.i(TAG, "Sending end-idle to " + who); - } - try { - who.service.stopIdleMaintenance(mCallback, task.token); - } catch (RemoteException e) { - // We bound to it, but now we can't reach it. Bail and go on to the - // next service. - mContext.unbindService(mConnection); - if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); } - mHandler.removeMessages(MSG_TIMEOUT); - startNextIdleServiceTm(); - } - } - - ServiceConnection mConnection = new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - if (DEBUG) { - Slog.i(TAG, "onServiceConnected(" + name + ")"); - } - IdleServiceInfo info = mIdleServices.get(name); - if (info != null) { - // Bound! Cancel the bind timeout - mHandler.removeMessages(MSG_TIMEOUT); - // Now tell it to start its idle work - info.service = IIdleService.Stub.asInterface(service); - sendStartIdleTm(info); - } else { - // We bound to a service we don't know about. That's ungood. - Slog.e(TAG, "Connected to unexpected component " + name); - mContext.unbindService(this); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - if (DEBUG) { - Slog.i(TAG, "onServiceDisconnected(" + name + ")"); - } - IdleServiceInfo who = mIdleServices.get(name); - if (who == mCurrentIdler) { - // Hm, okay; they didn't tell us they were finished but they - // went away. Crashed, probably. Oh well. They're gone, so - // we can't finish them cleanly; just force things along. - Slog.w(TAG, "Idler unexpectedly vanished: " + mCurrentIdler); - mContext.unbindService(this); - mHandler.removeMessages(MSG_TIMEOUT); - startNextIdleServiceTm(); - } else { - // Not the current idler, so we don't interrupt our process... - if (DEBUG) { - Slog.w(TAG, "Disconnect of abandoned or unexpected service " + name); - } - } - } - }; - - // Schedules a timeout / end-of-work based on the task verb - void scheduleOpTimeoutTm(ActiveTask task) { - final long timeoutMillis = (task.verb == VERB_IDLING) ? IDLE_TIMESLICE : OP_TIMEOUT; - if (DEBUG) { - Slog.i(TAG, "Scheduling timeout (token " + task.token - + " : verb " + task.verb + ") for " + task + " in " + timeoutMillis); - } - mPendingOperations.put(task.token, task); - mHandler.removeMessages(MSG_TIMEOUT); - final Message msg = mHandler.obtainMessage(MSG_TIMEOUT, 0, task.token); - mHandler.sendMessageDelayed(msg, timeoutMillis); - } - - // ------------------------------------------------------------------------------- - public IdleMaintenanceService(Context context, BatteryService batteryService) { - mContext = context; - mBatteryService = batteryService; - - mAlarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - - PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE); - mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); - - mHandler = new IdleHandler(mContext.getMainLooper()); - mCallback = new IdleCallback(); - - Intent intent = new Intent(ACTION_UPDATE_IDLE_MAINTENANCE_STATE); - intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - mUpdateIdleMaintenanceStatePendingIntent = PendingIntent.getBroadcast(mContext, 0, - intent, PendingIntent.FLAG_UPDATE_CURRENT); - - register(mHandler); - } - - public void register(Handler handler) { - IntentFilter intentFilter = new IntentFilter(); - - // Alarm actions. - intentFilter.addAction(ACTION_UPDATE_IDLE_MAINTENANCE_STATE); - - // Battery actions. - intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); - - // Screen actions. - intentFilter.addAction(Intent.ACTION_SCREEN_ON); - intentFilter.addAction(Intent.ACTION_SCREEN_OFF); - - // Dream actions. - intentFilter.addAction(Intent.ACTION_DREAMING_STARTED); - intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED); - - mContext.registerReceiverAsUser(this, UserHandle.ALL, - intentFilter, null, mHandler); - - intentFilter = new IntentFilter(); - intentFilter.addAction(ACTION_FORCE_IDLE_MAINTENANCE); - mContext.registerReceiverAsUser(this, UserHandle.ALL, - intentFilter, android.Manifest.permission.SET_ACTIVITY_WATCHER, mHandler); - } - - private void scheduleUpdateIdleMaintenanceState(long delayMillis) { - final long triggetRealTimeMillis = SystemClock.elapsedRealtime() + delayMillis; - mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggetRealTimeMillis, - mUpdateIdleMaintenanceStatePendingIntent); - } - - private void unscheduleUpdateIdleMaintenanceState() { - mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent); - } - - private void updateIdleMaintenanceStateTm(boolean noisy) { - if (mIdleMaintenanceStarted) { - // Idle maintenance can be interrupted by user activity, or duration - // time out, or low battery. - final boolean batteryOk - = batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning(); - if (!lastUserActivityPermitsIdleMaintenanceRunning() || !batteryOk) { - unscheduleUpdateIdleMaintenanceState(); - mIdleMaintenanceStarted = false; - // We stopped since we don't have enough battery or timed out but the - // user is not using the device, so we should be able to run maintenance - // in the next maintenance window since the battery may be charged - // without interaction and the min interval between maintenances passed. - if (!batteryOk) { - scheduleUpdateIdleMaintenanceState( - getNextIdleMaintenanceIntervalStartFromNow()); - } - - EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(), - mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(), - isBatteryCharging() ? 1 : 0); - scheduleIdleFinishTm(); - } - } else if (deviceStatePermitsIdleMaintenanceStart(noisy) - && lastUserActivityPermitsIdleMaintenanceStart(noisy) - && lastRunPermitsIdleMaintenanceStart(noisy)) { - // Now that we started idle maintenance, we should schedule another - // update for the moment when the idle maintenance times out. - scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION); - mIdleMaintenanceStarted = true; - EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(), - mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(), - isBatteryCharging() ? 1 : 0); - mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime(); - startIdleMaintenanceTm(); - } else if (lastUserActivityPermitsIdleMaintenanceStart(noisy)) { - if (lastRunPermitsIdleMaintenanceStart(noisy)) { - // The user does not use the device and we did not run maintenance in more - // than the min interval between runs, so schedule an update - maybe the - // battery will be charged latter. - scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START); - } else { - // The user does not use the device but we have run maintenance in the min - // interval between runs, so schedule an update after the min interval ends. - scheduleUpdateIdleMaintenanceState( - getNextIdleMaintenanceIntervalStartFromNow()); - } - } - } - - void startIdleMaintenanceTm() { - if (DEBUG) { - Slog.i(TAG, "*** Starting idle maintenance ***"); - } - if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); } - mWakeLock.setWorkSource(mSystemWorkSource); - mWakeLock.acquire(); - updateIdleServiceQueueTm(); - mCurrentIdler = null; - mLastIdler = (mIdleServiceQueue.size() > 0) ? mIdleServiceQueue.peekLast() : null; - startNextIdleServiceTm(); - } - - // Start a graceful wind-down of the idle maintenance state: end the current idler - // and pretend that we've finished running the queue. If there's no current idler, - // this is a no-op. - void scheduleIdleFinishTm() { - if (mCurrentIdler != null) { - if (DEBUG) { - Slog.i(TAG, "*** Finishing idle maintenance ***"); - } - mLastIdler = mCurrentIdler; - sendEndIdleTm(mCurrentIdler); - } else { - if (DEBUG) { - Slog.w(TAG, "Asked to finish idle maintenance but we're done already"); - } - } - } - - // Actual finalization of the idle maintenance sequence - void stopIdleMaintenanceTm() { - if (mLastIdler != null) { - if (DEBUG) { - Slog.i(TAG, "*** Idle maintenance shutdown ***"); - } - mWakeLock.setWorkSource(mSystemWorkSource); - mLastIdler = mCurrentIdler = null; - updateIdleMaintenanceStateTm(false); // resets 'started' and schedules next window - mWakeLock.release(); - } else { - Slog.e(TAG, "ERROR: idle shutdown but invariants not held. last=" + mLastIdler - + " cur=" + mCurrentIdler + " size=" + mIdleServiceQueue.size()); - } - } - - private long getNextIdleMaintenanceIntervalStartFromNow() { - return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS - - SystemClock.elapsedRealtime(); - } - - private boolean deviceStatePermitsIdleMaintenanceStart(boolean noisy) { - final int minBatteryLevel = isBatteryCharging() - ? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING - : MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING; - boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID - && mBatteryService.getBatteryLevel() > minBatteryLevel); - if (!allowed && noisy) { - Slog.i("IdleMaintenance", "Idle maintenance not allowed due to power"); - } - return allowed; - } - - private boolean lastUserActivityPermitsIdleMaintenanceStart(boolean noisy) { - // The last time the user poked the device is above the threshold. - boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID - && SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis - > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START); - if (!allowed && noisy) { - Slog.i("IdleMaintenance", "Idle maintenance not allowed due to last user activity"); - } - return allowed; - } - - private boolean lastRunPermitsIdleMaintenanceStart(boolean noisy) { - // Enough time passed since the last maintenance run. - boolean allowed = SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis - > MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS; - if (!allowed && noisy) { - Slog.i("IdleMaintenance", "Idle maintenance not allowed due time since last"); - } - return allowed; - } - - private boolean lastUserActivityPermitsIdleMaintenanceRunning() { - // The user is not using the device. - return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID); - } - - private boolean batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning() { - // Battery not too low and the maintenance duration did not timeout. - return (mBatteryService.getBatteryLevel() > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING - && mLastIdleMaintenanceStartTimeMillis + MAX_IDLE_MAINTENANCE_DURATION - > SystemClock.elapsedRealtime()); - } - - private boolean isBatteryCharging() { - return mBatteryService.getPlugType() > 0 - && mBatteryService.getInvalidCharger() == 0; - } - - @Override - public void onReceive(Context context, Intent intent) { - if (DEBUG) { - Log.i(TAG, intent.getAction()); - } - String action = intent.getAction(); - if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { - // We care about battery only if maintenance is in progress so we can - // stop it if battery is too low. Note that here we assume that the - // maintenance clients are properly holding a wake lock. We will - // refactor the maintenance to use services instead of intents for the - // next release. The only client for this for now is internal an holds - // a wake lock correctly. - if (mIdleMaintenanceStarted) { - updateIdleMaintenanceStateTm(false); - } - } else if (Intent.ACTION_SCREEN_ON.equals(action) - || Intent.ACTION_DREAMING_STOPPED.equals(action)) { - mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID; - // Unschedule any future updates since we already know that maintenance - // cannot be performed since the user is back. - unscheduleUpdateIdleMaintenanceState(); - // If the screen went on/stopped dreaming, we know the user is using the - // device which means that idle maintenance should be stopped if running. - updateIdleMaintenanceStateTm(false); - } else if (Intent.ACTION_SCREEN_OFF.equals(action) - || Intent.ACTION_DREAMING_STARTED.equals(action)) { - mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime(); - // If screen went off/started dreaming, we may be able to start idle maintenance - // after the minimal user inactivity elapses. We schedule an alarm for when - // this timeout elapses since the device may go to sleep by then. - scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START); - } else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) { - updateIdleMaintenanceStateTm(false); - } else if (ACTION_FORCE_IDLE_MAINTENANCE.equals(action)) { - long now = SystemClock.elapsedRealtime() - 1; - mLastUserActivityElapsedTimeMillis = now - MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START; - mLastIdleMaintenanceStartTimeMillis = now - MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS; - updateIdleMaintenanceStateTm(true); - } - } -} diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java new file mode 100644 index 000000000000..29415745c3b8 --- /dev/null +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -0,0 +1,328 @@ +/** + * 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 com.android.server.fingerprint; + +import android.app.Service; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.IBinder; +import android.os.PowerManager; +import android.os.RemoteException; +import android.provider.Settings; +import android.service.fingerprint.FingerprintManager; +import android.util.ArrayMap; +import android.util.Slog; + +import com.android.server.SystemService; + +import android.service.fingerprint.FingerprintUtils; +import android.service.fingerprint.IFingerprintService; +import android.service.fingerprint.IFingerprintServiceReceiver; + +import java.io.PrintWriter; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Map.Entry; +import java.util.Set; + +/** + * A service to manage multiple clients that want to access the fingerprint HAL API. + * The service is responsible for maintaining a list of clients and dispatching all + * fingerprint -related events. + * + * @hide + */ +public class FingerprintService extends SystemService { + private final String TAG = "FingerprintService"; + private static final boolean DEBUG = true; + private ArrayMap<IBinder, ClientData> mClients = new ArrayMap<IBinder, ClientData>(); + + private static final int MSG_NOTIFY = 10; + + Handler mHandler = new Handler() { + public void handleMessage(android.os.Message msg) { + switch (msg.what) { + case MSG_NOTIFY: + handleNotify(msg.arg1, msg.arg2, (Integer) msg.obj); + break; + + default: + Slog.w(TAG, "Unknown message:" + msg.what); + } + } + }; + private Context mContext; + + private static final int STATE_IDLE = 0; + private static final int STATE_LISTENING = 1; + private static final int STATE_ENROLLING = 2; + private static final int STATE_REMOVING = 3; + private static final long MS_PER_SEC = 1000; + public static final String USE_FINGERPRINT = "android.permission.USE_FINGERPRINT"; + public static final String ENROLL_FINGERPRINT = "android.permission.ENROLL_FINGERPRINT"; + + private static final class ClientData { + public IFingerprintServiceReceiver receiver; + int state; + int userId; + public TokenWatcher tokenWatcher; + IBinder getToken() { return tokenWatcher.getToken(); } + } + + private class TokenWatcher implements IBinder.DeathRecipient { + WeakReference<IBinder> token; + + TokenWatcher(IBinder token) { + this.token = new WeakReference<IBinder>(token); + } + + IBinder getToken() { return token.get(); } + public void binderDied() { + mClients.remove(token); + this.token = null; + } + + protected void finalize() throws Throwable { + try { + if (token != null) { + if (DEBUG) Slog.w(TAG, "removing leaked reference: " + token); + mClients.remove(token); + } + } finally { + super.finalize(); + } + } + } + + public FingerprintService(Context context) { + super(context); + mContext = context; + nativeInit(this); + } + + // TODO: Move these into separate process + // JNI methods to communicate from FingerprintManagerService to HAL + native int nativeEnroll(int timeout); + native int nativeEnrollCancel(); + native int nativeRemove(int fingerprintId); + native int nativeOpenHal(); + native int nativeCloseHal(); + native void nativeInit(FingerprintService service); + + // JNI methods for communicating from HAL to clients + void notify(int msg, int arg1, int arg2) { + mHandler.obtainMessage(MSG_NOTIFY, msg, arg1, arg2).sendToTarget(); + } + + void handleNotify(int msg, int arg1, int arg2) { + Slog.v(TAG, "handleNotify(msg=" + msg + ", arg1=" + arg1 + ", arg2=" + arg2 + ")"); + for (int i = 0; i < mClients.size(); i++) { + ClientData clientData = mClients.valueAt(i); + if (clientData == null || clientData.receiver == null) { + if (DEBUG) Slog.v(TAG, "clientData at " + i + " is invalid!!"); + continue; + } + switch (msg) { + case FingerprintManager.FINGERPRINT_ERROR: { + final int error = arg1; + try { + clientData.receiver.onError(error); + } catch (RemoteException e) { + Slog.e(TAG, "can't send message to client. Did it die?", e); + mClients.remove(mClients.keyAt(i)); + } + } + break; + case FingerprintManager.FINGERPRINT_ACQUIRED: { + final int acquireInfo = arg1; + try { + clientData.receiver.onAcquired(acquireInfo); + } catch (RemoteException e) { + Slog.e(TAG, "can't send message to client. Did it die?", e); + mClients.remove(mClients.keyAt(i)); + } + break; + } + case FingerprintManager.FINGERPRINT_PROCESSED: { + final int fingerId = arg1; + try { + clientData.receiver.onProcessed(fingerId); + } catch (RemoteException e) { + Slog.e(TAG, "can't send message to client. Did it die?", e); + mClients.remove(mClients.keyAt(i)); + } + break; + } + case FingerprintManager.FINGERPRINT_TEMPLATE_ENROLLING: { + final int fingerId = arg1; + final int remaining = arg2; + if (clientData.state == STATE_ENROLLING) { + // Only send enroll updates to clients that are actually enrolling + try { + clientData.receiver.onEnrollResult(fingerId, remaining); + } catch (RemoteException e) { + Slog.e(TAG, "can't send message to client. Did it die?", e); + mClients.remove(mClients.keyAt(i)); + } + // Update the database with new finger id. + // TODO: move to client code (Settings) + if (remaining == 0) { + FingerprintUtils.addFingerprintIdForUser(fingerId, + mContext.getContentResolver(), clientData.userId); + clientData.state = STATE_IDLE; // Nothing left to do + } + } else { + if (DEBUG) Slog.w(TAG, "Client not enrolling"); + break; + } + break; + } + case FingerprintManager.FINGERPRINT_TEMPLATE_REMOVED: { + int fingerId = arg1; + if (fingerId == 0) throw new IllegalStateException("Got illegal id from HAL"); + FingerprintUtils.removeFingerprintIdForUser(fingerId, + mContext.getContentResolver(), clientData.userId); + if (clientData.receiver != null) { + try { + clientData.receiver.onRemoved(fingerId); + } catch (RemoteException e) { + Slog.e(TAG, "can't send message to client. Did it die?", e); + mClients.remove(mClients.keyAt(i)); + } + } + clientData.state = STATE_LISTENING; + } + break; + } + } + } + + void startEnroll(IBinder token, long timeout, int userId) { + ClientData clientData = mClients.get(token); + if (clientData != null) { + if (clientData.userId != userId) throw new IllegalStateException("Bad user"); + clientData.state = STATE_ENROLLING; + nativeEnroll((int) (timeout / MS_PER_SEC)); + } else { + Slog.w(TAG, "enroll(): No listener registered"); + } + } + + void startEnrollCancel(IBinder token, int userId) { + ClientData clientData = mClients.get(token); + if (clientData != null) { + if (clientData.userId != userId) throw new IllegalStateException("Bad user"); + clientData.state = STATE_LISTENING; + nativeEnrollCancel(); + } else { + Slog.w(TAG, "enrollCancel(): No listener registered"); + } + } + + // Remove all fingerprints for the given user. + void startRemove(IBinder token, int fingerId, int userId) { + ClientData clientData = mClients.get(token); + if (clientData != null) { + if (clientData.userId != userId) throw new IllegalStateException("Bad user"); + clientData.state = STATE_REMOVING; + // The fingerprint id will be removed when we get confirmation from the HAL + int result = nativeRemove(fingerId); + if (result != 0) { + Slog.w(TAG, "Error removing fingerprint with id = " + fingerId); + } + } else { + Slog.w(TAG, "remove(" + token + "): No listener registered"); + } + } + + void addListener(IBinder token, IFingerprintServiceReceiver receiver, int userId) { + if (DEBUG) Slog.v(TAG, "startListening(" + receiver + ")"); + if (mClients.get(token) == null) { + ClientData clientData = new ClientData(); + clientData.state = STATE_LISTENING; + clientData.receiver = receiver; + clientData.userId = userId; + clientData.tokenWatcher = new TokenWatcher(token); + try { + token.linkToDeath(clientData.tokenWatcher, 0); + mClients.put(token, clientData); + } catch (RemoteException e) { + Slog.w(TAG, "caught remote exception in linkToDeath: ", e); + } + } else { + if (DEBUG) Slog.v(TAG, "listener already registered for " + token); + } + } + + void removeListener(IBinder token, int userId) { + if (DEBUG) Slog.v(TAG, "stopListening(" + token + ")"); + ClientData clientData = mClients.get(token); + if (clientData != null) { + token.unlinkToDeath(clientData.tokenWatcher, 0); + mClients.remove(token); + } else { + if (DEBUG) Slog.v(TAG, "listener not registered: " + token); + } + mClients.remove(token); + } + + void checkPermission(String permisison) { + // TODO + } + + private final class FingerprintServiceWrapper extends IFingerprintService.Stub { + @Override // Binder call + public void enroll(IBinder token, long timeout, int userId) { + checkPermission(ENROLL_FINGERPRINT); + startEnroll(token, timeout, userId); + } + + @Override // Binder call + public void enrollCancel(IBinder token,int userId) { + checkPermission(ENROLL_FINGERPRINT); + startEnrollCancel(token, userId); + } + + @Override // Binder call + public void remove(IBinder token, int fingerprintId, int userId) { + checkPermission(ENROLL_FINGERPRINT); // TODO: Maybe have another permission + startRemove(token, fingerprintId, userId); + } + + @Override // Binder call + public void startListening(IBinder token, IFingerprintServiceReceiver receiver, int userId) + { + checkPermission(USE_FINGERPRINT); + addListener(token, receiver, userId); + } + + @Override // Binder call + public void stopListening(IBinder token, int userId) { + checkPermission(USE_FINGERPRINT); + removeListener(token, userId); + } + } + + @Override + public void onStart() { + publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper()); + nativeOpenHal(); + } + +} diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index f2db79117346..2a7b4f60a69a 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -16,12 +16,13 @@ package com.android.server.pm; -import android.content.BroadcastReceiver; +import android.app.job.JobInfo; +import android.app.job.JobParameters; +import android.app.job.JobScheduler; +import android.app.job.JobService; +import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.os.ServiceManager; -import android.os.UserHandle; import android.util.Log; import java.util.HashSet; @@ -30,62 +31,63 @@ import java.util.concurrent.atomic.AtomicBoolean; /** * {@hide} */ -public class BackgroundDexOptService { - +public class BackgroundDexOptService extends JobService { static final String TAG = "BackgroundDexOptService"; - private final BroadcastReceiver mIdleMaintenanceReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)) { - onIdleStart(); - } else if (Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) { - onIdleStop(); - } - } - }; - - final PackageManagerService mPackageManager; + static final int BACKGROUND_DEXOPT_JOB = 808; + private static ComponentName sDexoptServiceName = new ComponentName( + BackgroundDexOptService.class.getPackage().getName(), + BackgroundDexOptService.class.getName()); final AtomicBoolean mIdleTime = new AtomicBoolean(false); - public BackgroundDexOptService(Context context) { - mPackageManager = (PackageManagerService)ServiceManager.getService("package"); - - IntentFilter idleMaintenanceFilter = new IntentFilter(); - idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START); - idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_END); - context.registerReceiverAsUser(mIdleMaintenanceReceiver, UserHandle.ALL, - idleMaintenanceFilter, null, null); + public static void schedule(Context context) { + JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); + JobInfo job = new JobInfo.Builder(BACKGROUND_DEXOPT_JOB, sDexoptServiceName) + .setRequiresDeviceIdle(true) + .setRequiresCharging(true) + .build(); + js.schedule(job); } - public boolean onIdleStart() { + @Override + public boolean onStartJob(JobParameters params) { Log.i(TAG, "onIdleStart"); - if (mPackageManager.isStorageLow()) { + final PackageManagerService pm = + (PackageManagerService)ServiceManager.getService("package"); + + if (pm.isStorageLow()) { return false; } - final HashSet<String> pkgs = mPackageManager.getPackagesThatNeedDexOpt(); + final HashSet<String> pkgs = pm.getPackagesThatNeedDexOpt(); if (pkgs == null) { return false; } + + final JobParameters jobParams = params; mIdleTime.set(true); new Thread("BackgroundDexOptService_DexOpter") { @Override public void run() { for (String pkg : pkgs) { if (!mIdleTime.get()) { - break; + // stopped while still working, so we need to reschedule + schedule(BackgroundDexOptService.this); + return; } - mPackageManager.performDexOpt(pkg, false); + pm.performDexOpt(pkg, false); } + // ran to completion, so we abandon our timeslice and do not reschedule + jobFinished(jobParams, false); } }.start(); return true; } - public void onIdleStop() { + @Override + public boolean onStopJob(JobParameters params) { Log.i(TAG, "onIdleStop"); mIdleTime.set(false); + return false; } } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 6cb6b7690d66..d05d0c714126 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -961,8 +961,6 @@ final class AccessibilityController { // If the window is not touchable, do not report it but take into account // the space it takes since the content behind it cannot be touched. if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) { - unaccountedSpace.op(boundsInScreen, unaccountedSpace, - Region.Op.DIFFERENCE); continue; } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 3102cce4ea79..f9465956567d 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -44,6 +44,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.service.dreams.DreamService; +import android.service.fingerprint.FingerprintManager; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; @@ -63,6 +64,7 @@ import com.android.server.content.ContentService; import com.android.server.devicepolicy.DevicePolicyManagerService; import com.android.server.display.DisplayManagerService; import com.android.server.dreams.DreamManagerService; +import com.android.server.fingerprint.FingerprintService; import com.android.server.hdmi.HdmiControlService; import com.android.server.input.InputManagerService; import com.android.server.job.JobSchedulerService; @@ -933,13 +935,6 @@ public final class SystemServer { } try { - Slog.i(TAG, "IdleMaintenanceService"); - new IdleMaintenanceService(context, battery); - } catch (Throwable e) { - reportWtf("starting IdleMaintenanceService", e); - } - - try { if (pm.hasSystemFeature(PackageManager.FEATURE_PRINTING)) { mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS); } @@ -989,11 +984,19 @@ public final class SystemServer { } try { + Slog.i(TAG, "Fingerprint Manager"); + mSystemServiceManager.startService(FingerprintService.class); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting FingerprintService", e); + } + + try { Slog.i(TAG, "BackgroundDexOptService"); - new BackgroundDexOptService(context); + BackgroundDexOptService.schedule(context); } catch (Throwable e) { reportWtf("starting BackgroundDexOptService", e); } + } try { diff --git a/tests/IdleServiceTest/Android.mk b/tests/IdleServiceTest/Android.mk deleted file mode 100644 index a7879c591840..000000000000 --- a/tests/IdleServiceTest/Android.mk +++ /dev/null @@ -1,13 +0,0 @@ -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -LOCAL_MODULE_TAGS := tests - -LOCAL_SRC_FILES := $(call all-subdir-java-files) - -LOCAL_PACKAGE_NAME := IdleServiceTest -LOCAL_CERTIFICATE := platform - -LOCAL_PROGUARD_ENABLED := disabled - -include $(BUILD_PACKAGE) diff --git a/tests/IdleServiceTest/AndroidManifest.xml b/tests/IdleServiceTest/AndroidManifest.xml deleted file mode 100644 index 16d2324dcd04..000000000000 --- a/tests/IdleServiceTest/AndroidManifest.xml +++ /dev/null @@ -1,59 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2014 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.idleservicetest"> - - <application> - <service android:name="TestService" - android:exported="true" - android:enabled="true" - android:permission="android.permission.BIND_IDLE_SERVICE" > - <intent-filter> - <action android:name="android.service.idle.IdleService" /> - </intent-filter> - </service> - - <service android:name="CrashingTestService" - android:exported="true" - android:enabled="true" - android:permission="android.permission.BIND_IDLE_SERVICE" > - <intent-filter> - <action android:name="android.service.idle.IdleService" /> - </intent-filter> - </service> - - <service android:name="TimeoutTestService" - android:exported="true" - android:enabled="true" - android:permission="android.permission.BIND_IDLE_SERVICE" > - <intent-filter> - <action android:name="android.service.idle.IdleService" /> - </intent-filter> - </service> - - <!-- UnpermissionedTestService should never run because it does - not require the necessary permission in its <service> block --> - <service android:name="UnpermissionedTestService" - android:exported="true" - android:enabled="true" > - <intent-filter> - <action android:name="android.service.idle.IdleService" /> - </intent-filter> - </service> - - </application> -</manifest> diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java deleted file mode 100644 index 022ebcf6e927..000000000000 --- a/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 com.android.idleservicetest; - -import android.app.maintenance.IdleService; -import android.os.Handler; -import android.util.Log; - -public class CrashingTestService extends IdleService { - static final String TAG = "CrashingTestService"; - - String mNull = null; - - @Override - public boolean onIdleStart() { - Log.i(TAG, "Idle maintenance: onIdleStart()"); - - Handler h = new Handler(); - Runnable r = new Runnable() { - @Override - public void run() { - Log.i(TAG, "Explicitly crashing"); - if (mNull.equals("")) { - Log.i(TAG, "won't happen"); - } - } - }; - Log.i(TAG, "Posting explicit crash in 15 seconds"); - h.postDelayed(r, 15 * 1000); - return true; - } - - @Override - public void onIdleStop() { - Log.i(TAG, "Idle maintenance: onIdleStop()"); - } - -} diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java deleted file mode 100644 index 7e9805fb913c..000000000000 --- a/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 com.android.idleservicetest; - -import android.app.maintenance.IdleService; -import android.os.Handler; -import android.util.Log; - -public class TestService extends IdleService { - static final String TAG = "TestService"; - - @Override - public boolean onIdleStart() { - Log.i(TAG, "Idle maintenance: onIdleStart()"); - - Handler h = new Handler(); - Runnable r = new Runnable() { - @Override - public void run() { - Log.i(TAG, "Explicitly finishing idle"); - finishIdle(); - } - }; - Log.i(TAG, "Posting explicit finish in 15 seconds"); - h.postDelayed(r, 15 * 1000); - return true; - } - - @Override - public void onIdleStop() { - Log.i(TAG, "Idle maintenance: onIdleStop()"); - } - -} diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java deleted file mode 100644 index b9fe32bf3923..000000000000 --- a/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 com.android.idleservicetest; - -import android.app.maintenance.IdleService; -import android.util.Log; - -// Should never be invoked because its manifest declaration does not -// require the necessary permission. -public class UnpermissionedTestService extends IdleService { - private static final String TAG = "UnpermissionedTestService"; - - @Override - public boolean onIdleStart() { - Log.e(TAG, "onIdleStart() for this service should never be called!"); - return false; - } - - @Override - public void onIdleStop() { - Log.e(TAG, "onIdleStop() for this service should never be called!"); - } - -} |