summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk2
-rw-r--r--CleanSpec.mk19
-rw-r--r--api/current.txt42
-rw-r--r--core/java/android/app/ContextImpl.java17
-rw-r--r--core/java/android/app/backup/BackupTransport.java97
-rw-r--r--core/java/android/app/backup/RestoreDescription.aidl (renamed from tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java)21
-rw-r--r--core/java/android/app/backup/RestoreDescription.java101
-rw-r--r--core/java/android/app/maintenance/IIdleCallback.aidl53
-rw-r--r--core/java/android/app/maintenance/IIdleService.aidl34
-rw-r--r--core/java/android/app/maintenance/IdleService.java228
-rw-r--r--core/java/android/app/maintenance/package.html5
-rw-r--r--core/java/android/provider/ContactsContract.java3
-rw-r--r--core/java/android/service/fingerprint/FingerprintManager.java136
-rw-r--r--core/java/android/service/fingerprint/FingerprintManagerReceiver.java26
-rw-r--r--core/java/android/service/fingerprint/FingerprintService.java219
-rw-r--r--core/java/android/service/fingerprint/FingerprintUtils.java19
-rw-r--r--core/java/android/service/fingerprint/IFingerprintService.aidl17
-rw-r--r--core/java/android/service/fingerprint/IFingerprintServiceReceiver.aidl3
-rw-r--r--core/java/android/view/View.java13
-rw-r--r--core/java/com/android/internal/backup/IBackupTransport.aidl75
-rw-r--r--core/java/com/android/internal/backup/LocalTransport.java56
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/android_server_FingerprintManager.cpp195
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java16
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java9
-rw-r--r--services/core/java/com/android/server/IdleMaintenanceService.java818
-rw-r--r--services/core/java/com/android/server/fingerprint/FingerprintService.java328
-rw-r--r--services/core/java/com/android/server/pm/BackgroundDexOptService.java68
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java2
-rw-r--r--services/java/com/android/server/SystemServer.java19
-rw-r--r--tests/IdleServiceTest/Android.mk13
-rw-r--r--tests/IdleServiceTest/AndroidManifest.xml59
-rw-r--r--tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java52
-rw-r--r--tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java48
-rw-r--r--tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java38
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!");
- }
-
-}