am 76d4d5d1: (-s ours) am c550a6fa: Merge "docs: Add Studio 1.3.2 release notes and download links" into mnc-dev
* commit '76d4d5d19dc4548c90d73fd74174bf4e21674011':
diff --git a/Android.mk b/Android.mk
index 7df1bd7..6f560ea 100644
--- a/Android.mk
+++ b/Android.mk
@@ -90,6 +90,7 @@
core/java/android/app/IWallpaperManager.aidl \
core/java/android/app/IWallpaperManagerCallback.aidl \
core/java/android/app/admin/IDevicePolicyManager.aidl \
+ core/java/android/app/trust/IStrongAuthTracker.aidl \
core/java/android/app/trust/ITrustManager.aidl \
core/java/android/app/trust/ITrustListener.aidl \
core/java/android/app/backup/IBackupManager.aidl \
@@ -172,6 +173,7 @@
core/java/android/hardware/input/IInputManager.aidl \
core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
core/java/android/hardware/location/IActivityRecognitionHardware.aidl \
+ core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl \
core/java/android/hardware/location/IActivityRecognitionHardwareSink.aidl \
core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl \
core/java/android/hardware/location/IFusedLocationHardware.aidl \
diff --git a/api/system-current.txt b/api/system-current.txt
index 3c88e59..f464292 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -130,6 +130,7 @@
field public static final java.lang.String MODIFY_APPWIDGET_BIND_PERMISSIONS = "android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS";
field public static final java.lang.String MODIFY_AUDIO_ROUTING = "android.permission.MODIFY_AUDIO_ROUTING";
field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
+ field public static final java.lang.String MODIFY_CELL_BROADCASTS = "android.permission.MODIFY_CELL_BROADCASTS";
field public static final java.lang.String MODIFY_NETWORK_ACCOUNTING = "android.permission.MODIFY_NETWORK_ACCOUNTING";
field public static final java.lang.String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
field public static final java.lang.String MODIFY_PHONE_STATE = "android.permission.MODIFY_PHONE_STATE";
@@ -32715,17 +32716,22 @@
field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
+ field public static final java.lang.String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
field public static final java.lang.String ACTION_SHOW_RESPOND_VIA_SMS_SETTINGS = "android.telecom.action.SHOW_RESPOND_VIA_SMS_SETTINGS";
field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
+ field public static final java.lang.String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
field public static final java.lang.String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
field public static final java.lang.String EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME = "android.telecom.extra.CHANGE_DEFAULT_DIALER_PACKAGE_NAME";
+ field public static final java.lang.String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
field public static final java.lang.String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
field public static final java.lang.String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS";
field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
+ field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telecom.extra.NOTIFICATION_COUNT";
+ field public static final java.lang.String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
@@ -33425,6 +33431,7 @@
field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
+ field public static final java.lang.String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
field public static final int CALL_STATE_IDLE = 0; // 0x0
field public static final int CALL_STATE_OFFHOOK = 2; // 0x2
field public static final int CALL_STATE_RINGING = 1; // 0x1
@@ -33441,11 +33448,15 @@
field public static final int DATA_CONNECTING = 1; // 0x1
field public static final int DATA_DISCONNECTED = 0; // 0x0
field public static final int DATA_SUSPENDED = 3; // 0x3
+ field public static final java.lang.String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
+ field public static final java.lang.String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
+ field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
field public static final java.lang.String EXTRA_STATE = "state";
field public static final java.lang.String EXTRA_STATE_IDLE;
field public static final java.lang.String EXTRA_STATE_OFFHOOK;
field public static final java.lang.String EXTRA_STATE_RINGING;
+ field public static final java.lang.String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index fba462b..8f361ce 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -556,15 +556,10 @@
mZip->endIteration(cookie);
- // clear screen
glShadeModel(GL_FLAT);
glDisable(GL_DITHER);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
- glClearColor(0,0,0,1);
- glClear(GL_COLOR_BUFFER_BIT);
-
- eglSwapBuffers(mDisplay, mSurface);
glBindTexture(GL_TEXTURE_2D, 0);
glEnable(GL_TEXTURE_2D);
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 9f385fe..58c3a9c 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -983,6 +983,7 @@
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK: return "ask";
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS: return "always";
case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER: return "never";
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK : return "always ask";
}
return "Unknown link state: " + state;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f3f2428..9381327 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -4607,7 +4607,7 @@
* Size value for use with {@link #setCustomSizePreset} to show this notification with
* default sizing.
* <p>For custom display notifications created using {@link #setDisplayIntent},
- * the default is {@link #SIZE_LARGE}. All other notifications size automatically based
+ * the default is {@link #SIZE_MEDIUM}. All other notifications size automatically based
* on their content.
*/
public static final int SIZE_DEFAULT = 0;
diff --git a/core/java/android/app/trust/IStrongAuthTracker.aidl b/core/java/android/app/trust/IStrongAuthTracker.aidl
new file mode 100644
index 0000000..36c71bf
--- /dev/null
+++ b/core/java/android/app/trust/IStrongAuthTracker.aidl
@@ -0,0 +1,26 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+package android.app.trust;
+
+/**
+ * Private API to be notified about strong auth changes
+ *
+ * {@hide}
+ */
+oneway interface IStrongAuthTracker {
+ void onStrongAuthRequiredChanged(int strongAuthRequired, int userId);
+}
\ No newline at end of file
diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 32951d9..2dea545 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -26,11 +26,9 @@
interface ITrustManager {
void reportUnlockAttempt(boolean successful, int userId);
void reportEnabledTrustAgentsChanged(int userId);
- void reportRequireCredentialEntry(int userId);
void registerTrustListener(in ITrustListener trustListener);
void unregisterTrustListener(in ITrustListener trustListener);
void reportKeyguardShowingChanged();
boolean isDeviceLocked(int userId);
boolean isDeviceSecure(int userId);
- boolean hasUserAuthenticatedSinceBoot(int userId);
}
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index 8cab565..aff69f0 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -16,13 +16,19 @@
package android.app.trust;
+import android.annotation.IntDef;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.SparseIntArray;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* See {@link com.android.server.trust.TrustManagerService}
@@ -73,21 +79,6 @@
}
/**
- * Reports that trust is disabled until credentials have been entered for user {@param userId}.
- *
- * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
- *
- * @param userId either an explicit user id or {@link android.os.UserHandle#USER_ALL}
- */
- public void reportRequireCredentialEntry(int userId) {
- try {
- mService.reportRequireCredentialEntry(userId);
- } catch (RemoteException e) {
- onError(e);
- }
- }
-
- /**
* Reports that the visibility of the keyguard has changed.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
@@ -147,23 +138,6 @@
}
}
- /**
- * Checks whether the specified user has been authenticated since the last boot.
- *
- * @param userId the user id of the user to check for
- * @return true if the user has authenticated since boot, false otherwise
- *
- * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
- */
- public boolean hasUserAuthenticatedSinceBoot(int userId) {
- try {
- return mService.hasUserAuthenticatedSinceBoot(userId);
- } catch (RemoteException e) {
- onError(e);
- return false;
- }
- }
-
private void onError(Exception e) {
Log.e(TAG, "Error while calling TrustManagerService", e);
}
diff --git a/core/java/android/hardware/ICameraServiceProxy.aidl b/core/java/android/hardware/ICameraServiceProxy.aidl
index 0bb24bc..0e654d5 100644
--- a/core/java/android/hardware/ICameraServiceProxy.aidl
+++ b/core/java/android/hardware/ICameraServiceProxy.aidl
@@ -19,6 +19,8 @@
/**
* Binder interface for the camera service proxy running in system_server.
*
+ * Keep in sync with frameworks/av/include/camera/ICameraServiceProxy.h
+ *
* @hide
*/
interface ICameraServiceProxy
@@ -27,4 +29,9 @@
* Ping the service proxy to update the valid users for the camera service.
*/
oneway void pingForUserUpdate();
+
+ /**
+ * Update the status of a camera device
+ */
+ oneway void notifyCameraState(String cameraId, int newCameraState);
}
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 46ffe36..766868d 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -138,6 +138,48 @@
*/
public abstract void prepare(@NonNull Surface surface) throws CameraAccessException;
+ /**
+ * <p>Pre-allocate at most maxCount buffers for an output Surface.</p>
+ *
+ * <p>Like the {@link #prepare(Surface)} method, this method can be used to allocate output
+ * buffers for a given Surface. However, while the {@link #prepare(Surface)} method allocates
+ * the maximum possible buffer count, this method allocates at most maxCount buffers.</p>
+ *
+ * <p>If maxCount is greater than the possible maximum count (which is the sum of the buffer
+ * count requested by the creator of the Surface and the count requested by the camera device),
+ * only the possible maximum count is allocated, in which case the function acts exactly like
+ * {@link #prepare(Surface)}.</p>
+ *
+ * <p>The restrictions on when this method can be called are the same as for
+ * {@link #prepare(Surface)}.</p>
+ *
+ * <p>Repeated calls to this method are allowed, and a mix of {@link #prepare(Surface)} and
+ * this method is also allowed. Note that after the first call to {@link #prepare(Surface)},
+ * subsequent calls to either prepare method are effectively no-ops. In addition, this method
+ * is not additive in terms of buffer count. This means calling it twice with maxCount = 2
+ * will only allocate 2 buffers, not 4 (assuming the possible maximum is at least 2); to
+ * allocate two buffers on the first call and two on the second, the application needs to call
+ * prepare with prepare(surface, 2) and prepare(surface, 4).</p>
+ *
+ * @param maxCount the buffer count to try to allocate. If this is greater than the possible
+ * maximum for this output, the possible maximum is allocated instead. If
+ * maxCount buffers are already allocated, then prepare will do nothing.
+ * @param surface the output Surface for which buffers should be pre-allocated.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error.
+ * @throws IllegalStateException if this session is no longer active, either because the
+ * session was explicitly closed, a new session has been created
+ * or the camera device has been closed.
+ * @throws IllegalArgumentException if the Surface is invalid, not part of this Session,
+ * or has already been used as a target of a CaptureRequest in
+ * this session or immediately prior sessions without an
+ * intervening tearDown call.
+ *
+ * @hide
+ */
+ public abstract void prepare(int maxCount, @NonNull Surface surface)
+ throws CameraAccessException;
/**
* <p>Free all buffers allocated for an output Surface.</p>
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 4a71aa0..20b0be1 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -480,8 +480,11 @@
* the configurations in the tables below are also guaranteed for creating a reprocessable
* capture session if the camera device supports YUV reprocessing or PRIVATE reprocessing.
* However, not all output targets used to create a reprocessable session may be used in a
- * {@link CaptureRequest} simultaneously. The guaranteed output targets that can be included
- * in a {@link CaptureRequest} simultaneously are listed in the tables under
+ * {@link CaptureRequest} simultaneously. For devices that support only 1 output target in a
+ * reprocess {@link CaptureRequest}, submitting a reprocess {@link CaptureRequest} with multiple
+ * output targets will result in a {@link CaptureFailure}. For devices that support multiple
+ * output targets in a reprocess {@link CaptureRequest}, the guaranteed output targets that can
+ * be included in a {@link CaptureRequest} simultaneously are listed in the tables under
* {@link #createCaptureSession createCaptureSession}. For example, with a FULL-capability
* ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL} {@code == }
* {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL FULL}) device that supports PRIVATE
@@ -532,8 +535,6 @@
* <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code RECORD}</td> <td></td><td id="rb"></td> <td>High-resolution ZSL in-app video processing with regular preview.</td> </tr>
* <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>Maximum-resolution ZSL in-app processing with regular preview.</td> </tr>
* <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td></td><td id="rb"></td> <td>Maximum-resolution two-input ZSL in-app processing.</td> </tr>
- * <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code RECORD}</td> <td>{@code JPEG}</td><td id="rb">{@code RECORD}</td> <td>High-resolution ZSL in-app video processing and video snapshot with regular preview.</td> </tr>
- * <tr> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code MAXIMUM}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Maximum-resolution two-input ZSL in-app processing with regular preview.</td> </tr>
* <tr> <td>{@code PRIV}/{@code YUV}</td><td id="rb">{@code MAXIMUM}</td> <td>Same as input</td><td id="rb">{@code MAXIMUM}</td> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td> <td>ZSL still capture and in-app processing.</td> </tr>
* </table><br>
* </p>
@@ -711,7 +712,9 @@
* provide input images to camera device via {@link android.media.ImageWriter#queueInputImage}.
* The application must use the capture result of one of those output images to create a
* reprocess capture request so that the camera device can use the information to achieve
- * optimal reprocess image quality.
+ * optimal reprocess image quality. For camera devices that support only 1 output
+ * {@link Surface}, submitting a reprocess {@link CaptureRequest} with multiple
+ * output targets will result in a {@link CaptureFailure}.
*
* @param inputResult The capture result of the output image or one of the output images used
* to generate the reprocess input image for this capture request.
diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
index 7cb3673..c9c9abc 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -102,4 +102,6 @@
int prepare(int streamId);
int tearDown(int streamId);
+
+ int prepare2(int maxCount, int streamId);
}
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index d325c77..5573896 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -146,6 +146,11 @@
}
@Override
+ public void prepare(int maxCount, Surface surface) throws CameraAccessException {
+ mDeviceImpl.prepare(maxCount, surface);
+ }
+
+ @Override
public void tearDown(Surface surface) throws CameraAccessException {
mDeviceImpl.tearDown(surface);
}
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index a920e2b..8cd1da5 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -169,6 +169,11 @@
}
@Override
+ public void prepare(int maxCount, Surface surface) throws CameraAccessException {
+ mSessionImpl.prepare(maxCount, surface);
+ }
+
+ @Override
public void tearDown(Surface surface) throws CameraAccessException {
mSessionImpl.tearDown(surface);
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 91d623e..6e02df1 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -679,6 +679,33 @@
}
}
+ public void prepare(int maxCount, Surface surface) throws CameraAccessException {
+ if (surface == null) throw new IllegalArgumentException("Surface is null");
+ if (maxCount <= 0) throw new IllegalArgumentException("Invalid maxCount given: " +
+ maxCount);
+
+ synchronized(mInterfaceLock) {
+ int streamId = -1;
+ for (int i = 0; i < mConfiguredOutputs.size(); i++) {
+ if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
+ streamId = mConfiguredOutputs.keyAt(i);
+ break;
+ }
+ }
+ if (streamId == -1) {
+ throw new IllegalArgumentException("Surface is not part of this session");
+ }
+ try {
+ mRemoteDevice.prepare2(maxCount, streamId);
+ } catch (CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ return;
+ }
+ }
+ }
+
public void tearDown(Surface surface) throws CameraAccessException {
if (surface == null) throw new IllegalArgumentException("Surface is null");
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index e20eaa7..6b8e113 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -636,6 +636,11 @@
return CameraBinderDecorator.NO_ERROR;
}
+ public int prepare2(int maxCount, int streamId) {
+ // We don't support this in LEGACY mode.
+ return prepare(streamId);
+ }
+
public int tearDown(int streamId) {
if (DEBUG) {
Log.d(TAG, "tearDown called.");
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 7aa9787..a04cdce 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -19,7 +19,9 @@
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.utils.HashCodeHelpers;
+import android.hardware.camera2.utils.SurfaceUtils;
import android.util.Log;
+import android.util.Size;
import android.view.Surface;
import android.os.Parcel;
import android.os.Parcelable;
@@ -66,9 +68,7 @@
*
*/
public OutputConfiguration(Surface surface) {
- checkNotNull(surface, "Surface must not be null");
- mSurface = surface;
- mRotation = ROTATION_0;
+ this(surface, ROTATION_0);
}
/**
@@ -94,6 +94,9 @@
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
mSurface = surface;
mRotation = rotation;
+ mConfiguredSize = SurfaceUtils.getSurfaceSize(surface);
+ mConfiguredFormat = SurfaceUtils.getSurfaceFormat(surface);
+ mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(surface);
}
/**
@@ -106,6 +109,9 @@
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
mSurface = surface;
mRotation = rotation;
+ mConfiguredSize = SurfaceUtils.getSurfaceSize(mSurface);
+ mConfiguredFormat = SurfaceUtils.getSurfaceFormat(mSurface);
+ mConfiguredDataspace = SurfaceUtils.getSurfaceDataspace(mSurface);
}
/**
@@ -163,8 +169,9 @@
/**
* Check if this {@link OutputConfiguration} is equal to another {@link OutputConfiguration}.
*
- * <p>Two output configurations are only equal if and only if the underlying surface and
- * all other configuration parameters are equal. </p>
+ * <p>Two output configurations are only equal if and only if the underlying surfaces, surface
+ * properties (width, height, format, dataspace) when the output configurations are created,
+ * and all other configuration parameters are equal. </p>
*
* @return {@code true} if the objects were equal, {@code false} otherwise
*/
@@ -176,7 +183,11 @@
return true;
} else if (obj instanceof OutputConfiguration) {
final OutputConfiguration other = (OutputConfiguration) obj;
- return (mSurface == other.mSurface && mRotation == other.mRotation);
+ return mSurface == other.mSurface &&
+ mRotation == other.mRotation &&
+ mConfiguredSize.equals(other.mConfiguredSize) &&
+ mConfiguredFormat == other.mConfiguredFormat &&
+ mConfiguredDataspace == other.mConfiguredDataspace;
}
return false;
}
@@ -192,4 +203,9 @@
private static final String TAG = "OutputConfiguration";
private final Surface mSurface;
private final int mRotation;
+
+ // The size, format, and dataspace of the surface when OutputConfiguration is created.
+ private final Size mConfiguredSize;
+ private final int mConfiguredFormat;
+ private final int mConfiguredDataspace;
}
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index e71e49f..b8d6960 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -1290,7 +1290,7 @@
for (StreamConfiguration config : configurations) {
int fmt = config.getFormat();
if (fmt == format && config.isOutput() == output) {
- if (output) {
+ if (output && mListHighResolution) {
// Filter slow high-res output formats; include for
// highRes, remove for !highRes
long duration = 0;
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 6e29989..7cff11b 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -668,6 +668,25 @@
return 0;
}
+ /**
+ * Reset the lockout timer when asked to do so by keyguard.
+ *
+ * @param token an opaque token returned by password confirmation.
+ *
+ * @hide
+ */
+ public void resetTimeout(byte[] token) {
+ if (mService != null) {
+ try {
+ mService.resetTimeout(token);
+ } catch (RemoteException e) {
+ Log.v(TAG, "Remote exception in getAuthenticatorId(): ", e);
+ }
+ } else {
+ Log.w(TAG, "getAuthenticatorId(): Service not connected!");
+ }
+ }
+
private class MyHandler extends Handler {
private MyHandler(Context context) {
super(context.getMainLooper());
@@ -677,6 +696,7 @@
super(looper);
}
+ @Override
public void handleMessage(android.os.Message msg) {
switch(msg.what) {
case MSG_ENROLL_RESULT:
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 5e233b8..3356354 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -68,4 +68,7 @@
// Gets the authenticator ID for fingerprint
long getAuthenticatorId(String opPackageName);
+
+ // Reset the timeout when user authenticates with strong auth (e.g. PIN, pattern or password)
+ void resetTimeout(in byte [] cryptoToken);
}
diff --git a/core/java/android/hardware/location/ActivityRecognitionHardware.java b/core/java/android/hardware/location/ActivityRecognitionHardware.java
index 5d3953a..8acd1ff 100644
--- a/core/java/android/hardware/location/ActivityRecognitionHardware.java
+++ b/core/java/android/hardware/location/ActivityRecognitionHardware.java
@@ -30,20 +30,34 @@
* @hide
*/
public class ActivityRecognitionHardware extends IActivityRecognitionHardware.Stub {
- private static final String TAG = "ActivityRecognitionHardware";
+ private static final String TAG = "ActivityRecognitionHW";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE;
+ private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '"
+ + HARDWARE_PERMISSION + "' not granted to access ActivityRecognitionHardware";
+
private static final int INVALID_ACTIVITY_TYPE = -1;
private static final int NATIVE_SUCCESS_RESULT = 0;
+ private static final int EVENT_TYPE_DISABLED = 0;
+ private static final int EVENT_TYPE_ENABLED = 1;
- private static ActivityRecognitionHardware sSingletonInstance = null;
+ /**
+ * Contains the number of supported Event Types.
+ *
+ * NOTE: increment this counter every time a new EVENT_TYPE_ is added to
+ * com.android.location.provider.ActivityRecognitionProvider
+ */
+ private static final int EVENT_TYPE_COUNT = 3;
+
+ private static ActivityRecognitionHardware sSingletonInstance;
private static final Object sSingletonInstanceLock = new Object();
private final Context mContext;
+ private final int mSupportedActivitiesCount;
private final String[] mSupportedActivities;
-
- private final RemoteCallbackList<IActivityRecognitionHardwareSink> mSinks =
- new RemoteCallbackList<IActivityRecognitionHardwareSink>();
+ private final int[][] mSupportedActivitiesEnabledEvents;
+ private final SinkList mSinks = new SinkList();
private static class Event {
public int activity;
@@ -56,6 +70,8 @@
mContext = context;
mSupportedActivities = fetchSupportedActivities();
+ mSupportedActivitiesCount = mSupportedActivities.length;
+ mSupportedActivitiesEnabledEvents = new int[mSupportedActivitiesCount][EVENT_TYPE_COUNT];
}
public static ActivityRecognitionHardware getInstance(Context context) {
@@ -107,7 +123,11 @@
}
int result = nativeEnableActivityEvent(activityType, eventType, reportLatencyNs);
- return result == NATIVE_SUCCESS_RESULT;
+ if (result == NATIVE_SUCCESS_RESULT) {
+ mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_ENABLED;
+ return true;
+ }
+ return false;
}
@Override
@@ -120,7 +140,11 @@
}
int result = nativeDisableActivityEvent(activityType, eventType);
- return result == NATIVE_SUCCESS_RESULT;
+ if (result == NATIVE_SUCCESS_RESULT) {
+ mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
+ return true;
+ }
+ return false;
}
@Override
@@ -135,7 +159,7 @@
*/
private void onActivityChanged(Event[] events) {
if (events == null || events.length == 0) {
- Log.d(TAG, "No events to broadcast for onActivityChanged.");
+ if (DEBUG) Log.d(TAG, "No events to broadcast for onActivityChanged.");
return;
}
@@ -161,7 +185,6 @@
}
}
mSinks.finishBroadcast();
-
}
private String getActivityName(int activityType) {
@@ -193,10 +216,7 @@
}
private void checkPermissions() {
- String message = String.format(
- "Permission '%s' not granted to access ActivityRecognitionHardware",
- HARDWARE_PERMISSION);
- mContext.enforceCallingPermission(HARDWARE_PERMISSION, message);
+ mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE);
}
private String[] fetchSupportedActivities() {
@@ -208,6 +228,39 @@
return new String[0];
}
+ private class SinkList extends RemoteCallbackList<IActivityRecognitionHardwareSink> {
+ @Override
+ public void onCallbackDied(IActivityRecognitionHardwareSink callback) {
+ int callbackCount = mSinks.getRegisteredCallbackCount();
+ if (DEBUG) Log.d(TAG, "RegisteredCallbackCount: " + callbackCount);
+ if (callbackCount != 0) {
+ return;
+ }
+ // currently there is only one client for this, so if all its sinks have died, we clean
+ // up after them, this ensures that the AR HAL is not out of sink
+ for (int activity = 0; activity < mSupportedActivitiesCount; ++activity) {
+ for (int event = 0; event < EVENT_TYPE_COUNT; ++event) {
+ disableActivityEventIfEnabled(activity, event);
+ }
+ }
+ }
+
+ private void disableActivityEventIfEnabled(int activityType, int eventType) {
+ if (mSupportedActivitiesEnabledEvents[activityType][eventType] != EVENT_TYPE_ENABLED) {
+ return;
+ }
+
+ int result = nativeDisableActivityEvent(activityType, eventType);
+ mSupportedActivitiesEnabledEvents[activityType][eventType] = EVENT_TYPE_DISABLED;
+ String message = String.format(
+ "DisableActivityEvent: activityType=%d, eventType=%d, result=%d",
+ activityType,
+ eventType,
+ result);
+ Log.e(TAG, message);
+ }
+ }
+
// native bindings
static { nativeClassInit(); }
diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl
new file mode 100644
index 0000000..d2c3d75
--- /dev/null
+++ b/core/java/android/hardware/location/IActivityRecognitionHardwareClient.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/license/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.location;
+
+import android.hardware.location.IActivityRecognitionHardware;
+
+/**
+ * Activity Recognition Hardware client interface.
+ * This interface can be used to receive interfaces to implementations of
+ * {@link IActivityRecognitionHardware}.
+ *
+ * @hide
+ */
+interface IActivityRecognitionHardwareClient {
+ /**
+ * Hardware Activity-Recognition availability event.
+ *
+ * @param isSupported whether the platform has hardware support for the feature
+ * @param instance the available instance to provide access to the feature
+ */
+ void onAvailabilityChanged(in boolean isSupported, in IActivityRecognitionHardware instance);
+}
diff --git a/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl b/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl
index 0507f52..12e3117 100644
--- a/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl
+++ b/core/java/android/hardware/location/IActivityRecognitionHardwareWatcher.aidl
@@ -22,6 +22,8 @@
* Activity Recognition Hardware watcher. This interface can be used to receive interfaces to
* implementations of {@link IActivityRecognitionHardware}.
*
+ * @deprecated use {@link IActivityRecognitionHardwareClient} instead.
+
* @hide
*/
interface IActivityRecognitionHardwareWatcher {
@@ -29,4 +31,4 @@
* Hardware Activity-Recognition availability event.
*/
void onInstanceChanged(in IActivityRecognitionHardware instance);
-}
\ No newline at end of file
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ff6dd32..a4e6219 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1179,6 +1179,142 @@
return true;
}
+ /** @hide */
+ public static class PacketKeepaliveCallback {
+ /** The requested keepalive was successfully started. */
+ public void onStarted() {}
+ /** The keepalive was successfully stopped. */
+ public void onStopped() {}
+ /** An error occurred. */
+ public void onError(int error) {}
+ }
+
+ /**
+ * Allows applications to request that the system periodically send specific packets on their
+ * behalf, using hardware offload to save battery power.
+ *
+ * To request that the system send keepalives, call one of the methods that return a
+ * {@link ConnectivityManager.PacketKeepalive} object, such as {@link #startNattKeepalive},
+ * passing in a non-null callback. If the callback is successfully started, the callback's
+ * {@code onStarted} method will be called. If an error occurs, {@code onError} will be called,
+ * specifying one of the {@code ERROR_*} constants in this class.
+ *
+ * To stop an existing keepalive, call {@link stop}. The system will call {@code onStopped} if
+ * the operation was successfull or {@code onError} if an error occurred.
+ *
+ * @hide
+ */
+ public class PacketKeepalive {
+
+ private static final String TAG = "PacketKeepalive";
+
+ /** @hide */
+ public static final int SUCCESS = 0;
+
+ /** @hide */
+ public static final int NO_KEEPALIVE = -1;
+
+ /** @hide */
+ public static final int BINDER_DIED = -10;
+
+ /** The specified {@code Network} is not connected. */
+ public static final int ERROR_INVALID_NETWORK = -20;
+ /** The specified IP addresses are invalid. For example, the specified source IP address is
+ * not configured on the specified {@code Network}. */
+ public static final int ERROR_INVALID_IP_ADDRESS = -21;
+ /** The requested port is invalid. */
+ public static final int ERROR_INVALID_PORT = -22;
+ /** The packet length is invalid (e.g., too long). */
+ public static final int ERROR_INVALID_LENGTH = -23;
+ /** The packet transmission interval is invalid (e.g., too short). */
+ public static final int ERROR_INVALID_INTERVAL = -24;
+
+ /** The hardware does not support this request. */
+ public static final int ERROR_HARDWARE_UNSUPPORTED = -30;
+
+ public static final int NATT_PORT = 4500;
+
+ private final Network mNetwork;
+ private final PacketKeepaliveCallback mCallback;
+ private final Looper mLooper;
+ private final Messenger mMessenger;
+
+ private volatile Integer mSlot;
+
+ void stopLooper() {
+ mLooper.quit();
+ }
+
+ public void stop() {
+ try {
+ mService.stopKeepalive(mNetwork, mSlot);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error stopping packet keepalive: ", e);
+ stopLooper();
+ }
+ }
+
+ private PacketKeepalive(Network network, PacketKeepaliveCallback callback) {
+ checkNotNull(network, "network cannot be null");
+ checkNotNull(callback, "callback cannot be null");
+ mNetwork = network;
+ mCallback = callback;
+ HandlerThread thread = new HandlerThread(TAG);
+ thread.start();
+ mLooper = thread.getLooper();
+ mMessenger = new Messenger(new Handler(mLooper) {
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case NetworkAgent.EVENT_PACKET_KEEPALIVE:
+ int error = message.arg2;
+ try {
+ if (error == SUCCESS) {
+ if (mSlot == null) {
+ mSlot = message.arg1;
+ mCallback.onStarted();
+ } else {
+ mSlot = null;
+ stopLooper();
+ mCallback.onStopped();
+ }
+ } else {
+ stopLooper();
+ mCallback.onError(error);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Exception in keepalive callback(" + error + ")", e);
+ }
+ break;
+ default:
+ Log.e(TAG, "Unhandled message " + Integer.toHexString(message.what));
+ break;
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * Starts an IPsec NAT-T keepalive packet with the specified parameters.
+ *
+ * @hide
+ */
+ public PacketKeepalive startNattKeepalive(
+ Network network, int intervalSeconds, PacketKeepaliveCallback callback,
+ InetAddress srcAddr, int srcPort, InetAddress dstAddr) {
+ final PacketKeepalive k = new PacketKeepalive(network, callback);
+ try {
+ mService.startNattKeepalive(network, intervalSeconds, k.mMessenger, new Binder(),
+ srcAddr.getHostAddress(), srcPort, dstAddr.getHostAddress());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error starting packet keepalive: ", e);
+ k.stopLooper();
+ return null;
+ }
+ return k;
+ }
+
/**
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface. An attempt to add a route that
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 46c28a6..d4dd669 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -160,4 +160,9 @@
boolean setUnderlyingNetworksForVpn(in Network[] networks);
void factoryReset();
+
+ void startNattKeepalive(in Network network, int intervalSeconds, in Messenger messenger,
+ in IBinder binder, String srcAddr, int srcPort, String dstAddr);
+
+ void stopKeepalive(in Network network, int slot);
}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 7f5f377..9e639e8 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -48,6 +48,9 @@
/** Snooze limit on policy matching given template. */
void snoozeLimit(in NetworkTemplate template);
+ /** Snooze warning on policy matching given template. */
+ void snoozeWarning(in NetworkTemplate template);
+
/** Control if background data is restricted system-wide. */
void setRestrictBackground(boolean restrictBackground);
boolean getRestrictBackground();
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 808a882..20c2168 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -17,6 +17,7 @@
package android.net;
import android.content.Context;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -25,6 +26,7 @@
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
+import android.net.ConnectivityManager.PacketKeepalive;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -143,17 +145,60 @@
*/
public static final int CMD_SAVE_ACCEPT_UNVALIDATED = BASE + 9;
- /** Sent by ConnectivityService to the NetworkAgent to inform the agent to pull
+ /**
+ * Sent by ConnectivityService to the NetworkAgent to inform the agent to pull
* the underlying network connection for updated bandwidth information.
*/
public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 10;
/**
+ * Sent by ConnectivityService to the NetworkAgent to request that the specified packet be sent
+ * periodically on the given interval.
+ *
+ * arg1 = the slot number of the keepalive to start
+ * arg2 = interval in seconds
+ * obj = KeepalivePacketData object describing the data to be sent
+ *
+ * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
+ */
+ public static final int CMD_START_PACKET_KEEPALIVE = BASE + 11;
+
+ /**
+ * Requests that the specified keepalive packet be stopped.
+ *
+ * arg1 = slot number of the keepalive to stop.
+ *
+ * Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
+ */
+ public static final int CMD_STOP_PACKET_KEEPALIVE = BASE + 12;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to provide status on a packet keepalive
+ * request. This may either be the reply to a CMD_START_PACKET_KEEPALIVE, or an asynchronous
+ * error notification.
+ *
+ * This is also sent by KeepaliveTracker to the app's ConnectivityManager.PacketKeepalive to
+ * so that the app's PacketKeepaliveCallback methods can be called.
+ *
+ * arg1 = slot number of the keepalive
+ * arg2 = error code
+ */
+ public static final int EVENT_PACKET_KEEPALIVE = BASE + 13;
+
+ /**
+ * Sent by ConnectivityService to inform this network transport of signal strength thresholds
+ * that when crossed should trigger a system wakeup and a NetworkCapabilities update.
+ *
+ * obj = int[] describing signal strength thresholds.
+ */
+ public static final int CMD_SET_SIGNAL_STRENGTH_THRESHOLDS = BASE + 14;
+
+ /**
* Sent by ConnectivityService to the NeworkAgent to inform the agent to avoid
* automatically reconnecting to this network (e.g. via autojoin). Happens
* when user selects "No" option on the "Stay connected?" dialog box.
*/
- public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 11;
+ public static final int CMD_PREVENT_AUTOMATIC_RECONNECT = BASE + 15;
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
@@ -249,6 +294,27 @@
saveAcceptUnvalidated(msg.arg1 != 0);
break;
}
+ case CMD_START_PACKET_KEEPALIVE: {
+ startPacketKeepalive(msg);
+ break;
+ }
+ case CMD_STOP_PACKET_KEEPALIVE: {
+ stopPacketKeepalive(msg);
+ break;
+ }
+
+ case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: {
+ ArrayList<Integer> thresholds =
+ ((Bundle) msg.obj).getIntegerArrayList("thresholds");
+ // TODO: Change signal strength thresholds API to use an ArrayList<Integer>
+ // rather than convert to int[].
+ int[] intThresholds = new int[(thresholds != null) ? thresholds.size() : 0];
+ for (int i = 0; i < intThresholds.length; i++) {
+ intThresholds[i] = thresholds.get(i);
+ }
+ setSignalStrengthThresholds(intThresholds);
+ break;
+ }
case CMD_PREVENT_AUTOMATIC_RECONNECT: {
preventAutomaticReconnect();
break;
@@ -257,13 +323,27 @@
}
private void queueOrSendMessage(int what, Object obj) {
+ queueOrSendMessage(what, 0, 0, obj);
+ }
+
+ private void queueOrSendMessage(int what, int arg1, int arg2) {
+ queueOrSendMessage(what, arg1, arg2, null);
+ }
+
+ private void queueOrSendMessage(int what, int arg1, int arg2, Object obj) {
+ Message msg = Message.obtain();
+ msg.what = what;
+ msg.arg1 = arg1;
+ msg.arg2 = arg2;
+ msg.obj = obj;
+ queueOrSendMessage(msg);
+ }
+
+ private void queueOrSendMessage(Message msg) {
synchronized (mPreConnectedQueue) {
if (mAsyncChannel != null) {
- mAsyncChannel.sendMessage(what, obj);
+ mAsyncChannel.sendMessage(msg);
} else {
- Message msg = Message.obtain();
- msg.what = what;
- msg.obj = obj;
mPreConnectedQueue.add(msg);
}
}
@@ -378,6 +458,34 @@
}
/**
+ * Requests that the network hardware send the specified packet at the specified interval.
+ */
+ protected void startPacketKeepalive(Message msg) {
+ onPacketKeepaliveEvent(msg.arg1, PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+ }
+
+ /**
+ * Requests that the network hardware send the specified packet at the specified interval.
+ */
+ protected void stopPacketKeepalive(Message msg) {
+ onPacketKeepaliveEvent(msg.arg1, PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+ }
+
+ /**
+ * Called by the network when a packet keepalive event occurs.
+ */
+ public void onPacketKeepaliveEvent(int slot, int reason) {
+ queueOrSendMessage(EVENT_PACKET_KEEPALIVE, slot, reason);
+ }
+
+ /**
+ * Called by ConnectivityService to inform this network transport of signal strength thresholds
+ * that when crossed should trigger a system wakeup and a NetworkCapabilities update.
+ */
+ protected void setSignalStrengthThresholds(int[] thresholds) {
+ }
+
+ /**
* Called when the user asks to not stay connected to this network because it was found to not
* provide Internet access. Usually followed by call to {@code unwanted}. The transport is
* responsible for making sure the device does not automatically reconnect to the same network
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index d0e0cbe..3bd12c0 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -48,6 +48,7 @@
mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps;
mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps;
mNetworkSpecifier = nc.mNetworkSpecifier;
+ mSignalStrength = nc.mSignalStrength;
}
}
@@ -60,6 +61,7 @@
mNetworkCapabilities = mTransportTypes = 0;
mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = 0;
mNetworkSpecifier = null;
+ mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED;
}
/**
@@ -184,6 +186,28 @@
private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_CAPTIVE_PORTAL;
/**
+ * Network capabilities that are expected to be mutable, i.e., can change while a particular
+ * network is connected.
+ */
+ private static final long MUTABLE_CAPABILITIES =
+ // TRUSTED can change when user explicitly connects to an untrusted network in Settings.
+ // http://b/18206275
+ (1 << NET_CAPABILITY_TRUSTED) |
+ (1 << NET_CAPABILITY_VALIDATED) |
+ (1 << NET_CAPABILITY_CAPTIVE_PORTAL);
+
+ /**
+ * Network capabilities that are not allowed in NetworkRequests. This exists because the
+ * NetworkFactory / NetworkAgent model does not deal well with the situation where a
+ * capability's presence cannot be known in advance. If such a capability is requested, then we
+ * can get into a cycle where the NetworkFactory endlessly churns out NetworkAgents that then
+ * get immediately torn down because they do not have the requested capability.
+ */
+ private static final long NON_REQUESTABLE_CAPABILITIES =
+ (1 << NET_CAPABILITY_VALIDATED) |
+ (1 << NET_CAPABILITY_CAPTIVE_PORTAL);
+
+ /**
* Capabilities that are set by default when the object is constructed.
*/
private static final long DEFAULT_CAPABILITIES =
@@ -278,8 +302,31 @@
this.mNetworkCapabilities |= nc.mNetworkCapabilities;
}
- private boolean satisfiedByNetCapabilities(NetworkCapabilities nc) {
- return ((nc.mNetworkCapabilities & this.mNetworkCapabilities) == this.mNetworkCapabilities);
+ /**
+ * Convenience function that returns a human-readable description of the first mutable
+ * capability we find. Used to present an error message to apps that request mutable
+ * capabilities.
+ *
+ * @hide
+ */
+ public String describeFirstNonRequestableCapability() {
+ if (hasCapability(NET_CAPABILITY_VALIDATED)) return "NET_CAPABILITY_VALIDATED";
+ if (hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) return "NET_CAPABILITY_CAPTIVE_PORTAL";
+ // This cannot happen unless the preceding checks are incomplete.
+ if ((mNetworkCapabilities & NON_REQUESTABLE_CAPABILITIES) != 0) {
+ return "unknown non-requestable capabilities " + Long.toHexString(mNetworkCapabilities);
+ }
+ if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth";
+ if (hasSignalStrength()) return "signalStrength";
+ return null;
+ }
+
+ private boolean satisfiedByNetCapabilities(NetworkCapabilities nc, boolean onlyImmutable) {
+ long networkCapabilities = this.mNetworkCapabilities;
+ if (onlyImmutable) {
+ networkCapabilities = networkCapabilities & ~MUTABLE_CAPABILITIES;
+ }
+ return ((nc.mNetworkCapabilities & networkCapabilities) == networkCapabilities);
}
/** @hide */
@@ -287,6 +334,11 @@
return (nc.mNetworkCapabilities == this.mNetworkCapabilities);
}
+ private boolean equalsNetCapabilitiesImmutable(NetworkCapabilities that) {
+ return ((this.mNetworkCapabilities & ~MUTABLE_CAPABILITIES) ==
+ (that.mNetworkCapabilities & ~MUTABLE_CAPABILITIES));
+ }
+
/**
* Removes the NET_CAPABILITY_NOT_RESTRICTED capability if all the capabilities it provides are
* typically provided by restricted networks.
@@ -555,26 +607,130 @@
}
/**
+ * Magic value that indicates no signal strength provided. A request specifying this value is
+ * always satisfied.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_STRENGTH_UNSPECIFIED = Integer.MIN_VALUE;
+
+ /**
+ * Signal strength. This is a signed integer, and higher values indicate better signal.
+ * The exact units are bearer-dependent. For example, Wi-Fi uses RSSI.
+ */
+ private int mSignalStrength;
+
+ /**
+ * Sets the signal strength. This is a signed integer, with higher values indicating a stronger
+ * signal. The exact units are bearer-dependent. For example, Wi-Fi uses the same RSSI units
+ * reported by WifiManager.
+ * <p>
+ * Note that when used to register a network callback, this specifies the minimum acceptable
+ * signal strength. When received as the state of an existing network it specifies the current
+ * value. A value of code SIGNAL_STRENGTH_UNSPECIFIED} means no value when received and has no
+ * effect when requesting a callback.
+ *
+ * @param signalStrength the bearer-specific signal strength.
+ * @hide
+ */
+ public void setSignalStrength(int signalStrength) {
+ mSignalStrength = signalStrength;
+ }
+
+ /**
+ * Returns {@code true} if this object specifies a signal strength.
+ *
+ * @hide
+ */
+ public boolean hasSignalStrength() {
+ return mSignalStrength > SIGNAL_STRENGTH_UNSPECIFIED;
+ }
+
+ /**
+ * Retrieves the signal strength.
+ *
+ * @return The bearer-specific signal strength.
+ * @hide
+ */
+ public int getSignalStrength() {
+ return mSignalStrength;
+ }
+
+ private void combineSignalStrength(NetworkCapabilities nc) {
+ this.mSignalStrength = Math.max(this.mSignalStrength, nc.mSignalStrength);
+ }
+
+ private boolean satisfiedBySignalStrength(NetworkCapabilities nc) {
+ return this.mSignalStrength <= nc.mSignalStrength;
+ }
+
+ private boolean equalsSignalStrength(NetworkCapabilities nc) {
+ return this.mSignalStrength == nc.mSignalStrength;
+ }
+
+ /**
* Combine a set of Capabilities to this one. Useful for coming up with the complete set
- * {@hide}
+ * @hide
*/
public void combineCapabilities(NetworkCapabilities nc) {
combineNetCapabilities(nc);
combineTransportTypes(nc);
combineLinkBandwidths(nc);
combineSpecifiers(nc);
+ combineSignalStrength(nc);
}
/**
- * Check if our requirements are satisfied by the given Capabilities.
- * {@hide}
+ * Check if our requirements are satisfied by the given {@code NetworkCapabilities}.
+ *
+ * @param nc the {@code NetworkCapabilities} that may or may not satisfy our requirements.
+ * @param onlyImmutable if {@code true}, do not consider mutable requirements such as link
+ * bandwidth, signal strength, or validation / captive portal status.
+ *
+ * @hide
+ */
+ private boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc, boolean onlyImmutable) {
+ return (nc != null &&
+ satisfiedByNetCapabilities(nc, onlyImmutable) &&
+ satisfiedByTransportTypes(nc) &&
+ (onlyImmutable || satisfiedByLinkBandwidths(nc)) &&
+ satisfiedBySpecifier(nc) &&
+ (onlyImmutable || satisfiedBySignalStrength(nc)));
+ }
+
+ /**
+ * Check if our requirements are satisfied by the given {@code NetworkCapabilities}.
+ *
+ * @param nc the {@code NetworkCapabilities} that may or may not satisfy our requirements.
+ *
+ * @hide
*/
public boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc) {
- return (nc != null &&
- satisfiedByNetCapabilities(nc) &&
- satisfiedByTransportTypes(nc) &&
- satisfiedByLinkBandwidths(nc) &&
- satisfiedBySpecifier(nc));
+ return satisfiedByNetworkCapabilities(nc, false);
+ }
+
+ /**
+ * Check if our immutable requirements are satisfied by the given {@code NetworkCapabilities}.
+ *
+ * @param nc the {@code NetworkCapabilities} that may or may not satisfy our requirements.
+ *
+ * @hide
+ */
+ public boolean satisfiedByImmutableNetworkCapabilities(NetworkCapabilities nc) {
+ return satisfiedByNetworkCapabilities(nc, true);
+ }
+
+ /**
+ * Checks that our immutable capabilities are the same as those of the given
+ * {@code NetworkCapabilities}.
+ *
+ * @hide
+ */
+ public boolean equalImmutableCapabilities(NetworkCapabilities nc) {
+ if (nc == null) return false;
+ return (equalsNetCapabilitiesImmutable(nc) &&
+ equalsTransportTypes(nc) &&
+ equalsSpecifier(nc));
}
@Override
@@ -584,6 +740,7 @@
return (equalsNetCapabilities(that) &&
equalsTransportTypes(that) &&
equalsLinkBandwidths(that) &&
+ equalsSignalStrength(that) &&
equalsSpecifier(that));
}
@@ -595,7 +752,8 @@
((int)(mTransportTypes >> 32) * 7) +
(mLinkUpBandwidthKbps * 11) +
(mLinkDownBandwidthKbps * 13) +
- (TextUtils.isEmpty(mNetworkSpecifier) ? 0 : mNetworkSpecifier.hashCode() * 17));
+ (TextUtils.isEmpty(mNetworkSpecifier) ? 0 : mNetworkSpecifier.hashCode() * 17) +
+ (mSignalStrength * 19));
}
@Override
@@ -609,7 +767,9 @@
dest.writeInt(mLinkUpBandwidthKbps);
dest.writeInt(mLinkDownBandwidthKbps);
dest.writeString(mNetworkSpecifier);
+ dest.writeInt(mSignalStrength);
}
+
public static final Creator<NetworkCapabilities> CREATOR =
new Creator<NetworkCapabilities>() {
@Override
@@ -621,6 +781,7 @@
netCap.mLinkUpBandwidthKbps = in.readInt();
netCap.mLinkDownBandwidthKbps = in.readInt();
netCap.mNetworkSpecifier = in.readString();
+ netCap.mSignalStrength = in.readInt();
return netCap;
}
@Override
@@ -678,6 +839,8 @@
String specifier = (mNetworkSpecifier == null ?
"" : " Specifier: <" + mNetworkSpecifier + ">");
- return "[" + transports + capabilities + upBand + dnBand + specifier + "]";
+ String signalStrength = (hasSignalStrength() ? " SignalStrength: " + mSignalStrength : "");
+
+ return "[" + transports + capabilities + upBand + dnBand + specifier + signalStrength + "]";
}
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 3f40484..02dae8a 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -20,6 +20,7 @@
import static android.net.NetworkPolicy.CYCLE_NONE;
import static android.text.format.Time.MONTH_DAY;
+import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -80,6 +81,54 @@
*/
public static final String EXTRA_NETWORK_TEMPLATE = "android.net.NETWORK_TEMPLATE";
+ /**
+ * Broadcast intent action for informing a custom component about a network policy
+ * notification.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_SHOW_NETWORK_POLICY_NOTIFICATION =
+ "android.net.action.SHOW_NETWORK_POLICY_NOTIFICATION";
+
+ /**
+ * The sequence number associated with the notification - a higher number
+ * indicates previous notifications may be disregarded.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_NOTIFICATION_SEQUENCE_NUMBER =
+ "android.net.extra.NOTIFICATION_SEQUENCE_NUMBER";
+
+ /**
+ * The type of notification that should be presented to the user.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_NOTIFICATION_TYPE = "android.net.extra.NOTIFICATION_TYPE";
+
+ @SystemApi
+ public static final int NOTIFICATION_TYPE_NONE = 0;
+ @SystemApi
+ public static final int NOTIFICATION_TYPE_USAGE_WARNING = 1;
+ @SystemApi
+ public static final int NOTIFICATION_TYPE_USAGE_REACHED_LIMIT = 2;
+ @SystemApi
+ public static final int NOTIFICATION_TYPE_USAGE_EXCEEDED_LIMIT = 3;
+
+ /**
+ * The number of bytes used on the network in the notification.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_BYTES_USED = "android.net.extra.BYTES_USED";
+
+ /**
+ * The network policy for the network in the notification.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_NETWORK_POLICY = "android.net.extra.NETWORK_POLICY";
+
private final Context mContext;
private INetworkPolicyManager mService;
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index e184ec4..7da4818 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -191,6 +191,24 @@
mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
return this;
}
+
+ /**
+ * Sets the signal strength. This is a signed integer, with higher values indicating a
+ * stronger signal. The exact units are bearer-dependent. For example, Wi-Fi uses the same
+ * RSSI units reported by WifiManager.
+ * <p>
+ * Note that when used to register a network callback, this specifies the minimum acceptable
+ * signal strength. When received as the state of an existing network it specifies the
+ * current value. A value of {@code SIGNAL_STRENGTH_UNSPECIFIED} means no value when
+ * received and has no effect when requesting a callback.
+ *
+ * @param signalStrength the bearer-specific signal strength.
+ * @hide
+ */
+ public Builder setSignalStrength(int signalStrength) {
+ mNetworkCapabilities.setSignalStrength(signalStrength);
+ return this;
+ }
}
// implement the Parcelable interface
diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java
index cccc4be..1f3e9a7 100644
--- a/core/java/android/os/BatteryManager.java
+++ b/core/java/android/os/BatteryManager.java
@@ -101,6 +101,13 @@
*/
public static final String EXTRA_INVALID_CHARGER = "invalid_charger";
+ /**
+ * Extra for {@link android.content.Intent#ACTION_BATTERY_CHANGED}:
+ * Int value set to the maximum charging current supported by the charger in micro amperes.
+ * {@hide}
+ */
+ public static final String EXTRA_MAX_CHARGING_CURRENT = "max_charging_current";
+
// values for "status" field in the ACTION_BATTERY_CHANGED Intent
public static final int BATTERY_STATUS_UNKNOWN = 1;
public static final int BATTERY_STATUS_CHARGING = 2;
diff --git a/core/java/android/os/BatteryProperties.java b/core/java/android/os/BatteryProperties.java
index 8f5cf8b..29e868c 100644
--- a/core/java/android/os/BatteryProperties.java
+++ b/core/java/android/os/BatteryProperties.java
@@ -22,6 +22,7 @@
public boolean chargerAcOnline;
public boolean chargerUsbOnline;
public boolean chargerWirelessOnline;
+ public int maxChargingCurrent;
public int batteryStatus;
public int batteryHealth;
public boolean batteryPresent;
@@ -37,6 +38,7 @@
chargerAcOnline = other.chargerAcOnline;
chargerUsbOnline = other.chargerUsbOnline;
chargerWirelessOnline = other.chargerWirelessOnline;
+ maxChargingCurrent = other.maxChargingCurrent;
batteryStatus = other.batteryStatus;
batteryHealth = other.batteryHealth;
batteryPresent = other.batteryPresent;
@@ -55,6 +57,7 @@
chargerAcOnline = p.readInt() == 1 ? true : false;
chargerUsbOnline = p.readInt() == 1 ? true : false;
chargerWirelessOnline = p.readInt() == 1 ? true : false;
+ maxChargingCurrent = p.readInt();
batteryStatus = p.readInt();
batteryHealth = p.readInt();
batteryPresent = p.readInt() == 1 ? true : false;
@@ -68,6 +71,7 @@
p.writeInt(chargerAcOnline ? 1 : 0);
p.writeInt(chargerUsbOnline ? 1 : 0);
p.writeInt(chargerWirelessOnline ? 1 : 0);
+ p.writeInt(maxChargingCurrent);
p.writeInt(batteryStatus);
p.writeInt(batteryHealth);
p.writeInt(batteryPresent ? 1 : 0);
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index fce09dd..9f71ce1 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -1180,6 +1180,37 @@
_data.recycle();
}
}
+
+ @Override
+ public void createNewUserDir(int userHandle, String path) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(userHandle);
+ _data.writeString(path);
+ mRemote.transact(Stub.TRANSACTION_createNewUserDir, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
+ public void deleteUserKey(int userHandle) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(userHandle);
+ mRemote.transact(Stub.TRANSACTION_deleteUserKey, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
}
private static final String DESCRIPTOR = "IMountService";
@@ -1295,6 +1326,9 @@
static final int TRANSACTION_benchmark = IBinder.FIRST_CALL_TRANSACTION + 59;
static final int TRANSACTION_setDebugFlags = IBinder.FIRST_CALL_TRANSACTION + 60;
+ static final int TRANSACTION_createNewUserDir = IBinder.FIRST_CALL_TRANSACTION + 62;
+ static final int TRANSACTION_deleteUserKey = IBinder.FIRST_CALL_TRANSACTION + 63;
+
/**
* Cast an IBinder object into an IMountService interface, generating a
* proxy if needed.
@@ -1850,6 +1884,21 @@
reply.writeNoException();
return true;
}
+ case TRANSACTION_createNewUserDir: {
+ data.enforceInterface(DESCRIPTOR);
+ int userHandle = data.readInt();
+ String path = data.readString();
+ createNewUserDir(userHandle, path);
+ reply.writeNoException();
+ return true;
+ }
+ case TRANSACTION_deleteUserKey: {
+ data.enforceInterface(DESCRIPTOR);
+ int userHandle = data.readInt();
+ deleteUserKey(userHandle);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
}
@@ -2159,4 +2208,19 @@
public String getPrimaryStorageUuid() throws RemoteException;
public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
throws RemoteException;
+
+ /**
+ * Creates the user data directory, possibly encrypted
+ * @param userHandle Handle of the user whose directory we are creating
+ * @param path Path at which to create the directory.
+ */
+ public void createNewUserDir(int userHandle, String path)
+ throws RemoteException;
+
+ /**
+ * Securely delete the user's encryption key
+ * @param userHandle Handle of the user whose key we are deleting
+ */
+ public void deleteUserKey(int userHandle)
+ throws RemoteException;
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index d1f3743..1d92453 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -960,6 +960,24 @@
}
/** {@hide} */
+ public void createNewUserDir(int userHandle, File path) {
+ try {
+ mMountService.createNewUserDir(userHandle, path.getAbsolutePath());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public void deleteUserKey(int userHandle) {
+ try {
+ mMountService.deleteUserKey(userHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
public static File maybeTranslateEmulatedPathToInternal(File path) {
final IMountService mountService = IMountService.Stub.asInterface(
ServiceManager.getService("mount"));
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fe95864..89de101 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5712,6 +5712,13 @@
public static final String ASSISTANT = "assistant";
/**
+ * Whether the camera launch gesture should be disabled.
+ *
+ * @hide
+ */
+ public static final String CAMERA_GESTURE_DISABLED = "camera_gesture_disabled";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -5767,6 +5774,7 @@
MOUNT_UMS_NOTIFY_ENABLED,
SLEEP_TIMEOUT,
DOUBLE_TAP_TO_WAKE,
+ CAMERA_GESTURE_DISABLED,
};
/**
@@ -8080,6 +8088,13 @@
* @hide
*/
public static final String CONTACT_METADATA_SYNC = "contact_metadata_sync";
+
+ /**
+ * Whether to enable cellular on boot.
+ * The value 1 - enable, 0 - disable
+ * @hide
+ */
+ public static final String ENABLE_CELLULAR_ON_BOOT = "enable_cellular_on_boot";
}
/**
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index a9bf92b..52852e7 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -761,6 +761,19 @@
* Backs out one level of a navigation hierarchy or collapses the item that currently has
* focus. */
public static final int KEYCODE_NAVIGATE_OUT = 263;
+ /** Key code constant: Primary stem key for Wear
+ * Main power/reset button on watch.
+ * @hide */
+ public static final int KEYCODE_STEM_PRIMARY = 264;
+ /** Key code constant: Generic stem key 1 for Wear
+ * @hide */
+ public static final int KEYCODE_STEM_1 = 265;
+ /** Key code constant: Generic stem key 2 for Wear
+ * @hide */
+ public static final int KEYCODE_STEM_2 = 266;
+ /** Key code constant: Generic stem key 3 for Wear
+ * @hide */
+ public static final int KEYCODE_STEM_3 = 267;
/** Key code constant: Skip forward media key. */
public static final int KEYCODE_MEDIA_SKIP_FORWARD = 272;
/** Key code constant: Skip backward media key. */
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4ac807e..b3e3ecb 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7391,11 +7391,7 @@
* @hide
*/
public void getOutsets(Rect outOutsetRect) {
- if (mAttachInfo != null) {
- outOutsetRect.set(mAttachInfo.mOutsets);
- } else {
- outOutsetRect.setEmpty();
- }
+ outOutsetRect.set(mAttachInfo.mOutsets);
}
/**
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index 6962711..0cc1b25 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -612,7 +612,7 @@
View listItem = view;
try {
View v;
- while (!(v = (View) listItem.getParent()).equals(this)) {
+ while ((v = (View) listItem.getParent()) != null && !v.equals(this)) {
listItem = v;
}
} catch (ClassCastException e) {
@@ -620,11 +620,13 @@
return INVALID_POSITION;
}
- // Search the children for the list item
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- if (getChildAt(i).equals(listItem)) {
- return mFirstPosition + i;
+ if (listItem != null) {
+ // Search the children for the list item
+ final int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ if (getChildAt(i).equals(listItem)) {
+ return mFirstPosition + i;
+ }
}
}
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index 55493c3..6da0f63 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -29,6 +29,21 @@
// Temporary constants go here, to await migration to MetricsConstants.
// next value is 239;
public static final int ACTION_ASSIST_LONG_PRESS = 239;
+ public static final int FINGERPRINT_ENROLLING = 240;
+ public static final int FINGERPRINT_FIND_SENSOR = 241;
+ public static final int FINGERPRINT_ENROLL_FINISH = 242;
+ public static final int FINGERPRINT_ENROLL_INTRO = 243;
+ public static final int FINGERPRINT_ENROLL_ONBOARD = 244;
+ public static final int FINGERPRINT_ENROLL_SIDECAR = 245;
+ public static final int FINGERPRINT_ENROLLING_SETUP = 246;
+ public static final int FINGERPRINT_FIND_SENSOR_SETUP = 247;
+ public static final int FINGERPRINT_ENROLL_FINISH_SETUP = 248;
+ public static final int FINGERPRINT_ENROLL_INTRO_SETUP = 249;
+ public static final int FINGERPRINT_ENROLL_ONBOARD_SETUP = 250;
+ public static final int ACTION_FINGERPRINT_ENROLL = 251;
+ public static final int ACTION_FINGERPRINT_AUTH = 252;
+ public static final int ACTION_FINGERPRINT_DELETE = 253;
+ public static final int ACTION_FINGERPRINT_RENAME = 254;
public static void visible(Context context, int category) throws IllegalArgumentException {
if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) {
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index c1645c3..feed3903 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -69,5 +69,10 @@
void showAssistDisclosure();
void startAssist(in Bundle args);
+
+ /**
+ * Notifies the status bar that a camera launch gesture has been detected.
+ */
+ void onCameraLaunchGestureDetected();
}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index dfb7c50..4e4552d 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -16,6 +16,7 @@
package com.android.internal.widget;
+import android.app.trust.IStrongAuthTracker;
import com.android.internal.widget.VerifyCredentialResponse;
/** {@hide} */
@@ -35,4 +36,7 @@
boolean checkVoldPassword(int userId);
boolean havePattern(int userId);
boolean havePassword(int userId);
+ void registerStrongAuthTracker(in IStrongAuthTracker tracker);
+ void unregisterStrongAuthTracker(in IStrongAuthTracker tracker);
+ void requireStrongAuth(int strongAuthReason, int userId);
}
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 86d11be..d24b10b 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -16,18 +16,19 @@
package com.android.internal.widget;
-import android.Manifest;
+import android.annotation.IntDef;
import android.app.ActivityManager;
-import android.app.ActivityManagerNative;
import android.app.admin.DevicePolicyManager;
+import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
-import android.bluetooth.BluetoothClass;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.pm.PackageManager;
import android.os.AsyncTask;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -38,9 +39,12 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseIntArray;
import com.google.android.collect.Lists;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@@ -228,7 +232,7 @@
public void reportFailedPasswordAttempt(int userId) {
getDevicePolicyManager().reportFailedPasswordAttempt(userId);
getTrustManager().reportUnlockAttempt(false /* authenticated */, userId);
- getTrustManager().reportRequireCredentialEntry(userId);
+ requireCredentialEntry(userId);
}
public void reportSuccessfulPasswordAttempt(int userId) {
@@ -1163,10 +1167,32 @@
}
/**
- * @see android.app.trust.TrustManager#reportRequireCredentialEntry(int)
+ * Disable trust until credentials have been entered for user {@param userId}.
+ *
+ * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+ *
+ * @param userId either an explicit user id or {@link android.os.UserHandle#USER_ALL}
*/
public void requireCredentialEntry(int userId) {
- getTrustManager().reportRequireCredentialEntry(userId);
+ requireStrongAuth(StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId);
+ }
+
+ /**
+ * Requests strong authentication for user {@param userId}.
+ *
+ * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
+ *
+ * @param strongAuthReason a combination of {@link StrongAuthTracker.StrongAuthFlags} indicating
+ * the reason for and the strength of the requested authentication.
+ * @param userId either an explicit user id or {@link android.os.UserHandle#USER_ALL}
+ */
+ public void requireStrongAuth(@StrongAuthTracker.StrongAuthFlags int strongAuthReason,
+ int userId) {
+ try {
+ getLockSettings().requireStrongAuth(strongAuthReason, userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error while requesting strong auth: " + e);
+ }
}
private void onAfterChangingPassword(int userHandle) {
@@ -1198,4 +1224,154 @@
private boolean shouldEncryptWithCredentials(boolean defaultValue) {
return isCredentialRequiredToDecrypt(defaultValue) && !isDoNotAskCredentialsOnBootSet();
}
+
+
+ public void registerStrongAuthTracker(final StrongAuthTracker strongAuthTracker) {
+ try {
+ getLockSettings().registerStrongAuthTracker(strongAuthTracker.mStub);
+ } catch (RemoteException e) {
+ throw new RuntimeException("Could not register StrongAuthTracker");
+ }
+ }
+
+ public void unregisterStrongAuthTracker(final StrongAuthTracker strongAuthTracker) {
+ try {
+ getLockSettings().unregisterStrongAuthTracker(strongAuthTracker.mStub);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not unregister StrongAuthTracker", e);
+ }
+ }
+
+ /**
+ * Tracks the global strong authentication state.
+ */
+ public static class StrongAuthTracker {
+
+ @IntDef(flag = true,
+ value = { STRONG_AUTH_NOT_REQUIRED,
+ STRONG_AUTH_REQUIRED_AFTER_BOOT,
+ STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW,
+ SOME_AUTH_REQUIRED_AFTER_USER_REQUEST})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StrongAuthFlags {}
+
+ /**
+ * Strong authentication is not required.
+ */
+ public static final int STRONG_AUTH_NOT_REQUIRED = 0x0;
+
+ /**
+ * Strong authentication is required because the user has not authenticated since boot.
+ */
+ public static final int STRONG_AUTH_REQUIRED_AFTER_BOOT = 0x1;
+
+ /**
+ * Strong authentication is required because a device admin has requested it.
+ */
+ public static final int STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW = 0x2;
+
+ /**
+ * Some authentication is required because the user has temporarily disabled trust.
+ */
+ public static final int SOME_AUTH_REQUIRED_AFTER_USER_REQUEST = 0x4;
+
+ /**
+ * Strong authentication is required because the user has been locked out after too many
+ * attempts.
+ */
+ public static final int STRONG_AUTH_REQUIRED_AFTER_LOCKOUT = 0x8;
+
+ public static final int DEFAULT = STRONG_AUTH_REQUIRED_AFTER_BOOT;
+ private static final int ALLOWING_FINGERPRINT = SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
+
+ final SparseIntArray mStrongAuthRequiredForUser = new SparseIntArray();
+
+ private final H mHandler;
+
+ public StrongAuthTracker() {
+ this(Looper.myLooper());
+ }
+
+ /**
+ * @param looper the looper on whose thread calls to {@link #onStrongAuthRequiredChanged}
+ * will be scheduled.
+ */
+ public StrongAuthTracker(Looper looper) {
+ mHandler = new H(looper);
+ }
+
+ /**
+ * Returns {@link #STRONG_AUTH_NOT_REQUIRED} if strong authentication is not required,
+ * otherwise returns a combination of {@link StrongAuthFlags} indicating why strong
+ * authentication is required.
+ *
+ * @param userId the user for whom the state is queried.
+ */
+ public @StrongAuthFlags int getStrongAuthForUser(int userId) {
+ return mStrongAuthRequiredForUser.get(userId, DEFAULT);
+ }
+
+ /**
+ * @return true if unlocking with trust alone is allowed for {@param userId} by the current
+ * strong authentication requirements.
+ */
+ public boolean isTrustAllowedForUser(int userId) {
+ return getStrongAuthForUser(userId) == STRONG_AUTH_NOT_REQUIRED;
+ }
+
+ /**
+ * @return true if unlocking with fingerprint alone is allowed for {@param userId} by the
+ * current strong authentication requirements.
+ */
+ public boolean isFingerprintAllowedForUser(int userId) {
+ return (getStrongAuthForUser(userId) & ~ALLOWING_FINGERPRINT) == 0;
+ }
+
+ /**
+ * Called when the strong authentication requirements for {@param userId} changed.
+ */
+ public void onStrongAuthRequiredChanged(int userId) {
+ }
+
+ void handleStrongAuthRequiredChanged(@StrongAuthFlags int strongAuthFlags,
+ int userId) {
+
+ int oldValue = getStrongAuthForUser(userId);
+ if (strongAuthFlags != oldValue) {
+ if (strongAuthFlags == DEFAULT) {
+ mStrongAuthRequiredForUser.delete(userId);
+ } else {
+ mStrongAuthRequiredForUser.put(userId, strongAuthFlags);
+ }
+ onStrongAuthRequiredChanged(userId);
+ }
+ }
+
+
+ final IStrongAuthTracker.Stub mStub = new IStrongAuthTracker.Stub() {
+ @Override
+ public void onStrongAuthRequiredChanged(@StrongAuthFlags int strongAuthFlags,
+ int userId) {
+ mHandler.obtainMessage(H.MSG_ON_STRONG_AUTH_REQUIRED_CHANGED,
+ strongAuthFlags, userId).sendToTarget();
+ }
+ };
+
+ private class H extends Handler {
+ static final int MSG_ON_STRONG_AUTH_REQUIRED_CHANGED = 1;
+
+ public H(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_ON_STRONG_AUTH_REQUIRED_CHANGED:
+ handleStrongAuthRequiredChanged(msg.arg1, msg.arg2);
+ break;
+ }
+ }
+ };
+ }
}
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index 35ed63b..d88f479 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -89,14 +89,21 @@
}
};
private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
+ private Runnable mRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mDismissed) {
+ dismiss();
+ } else {
+ cancel();
+ }
+ resetMembers();
+ }
+ };
+
@Override
public void onReceive(Context context, Intent intent) {
- if (mDismissed) {
- dismiss();
- } else {
- cancel();
- }
- resetMembers();
+ post(mRunnable);
}
};
private IntentFilter mScreenOffFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 6b07a47..fc15b964 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -20,6 +20,10 @@
LOCAL_CFLAGS += -DENABLE_CPUSETS
endif
+ifneq ($(ENABLE_SCHED_BOOST),)
+ LOCAL_CFLAGS += -DENABLE_SCHED_BOOST
+endif
+
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_CFLAGS += -DU_USING_ICU_NAMESPACE=0
diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp
index ec56507..7d0afdc 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -138,7 +138,7 @@
(JNIEnv *env, jclass clazz, jstring opPackageName)
{
ScopedUtfChars opPackageNameUtf(env, opPackageName);
- return (jlong) new SensorManager(String16(opPackageNameUtf.c_str()));
+ return (jlong) &SensorManager::getInstanceForPackage(String16(opPackageNameUtf.c_str()));
}
static jboolean
diff --git a/core/jni/android_hardware_camera2_DngCreator.cpp b/core/jni/android_hardware_camera2_DngCreator.cpp
index ba08237..e10a644 100644
--- a/core/jni/android_hardware_camera2_DngCreator.cpp
+++ b/core/jni/android_hardware_camera2_DngCreator.cpp
@@ -20,6 +20,7 @@
#include <string.h>
#include <algorithm>
#include <memory>
+#include <vector>
#include <utils/Log.h>
#include <utils/Errors.h>
@@ -1659,8 +1660,7 @@
lsmHeight = static_cast<uint32_t>(entry1.data.i32[1]);
}
- camera_metadata_entry entry2 =
- results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
+ camera_metadata_entry entry2 = results.find(ANDROID_STATISTICS_LENS_SHADING_MAP);
camera_metadata_entry entry =
characteristics.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
@@ -1685,6 +1685,45 @@
}
}
+
+ // Set up bad pixel correction list
+ camera_metadata_entry entry3 = characteristics.find(ANDROID_STATISTICS_HOT_PIXEL_MAP);
+
+ if ((entry3.count % 2) != 0) {
+ ALOGE("%s: Hot pixel map contains odd number of values, cannot map to pairs!",
+ __FUNCTION__);
+ jniThrowRuntimeException(env, "failed to add hotpixel map.");
+ return nullptr;
+ }
+
+ // Adjust the bad pixel coordinates to be relative to the origin of the active area DNG tag
+ std::vector<uint32_t> v;
+ for (size_t i = 0; i < entry3.count; i+=2) {
+ int32_t x = entry3.data.i32[i];
+ int32_t y = entry3.data.i32[i + 1];
+ x -= static_cast<int32_t>(xmin);
+ y -= static_cast<int32_t>(ymin);
+ if (x < 0 || y < 0 || static_cast<uint32_t>(x) >= width ||
+ static_cast<uint32_t>(y) >= width) {
+ continue;
+ }
+ v.push_back(x);
+ v.push_back(y);
+ }
+ const uint32_t* badPixels = &v[0];
+ uint32_t badPixelCount = v.size();
+
+ if (badPixelCount > 0) {
+ err = builder.addBadPixelListForMetadata(badPixels, badPixelCount, opcodeCfaLayout);
+
+ if (err != OK) {
+ ALOGE("%s: Could not add hotpixel map.", __FUNCTION__);
+ jniThrowRuntimeException(env, "failed to add hotpixel map.");
+ return nullptr;
+ }
+ }
+
+
size_t listSize = builder.getSize();
uint8_t opcodeListBuf[listSize];
err = builder.buildOpList(opcodeListBuf);
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 5e7b444..414cbb4 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -408,6 +408,27 @@
}
}
+#ifdef ENABLE_SCHED_BOOST
+static void SetForkLoad(bool boost) {
+ // set scheduler knob to boost forked processes
+ pid_t currentPid = getpid();
+ // fits at most "/proc/XXXXXXX/sched_init_task_load\0"
+ char schedPath[35];
+ snprintf(schedPath, sizeof(schedPath), "/proc/%u/sched_init_task_load", currentPid);
+ int schedBoostFile = open(schedPath, O_WRONLY);
+ if (schedBoostFile < 0) {
+ ALOGW("Unable to set zygote scheduler boost");
+ return;
+ }
+ if (boost) {
+ write(schedBoostFile, "100\0", 4);
+ } else {
+ write(schedBoostFile, "0\0", 2);
+ }
+ close(schedBoostFile);
+}
+#endif
+
// Utility routine to fork zygote and specialize the child process.
static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
jint debug_flags, jobjectArray javaRlimits,
@@ -418,6 +439,10 @@
jstring instructionSet, jstring dataDir) {
SetSigChldHandler();
+#ifdef ENABLE_SCHED_BOOST
+ SetForkLoad(true);
+#endif
+
pid_t pid = fork();
if (pid == 0) {
@@ -558,6 +583,12 @@
}
} else if (pid > 0) {
// the parent process
+
+#ifdef ENABLE_SCHED_BOOST
+ // unset scheduler knob
+ SetForkLoad(false);
+#endif
+
}
return pid;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 99e55bf..86659ae 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -320,6 +320,8 @@
<protected-broadcast android:name="android.internal.policy.action.BURN_IN_PROTECTION" />
<protected-broadcast android:name="android.app.action.SYSTEM_UPDATE_POLICY_CHANGED" />
<protected-broadcast android:name="android.app.action.DEVICE_OWNER_CHANGED" />
+
+ <protected-broadcast android:name="android.net.action.SHOW_NETWORK_POLICY_NOTIFICATION" />
<!-- ====================================================================== -->
<!-- RUNTIME PERMISSIONS -->
<!-- ====================================================================== -->
@@ -865,6 +867,11 @@
<permission android:name="android.permission.BIND_DIRECTORY_SEARCH"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi @hide Allows an application to modify cell broadcasts through the content provider.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.MODIFY_CELL_BROADCASTS"
+ android:protectionLevel="signature|system" />
+
<!-- =============================================================== -->
<!-- Permissions for setting the device alarm -->
<!-- =============================================================== -->
@@ -2505,6 +2512,10 @@
<permission android:name="android.permission.MANAGE_FINGERPRINT"
android:protectionLevel="system|signature" />
+ <!-- Allows an app to reset fingerprint attempt counter. Reserved for the system. @hide -->
+ <permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to control keyguard. Only allowed for system processes.
@hide -->
<permission android:name="android.permission.CONTROL_KEYGUARD"
diff --git a/core/res/res/values-mcc219-mnc02/config.xml b/core/res/res/values-mcc219-mnc02/config.xml
new file mode 100644
index 0000000..2ac6ba6
--- /dev/null
+++ b/core/res/res/values-mcc219-mnc02/config.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You my 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.
+*/
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>21901</item>
+ </string-array>
+</resources>
diff --git a/core/res/res/values-watch/strings.xml b/core/res/res/values-watch/strings.xml
index 4ea2b52..e5991fc 100644
--- a/core/res/res/values-watch/strings.xml
+++ b/core/res/res/values-watch/strings.xml
@@ -23,4 +23,50 @@
<string name="android_upgrading_apk">App
<xliff:g id="number" example="123">%1$d</xliff:g> of
<xliff:g id="number" example="123">%2$d</xliff:g>.</string>
+
+ <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. Override from base which says "Body Sensors". [CHAR_LIMIT=25] -->
+ <string name="permgrouplab_sensors">Sensors</string>
+
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permgrouplab_contactswear">access your contacts</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permgrouplab_locationwear">access this watch\'s location</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permgrouplab_calendarwear">access your calendar</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permgrouplab_smswear">send and view SMS messages</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permgrouplab_storagewear">access photos, media, and files on your watch</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permgrouplab_microphonewear">record audio</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permgrouplab_camerawear">take pictures and record video</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permgrouplab_phonewear">make and manage phone calls</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permgrouplab_sensorswear">access sensor data about your vital signs</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permlab_statusBarServicewear">be the status bar</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permlab_bodySensorswear">access body sensors (like heart rate monitors)</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permlab_accessFineLocationwear">access precise location (GPS and network-based)</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permlab_accessCoarseLocationwear">access approximate location (network-based)</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permlab_sim_communicationwear">send commands to the SIM</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permlab_createNetworkSocketswear">have full network access</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permlab_manageProfileAndDeviceOwnerswear">manage profile and device owners</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permlab_changeWimaxStatewear">change WiMAX state</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permlab_handoverStatuswear">receive Android Beam transfer status</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permlab_route_media_outputwear">route media output</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permlab_readInstallSessionswear">read install sessions</string>
+ <!-- Description of a permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR_LIMIT=100] -->
+ <string name="permlab_requestInstallPackageswear">request install packages</string>
</resources>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 67abe8d..a0246560 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1806,6 +1806,10 @@
<enum name="KEYCODE_NAVIGATE_NEXT" value="261" />
<enum name="KEYCODE_NAVIGATE_IN" value="262" />
<enum name="KEYCODE_NAVIGATE_OUT" value="263" />
+ <enum name="KEYCODE_STEM_PRIMARY" value="264" />
+ <enum name="KEYCODE_STEM_1" value="265" />
+ <enum name="KEYCODE_STEM_2" value="266" />
+ <enum name="KEYCODE_STEM_3" value="267" />
<enum name="KEYCODE_MEDIA_SKIP_FORWARD" value="272" />
<enum name="KEYCODE_MEDIA_SKIP_BACKWARD" value="273" />
<enum name="KEYCODE_MEDIA_STEP_FORWARD" value="274" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index ae8cae8..694d263 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -619,6 +619,17 @@
<!-- rotation: 270 (rotate CW) --> <item>-25</item> <item>65</item>
</integer-array>
+ <!-- Indicate the name of the window orientation sensor type if present. A
+ window orientation sensor produces values to be used in lieu of the
+ typical, accelerometer based sensor. It must only produce integral
+ values between 0 and 3, inclusive, with each one corresponding to a
+ given rotation:
+ 0: 0 degrees of rotation (natural)
+ 1: 90 degrees of rotation (rotate CCW)
+ 2: 180 degrees of rotation (reverse)
+ 3: 270 degrees of rotation (rotate CW) -->
+ <string name="config_orientationSensorType" translatable="false">@null</string>
+
<!-- Lid switch behavior -->
<!-- The number of degrees to rotate the display when the keyboard is open.
@@ -2270,4 +2281,13 @@
<string-array name="config_cell_retries_per_error_code">
</string-array>
+ <!-- The OEM specified sensor type for the gesture to launch the camear app. -->
+ <integer name="config_cameraLaunchGestureSensorType">-1</integer>
+ <!-- The OEM specified sensor string type for the gesture to launch camera app, this value
+ must match the value of config_cameraLaunchGestureSensorType in OEM's HAL -->
+ <string translatable="false" name="config_cameraLaunchGestureSensorStringType"></string>
+
+ <!-- Name of the component to handle network policy notifications. If present,
+ disables NetworkPolicyManagerService's presentation of data-usage notifications. -->
+ <string translatable="false" name="config_networkPolicyNotificationComponent"></string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 262aa76..4e94a64 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1547,6 +1547,7 @@
<java-symbol type="string" name="bugreport_title" />
<java-symbol type="string" name="bugreport_message" />
<java-symbol type="string" name="bugreport_status" />
+ <java-symbol type="string" name="config_orientationSensorType" />
<java-symbol type="string" name="faceunlock_multiple_failures" />
<java-symbol type="string" name="global_action_power_off" />
<java-symbol type="string" name="global_actions_airplane_mode_off_status" />
@@ -2318,6 +2319,10 @@
<java-symbol type="array" name="config_cell_retries_per_error_code" />
<java-symbol type="drawable" name="ic_more_items" />
- <java-symbol type="drawable" name="platlogo_m" />
+ <!-- Gesture -->
+ <java-symbol type="integer" name="config_cameraLaunchGestureSensorType" />
+ <java-symbol type="string" name="config_cameraLaunchGestureSensorStringType" />
+ <java-symbol type="string" name="config_networkPolicyNotificationComponent" />
+ <java-symbol type="drawable" name="platlogo_m" />
</resources>
diff --git a/docs/html/images/training/auto-desktop-head-unit-context-menu-enabled.png b/docs/html/images/training/auto-desktop-head-unit-context-menu-enabled.png
new file mode 100644
index 0000000..99e60e9
--- /dev/null
+++ b/docs/html/images/training/auto-desktop-head-unit-context-menu-enabled.png
Binary files differ
diff --git a/docs/html/images/training/auto-desktop-head-unit-launch.png b/docs/html/images/training/auto-desktop-head-unit-launch.png
new file mode 100644
index 0000000..012857a4
--- /dev/null
+++ b/docs/html/images/training/auto-desktop-head-unit-launch.png
Binary files differ
diff --git a/docs/html/images/training/auto-desktop-head-unit-server-running.png b/docs/html/images/training/auto-desktop-head-unit-server-running.png
new file mode 100644
index 0000000..5aa3b83
--- /dev/null
+++ b/docs/html/images/training/auto-desktop-head-unit-server-running.png
Binary files differ
diff --git a/docs/html/images/training/auto-desktop-head-unit-wkst-launch.png b/docs/html/images/training/auto-desktop-head-unit-wkst-launch.png
new file mode 100644
index 0000000..4d48ac4
--- /dev/null
+++ b/docs/html/images/training/auto-desktop-head-unit-wkst-launch.png
Binary files differ
diff --git a/docs/html/tools/help/desktop-head-unit.jd b/docs/html/tools/help/desktop-head-unit.jd
new file mode 100644
index 0000000..981979c
--- /dev/null
+++ b/docs/html/tools/help/desktop-head-unit.jd
@@ -0,0 +1,439 @@
+page.title=Desktop Head Unit
+page.tags="auto", "car", "testing","dhu"
+@jd:body
+
+
+<div id="qv-wrapper">
+<div id="qv">
+
+ <h2>In this document</h2>
+ <ol>
+ <li><a href="#launching-dhu">Launching the DHU</a></li>
+ <li><a href="#dhu-commands">Issuing DHU Commands</a></li>
+ </ol>
+
+ <h2>See also</h2>
+ <ol>
+ <li><a href="{@docRoot}training/auto/start/index.html#test-it-dhu">Run and Test Auto Apps</a></li>
+ </ol>
+
+</div>
+</div>
+
+
+<p>The Desktop Head Unit (DHU) enables your development machine to emulate an Android Auto
+head unit, so you can easily run and test Android Auto apps. The DHU runs on
+Windows, Mac, and Linux hosts and replaces previous Android Auto simulators,
+such as the Android Media Browser and Messaging
+simulators.</p>
+
+<p class="note"><strong>Note:</strong> For other information about testing Auto apps, see the
+training lesson
+<a href="{@docRoot}training/auto/start/index.html#test-it-dhu">Run and Test Auto Apps</a>. </p>
+
+
+<h2 id="launching-dhu">Launching the DHU</h2>
+
+<p>
+ To launch the DHU, run the <code>desktop-head-unit.exe</code> (on Windows)
+ or <code>desktop-head-unit</code> (on Mac or Linux) command, as described in
+ <a href="{@docRoot}training/auto/start/index.html#connecting-dhu">Connecting
+ the DHU to your mobile device</a>.
+</p>
+
+<p>
+ By default, the DHU emulates the most common form of Android Auto-compatible
+ head unit, which uses a touch screen user interface. You can simulate user
+ touches by clicking the DHU with a mouse. To emulate head units which use
+ a rotary controller for input, you can use the <code>-i controller</code> flag,
+ as in this example:
+</p>
+
+<pre class="no-pretty-print">$ ./desktop-head-unit -i controller</pre>
+
+<p>
+ When the DHU is in rotary-controller mode you can simulate controller
+ operations by using keyboard shortcuts, as described in <a href=
+ "#cmd-bindings">DHU commands and key bindings</a>. If the DHU is in rotary
+ controller mode, it ignores mouse clicks; you must operate Android Auto with
+ the simulated rotary controller operations.
+</p>
+
+<h2 id="dhu-commands">Issuing DHU Commands</h2>
+
+<p>
+ DHU commands allow you to test your app with Android Auto features, such as
+ playing voice input or switching between night and day display mode. You can issue commands to
+ the DHU by running commands from the terminal window where you launched DHU.
+ You can also issue commands by selecting the DHU window and
+ using keyboard shortcuts. The DHU commands
+ and key bindings for all controls are listed in <a href="#cmd-bindings">DHU
+ commands and key bindings</a>.
+</p>
+
+
+<h3 id="day-night">Switching between day and night mode</h3>
+
+<p>
+ Android Auto supports different color schemes for day and night. You should test your app in both
+ day and night mode. You can switch between night and day mode in either of the
+ following ways:
+</p>
+
+<ul>
+ <li>Run the command <code>daynight</code> in the terminal where you launched the DHU.
+ </li>
+
+ <li>Select the DHU window and press the <strong>N</strong> key.
+ </li>
+</ul>
+
+<h3>Microphone testing</h3>
+
+<p>The DHU supports using a microphone for voice input. You can also instruct the DHU to treat
+a pre-recorded voice track as input, as if the DHU had heard the track through the microphone.</p>
+
+<p>To use a pre-recorded sound file as input, enter this command: </p>
+<pre class="no-pretty-print">
+$ mic play <sound_file_path>/<sound_file>.wav
+</pre>
+
+<p>For your convenience, we have provided the following sound files for common
+voice commands. These sound files are installed in the
+<code><sdk>/extras/google/auto/voice/</code> directory.</p>
+
+<dl>
+ <dt>
+ <code>exitnav.wav</code>
+ </dt>
+
+ <dd>
+ "Exit navigation."
+ </dd>
+
+ <dt>
+ <code>navgoogle.wav</code>
+ </dt>
+
+ <dd>
+ "Navigate to 1600 Amphitheatre Parkway, Mountain View."
+ </dd>
+
+ <dt>
+ <code>navsoh.wav</code>
+ </dt>
+
+ <dd>
+ "Navigate to Sydney Opera House."
+ </dd>
+
+ <dt>
+ <code>nextturn.wav</code>
+ </dt>
+
+ <dd>
+ "When is my next turn?"
+ </dd>
+
+ <dt>
+ <code>showalternateroute.wav</code>
+ </dt>
+
+ <dd>
+ "Show alternate routes.""
+ </dd>
+
+ <dt>
+ <code>howlong.wav</code>
+ </dt>
+
+ <dd>
+ "How long until I get there?"
+ </dd>
+
+ <dt>
+ <code>navhome.wav</code>
+ </dt>
+
+ <dd>
+ "Navigate to home."
+ </dd>
+
+ <dt>
+ <code>navwork.wav</code>
+ </dt>
+
+ <dd>
+ "Navigate to work.""
+ </dd>
+
+ <dt>
+ <code>pause.wav</code>
+ </dt>
+
+ <dd>
+ "Pause music."
+ </dd>
+
+ <dt>
+ <code>showtraffic.wav</code>
+ </dt>
+
+ <dd>
+ "Show traffic."
+ </dd>
+</dl>
+<h3 id="cmd-bindings">DHU commands and key bindings</h3>
+
+<p>The DHU supports the following commands.</p>
+
+<p class="table-caption" id="table-commands"><strong>Table 1.</strong> Commands and key bindings</p>
+<table>
+<tr>
+ <th>Category</th>
+ <th>Command</th>
+ <th>Subcommand</th>
+ <th>Argument(s)</th>
+ <th>Keyboard Shortcut(s)</th>
+ <th>Description</th>
+</tr>
+
+<!--system-->
+
+<tr>
+<td rowspan="4">System</td>
+<td>help</td>
+<td></td>
+<td>[command]</td>
+<td></td>
+<td>Shows the full command set. Specifying a command name (for example, <code>help day</code>)
+ causes the system to show help for that command.</td>
+</tr>
+
+<tr>
+
+<td>quit</td>
+<td></td>
+<td></td>
+<td>Alt+q</td>
+<td>Quits the head unit.</td>
+</tr>
+
+<tr>
+
+<td>sleep</td>
+<td></td>
+<td>[seconds]</td>
+<td></td>
+<td>Sleeps for one second. Specifying an argument (for example, <code>sleep 30</code>) causes the
+system to sleep the specified number of seconds. This command
+is useful if you are writing scripts for the DHU. (You can run a script by using I/O redirection
+from the command line: <code>./desktop-head-unit < script.txt</code> loads commands from the
+file <code>script.txt</code>.)</td>
+</tr>
+
+<tr>
+
+<td>screenshot</td>
+<td></td>
+<td>filename.png</td>
+<td></td>
+<td>Saves a screenshot to <code>filename.png</code>.</td>
+</tr>
+
+
+<!--microphone-->
+
+<tr>
+<td rowspan="3">Microphone</td>
+<td rowspan="3">mic</td>
+<td>begin</td>
+<td></td>
+<td>m </td>
+<td>Activates the microphone (equivalent to clicking the steering wheel's microphone button) and
+waits for input from the computer microphone.</td>
+</tr>
+
+<tr>
+
+
+<td>play</td>
+<td>filename.wav</td>
+<td></td>
+<td>Causes the DHU to treat <code>filename.wav</code> as voice input, as if it had heard that sound
+ through the microphone. You do not hear the sound file being played, but you do hear
+ the response from Android Auto.</td>
+</tr>
+
+<tr>
+
+
+<td>repeat</td>
+<td></td>
+<td></td>
+<td>Repeats the last <code>mic play</code> command, as if you had called <code>mic play</code>
+ again with the same sound file parameter.</td>
+</tr>
+
+<!--Input-->
+
+<tr>
+<td rowspan="7">Input</td>
+<td rowspan="6">dpad</td>
+<td>up <br> down <br> left <br> right</td>
+<td></td>
+<td>Arrow keys</td>
+<td>Simulates moving the rotary controller.</td>
+</tr>
+
+<tr>
+
+
+<td>soft left <br> soft right</td>
+<td></td>
+<td>Shift+Arrow keys</td>
+<td>Simulates pressing the side buttons available on some rotary controllers.</td>
+</tr>
+
+<tr>
+
+
+<td>click</td>
+<td></td>
+<td>Return</td>
+<td>Simulates pressing the rotary controller.</td>
+</tr>
+
+<tr>
+
+
+<td>back</td>
+<td></td>
+<td>Backspace</td>
+<td>Simulates pressing the <strong>back</strong> button available below some rotary
+ controllers.</td>
+</tr>
+
+<tr>
+
+
+<td>rotate left <br> rotate right</td>
+<td></td>
+<td>1 <br> 2</td>
+<td>Simulates rotating the rotary controller left (counter-clockwise) or right (clockwise).</td>
+</tr>
+
+<tr>
+
+
+<td>flick left <br> flick right</td>
+<td></td>
+<td>Shift+1 <br> Shift+2</td>
+<td>Simulates a fast spin of the rotary controller to the left (counter-clockwise) or right
+ (clockwise).</td>
+</tr>
+
+<tr>
+
+<td>tap</td>
+<td></td>
+<td>x y</td>
+<td></td>
+<td>Simulates a touch event at the specified coordinates. For example, <code>tap 50 100</code></td>
+</tr>
+
+
+<!--Day/Night-->
+
+<tr>
+<td rowspan="3">Day/Night</td>
+<td>day</td>
+<td></td>
+<td></td>
+<td>Shift+n</td>
+<td>Activates day mode (high brightness, full color).</td>
+</tr>
+
+<tr>
+
+<td>night</td>
+<td></td>
+<td> </td>
+<td>Ctrl+n </td>
+<td>Activates night mode (low brightness, high contrast).</td>
+</tr>
+
+<tr>
+
+<td>daynight</td>
+<td></td>
+<td></td>
+<td>n </td>
+<td>Toggles current day/night mode.</td>
+</tr>
+
+</table>
+
+
+
+
+<h2 id="auto-simulators">Media Browser and Messaging Simulators</h2>
+
+<p class="caution"><strong>Important:</strong> Use of the Android Media Browser and Messaging
+Simulators for testing Android Auto apps is deprecated. Instead, we recommend using the
+Desktop Head Unit, which enables your development machine to act as if it were an Android Auto head
+unit.</p>
+
+<p>To get the simulators, open the
+<a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a> and download
+them from <strong>Extras > Android Auto API Simulators</strong>.</p>
+
+<p>Before you begin testing, compile your app in your development environment.
+Install your app and the Android simulator for the features you want to test
+(that is, audio or messaging) on a physical or virtual device running Android
+5.0 (API level 21) or higher. To check the version of Android on the device, go
+to <strong>Settings > About phone</strong> (or <strong>About tablet</strong>)
+<strong>> Android Version</strong>.</p>
+
+<h3 id="testing-audio-apps">Testing audio apps</h3>
+<p>To run and test audio apps:</p>
+
+<ol>
+<li>Install the Android Media Browser simulator
+({@code <sdk>/extras/google/simulators/media-browser-simulator.apk}) on
+the test device. You can do this using
+the <a href="{@docRoot}tools/help/adb.html#move">adb</a> command line tool.</li>
+<li>Enable <a href="{@docRoot}tools/device.html#developer-device-options">
+developer options</a> on the test device.</li>
+<li>Install your app on the test device.</li>
+<li>Launch the Android Media Browser simulator to see how your audio app
+appears in Auto. If your app does not appear, stop the simulator from
+<strong>Settings > Apps</strong> and restart it.</li>
+</ol>
+
+
+<h3 id="testing-messaging-apps">Testing messaging apps</h3>
+<p>To run and test messaging apps:</p>
+
+<ol>
+<li>Install the Android Messaging simulator
+ ({@code <sdk>/extras/google/simulators/messaging-simulator.apk})
+on the test device. You can do this using the
+<a href="{@docRoot}tools/help/adb.html#move">adb</a> command line tool.</li>
+<li>Enable the simulator to read notifications posted on the system:
+<ol type="a">
+ <li>Enable <a href="{@docRoot}tools/device.html#developer-device-options">
+developer options</a> on the test device.</li>
+ <li>Click <strong>Settings > Sounds & Notifications > Notification
+ Access</strong> and check the box labeled
+ <strong>Messaging Simulator</strong>.</li>
+</ol>
+<li>Install your app on the test device.</li>
+<li>Launch the Android Messaging Simulator to see how your messaging app appears
+in Auto. If your app does not appear, stop the simulator from
+<strong>Settings > Apps</strong> and restart it.</li>
+</ol>
+
+
+
+
diff --git a/docs/html/tools/support-library/features.jd b/docs/html/tools/support-library/features.jd
index b3af7a2..19f93e9 100644
--- a/docs/html/tools/support-library/features.jd
+++ b/docs/html/tools/support-library/features.jd
@@ -307,8 +307,8 @@
on how to set up your project, follow the instructions in <a
href="{@docRoot}tools/support-library/setup.html#libs-with-res">Adding libraries
with resources</a>. If you are developing in Eclipse/ADT, make sure to include
-both the <code>android-support-v7-mediarouter.jar</code> and
-<code>android-support-v7-appcompat.jar</code> files.</p>
+the <code>android-support-v7-mediarouter.jar</code>, <code>android-support-v7-appcompat.jar</code>,
+and <code>android-support-v7-palette.jar</code> files.</p>
<p>If you are using Android Studio, all you need to do is specify the Gradle build
script dependency identifier <code>com.android.support:support-v7-mediarouter:<revision></code>,
diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs
index db19d4f..72f9f21 100644
--- a/docs/html/tools/tools_toc.cs
+++ b/docs/html/tools/tools_toc.cs
@@ -160,6 +160,7 @@
<li><a href="<?cs var:toroot ?>tools/help/avd-manager.html">AVD Manager</a></li>
<li><a href="<?cs var:toroot ?>tools/help/bmgr.html">bmgr</a>
<li><a href="<?cs var:toroot ?>tools/help/monitor.html">Device Monitor</a></li>
+ <li><a href="<?cs var:toroot ?>tools/help/desktop-head-unit.html">Desktop Head Unit</a></li>
<li><a href="<?cs var:toroot ?>tools/help/dmtracedump.html">dmtracedump</a></li>
<li><a href="<?cs var:toroot ?>tools/help/draw9patch.html">Draw 9-Patch</a></li>
<li><a href="<?cs var:toroot ?>tools/help/emulator.html">Emulator</a></li>
diff --git a/docs/html/training/auto/start/index.jd b/docs/html/training/auto/start/index.jd
index 22e7521..f6cdbd1 100644
--- a/docs/html/training/auto/start/index.jd
+++ b/docs/html/training/auto/start/index.jd
@@ -16,7 +16,7 @@
<ol>
<li><a href="#dev-project">Set Up an Auto Project</a></li>
<li><a href="#build-it">Build Auto Apps</a></li>
- <li><a href="#test-it">Run and Test Auto Apps</a></li>
+ <li><a href="#test-it-dhu">Run and Test Auto Apps </a></li>
</ol>
<h2>You should also read</h2>
@@ -149,57 +149,124 @@
more information, see
<a href="{@docRoot}distribute/essentials/quality/auto.html">Auto App Quality</a>.</p>
-<h2 id="test-it">Run and Test Auto Apps</h2>
-<p>As you prepare to publish your app, make sure that your app looks correct
-when projected on the Auto user interface. Use the Android Media Browser
-simulator and Android Messaging simulators to view and test your audio or
-messaging apps in a screen that looks similar to what is projected on Auto.</p>
-<p>To get the simulators, open the
-<a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a> and download
-them from <strong>Extras > Android Auto API Simulators</strong>.</p>
+<h2 id="test-it-dhu">Run and Test Auto Apps </h2>
-<p>Before you begin testing, compile your app in your development environment.
-Install your app and the Android simulator for the features you want to test
-(that is, audio or messaging) on a physical or virtual device running Android
-5.0 (API level 21) or higher. To check the version of Android on the device, go
-to <strong>Settings > About > Android Version</strong>.</p>
+<p>
+ As you develop, you can run and test your app on your development machine
+ using the <em>Desktop Head Unit</em> (DHU). The DHU replaces the existing
+ simulators and enables your development machine to simulate a vehicle
+ dashboard system running Android Auto.
+</p>
-<h3 id="testing-audio-apps">Testing audio apps</h3>
-<p>To run and test audio apps:</p>
+<h3 id="installing-dhu">Installing the DHU</h3>
<ol>
-<li>Install the Android Media Browser simulator
-({@code <sdk>/extras/google/simulators/media-browser-simulator.apk}) on
-the test device. You can do this using
-the <a href="{@docRoot}tools/help/adb.html#move">adb</a> command line tool.</li>
-<li>Enable <a href="{@docRoot}tools/device.html#device-developer-options">
-developer options</a> on the test device.</li>
-<li>Install your app on the test device.</li>
-<li>Launch the Android Media Browser simulator to see how your audio app
-appears in Auto. If your app does not appear, stop the simulator from
-<strong>Settings > Apps</strong> then restart it.</li>
+ <li>Enable developer mode on your mobile device, as described in
+ <a href="{@docRoot}tools/device.html#developer-device-options">Enabling On-device
+ Developer Options</a>. </li>
+ <li>Compile your app in your development environment and install your app on
+ a physical mobile device running Android 5.0 (API level 21) or higher. To check the
+ version of Android on a Nexus device, go to
+ <strong>Settings > About phone</strong> (or <strong>About tablet</strong>) <strong>>
+ Android version</strong>.</li>
+
+ <li>Install the
+ <a class="external-link"
+ href="https://play.google.com/store/apps/details?id=com.google.android.projection.gearhead&hl=en"
+ >Android Auto app</a> on the mobile device.</li>
+ <li>Open the <a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a> and
+ download the DHU package <strong>Android Auto Desktop Head Unit</strong> from the
+ <em>SDK Tools</em> tab. The DHU installs in the <code><sdk>/extras/google/auto/</code>
+ directory.</li>
+ <li>If you are running the DHU on Linux, you must also install
+ the portaudio, libpng, sdl2, and sdl2_ttf libraries.
+ The procedure to do this varies depending on your Linux distribution. For example, on
+ Debian-derived Linux distributions, you can install the libraries with this command:
+
+<pre class="no-pretty-print">
+$ sudo apt-get install libsdl2-2.0-0 libsdl2-ttf-2.0-0 libportaudio2 libpng12-0
+</pre>
+
+ </li>
</ol>
-<h3 id="testing-messaging-apps">Testing messaging apps</h3>
-<p>To run and test messaging apps:</p>
+<div class="figure" style="width:330px">
+ <img src="{@docRoot}images/training/auto-desktop-head-unit-server-running.png"
+ alt="" >
+ <p class="img-caption">
+ <strong>Figure 2.</strong> Notification that the head unit server is running.
+ </p>
+</div>
+<img src="{@docRoot}images/training/auto-desktop-head-unit-context-menu-enabled.png"
+ alt="" >
+<p class="img-caption">
+ <strong>Figure 1.</strong> Context menu with developer options.
+</p>
+
+<h3 id="connecting-dhu">Connecting the DHU to your mobile device</h3>
+
+<p>Run the DHU by connecting your mobile device to a development machine and setting up a connection to
+ the head unit server over <a href="{@docRoot}tools/help/adb.html">Android Debug Bridge
+ (ADB)</a>. Follow these steps to set up tunneling and start the DHU:</p>
<ol>
-<li>Install the Android Messaging simulator
- ({@code <sdk>/extras/google/simulators/messaging-simulator.apk})
-on the test device. You can do this using the
-<a href="{@docRoot}tools/help/adb.html#move">adb</a> command line tool.</li>
-<li>Enable the simulator to read notifications posted on the system:
-<ol type="a">
- <li>Enable <a href="{@docRoot}tools/device.html#device-developer-options">
-developer options</a> on the test device.</li>
- <li>Click <strong>Settings > Sounds & Notifications > Notification
- Access</strong> and check the box labeled
- <strong>Messaging Simulator</strong>.</li>
+ <li>On the mobile device, enable Android Auto developer mode by starting the Android Auto
+ companion app, and then tapping the header image 10 times.
+ This step is only required the first time you run the companion app.
+ </li>
+ <li>If the server is not already running, select <strong>Start head unit server</strong>
+ from the Android Auto menu.
+ <p>On the device, a foreground service appears in the notification area. </p>
+ </li>
+
+ <li>Connect the mobile device to the development machine via USB. Your device must be unlocked to
+ launch the DHU.
+ </li>
+ <li>On the development machine, run the following {@code adb} command to
+ forward socket connections from the
+ development machine's port 5277 to the same port number on the Android device.
+ This configuration allows the DHU to connect to the head unit server running on your phone over
+ a TCP socket.
+ <pre class="no-pretty-print">$ adb forward tcp:5277 tcp:5277</pre>
+ </li>
+
+ <li>Start the DHU by running the command <code>desktop-head-unit.exe</code> (on Windows)
+ or <code>./desktop-head-unit</code> (on Mac or Linux) from the
+ <code><sdk>/extras/google/auto/</code> directory.
+
+<pre class="no-pretty-print">$ cd <sdk>/extras/google/auto
+$ ./desktop-head-unit</pre>
+
+ <p>
+ By default, the head unit server connects over port 5277. To override the host or port
+ (for example, to forward over SSH), use the <code>--adb</code> flag.
+ </p>
+
+ </li>
</ol>
-<li>Install your app on the test device.</li>
-<li>Launch the Android Messaging Simulator to see how your messaging app appears
-in Auto. If your app does not appear, stop the simulator from
-<strong>Settings > Apps</strong> then restart it.</li>
-</ol>
+
+<div class="figure" style="width:432px">
+
+ <img src="{@docRoot}images/training/auto-desktop-head-unit-wkst-launch.png"
+ alt="" >
+ <p class="img-caption">
+ <strong>Figure 4.</strong> DHU launches on the development machine.
+ </p>
+</div>
+
+ <img src="{@docRoot}images/training/auto-desktop-head-unit-launch.png"
+ alt="" >
+ <p class="img-caption">
+ <strong>Figure 3.</strong> Android Auto launches on the mobile device.
+ </p>
+
+<p>
+ After you set up and start the DHU, you can run DHU commands from the command line to run and
+ test your app from the terminal. You can also run these commands by using keyboard shortcuts. For
+ more information about DHU configuration and commands, see <a href=
+ "{@docRoot}tools/help/desktop-head-unit.html">Desktop Head Unit</a>.
+</p>
+
+
diff --git a/graphics/java/android/graphics/drawable/RippleBackground.java b/graphics/java/android/graphics/drawable/RippleBackground.java
index 1c14e2f..c7aa8c3 100644
--- a/graphics/java/android/graphics/drawable/RippleBackground.java
+++ b/graphics/java/android/graphics/drawable/RippleBackground.java
@@ -48,8 +48,8 @@
// Software rendering properties.
private float mOpacity = 0;
- public RippleBackground(RippleDrawable owner, Rect bounds) {
- super(owner, bounds);
+ public RippleBackground(RippleDrawable owner, Rect bounds, boolean forceSoftware) {
+ super(owner, bounds, forceSoftware);
}
public boolean isVisible() {
diff --git a/graphics/java/android/graphics/drawable/RippleComponent.java b/graphics/java/android/graphics/drawable/RippleComponent.java
index 23a3ee3..2d378c6 100644
--- a/graphics/java/android/graphics/drawable/RippleComponent.java
+++ b/graphics/java/android/graphics/drawable/RippleComponent.java
@@ -52,9 +52,16 @@
/** Screen density used to adjust pixel-based constants. */
protected float mDensity;
- public RippleComponent(RippleDrawable owner, Rect bounds) {
+ /**
+ * If set, force all ripple animations to not run on RenderThread, even if it would be
+ * available.
+ */
+ private final boolean mForceSoftware;
+
+ public RippleComponent(RippleDrawable owner, Rect bounds, boolean forceSoftware) {
mOwner = owner;
mBounds = bounds;
+ mForceSoftware = forceSoftware;
}
public void onBoundsChange() {
@@ -143,7 +150,7 @@
* @return {@code true} if something was drawn, {@code false} otherwise
*/
public boolean draw(Canvas c, Paint p) {
- final boolean hasDisplayListCanvas = c.isHardwareAccelerated()
+ final boolean hasDisplayListCanvas = !mForceSoftware && c.isHardwareAccelerated()
&& c instanceof DisplayListCanvas;
if (mHasDisplayListCanvas != hasDisplayListCanvas) {
mHasDisplayListCanvas = hasDisplayListCanvas;
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 464f3de..2690223 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -166,6 +166,12 @@
private boolean mOverrideBounds;
/**
+ * If set, force all ripple animations to not run on RenderThread, even if it would be
+ * available.
+ */
+ private boolean mForceSoftware;
+
+ /**
* Constructor used for drawable inflation.
*/
RippleDrawable() {
@@ -546,7 +552,7 @@
*/
private void tryBackgroundEnter(boolean focused) {
if (mBackground == null) {
- mBackground = new RippleBackground(this, mHotspotBounds);
+ mBackground = new RippleBackground(this, mHotspotBounds, mForceSoftware);
}
mBackground.setup(mState.mMaxRadius, mDensity);
@@ -584,7 +590,7 @@
}
final boolean isBounded = isBounded();
- mRipple = new RippleForeground(this, mHotspotBounds, x, y, isBounded);
+ mRipple = new RippleForeground(this, mHotspotBounds, x, y, isBounded, mForceSoftware);
}
mRipple.setup(mState.mMaxRadius, mDensity);
@@ -949,6 +955,16 @@
}
}
+ /**
+ * Sets whether to disable RenderThread animations for this ripple.
+ *
+ * @param forceSoftware true if RenderThread animations should be disabled, false otherwise
+ * @hide
+ */
+ public void setForceSoftware(boolean forceSoftware) {
+ mForceSoftware = forceSoftware;
+ }
+
@Override
public ConstantState getConstantState() {
return mState;
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index 4853b04..c660846 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -87,8 +87,8 @@
private boolean mHasFinishedExit;
public RippleForeground(RippleDrawable owner, Rect bounds, float startingX, float startingY,
- boolean isBounded) {
- super(owner, bounds);
+ boolean isBounded, boolean forceSoftware) {
+ super(owner, bounds, forceSoftware);
mIsBounded = isBounded;
mStartingX = startingX;
diff --git a/libs/hwui/thread/TaskManager.cpp b/libs/hwui/thread/TaskManager.cpp
index f0ed0bb..e9dde29 100644
--- a/libs/hwui/thread/TaskManager.cpp
+++ b/libs/hwui/thread/TaskManager.cpp
@@ -33,7 +33,9 @@
// Get the number of available CPUs. This value does not change over time.
int cpuCount = sysconf(_SC_NPROCESSORS_CONF);
- int workerCount = MathUtils::max(1, cpuCount / 2);
+ // Really no point in making more than 2 of these worker threads, but
+ // we do want to limit ourselves to 1 worker thread on dual-core devices.
+ int workerCount = cpuCount > 2 ? 2 : 1;
for (int i = 0; i < workerCount; i++) {
String8 name;
name.appendFormat("hwuiTask%d", i + 1);
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
index da33464..bc2dae1 100644
--- a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java
@@ -31,8 +31,7 @@
*/
public final class ActivityRecognitionProvider {
private final IActivityRecognitionHardware mService;
- private final HashSet<Sink> mSinkSet = new HashSet<Sink>();
- private final SinkTransport mSinkTransport = new SinkTransport();
+ private final HashSet<Sink> mSinkSet = new HashSet<>();
// the following constants must remain in sync with activity_recognition.h
@@ -43,6 +42,8 @@
public static final String ACTIVITY_STILL = "android.activity_recognition.still";
public static final String ACTIVITY_TILTING = "android.activity_recognition.tilting";
+ // NOTE: when adding an additional EVENT_TYPE_, EVENT_TYPE_COUNT needs to be updated in
+ // android.hardware.location.ActivityRecognitionHardware
public static final int EVENT_TYPE_FLUSH_COMPLETE = 0;
public static final int EVENT_TYPE_ENTER = 1;
public static final int EVENT_TYPE_EXIT = 2;
@@ -60,7 +61,7 @@
throws RemoteException {
Preconditions.checkNotNull(service);
mService = service;
- mService.registerSink(mSinkTransport);
+ mService.registerSink(new SinkTransport());
}
public String[] getSupportedActivities() throws RemoteException {
@@ -102,26 +103,23 @@
private final class SinkTransport extends IActivityRecognitionHardwareSink.Stub {
@Override
- public void onActivityChanged(
- android.hardware.location.ActivityChangedEvent activityChangedEvent) {
+ public void onActivityChanged(android.hardware.location.ActivityChangedEvent event) {
Collection<Sink> sinks;
synchronized (mSinkSet) {
if (mSinkSet.isEmpty()) {
return;
}
-
- sinks = new ArrayList<Sink>(mSinkSet);
+ sinks = new ArrayList<>(mSinkSet);
}
// translate the event from platform internal and GmsCore types
- ArrayList<ActivityRecognitionEvent> gmsEvents =
- new ArrayList<ActivityRecognitionEvent>();
- for (android.hardware.location.ActivityRecognitionEvent event
- : activityChangedEvent.getActivityRecognitionEvents()) {
+ ArrayList<ActivityRecognitionEvent> gmsEvents = new ArrayList<>();
+ for (android.hardware.location.ActivityRecognitionEvent reportingEvent
+ : event.getActivityRecognitionEvents()) {
ActivityRecognitionEvent gmsEvent = new ActivityRecognitionEvent(
- event.getActivity(),
- event.getEventType(),
- event.getTimestampNs());
+ reportingEvent.getActivity(),
+ reportingEvent.getEventType(),
+ reportingEvent.getTimestampNs());
gmsEvents.add(gmsEvent);
}
ActivityChangedEvent gmsEvent = new ActivityChangedEvent(gmsEvents);
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java
new file mode 100644
index 0000000..0b878d7
--- /dev/null
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderClient.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.location.provider;
+
+import android.annotation.NonNull;
+import android.hardware.location.IActivityRecognitionHardware;
+import android.hardware.location.IActivityRecognitionHardwareClient;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * A client class for interaction with an Activity-Recognition provider.
+ */
+public abstract class ActivityRecognitionProviderClient {
+ private static final String TAG = "ArProviderClient";
+
+ protected ActivityRecognitionProviderClient() {}
+
+ private IActivityRecognitionHardwareClient.Stub mClient =
+ new IActivityRecognitionHardwareClient.Stub() {
+ @Override
+ public void onAvailabilityChanged(
+ boolean isSupported,
+ IActivityRecognitionHardware instance) {
+ int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.SYSTEM_UID) {
+ Log.d(TAG, "Ignoring calls from non-system server. Uid: " + callingUid);
+ return;
+ }
+ ActivityRecognitionProvider provider;
+ try {
+ provider = isSupported ? new ActivityRecognitionProvider(instance) : null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error creating Hardware Activity-Recognition Provider.", e);
+ return;
+ }
+ onProviderChanged(isSupported, provider);
+ }
+ };
+
+ /**
+ * Gets the binder needed to interact with proxy provider in the platform.
+ */
+ @NonNull
+ public IBinder getBinder() {
+ return mClient;
+ }
+
+ /**
+ * Called when a change in the availability of {@link ActivityRecognitionProvider} is detected.
+ *
+ * @param isSupported whether the platform supports the provider natively
+ * @param instance the available provider's instance
+ */
+ public abstract void onProviderChanged(
+ boolean isSupported,
+ ActivityRecognitionProvider instance);
+}
diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java
index 03dd042..7139025 100644
--- a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java
+++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java
@@ -28,7 +28,10 @@
/**
* A watcher class for Activity-Recognition instances.
+ *
+ * @deprecated use {@link ActivityRecognitionProviderClient} instead.
*/
+@Deprecated
public class ActivityRecognitionProviderWatcher {
private static final String TAG = "ActivityRecognitionProviderWatcher";
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 0f1be6b..e92f294 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -671,15 +671,15 @@
case USAGE_VOICE_COMMUNICATION:
return new String("USAGE_VOICE_COMMUNICATION");
case USAGE_VOICE_COMMUNICATION_SIGNALLING:
- return new String("USAGE_VOICE_COMMUNICATION");
+ return new String("USAGE_VOICE_COMMUNICATION_SIGNALLING");
case USAGE_ALARM:
return new String("USAGE_ALARM");
case USAGE_NOTIFICATION:
return new String("USAGE_NOTIFICATION");
case USAGE_NOTIFICATION_RINGTONE:
- return new String("USAGE_NOTIFICATION");
+ return new String("USAGE_NOTIFICATION_RINGTONE");
case USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
- return new String("USAGE_NOTIFICATION");
+ return new String("USAGE_NOTIFICATION_COMMUNICATION_REQUEST");
case USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
return new String("USAGE_NOTIFICATION_COMMUNICATION_INSTANT");
case USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
diff --git a/native/android/sensor.cpp b/native/android/sensor.cpp
index 26b41e8..76c701a 100644
--- a/native/android/sensor.cpp
+++ b/native/android/sensor.cpp
@@ -38,10 +38,6 @@
using android::String16;
/*****************************************************************************/
-
-android::Mutex android::SensorManager::sLock;
-std::map<String16, SensorManager*> android::SensorManager::sPackageInstances;
-
ASensorManager* ASensorManager_getInstance()
{
return ASensorManager_getInstanceForPackage(NULL);
@@ -206,4 +202,4 @@
bool ASensor_isWakeUpSensor(ASensor const* sensor)
{
return static_cast<Sensor const*>(sensor)->isWakeUpSensor();
-}
\ No newline at end of file
+}
diff --git a/packages/Keyguard/res/values/config.xml b/packages/Keyguard/res/values/config.xml
index 8d9d6ee..b398ab2 100644
--- a/packages/Keyguard/res/values/config.xml
+++ b/packages/Keyguard/res/values/config.xml
@@ -22,4 +22,10 @@
<!-- Allow the menu hard key to be disabled in LockScreen on some devices [DO NOT TRANSLATE] -->
<bool name="config_disableMenuKeyInLockScreen">false</bool>
+
+ <!-- Threshold in micro amperes below which a charger is rated as "slow" -->
+ <integer name="config_chargingSlowlyThreshold">1000000</integer>
+
+ <!-- Threshold in micro amperes above which a charger is rated as "fast" -->
+ <integer name="config_chargingFastThreshold">1500000</integer>
</resources>
diff --git a/packages/Keyguard/res/values/strings.xml b/packages/Keyguard/res/values/strings.xml
index cba122f..d1e84f5 100644
--- a/packages/Keyguard/res/values/strings.xml
+++ b/packages/Keyguard/res/values/strings.xml
@@ -56,6 +56,14 @@
is not fully charged, say that it's charging. -->
<string name="keyguard_plugged_in">Charging</string>
+ <!-- When the lock screen is showing and the phone plugged in, and the battery
+ is not fully charged, and it's plugged into a fast charger, say that it's charging fast. -->
+ <string name="keyguard_plugged_in_charging_fast">Charging rapidly</string>
+
+ <!-- When the lock screen is showing and the phone plugged in, and the battery
+ is not fully charged, and it's plugged into a slow charger, say that it's charging slowly. -->
+ <string name="keyguard_plugged_in_charging_slowly">Charging slowly</string>
+
<!-- When the lock screen is showing and the battery is low, warn user to plug
in the phone soon. -->
<string name="keyguard_low_battery">Connect your charger.</string>
@@ -308,6 +316,15 @@
<!-- An explanation text that the password needs to be entered since the device has just been restarted. [CHAR LIMIT=80] -->
<string name="kg_prompt_reason_restart_password">Password required when you restart device.</string>
+ <!-- An explanation text that the pattern needs to be solved since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
+ <string name="kg_prompt_reason_timeout_pattern">Pattern required for additional security.</string>
+
+ <!-- An explanation text that the pin needs to be entered since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
+ <string name="kg_prompt_reason_timeout_pin">PIN required for additional security.</string>
+
+ <!-- An explanation text that the password needs to be entered since the user hasn't used strong authentication since quite some time. [CHAR LIMIT=80] -->
+ <string name="kg_prompt_reason_timeout_password">Password required for additional security.</string>
+
<!-- An explanation text that the pattern needs to be solved since profiles have just been switched. [CHAR LIMIT=80] -->
<string name="kg_prompt_reason_switch_profiles_pattern">Pattern required when you switch profiles.</string>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index 8f792de..b03871a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -40,6 +40,7 @@
protected SecurityMessageDisplay mSecurityMessageDisplay;
protected View mEcaView;
protected boolean mEnableHaptics;
+ private boolean mDismissing;
// To avoid accidental lockout due to events while the device in in the pocket, ignore
// any passwords with length less than or equal to this length.
@@ -67,6 +68,7 @@
@Override
public void reset() {
// start fresh
+ mDismissing = false;
resetPasswordText(false /* animate */);
// if the user is currently locked out, enforce it.
long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
@@ -113,6 +115,8 @@
}
protected void verifyPasswordAndUnlock() {
+ if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
+
final String entry = getPasswordText();
setPasswordEntryInputEnabled(false);
if (mPendingLockCheck != null) {
@@ -143,6 +147,7 @@
private void onPasswordChecked(boolean matched, int timeoutMs, boolean isValidPassword) {
if (matched) {
+ mDismissing = true;
mCallback.reportUnlockAttempt(true, 0);
mCallback.dismiss(true);
} else {
@@ -234,6 +239,12 @@
}
}
+ @Override
+ public void showMessage(String message, int color) {
+ mSecurityMessageDisplay.setNextMessageColor(color);
+ mSecurityMessageDisplay.setMessage(message, true /* important */);
+ }
+
protected abstract int getPromtReasonStringRes(int reason);
// Cause a VIRTUAL_KEY vibration
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index ff4e815..ec2a173 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -163,13 +163,18 @@
* Show a string explaining why the security view needs to be solved.
*
* @param reason a flag indicating which string should be shown, see
- * {@link KeyguardSecurityView#PROMPT_REASON_NONE}
- * and {@link KeyguardSecurityView#PROMPT_REASON_RESTART}
+ * {@link KeyguardSecurityView#PROMPT_REASON_NONE},
+ * {@link KeyguardSecurityView#PROMPT_REASON_RESTART} and
+ * {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}.
*/
public void showPromptReason(int reason) {
mSecurityContainer.showPromptReason(reason);
}
+ public void showMessage(String message, int color) {
+ mSecurityContainer.showMessage(message, color);
+ }
+
/**
* Dismisses the keyguard by going to the next screen or making it gone.
*
@@ -209,9 +214,12 @@
/**
* Authentication has happened and it's time to dismiss keyguard. This function
* should clean up and inform KeyguardViewMediator.
+ *
+ * @param strongAuth whether the user has authenticated with strong authentication like
+ * pattern, password or PIN but not by trust agents or fingerprint
*/
@Override
- public void finish() {
+ public void finish(boolean strongAuth) {
// If there's a pending runnable because the user interacted with a widget
// and we're leaving keyguard, then run it.
boolean deferKeyguardDone = false;
@@ -222,9 +230,9 @@
}
if (mViewMediatorCallback != null) {
if (deferKeyguardDone) {
- mViewMediatorCallback.keyguardDonePending();
+ mViewMediatorCallback.keyguardDonePending(strongAuth);
} else {
- mViewMediatorCallback.keyguardDone(true);
+ mViewMediatorCallback.keyguardDone(strongAuth);
}
}
}
@@ -281,32 +289,6 @@
}
/**
- * Verify that the user can get past the keyguard securely. This is called,
- * for example, when the phone disables the keyguard but then wants to launch
- * something else that requires secure access.
- *
- * The result will be propogated back via {@link KeyguardViewCallback#keyguardDone(boolean)}
- */
- public void verifyUnlock() {
- SecurityMode securityMode = mSecurityContainer.getSecurityMode();
- if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
- if (mViewMediatorCallback != null) {
- mViewMediatorCallback.keyguardDone(true);
- }
- } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
- && securityMode != KeyguardSecurityModel.SecurityMode.PIN
- && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
- // can only verify unlock when in pattern/password mode
- if (mViewMediatorCallback != null) {
- mViewMediatorCallback.keyguardDone(false);
- }
- } else {
- // otherwise, go to the unlock screen, see if they can verify it
- mSecurityContainer.verifyUnlock();
- }
- }
-
- /**
* Called before this view is being removed.
*/
public void cleanUp() {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
index 2951af9..c8adf64 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardMessageArea.java
@@ -39,15 +39,18 @@
* lift-to-type from interrupting itself.
*/
private static final long ANNOUNCEMENT_DELAY = 250;
+ private static final int DEFAULT_COLOR = -1;
private static final int SECURITY_MESSAGE_DURATION = 5000;
private final KeyguardUpdateMonitor mUpdateMonitor;
private final Handler mHandler;
+ private final int mDefaultColor;
// Timeout before we reset the message to show charging/owner info
long mTimeout = SECURITY_MESSAGE_DURATION;
CharSequence mMessage;
+ private int mNextMessageColor = DEFAULT_COLOR;
private final Runnable mClearMessageRunnable = new Runnable() {
@Override
@@ -78,10 +81,16 @@
mUpdateMonitor.registerCallback(mInfoCallback);
mHandler = new Handler(Looper.myLooper());
+ mDefaultColor = getCurrentTextColor();
update();
}
@Override
+ public void setNextMessageColor(int color) {
+ mNextMessageColor = color;
+ }
+
+ @Override
public void setMessage(CharSequence msg, boolean important) {
if (!TextUtils.isEmpty(msg) && important) {
securityMessageChanged(msg);
@@ -151,6 +160,12 @@
CharSequence status = mMessage;
setVisibility(TextUtils.isEmpty(status) ? INVISIBLE : VISIBLE);
setText(status);
+ int color = mDefaultColor;
+ if (mNextMessageColor != DEFAULT_COLOR) {
+ color = mNextMessageColor;
+ mNextMessageColor = DEFAULT_COLOR;
+ }
+ setTextColor(color);
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
index 2db87b3..3a7e6d0 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPasswordView.java
@@ -115,6 +115,8 @@
switch (reason) {
case PROMPT_REASON_RESTART:
return R.string.kg_prompt_reason_restart_password;
+ case PROMPT_REASON_TIMEOUT:
+ return R.string.kg_prompt_reason_timeout_password;
default:
return 0;
}
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 4bd1a2e..9a91ca4 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -15,10 +15,6 @@
*/
package com.android.keyguard;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.os.AsyncTask;
@@ -28,7 +24,6 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
-import android.view.RenderNodeAnimator;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
@@ -337,11 +332,22 @@
mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_restart_pattern,
true /* important */);
break;
+ case PROMPT_REASON_TIMEOUT:
+ mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern,
+ true /* important */);
+ break;
default:
+ break;
}
}
@Override
+ public void showMessage(String message, int color) {
+ mSecurityMessageDisplay.setNextMessageColor(color);
+ mSecurityMessageDisplay.setMessage(message, true /* important */);
+ }
+
+ @Override
public void startAppearAnimation() {
enableClipping(false);
setAlpha(1f);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 07947b1..ce2d11a 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -62,6 +62,7 @@
return mPasswordEntry.requestFocus(direction, previouslyFocusedRect);
}
+ @Override
protected void resetState() {
mPasswordEntry.setEnabled(true);
}
@@ -69,11 +70,13 @@
@Override
protected void setPasswordEntryEnabled(boolean enabled) {
mPasswordEntry.setEnabled(enabled);
+ mOkButton.setEnabled(enabled);
}
@Override
protected void setPasswordEntryInputEnabled(boolean enabled) {
mPasswordEntry.setEnabled(enabled);
+ mOkButton.setEnabled(enabled);
}
@Override
@@ -98,6 +101,8 @@
switch (reason) {
case PROMPT_REASON_RESTART:
return R.string.kg_prompt_reason_restart_pin;
+ case PROMPT_REASON_TIMEOUT:
+ return R.string.kg_prompt_reason_timeout_pin;
default:
return 0;
}
@@ -184,6 +189,7 @@
mDeleteButton = findViewById(R.id.delete_button);
mDeleteButton.setVisibility(View.VISIBLE);
mDeleteButton.setOnClickListener(new OnClickListener() {
+ @Override
public void onClick(View v) {
// check for time-based lockouts
if (mPasswordEntry.isEnabled()) {
@@ -193,6 +199,7 @@
}
});
mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
public boolean onLongClick(View v) {
// check for time-based lockouts
if (mPasswordEntry.isEnabled()) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
index 85da298..77215a7 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -54,7 +54,12 @@
public boolean dismiss(boolean authenticated);
public void userActivity();
public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
- public void finish();
+
+ /**
+ * @param strongAuth wheher the user has authenticated with strong authentication like
+ * pattern, password or PIN but not by trust agents or fingerprint
+ */
+ public void finish(boolean strongAuth);
public void reset();
}
@@ -282,7 +287,7 @@
showWipeDialog(failedAttempts, userType);
}
}
- monitor.reportFailedUnlockAttempt();
+ monitor.reportFailedStrongAuthUnlockAttempt();
mLockPatternUtils.reportFailedPasswordAttempt(KeyguardUpdateMonitor.getCurrentUser());
if (timeoutMs > 0) {
showTimeoutDialog(timeoutMs);
@@ -308,6 +313,7 @@
boolean showNextSecurityScreenOrFinish(boolean authenticated) {
if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
boolean finish = false;
+ boolean strongAuth = false;
if (mUpdateMonitor.getUserCanSkipBouncer(
KeyguardUpdateMonitor.getCurrentUser())) {
finish = true;
@@ -323,6 +329,7 @@
case Pattern:
case Password:
case PIN:
+ strongAuth = true;
finish = true;
break;
@@ -346,7 +353,7 @@
}
}
if (finish) {
- mSecurityCallback.finish();
+ mSecurityCallback.finish(strongAuth);
}
return finish;
}
@@ -518,6 +525,13 @@
}
}
+
+ public void showMessage(String message, int color) {
+ if (mCurrentSecuritySelection != SecurityMode.None) {
+ getSecurityView(mCurrentSecuritySelection).showMessage(message, color);
+ }
+ }
+
@Override
public void showUsabilityHint() {
mSecurityViewFlipper.showUsabilityHint();
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
index 5658a74..38302fb 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityView.java
@@ -22,9 +22,18 @@
static public final int VIEW_REVEALED = 2;
int PROMPT_REASON_NONE = 0;
+
+ /**
+ * Strong auth is required because the device has just booted.
+ */
int PROMPT_REASON_RESTART = 1;
/**
+ * Strong auth is required because the user hasn't used strong auth since a while.
+ */
+ int PROMPT_REASON_TIMEOUT = 2;
+
+ /**
* Interface back to keyguard to tell it when security
* @param callback
*/
@@ -77,6 +86,14 @@
void showPromptReason(int reason);
/**
+ * Show a message on the security view with a specified color
+ *
+ * @param message the message to show
+ * @param color the color to use
+ */
+ void showMessage(String message, int color);
+
+ /**
* Instruct the view to show usability hints, if any.
*
*/
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index a0ff21b..6012c45 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -139,6 +139,14 @@
}
@Override
+ public void showMessage(String message, int color) {
+ KeyguardSecurityView ksv = getSecurityView();
+ if (ksv != null) {
+ ksv.showMessage(message, color);
+ }
+ }
+
+ @Override
public void showUsabilityHint() {
KeyguardSecurityView ksv = getSecurityView();
if (ksv != null) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index c7adc98..ead0307 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -29,46 +29,37 @@
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.graphics.Bitmap;
-
-import static android.os.BatteryManager.BATTERY_STATUS_FULL;
-import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
-import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
-import static android.os.BatteryManager.EXTRA_STATUS;
-import static android.os.BatteryManager.EXTRA_PLUGGED;
-import static android.os.BatteryManager.EXTRA_LEVEL;
-import static android.os.BatteryManager.EXTRA_HEALTH;
-
+import android.hardware.fingerprint.FingerprintManager;
+import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
+import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
import android.media.AudioManager;
import android.os.BatteryManager;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.Message;
-import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
-
-import com.android.internal.telephony.IccCardConstants;
-import com.android.internal.telephony.IccCardConstants.State;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyIntents;
-
-import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
-import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
import android.telephony.ServiceState;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
+import android.util.ArraySet;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import com.google.android.collect.Lists;
+import com.android.internal.telephony.IccCardConstants;
+import com.android.internal.telephony.IccCardConstants.State;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.widget.LockPatternUtils;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -77,6 +68,15 @@
import java.util.List;
import java.util.Map.Entry;
+import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN;
+import static android.os.BatteryManager.BATTERY_STATUS_FULL;
+import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
+import static android.os.BatteryManager.EXTRA_HEALTH;
+import static android.os.BatteryManager.EXTRA_LEVEL;
+import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT;
+import static android.os.BatteryManager.EXTRA_PLUGGED;
+import static android.os.BatteryManager.EXTRA_STATUS;
+
/**
* Watches for updates that may be interesting to the keyguard, and provides
* the up to date information as well as a registration for callbacks that care
@@ -92,32 +92,24 @@
private static final String TAG = "KeyguardUpdateMonitor";
private static final boolean DEBUG = KeyguardConstants.DEBUG;
private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
- private static final boolean DEBUG_FP_WAKELOCK = KeyguardConstants.DEBUG_FP_WAKELOCK;
private static final int LOW_BATTERY_THRESHOLD = 20;
- private static final long FINGERPRINT_WAKELOCK_TIMEOUT_MS = 15 * 1000;
private static final String ACTION_FACE_UNLOCK_STARTED
= "com.android.facelock.FACE_UNLOCK_STARTED";
private static final String ACTION_FACE_UNLOCK_STOPPED
= "com.android.facelock.FACE_UNLOCK_STOPPED";
- private static final String FINGERPRINT_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
+
+ private static final String ACTION_STRONG_AUTH_TIMEOUT =
+ "com.android.systemui.ACTION_STRONG_AUTH_TIMEOUT";
+ private static final String USER_ID = "com.android.systemui.USER_ID";
+
+ private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
/**
- * Mode in which we don't need to wake up the device when we get a fingerprint.
+ * Milliseconds after unlocking with fingerprint times out, i.e. the user has to use a
+ * strong auth method like password, PIN or pattern.
*/
- private static final int FP_WAKE_NONE = 0;
-
- /**
- * Mode in which we wake up the device, and directly dismiss Keyguard. Active when we acquire
- * a fingerprint while the screen is off and the device was sleeping.
- */
- private static final int FP_WAKE_DIRECT_UNLOCK = 1;
-
- /**
- * Mode in which we wake up the device, but play the normal dismiss animation. Active when we
- * acquire a fingerprint pulsing in doze mode.
- * */
- private static final int FP_WAKE_WAKE_TO_BOUNCER = 2;
+ private static final long FINGERPRINT_UNLOCK_TIMEOUT_MS = 72 * 60 * 60 * 1000;
// Callback messages
private static final int MSG_TIME_UPDATE = 301;
@@ -141,6 +133,26 @@
private static final int MSG_SIM_SUBSCRIPTION_INFO_CHANGED = 328;
private static final int MSG_AIRPLANE_MODE_CHANGED = 329;
private static final int MSG_SERVICE_STATE_CHANGE = 330;
+ private static final int MSG_SCREEN_TURNED_ON = 331;
+ private static final int MSG_SCREEN_TURNED_OFF = 332;
+
+ /** Fingerprint state: Not listening to fingerprint. */
+ private static final int FINGERPRINT_STATE_STOPPED = 0;
+
+ /** Fingerprint state: Listening. */
+ private static final int FINGERPRINT_STATE_RUNNING = 1;
+
+ /**
+ * Fingerprint state: Cancelling and waiting for the confirmation from FingerprintService to
+ * send us the confirmation that cancellation has happened.
+ */
+ private static final int FINGERPRINT_STATE_CANCELLING = 2;
+
+ /**
+ * Fingerprint state: During cancelling we got another request to start listening, so when we
+ * receive the cancellation done signal, we should start listening again.
+ */
+ private static final int FINGERPRINT_STATE_CANCELLING_RESTARTING = 3;
private static KeyguardUpdateMonitor sInstance;
@@ -151,9 +163,14 @@
private int mRingMode;
private int mPhoneState;
private boolean mKeyguardIsVisible;
+
+ /**
+ * If true, fingerprint was already authenticated and we don't need to start listening again
+ * until the Keyguard has been dismissed.
+ */
+ private boolean mFingerprintAlreadyAuthenticated;
private boolean mBouncer;
private boolean mBootCompleted;
- private boolean mUserHasAuthenticatedSinceBoot;
// Device provisioning state
private boolean mDeviceProvisioned;
@@ -164,7 +181,9 @@
// Password attempts
private SparseIntArray mFailedAttempts = new SparseIntArray();
- private boolean mClockVisible;
+ /** Tracks whether strong authentication hasn't been used since quite some time per user. */
+ private ArraySet<Integer> mStrongAuthNotTimedOut = new ArraySet<>();
+ private final StrongAuthTracker mStrongAuthTracker = new StrongAuthTracker();
private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
mCallbacks = Lists.newArrayList();
@@ -175,10 +194,10 @@
private boolean mDeviceInteractive;
private boolean mScreenOn;
private SubscriptionManager mSubscriptionManager;
+ private AlarmManager mAlarmManager;
private List<SubscriptionInfo> mSubscriptionInfo;
- private boolean mFingerprintDetectionRunning;
private TrustManager mTrustManager;
- private PowerManager mPowerManager;
+ private int mFingerprintRunningState = FINGERPRINT_STATE_STOPPED;
private final Handler mHandler = new Handler() {
@Override
@@ -247,6 +266,12 @@
case MSG_SERVICE_STATE_CHANGE:
handleServiceStateChange(msg.arg1, (ServiceState) msg.obj);
break;
+ case MSG_SCREEN_TURNED_ON:
+ handleScreenTurnedOn();
+ break;
+ case MSG_SCREEN_TURNED_OFF:
+ handleScreenTurnedOff();
+ break;
}
}
};
@@ -266,8 +291,6 @@
private static int sCurrentUser;
- private int mFpWakeMode;
-
public synchronized static void setCurrentUser(int currentUser) {
sCurrentUser = currentUser;
}
@@ -368,18 +391,28 @@
}
}
- private void onFingerprintAuthenticated(int userId, boolean wakeAndUnlocking) {
+ private void onFingerprintAuthenticated(int userId) {
mUserFingerprintAuthenticated.put(userId, true);
+
+ // If fingerprint unlocking is allowed, this event will lead to a Keyguard dismiss or to a
+ // wake-up (if Keyguard is not showing), so we don't need to listen until Keyguard is
+ // fully gone.
+ mFingerprintAlreadyAuthenticated = isUnlockingWithFingerprintAllowed();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onFingerprintAuthenticated(userId, wakeAndUnlocking);
+ cb.onFingerprintAuthenticated(userId);
}
}
}
private void handleFingerprintAuthFailed() {
- releaseFingerprintWakeLock();
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onFingerprintAuthFailed();
+ }
+ }
handleFingerprintHelp(-1, mContext.getString(R.string.fingerprint_not_recognized));
}
@@ -387,53 +420,15 @@
if (acquireInfo != FingerprintManager.FINGERPRINT_ACQUIRED_GOOD) {
return;
}
- if (!mDeviceInteractive && !mScreenOn) {
- releaseFingerprintWakeLock();
- mWakeLock = mPowerManager.newWakeLock(
- PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
- mWakeLock.acquire();
- mFpWakeMode = FP_WAKE_DIRECT_UNLOCK;
- if (DEBUG_FP_WAKELOCK) {
- Log.i(TAG, "fingerprint acquired, grabbing fp wakelock");
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onFingerprintAcquired();
}
- mHandler.postDelayed(mReleaseFingerprintWakeLockRunnable,
- FINGERPRINT_WAKELOCK_TIMEOUT_MS);
- } else if (!mDeviceInteractive) {
- mFpWakeMode = FP_WAKE_WAKE_TO_BOUNCER;
- } else {
- mFpWakeMode = FP_WAKE_NONE;
- }
- }
-
- private final Runnable mReleaseFingerprintWakeLockRunnable = new Runnable() {
- @Override
- public void run() {
- if (DEBUG_FP_WAKELOCK) {
- Log.i(TAG, "fp wakelock: TIMEOUT!!");
- }
- releaseFingerprintWakeLock();
- }
- };
-
- private void releaseFingerprintWakeLock() {
- if (mWakeLock != null) {
- mHandler.removeCallbacks(mReleaseFingerprintWakeLockRunnable);
- if (DEBUG_FP_WAKELOCK) {
- Log.i(TAG, "releasing fp wakelock");
- }
- mWakeLock.release();
- mWakeLock = null;
}
}
private void handleFingerprintAuthenticated() {
- if (mFpWakeMode == FP_WAKE_WAKE_TO_BOUNCER || mFpWakeMode == FP_WAKE_DIRECT_UNLOCK) {
- if (DEBUG_FP_WAKELOCK) {
- Log.i(TAG, "fp wakelock: Authenticated, waking up...");
- }
- mPowerManager.wakeUp(SystemClock.uptimeMillis());
- }
- releaseFingerprintWakeLock();
try {
final int userId;
try {
@@ -446,9 +441,9 @@
Log.d(TAG, "Fingerprint disabled by DPM for userId: " + userId);
return;
}
- onFingerprintAuthenticated(userId, mFpWakeMode == FP_WAKE_DIRECT_UNLOCK);
+ onFingerprintAuthenticated(userId);
} finally {
- setFingerprintRunningDetectionRunning(false);
+ setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
}
}
@@ -462,7 +457,13 @@
}
private void handleFingerprintError(int msgId, String errString) {
- setFingerprintRunningDetectionRunning(false);
+ if (msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
+ && mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) {
+ setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+ startListeningForFingerprint();
+ } else {
+ setFingerprintRunningState(FINGERPRINT_STATE_STOPPED);
+ }
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
@@ -471,9 +472,15 @@
}
}
- private void setFingerprintRunningDetectionRunning(boolean running) {
- if (running != mFingerprintDetectionRunning) {
- mFingerprintDetectionRunning = running;
+ private void setFingerprintRunningState(int fingerprintRunningState) {
+ boolean wasRunning = mFingerprintRunningState == FINGERPRINT_STATE_RUNNING;
+ boolean isRunning = fingerprintRunningState == FINGERPRINT_STATE_RUNNING;
+ mFingerprintRunningState = fingerprintRunningState;
+
+ // Clients of KeyguardUpdateMonitor don't care about the internal state about the
+ // asynchronousness of the cancel cycle. So only notify them if the actualy running state
+ // has changed.
+ if (wasRunning != isRunning) {
notifyFingerprintRunningStateChanged();
}
}
@@ -482,7 +489,7 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onFingerprintRunningStateChanged(mFingerprintDetectionRunning);
+ cb.onFingerprintRunningStateChanged(isFingerprintDetectionRunning());
}
}
}
@@ -501,7 +508,7 @@
}
public boolean isFingerprintDetectionRunning() {
- return mFingerprintDetectionRunning;
+ return mFingerprintRunningState == FINGERPRINT_STATE_RUNNING;
}
private boolean isTrustDisabled(int userId) {
@@ -533,7 +540,48 @@
}
public boolean isUnlockingWithFingerprintAllowed() {
- return mUserHasAuthenticatedSinceBoot;
+ return mStrongAuthTracker.isUnlockingWithFingerprintAllowed()
+ && !hasFingerprintUnlockTimedOut(sCurrentUser);
+ }
+
+ public StrongAuthTracker getStrongAuthTracker() {
+ return mStrongAuthTracker;
+ }
+
+ /**
+ * @return true if the user hasn't use strong authentication (pattern, PIN, password) since a
+ * while and thus can't unlock with fingerprint, false otherwise
+ */
+ public boolean hasFingerprintUnlockTimedOut(int userId) {
+ return !mStrongAuthNotTimedOut.contains(userId);
+ }
+
+ public void reportSuccessfulStrongAuthUnlockAttempt() {
+ mStrongAuthNotTimedOut.add(sCurrentUser);
+ scheduleStrongAuthTimeout();
+ if (mFpm != null) {
+ byte[] token = null; /* TODO: pass real auth token once fp HAL supports it */
+ mFpm.resetTimeout(token);
+ }
+ }
+
+ private void scheduleStrongAuthTimeout() {
+ long when = SystemClock.elapsedRealtime() + FINGERPRINT_UNLOCK_TIMEOUT_MS;
+ Intent intent = new Intent(ACTION_STRONG_AUTH_TIMEOUT);
+ intent.putExtra(USER_ID, sCurrentUser);
+ PendingIntent sender = PendingIntent.getBroadcast(mContext,
+ sCurrentUser, intent, PendingIntent.FLAG_CANCEL_CURRENT);
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, sender);
+ notifyStrongAuthStateChanged(sCurrentUser);
+ }
+
+ private void notifyStrongAuthStateChanged(int userId) {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onStrongAuthStateChanged(userId);
+ }
+ }
}
static class DisplayClientState {
@@ -561,8 +609,10 @@
final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0);
final int level = intent.getIntExtra(EXTRA_LEVEL, 0);
final int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN);
+ final int maxChargingCurrent = intent.getIntExtra(EXTRA_MAX_CHARGING_CURRENT, -1);
final Message msg = mHandler.obtainMessage(
- MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health));
+ MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health,
+ maxChargingCurrent));
mHandler.sendMessage(msg);
} else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) {
SimData args = SimData.fromIntent(intent);
@@ -619,6 +669,17 @@
}
};
+ private final BroadcastReceiver mStrongAuthTimeoutReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_STRONG_AUTH_TIMEOUT.equals(intent.getAction())) {
+ int userId = intent.getIntExtra(USER_ID, -1);
+ mStrongAuthNotTimedOut.remove(userId);
+ notifyStrongAuthStateChanged(userId);
+ }
+ }
+ };
+
private FingerprintManager.AuthenticationCallback mAuthenticationCallback
= new AuthenticationCallback() {
@@ -649,7 +710,6 @@
};
private CancellationSignal mFingerprintCancelSignal;
private FingerprintManager mFpm;
- private PowerManager.WakeLock mWakeLock;
/**
* When we receive a
@@ -719,15 +779,22 @@
}
public static class BatteryStatus {
+ public static final int CHARGING_UNKNOWN = -1;
+ public static final int CHARGING_SLOWLY = 0;
+ public static final int CHARGING_REGULAR = 1;
+ public static final int CHARGING_FAST = 2;
+
public final int status;
public final int level;
public final int plugged;
public final int health;
- public BatteryStatus(int status, int level, int plugged, int health) {
+ public final int maxChargingCurrent;
+ public BatteryStatus(int status, int level, int plugged, int health, int maxChargingCurrent) {
this.status = status;
this.level = level;
this.plugged = plugged;
this.health = health;
+ this.maxChargingCurrent = maxChargingCurrent;
}
/**
@@ -758,6 +825,31 @@
return level < LOW_BATTERY_THRESHOLD;
}
+ public final int getChargingSpeed(int slowThreshold, int fastThreshold) {
+ return maxChargingCurrent <= 0 ? CHARGING_UNKNOWN :
+ maxChargingCurrent < slowThreshold ? CHARGING_SLOWLY :
+ maxChargingCurrent > fastThreshold ? CHARGING_FAST :
+ CHARGING_REGULAR;
+ }
+ }
+
+ public class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
+
+ public boolean isUnlockingWithFingerprintAllowed() {
+ int userId = getCurrentUser();
+ return isFingerprintAllowedForUser(userId);
+ }
+
+ public boolean hasUserAuthenticatedSinceBoot() {
+ int userId = getCurrentUser();
+ return (getStrongAuthForUser(userId)
+ & STRONG_AUTH_REQUIRED_AFTER_BOOT) == 0;
+ }
+
+ @Override
+ public void onStrongAuthRequiredChanged(int userId) {
+ notifyStrongAuthStateChanged(userId);
+ }
}
public static KeyguardUpdateMonitor getInstance(Context context) {
@@ -787,9 +879,30 @@
cb.onFinishedGoingToSleep(arg1);
}
}
+ mFingerprintAlreadyAuthenticated = false;
updateFingerprintListeningState();
}
+ private void handleScreenTurnedOn() {
+ final int count = mCallbacks.size();
+ for (int i = 0; i < count; i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onScreenTurnedOn();
+ }
+ }
+ }
+
+ private void handleScreenTurnedOff() {
+ final int count = mCallbacks.size();
+ for (int i = 0; i < count; i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onScreenTurnedOff();
+ }
+ }
+ }
+
/**
* IMPORTANT: Must be called from UI thread.
*/
@@ -816,8 +929,9 @@
private KeyguardUpdateMonitor(Context context) {
mContext = context;
mSubscriptionManager = SubscriptionManager.from(context);
- mPowerManager = context.getSystemService(PowerManager.class);
+ mAlarmManager = context.getSystemService(AlarmManager.class);
mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
+
// Since device can't be un-provisioned, we only need to register a content observer
// to update mDeviceProvisioned when we are...
if (!mDeviceProvisioned) {
@@ -825,7 +939,7 @@
}
// Take a guess at initial SIM state, battery status and PLMN until we get an update
- mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0);
+ mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0, 0);
// Watch for interesting updates
final IntentFilter filter = new IntentFilter();
@@ -878,8 +992,13 @@
e.printStackTrace();
}
+ IntentFilter strongAuthTimeoutFilter = new IntentFilter();
+ strongAuthTimeoutFilter.addAction(ACTION_STRONG_AUTH_TIMEOUT);
+ context.registerReceiver(mStrongAuthTimeoutReceiver, strongAuthTimeoutFilter,
+ PERMISSION_SELF, null /* handler */);
mTrustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE);
mTrustManager.registerTrustListener(this);
+ new LockPatternUtils(context).registerStrongAuthTracker(mStrongAuthTracker);
mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
updateFingerprintListeningState();
@@ -887,44 +1006,51 @@
private void updateFingerprintListeningState() {
boolean shouldListenForFingerprint = shouldListenForFingerprint();
- if (mFingerprintDetectionRunning && !shouldListenForFingerprint) {
+ if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING && !shouldListenForFingerprint) {
stopListeningForFingerprint();
- } else if (!mFingerprintDetectionRunning && shouldListenForFingerprint) {
+ } else if (mFingerprintRunningState != FINGERPRINT_STATE_RUNNING
+ && shouldListenForFingerprint) {
startListeningForFingerprint();
}
}
private boolean shouldListenForFingerprint() {
- return mKeyguardIsVisible && !mSwitchingUser;
+ return (mKeyguardIsVisible || !mDeviceInteractive) && !mSwitchingUser
+ && !mFingerprintAlreadyAuthenticated;
}
private void startListeningForFingerprint() {
+ if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING) {
+ setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING_RESTARTING);
+ return;
+ }
if (DEBUG) Log.v(TAG, "startListeningForFingerprint()");
int userId = ActivityManager.getCurrentUser();
- if (isUnlockWithFingerPrintPossible(userId)) {
- mUserHasAuthenticatedSinceBoot = mTrustManager.hasUserAuthenticatedSinceBoot(
- ActivityManager.getCurrentUser());
+ if (isUnlockWithFingerprintPossible(userId)) {
if (mFingerprintCancelSignal != null) {
mFingerprintCancelSignal.cancel();
}
mFingerprintCancelSignal = new CancellationSignal();
mFpm.authenticate(null, mFingerprintCancelSignal, 0, mAuthenticationCallback, null, userId);
- setFingerprintRunningDetectionRunning(true);
+ setFingerprintRunningState(FINGERPRINT_STATE_RUNNING);
}
}
- public boolean isUnlockWithFingerPrintPossible(int userId) {
+ public boolean isUnlockWithFingerprintPossible(int userId) {
return mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId)
&& mFpm.getEnrolledFingerprints(userId).size() > 0;
}
private void stopListeningForFingerprint() {
if (DEBUG) Log.v(TAG, "stopListeningForFingerprint()");
- if (isFingerprintDetectionRunning()) {
+ if (mFingerprintRunningState == FINGERPRINT_STATE_RUNNING) {
mFingerprintCancelSignal.cancel();
mFingerprintCancelSignal = null;
+ setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING);
}
- setFingerprintRunningDetectionRunning(false);
+ if (mFingerprintRunningState == FINGERPRINT_STATE_CANCELLING_RESTARTING) {
+ setFingerprintRunningState(FINGERPRINT_STATE_CANCELLING);
+ }
}
private boolean isDeviceProvisionedInSettingsDb() {
@@ -1193,6 +1319,9 @@
cb.onKeyguardVisibilityChangedRaw(isShowing);
}
}
+ if (!isShowing) {
+ mFingerprintAlreadyAuthenticated = false;
+ }
updateFingerprintListeningState();
}
@@ -1255,6 +1384,12 @@
if (!nowPluggedIn && current.isBatteryLow() && current.level != old.level) {
return true;
}
+
+ // change in charging current while plugged in
+ if (nowPluggedIn && current.maxChargingCurrent != old.maxChargingCurrent) {
+ return true;
+ }
+
return false;
}
@@ -1373,7 +1508,7 @@
return mFailedAttempts.get(sCurrentUser, 0);
}
- public void reportFailedUnlockAttempt() {
+ public void reportFailedStrongAuthUnlockAttempt() {
mFailedAttempts.put(sCurrentUser, getFailedUnlockAttempts() + 1);
}
@@ -1464,12 +1599,14 @@
synchronized (this) {
mScreenOn = true;
}
+ mHandler.sendEmptyMessage(MSG_SCREEN_TURNED_ON);
}
public void dispatchScreenTurnedOff() {
synchronized(this) {
mScreenOn = false;
}
+ mHandler.sendEmptyMessage(MSG_SCREEN_TURNED_OFF);
}
public boolean isDeviceInteractive() {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 0cdf999..15ffe9f 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -161,6 +161,16 @@
public void onFinishedGoingToSleep(int why) { }
/**
+ * Called when the screen has been turned on.
+ */
+ public void onScreenTurnedOn() { }
+
+ /**
+ * Called when the screen has been turned off.
+ */
+ public void onScreenTurnedOff() { }
+
+ /**
* Called when trust changes for a user.
*/
public void onTrustChanged(int userId) { }
@@ -176,12 +186,23 @@
public void onTrustGrantedWithFlags(int flags, int userId) { }
/**
+ * Called when a finger has been acquired.
+ * <p>
+ * It is guaranteed that either {@link #onFingerprintAuthenticated} or
+ * {@link #onFingerprintAuthFailed()} is called after this method eventually.
+ */
+ public void onFingerprintAcquired() { }
+
+ /**
+ * Called when a fingerprint couldn't be authenticated.
+ */
+ public void onFingerprintAuthFailed() { }
+
+ /**
* Called when a fingerprint is recognized.
* @param userId the user id for which the fingerprint was authenticated
- * @param wakeAndUnlocking whether the authentication woke the device up and thus we'd like to
- * dismiss the lockscreen before turning on the screen
*/
- public void onFingerprintAuthenticated(int userId, boolean wakeAndUnlocking) { }
+ public void onFingerprintAuthenticated(int userId) { }
/**
* Called when fingerprint provides help string (e.g. "Try again")
@@ -207,4 +228,10 @@
* Called when the fingerprint running state changed.
*/
public void onFingerprintRunningStateChanged(boolean running) { }
+
+ /**
+ * Called when the state that the user hasn't used strong authentication since quite some time
+ * has changed.
+ */
+ public void onStrongAuthStateChanged(int userId) { }
}
diff --git a/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java b/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java
index b38cfd5..ddb1f6e 100644
--- a/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java
+++ b/packages/Keyguard/src/com/android/keyguard/SecurityMessageDisplay.java
@@ -17,11 +17,14 @@
package com.android.keyguard;
public interface SecurityMessageDisplay {
- public void setMessage(CharSequence msg, boolean important);
- public void setMessage(int resId, boolean important);
+ void setNextMessageColor(int color);
- public void setMessage(int resId, boolean important, Object... formatArgs);
+ void setMessage(CharSequence msg, boolean important);
- public void setTimeout(int timeout_ms);
+ void setMessage(int resId, boolean important);
+
+ void setMessage(int resId, boolean important, Object... formatArgs);
+
+ void setTimeout(int timeout_ms);
}
diff --git a/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java b/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
index ff463c6..8ab3011 100644
--- a/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/ViewMediatorCallback.java
@@ -28,12 +28,11 @@
/**
* Report that the keyguard is done.
- * @param authenticated Whether the user securely got past the keyguard.
- * the only reason for this to be false is if the keyguard was instructed
- * to appear temporarily to verify the user is supposed to get past the
- * keyguard, and the user fails to do so.
+ *
+ * @param strongAuth whether the user has authenticated with strong authentication like
+ * pattern, password or PIN but not by trust agents or fingerprint
*/
- void keyguardDone(boolean authenticated);
+ void keyguardDone(boolean strongAuth);
/**
* Report that the keyguard is done drawing.
@@ -48,8 +47,11 @@
/**
* Report that the keyguard is dismissable, pending the next keyguardDone call.
+ *
+ * @param strongAuth whether the user has authenticated with strong authentication like
+ * pattern, password or PIN but not by trust agents or fingerprint
*/
- void keyguardDonePending();
+ void keyguardDonePending(boolean strongAuth);
/**
* Report when keyguard is actually gone
@@ -85,8 +87,9 @@
/**
* @return one of the reasons why the bouncer needs to be shown right now and the user can't use
* his normal unlock method like fingerprint or trust agents. See
- * {@link KeyguardSecurityView#PROMPT_REASON_NONE}
- * and {@link KeyguardSecurityView#PROMPT_REASON_RESTART}.
+ * {@link KeyguardSecurityView#PROMPT_REASON_NONE},
+ * {@link KeyguardSecurityView#PROMPT_REASON_RESTART} and
+ * {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}.
*/
int getBouncerPromptReason();
}
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 1c4b05f..3ad8f21 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -21,4 +21,6 @@
<!-- The translation for disappearing security views after having solved them. -->
<dimen name="disappear_y_translation">-32dp</dimen>
-</resources>
\ No newline at end of file
+
+ <dimen name="circle_avatar_size">40dp</dimen>
+</resources>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index b03f100..9b1f103 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -195,4 +195,34 @@
<!-- Content description of the WIFI signal when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] -->
<string name="accessibility_wifi_signal_full">Wifi signal full.</string>
+ <!-- Label for kernel threads in battery usage -->
+ <string name="process_kernel_label">Android OS</string>
+ <!-- Title of data usage item that represents all uninstalled applications. [CHAR LIMIT=48] -->
+ <string name="data_usage_uninstalled_apps">Removed apps</string>
+ <!-- Title of data usage item that represents all uninstalled applications or removed users. [CHAR LIMIT=48] -->
+ <string name="data_usage_uninstalled_apps_users">Removed apps and users</string>
+
+ <!-- Tethering controls, item title to go into the tethering settings -->
+ <!-- Tethering controls, item title to go into the tethering settings when only USB tethering is available [CHAR LIMIT=25]-->
+ <string name="tether_settings_title_usb">USB tethering</string>
+ <!-- Tethering controls, item title to go into the tethering settings when only Wifi tethering is available [CHAR LIMIT=25]-->
+ <string name="tether_settings_title_wifi">Portable hotspot</string>
+ <!-- Tethering controls, item title to go into the tethering settings when only Bluetooth tethering is available [CHAR LIMIT=25]-->
+ <string name="tether_settings_title_bluetooth">Bluetooth tethering</string>
+ <!-- Tethering controls, item title to go into the tethering settings when USB and Bluetooth tethering are available [CHAR LIMIT=25]-->
+ <string name="tether_settings_title_usb_bluetooth">Tethering</string>
+ <!-- Tethering controls, item title to go into the tethering settings when USB, Bluetooth and Wifi tethering are available [CHAR LIMIT=25]-->
+ <string name="tether_settings_title_all">Tethering & portable hotspot</string>
+
+ <!-- Title for a work profile. [CHAR LIMIT=25] -->
+ <string name="managed_user_title">Work profile</string>
+
+ <!-- Title for Guest user [CHAR LIMIT=35] -->
+ <string name="user_guest">Guest</string>
+
+ <!-- Manage apps, individual app screen, substituted for the application's label when the app's label CAN NOT be determined.-->
+ <string name="unknown">Unknown</string>
+
+ <!-- [CHAR LIMIT=NONE] Label of a running process that represents another user -->
+ <string name="running_process_item_user_label">User: <xliff:g id="user_name">%1$s</xliff:g></string>
</resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/AppItem.java b/packages/SettingsLib/src/com/android/settingslib/AppItem.java
new file mode 100644
index 0000000..1729e09
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/AppItem.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2011 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.settingslib;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseBooleanArray;
+
+public class AppItem implements Comparable<AppItem>, Parcelable {
+ public static final int CATEGORY_USER = 0;
+ public static final int CATEGORY_APP_TITLE = 1;
+ public static final int CATEGORY_APP = 2;
+
+ public final int key;
+ public boolean restricted;
+ public int category;
+
+ public SparseBooleanArray uids = new SparseBooleanArray();
+ public long total;
+
+ public AppItem() {
+ this.key = 0;
+ }
+
+ public AppItem(int key) {
+ this.key = key;
+ }
+
+ public AppItem(Parcel parcel) {
+ key = parcel.readInt();
+ uids = parcel.readSparseBooleanArray();
+ total = parcel.readLong();
+ }
+
+ public void addUid(int uid) {
+ uids.put(uid, true);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(key);
+ dest.writeSparseBooleanArray(uids);
+ dest.writeLong(total);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public int compareTo(AppItem another) {
+ int comparison = Integer.compare(category, another.category);
+ if (comparison == 0) {
+ comparison = Long.compare(another.total, total);
+ }
+ return comparison;
+ }
+
+ public static final Creator<AppItem> CREATOR = new Creator<AppItem>() {
+ @Override
+ public AppItem createFromParcel(Parcel in) {
+ return new AppItem(in);
+ }
+
+ @Override
+ public AppItem[] newArray(int size) {
+ return new AppItem[size];
+ }
+ };
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java
new file mode 100644
index 0000000..cf08f50
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/NetworkPolicyEditor.java
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2011 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.settingslib;
+
+import static android.net.NetworkPolicy.CYCLE_NONE;
+import static android.net.NetworkPolicy.LIMIT_DISABLED;
+import static android.net.NetworkPolicy.SNOOZE_NEVER;
+import static android.net.NetworkPolicy.WARNING_DISABLED;
+import static android.net.NetworkTemplate.MATCH_WIFI;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
+import android.net.NetworkTemplate;
+import android.net.wifi.WifiInfo;
+import android.os.AsyncTask;
+import android.text.TextUtils;
+import android.text.format.Time;
+
+import com.google.android.collect.Lists;
+
+import java.util.ArrayList;
+
+/**
+ * Utility class to modify list of {@link NetworkPolicy}. Specifically knows
+ * about which policies can coexist. This editor offers thread safety when
+ * talking with {@link NetworkPolicyManager}.
+ *
+ * @hide
+ */
+public class NetworkPolicyEditor {
+ // TODO: be more robust when missing policies from service
+
+ public static final boolean ENABLE_SPLIT_POLICIES = false;
+
+ private NetworkPolicyManager mPolicyManager;
+ private ArrayList<NetworkPolicy> mPolicies = Lists.newArrayList();
+
+ public NetworkPolicyEditor(NetworkPolicyManager policyManager) {
+ mPolicyManager = checkNotNull(policyManager);
+ }
+
+ public void read() {
+ final NetworkPolicy[] policies = mPolicyManager.getNetworkPolicies();
+
+ boolean modified = false;
+ mPolicies.clear();
+ for (NetworkPolicy policy : policies) {
+ // TODO: find better place to clamp these
+ if (policy.limitBytes < -1) {
+ policy.limitBytes = LIMIT_DISABLED;
+ modified = true;
+ }
+ if (policy.warningBytes < -1) {
+ policy.warningBytes = WARNING_DISABLED;
+ modified = true;
+ }
+
+ mPolicies.add(policy);
+ }
+
+ // when we cleaned policies above, write back changes
+ if (modified) writeAsync();
+ }
+
+ public void writeAsync() {
+ // TODO: consider making more robust by passing through service
+ final NetworkPolicy[] policies = mPolicies.toArray(new NetworkPolicy[mPolicies.size()]);
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ write(policies);
+ return null;
+ }
+ }.execute();
+ }
+
+ public void write(NetworkPolicy[] policies) {
+ mPolicyManager.setNetworkPolicies(policies);
+ }
+
+ public boolean hasLimitedPolicy(NetworkTemplate template) {
+ final NetworkPolicy policy = getPolicy(template);
+ return policy != null && policy.limitBytes != LIMIT_DISABLED;
+ }
+
+ public NetworkPolicy getOrCreatePolicy(NetworkTemplate template) {
+ NetworkPolicy policy = getPolicy(template);
+ if (policy == null) {
+ policy = buildDefaultPolicy(template);
+ mPolicies.add(policy);
+ }
+ return policy;
+ }
+
+ public NetworkPolicy getPolicy(NetworkTemplate template) {
+ for (NetworkPolicy policy : mPolicies) {
+ if (policy.template.equals(template)) {
+ return policy;
+ }
+ }
+ return null;
+ }
+
+ public NetworkPolicy getPolicyMaybeUnquoted(NetworkTemplate template) {
+ NetworkPolicy policy = getPolicy(template);
+ if (policy != null) {
+ return policy;
+ } else {
+ return getPolicy(buildUnquotedNetworkTemplate(template));
+ }
+ }
+
+ @Deprecated
+ private static NetworkPolicy buildDefaultPolicy(NetworkTemplate template) {
+ // TODO: move this into framework to share with NetworkPolicyManagerService
+ final int cycleDay;
+ final String cycleTimezone;
+ final boolean metered;
+
+ if (template.getMatchRule() == MATCH_WIFI) {
+ cycleDay = CYCLE_NONE;
+ cycleTimezone = Time.TIMEZONE_UTC;
+ metered = false;
+ } else {
+ final Time time = new Time();
+ time.setToNow();
+ cycleDay = time.monthDay;
+ cycleTimezone = time.timezone;
+ metered = true;
+ }
+
+ return new NetworkPolicy(template, cycleDay, cycleTimezone, WARNING_DISABLED,
+ LIMIT_DISABLED, SNOOZE_NEVER, SNOOZE_NEVER, metered, true);
+ }
+
+ public int getPolicyCycleDay(NetworkTemplate template) {
+ final NetworkPolicy policy = getPolicy(template);
+ return (policy != null) ? policy.cycleDay : -1;
+ }
+
+ public void setPolicyCycleDay(NetworkTemplate template, int cycleDay, String cycleTimezone) {
+ final NetworkPolicy policy = getOrCreatePolicy(template);
+ policy.cycleDay = cycleDay;
+ policy.cycleTimezone = cycleTimezone;
+ policy.inferred = false;
+ policy.clearSnooze();
+ writeAsync();
+ }
+
+ public long getPolicyWarningBytes(NetworkTemplate template) {
+ final NetworkPolicy policy = getPolicy(template);
+ return (policy != null) ? policy.warningBytes : WARNING_DISABLED;
+ }
+
+ public void setPolicyWarningBytes(NetworkTemplate template, long warningBytes) {
+ final NetworkPolicy policy = getOrCreatePolicy(template);
+ policy.warningBytes = warningBytes;
+ policy.inferred = false;
+ policy.clearSnooze();
+ writeAsync();
+ }
+
+ public long getPolicyLimitBytes(NetworkTemplate template) {
+ final NetworkPolicy policy = getPolicy(template);
+ return (policy != null) ? policy.limitBytes : LIMIT_DISABLED;
+ }
+
+ public void setPolicyLimitBytes(NetworkTemplate template, long limitBytes) {
+ final NetworkPolicy policy = getOrCreatePolicy(template);
+ policy.limitBytes = limitBytes;
+ policy.inferred = false;
+ policy.clearSnooze();
+ writeAsync();
+ }
+
+ public boolean getPolicyMetered(NetworkTemplate template) {
+ NetworkPolicy policy = getPolicy(template);
+ if (policy != null) {
+ return policy.metered;
+ } else {
+ return false;
+ }
+ }
+
+ public void setPolicyMetered(NetworkTemplate template, boolean metered) {
+ boolean modified = false;
+
+ NetworkPolicy policy = getPolicy(template);
+ if (metered) {
+ if (policy == null) {
+ policy = buildDefaultPolicy(template);
+ policy.metered = true;
+ policy.inferred = false;
+ mPolicies.add(policy);
+ modified = true;
+ } else if (!policy.metered) {
+ policy.metered = true;
+ policy.inferred = false;
+ modified = true;
+ }
+
+ } else {
+ if (policy == null) {
+ // ignore when policy doesn't exist
+ } else if (policy.metered) {
+ policy.metered = false;
+ policy.inferred = false;
+ modified = true;
+ }
+ }
+
+ // Remove legacy unquoted policies while we're here
+ final NetworkTemplate unquoted = buildUnquotedNetworkTemplate(template);
+ final NetworkPolicy unquotedPolicy = getPolicy(unquoted);
+ if (unquotedPolicy != null) {
+ mPolicies.remove(unquotedPolicy);
+ modified = true;
+ }
+
+ if (modified) writeAsync();
+ }
+
+ /**
+ * Build a revised {@link NetworkTemplate} that matches the same rule, but
+ * with an unquoted {@link NetworkTemplate#getNetworkId()}. Used to work
+ * around legacy bugs.
+ */
+ private static NetworkTemplate buildUnquotedNetworkTemplate(NetworkTemplate template) {
+ if (template == null) return null;
+ final String networkId = template.getNetworkId();
+ final String strippedNetworkId = WifiInfo.removeDoubleQuotes(networkId);
+ if (!TextUtils.equals(strippedNetworkId, networkId)) {
+ return new NetworkTemplate(
+ template.getMatchRule(), template.getSubscriberId(), strippedNetworkId);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
index 58e5e29a..5062423 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
+++ b/packages/SettingsLib/src/com/android/settingslib/TetherUtil.java
@@ -54,29 +54,7 @@
public static boolean setWifiTethering(boolean enable, Context context) {
final WifiManager wifiManager =
(WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- final ContentResolver cr = context.getContentResolver();
- /**
- * Disable Wifi if enabling tethering
- */
- int wifiState = wifiManager.getWifiState();
- if (enable && ((wifiState == WifiManager.WIFI_STATE_ENABLING) ||
- (wifiState == WifiManager.WIFI_STATE_ENABLED))) {
- wifiManager.setWifiEnabled(false);
- Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 1);
- }
-
- boolean success = wifiManager.setWifiApEnabled(null, enable);
- /**
- * If needed, restore Wifi on tether disable
- */
- if (!enable) {
- int wifiSavedState = Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE, 0);
- if (wifiSavedState == 1) {
- wifiManager.setWifiEnabled(true);
- Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0);
- }
- }
- return success;
+ return wifiManager.setWifiApEnabled(null, enable);
}
public static boolean isWifiTetherEnabled(Context context) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
new file mode 100644
index 0000000..621a09cd
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -0,0 +1,84 @@
+package com.android.settingslib;
+
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.os.UserManager;
+
+import com.android.internal.util.UserIcons;
+import com.android.settingslib.drawable.CircleFramedDrawable;
+
+public final class Utils {
+
+ /**
+ * Return string resource that best describes combination of tethering
+ * options available on this device.
+ */
+ public static int getTetheringLabel(ConnectivityManager cm) {
+ String[] usbRegexs = cm.getTetherableUsbRegexs();
+ String[] wifiRegexs = cm.getTetherableWifiRegexs();
+ String[] bluetoothRegexs = cm.getTetherableBluetoothRegexs();
+
+ boolean usbAvailable = usbRegexs.length != 0;
+ boolean wifiAvailable = wifiRegexs.length != 0;
+ boolean bluetoothAvailable = bluetoothRegexs.length != 0;
+
+ if (wifiAvailable && usbAvailable && bluetoothAvailable) {
+ return R.string.tether_settings_title_all;
+ } else if (wifiAvailable && usbAvailable) {
+ return R.string.tether_settings_title_all;
+ } else if (wifiAvailable && bluetoothAvailable) {
+ return R.string.tether_settings_title_all;
+ } else if (wifiAvailable) {
+ return R.string.tether_settings_title_wifi;
+ } else if (usbAvailable && bluetoothAvailable) {
+ return R.string.tether_settings_title_usb_bluetooth;
+ } else if (usbAvailable) {
+ return R.string.tether_settings_title_usb;
+ } else {
+ return R.string.tether_settings_title_bluetooth;
+ }
+ }
+
+ /**
+ * Returns a label for the user, in the form of "User: user name" or "Work profile".
+ */
+ public static String getUserLabel(Context context, UserInfo info) {
+ String name = info != null ? info.name : null;
+ if (info.isManagedProfile()) {
+ // We use predefined values for managed profiles
+ return context.getString(R.string.managed_user_title);
+ } else if (info.isGuest()) {
+ name = context.getString(R.string.user_guest);
+ }
+ if (name == null && info != null) {
+ name = Integer.toString(info.id);
+ } else if (info == null) {
+ name = context.getString(R.string.unknown);
+ }
+ return context.getResources().getString(R.string.running_process_item_user_label, name);
+ }
+
+ /**
+ * Returns a circular icon for a user.
+ */
+ public static Drawable getUserIcon(Context context, UserManager um, UserInfo user) {
+ if (user.isManagedProfile()) {
+ // We use predefined values for managed profiles
+ Bitmap b = BitmapFactory.decodeResource(context.getResources(),
+ com.android.internal.R.drawable.ic_corp_icon);
+ return CircleFramedDrawable.getInstance(context, b);
+ }
+ if (user.iconPath != null) {
+ Bitmap icon = um.getUserIcon(user.id);
+ if (icon != null) {
+ return CircleFramedDrawable.getInstance(context, icon);
+ }
+ }
+ return CircleFramedDrawable.getInstance(context, UserIcons.convertToBitmap(
+ UserIcons.getDefaultUserIcon(user.id, /* light= */ false)));
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
new file mode 100644
index 0000000..278b57d
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/CircleFramedDrawable.java
@@ -0,0 +1,136 @@
+/*
+ * 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.settingslib.drawable;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+import com.android.settingslib.R;
+
+/**
+ * Converts the user avatar icon to a circularly clipped one.
+ * TODO: Move this to an internal framework class and share with the one in Keyguard.
+ */
+public class CircleFramedDrawable extends Drawable {
+
+ private final Bitmap mBitmap;
+ private final int mSize;
+ private final Paint mPaint;
+
+ private float mScale;
+ private Rect mSrcRect;
+ private RectF mDstRect;
+
+ public static CircleFramedDrawable getInstance(Context context, Bitmap icon) {
+ Resources res = context.getResources();
+ float iconSize = res.getDimension(R.dimen.circle_avatar_size);
+
+ CircleFramedDrawable instance = new CircleFramedDrawable(icon, (int) iconSize);
+ return instance;
+ }
+
+ public CircleFramedDrawable(Bitmap icon, int size) {
+ super();
+ mSize = size;
+
+ mBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(mBitmap);
+
+ final int width = icon.getWidth();
+ final int height = icon.getHeight();
+ final int square = Math.min(width, height);
+
+ final Rect cropRect = new Rect((width - square) / 2, (height - square) / 2, square, square);
+ final RectF circleRect = new RectF(0f, 0f, mSize, mSize);
+
+ final Path fillPath = new Path();
+ fillPath.addArc(circleRect, 0f, 360f);
+
+ canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+
+ // opaque circle matte
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setColor(Color.BLACK);
+ mPaint.setStyle(Paint.Style.FILL);
+ canvas.drawPath(fillPath, mPaint);
+
+ // mask in the icon where the bitmap is opaque
+ mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
+ canvas.drawBitmap(icon, cropRect, circleRect, mPaint);
+
+ // prepare paint for frame drawing
+ mPaint.setXfermode(null);
+
+ mScale = 1f;
+
+ mSrcRect = new Rect(0, 0, mSize, mSize);
+ mDstRect = new RectF(0, 0, mSize, mSize);
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ final float inside = mScale * mSize;
+ final float pad = (mSize - inside) / 2f;
+
+ mDstRect.set(pad, pad, mSize - pad, mSize - pad);
+ canvas.drawBitmap(mBitmap, mSrcRect, mDstRect, null);
+ }
+
+ public void setScale(float scale) {
+ mScale = scale;
+ }
+
+ public float getScale() {
+ return mScale;
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mSize;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mSize;
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartData.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartData.java
new file mode 100644
index 0000000..e30aac5
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartData.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 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.settingslib.net;
+
+import android.net.NetworkStatsHistory;
+
+public class ChartData {
+ public NetworkStatsHistory network;
+
+ public NetworkStatsHistory detail;
+ public NetworkStatsHistory detailDefault;
+ public NetworkStatsHistory detailForeground;
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoader.java
new file mode 100644
index 0000000..223c055
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoader.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2011 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.settingslib.net;
+
+import static android.net.NetworkStats.SET_DEFAULT;
+import static android.net.NetworkStats.SET_FOREGROUND;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
+import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStatsHistory;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.settingslib.AppItem;
+
+/**
+ * Loader for historical chart data for both network and UID details.
+ */
+public class ChartDataLoader extends AsyncTaskLoader<ChartData> {
+ private static final String KEY_TEMPLATE = "template";
+ private static final String KEY_APP = "app";
+ private static final String KEY_FIELDS = "fields";
+
+ private final INetworkStatsSession mSession;
+ private final Bundle mArgs;
+
+ public static Bundle buildArgs(NetworkTemplate template, AppItem app) {
+ return buildArgs(template, app, FIELD_RX_BYTES | FIELD_TX_BYTES);
+ }
+
+ public static Bundle buildArgs(NetworkTemplate template, AppItem app, int fields) {
+ final Bundle args = new Bundle();
+ args.putParcelable(KEY_TEMPLATE, template);
+ args.putParcelable(KEY_APP, app);
+ args.putInt(KEY_FIELDS, fields);
+ return args;
+ }
+
+ public ChartDataLoader(Context context, INetworkStatsSession session, Bundle args) {
+ super(context);
+ mSession = session;
+ mArgs = args;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ super.onStartLoading();
+ forceLoad();
+ }
+
+ @Override
+ public ChartData loadInBackground() {
+ final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
+ final AppItem app = mArgs.getParcelable(KEY_APP);
+ final int fields = mArgs.getInt(KEY_FIELDS);
+
+ try {
+ return loadInBackground(template, app, fields);
+ } catch (RemoteException e) {
+ // since we can't do much without history, and we don't want to
+ // leave with half-baked UI, we bail hard.
+ throw new RuntimeException("problem reading network stats", e);
+ }
+ }
+
+ private ChartData loadInBackground(NetworkTemplate template, AppItem app, int fields)
+ throws RemoteException {
+ final ChartData data = new ChartData();
+ data.network = mSession.getHistoryForNetwork(template, fields);
+
+ if (app != null) {
+ // load stats for current uid and template
+ final int size = app.uids.size();
+ for (int i = 0; i < size; i++) {
+ final int uid = app.uids.keyAt(i);
+ data.detailDefault = collectHistoryForUid(
+ template, uid, SET_DEFAULT, data.detailDefault);
+ data.detailForeground = collectHistoryForUid(
+ template, uid, SET_FOREGROUND, data.detailForeground);
+ }
+
+ if (size > 0) {
+ data.detail = new NetworkStatsHistory(data.detailForeground.getBucketDuration());
+ data.detail.recordEntireHistory(data.detailDefault);
+ data.detail.recordEntireHistory(data.detailForeground);
+ } else {
+ data.detailDefault = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ data.detailForeground = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ data.detail = new NetworkStatsHistory(HOUR_IN_MILLIS);
+ }
+ }
+
+ return data;
+ }
+
+ @Override
+ protected void onStopLoading() {
+ super.onStopLoading();
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ cancelLoad();
+ }
+
+ /**
+ * Collect {@link NetworkStatsHistory} for the requested UID, combining with
+ * an existing {@link NetworkStatsHistory} if provided.
+ */
+ private NetworkStatsHistory collectHistoryForUid(
+ NetworkTemplate template, int uid, int set, NetworkStatsHistory existing)
+ throws RemoteException {
+ final NetworkStatsHistory history = mSession.getHistoryForUid(
+ template, uid, set, TAG_NONE, FIELD_RX_BYTES | FIELD_TX_BYTES);
+
+ if (existing != null) {
+ existing.recordEntireHistory(history);
+ return existing;
+ } else {
+ return history;
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java
new file mode 100644
index 0000000..572bae1
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/SummaryForAllUidLoader.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2011 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.settingslib.net;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+import android.net.INetworkStatsSession;
+import android.net.NetworkStats;
+import android.net.NetworkTemplate;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+public class SummaryForAllUidLoader extends AsyncTaskLoader<NetworkStats> {
+ private static final String KEY_TEMPLATE = "template";
+ private static final String KEY_START = "start";
+ private static final String KEY_END = "end";
+
+ private final INetworkStatsSession mSession;
+ private final Bundle mArgs;
+
+ public static Bundle buildArgs(NetworkTemplate template, long start, long end) {
+ final Bundle args = new Bundle();
+ args.putParcelable(KEY_TEMPLATE, template);
+ args.putLong(KEY_START, start);
+ args.putLong(KEY_END, end);
+ return args;
+ }
+
+ public SummaryForAllUidLoader(Context context, INetworkStatsSession session, Bundle args) {
+ super(context);
+ mSession = session;
+ mArgs = args;
+ }
+
+ @Override
+ protected void onStartLoading() {
+ super.onStartLoading();
+ forceLoad();
+ }
+
+ @Override
+ public NetworkStats loadInBackground() {
+ final NetworkTemplate template = mArgs.getParcelable(KEY_TEMPLATE);
+ final long start = mArgs.getLong(KEY_START);
+ final long end = mArgs.getLong(KEY_END);
+
+ try {
+ return mSession.getSummaryForAllUid(template, start, end, false);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ @Override
+ protected void onStopLoading() {
+ super.onStopLoading();
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ super.onReset();
+ cancelLoad();
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java b/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java
new file mode 100644
index 0000000..5e42281
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/UidDetail.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2011 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.settingslib.net;
+
+import android.graphics.drawable.Drawable;
+
+public class UidDetail {
+ public CharSequence label;
+ public CharSequence contentDescription;
+ public CharSequence[] detailLabels;
+ public CharSequence[] detailContentDescriptions;
+ public Drawable icon;
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java b/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java
new file mode 100644
index 0000000..224b967
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/net/UidDetailProvider.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2011 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.settingslib.net;
+
+import android.app.AppGlobals;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.net.ConnectivityManager;
+import android.net.TrafficStats;
+import android.os.UserManager;
+import android.os.UserHandle;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.settingslib.R;
+import com.android.settingslib.Utils;
+
+/**
+ * Return details about a specific UID, handling special cases like
+ * {@link TrafficStats#UID_TETHERING} and {@link UserInfo}.
+ */
+public class UidDetailProvider {
+ private static final String TAG = "DataUsage";
+ private final Context mContext;
+ private final SparseArray<UidDetail> mUidDetailCache;
+
+ public static final int OTHER_USER_RANGE_START = -2000;
+
+ public static int buildKeyForUser(int userHandle) {
+ return OTHER_USER_RANGE_START - userHandle;
+ }
+
+ public static boolean isKeyForUser(int key) {
+ return key <= OTHER_USER_RANGE_START;
+ }
+
+ public static int getUserIdForKey(int key) {
+ return OTHER_USER_RANGE_START - key;
+ }
+
+ public UidDetailProvider(Context context) {
+ mContext = context.getApplicationContext();
+ mUidDetailCache = new SparseArray<UidDetail>();
+ }
+
+ public void clearCache() {
+ synchronized (mUidDetailCache) {
+ mUidDetailCache.clear();
+ }
+ }
+
+ /**
+ * Resolve best descriptive label for the given UID.
+ */
+ public UidDetail getUidDetail(int uid, boolean blocking) {
+ UidDetail detail;
+
+ synchronized (mUidDetailCache) {
+ detail = mUidDetailCache.get(uid);
+ }
+
+ if (detail != null) {
+ return detail;
+ } else if (!blocking) {
+ return null;
+ }
+
+ detail = buildUidDetail(uid);
+
+ synchronized (mUidDetailCache) {
+ mUidDetailCache.put(uid, detail);
+ }
+
+ return detail;
+ }
+
+ /**
+ * Build {@link UidDetail} object, blocking until all {@link Drawable}
+ * lookup is finished.
+ */
+ private UidDetail buildUidDetail(int uid) {
+ final Resources res = mContext.getResources();
+ final PackageManager pm = mContext.getPackageManager();
+
+ final UidDetail detail = new UidDetail();
+ detail.label = pm.getNameForUid(uid);
+ detail.icon = pm.getDefaultActivityIcon();
+
+ // handle special case labels
+ switch (uid) {
+ case android.os.Process.SYSTEM_UID:
+ detail.label = res.getString(R.string.process_kernel_label);
+ detail.icon = pm.getDefaultActivityIcon();
+ return detail;
+ case TrafficStats.UID_REMOVED:
+ detail.label = res.getString(UserManager.supportsMultipleUsers()
+ ? R.string.data_usage_uninstalled_apps_users
+ : R.string.data_usage_uninstalled_apps);
+ detail.icon = pm.getDefaultActivityIcon();
+ return detail;
+ case TrafficStats.UID_TETHERING:
+ final ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ detail.label = res.getString(Utils.getTetheringLabel(cm));
+ detail.icon = pm.getDefaultActivityIcon();
+ return detail;
+ }
+
+ final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+
+ // Handle keys that are actually user handles
+ if (isKeyForUser(uid)) {
+ final int userHandle = getUserIdForKey(uid);
+ final UserInfo info = um.getUserInfo(userHandle);
+ if (info != null) {
+ detail.label = Utils.getUserLabel(mContext, info);
+ detail.icon = Utils.getUserIcon(mContext, um, info);
+ return detail;
+ }
+ }
+
+ // otherwise fall back to using packagemanager labels
+ final String[] packageNames = pm.getPackagesForUid(uid);
+ final int length = packageNames != null ? packageNames.length : 0;
+ try {
+ final int userId = UserHandle.getUserId(uid);
+ UserHandle userHandle = new UserHandle(userId);
+ IPackageManager ipm = AppGlobals.getPackageManager();
+ if (length == 1) {
+ final ApplicationInfo info = ipm.getApplicationInfo(packageNames[0],
+ 0 /* no flags */, userId);
+ if (info != null) {
+ detail.label = info.loadLabel(pm).toString();
+ detail.icon = um.getBadgedIconForUser(info.loadIcon(pm),
+ new UserHandle(userId));
+ }
+ } else if (length > 1) {
+ detail.detailLabels = new CharSequence[length];
+ detail.detailContentDescriptions = new CharSequence[length];
+ for (int i = 0; i < length; i++) {
+ final String packageName = packageNames[i];
+ final PackageInfo packageInfo = pm.getPackageInfo(packageName, 0);
+ final ApplicationInfo appInfo = ipm.getApplicationInfo(packageName,
+ 0 /* no flags */, userId);
+
+ if (appInfo != null) {
+ detail.detailLabels[i] = appInfo.loadLabel(pm).toString();
+ detail.detailContentDescriptions[i] = um.getBadgedLabelForUser(
+ detail.detailLabels[i], userHandle);
+ if (packageInfo.sharedUserLabel != 0) {
+ detail.label = pm.getText(packageName, packageInfo.sharedUserLabel,
+ packageInfo.applicationInfo).toString();
+ detail.icon = um.getBadgedIconForUser(appInfo.loadIcon(pm), userHandle);
+ }
+ }
+ }
+ }
+ detail.contentDescription = um.getBadgedLabelForUser(detail.label, userHandle);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Error while building UI detail for uid "+uid, e);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Error while building UI detail for uid "+uid, e);
+ }
+
+ if (TextUtils.isEmpty(detail.label)) {
+ detail.label = Integer.toString(uid);
+ }
+
+ return detail;
+ }
+}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index c00fdf3..37e0db0 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -104,6 +104,7 @@
<uses-permission android:name="android.permission.REGISTER_CALL_PROVIDER" />
<uses-permission android:name="android.permission.REGISTER_CONNECTION_MANAGER" />
<uses-permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION" />
+ <uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
<application android:label="@string/app_label">
<provider
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 677ab91..372fa03 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -108,6 +108,7 @@
<uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
<uses-permission android:name="android.permission.TRUST_LISTENER" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
+ <uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" />
<!-- Needed for WallpaperManager.clear in ImageWallpaper.updateWallpaperLocked -->
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
diff --git a/packages/SystemUI/res/anim/navbar_fade_in.xml b/packages/SystemUI/res/anim/navbar_fade_in.xml
index e3429e6..7051730 100644
--- a/packages/SystemUI/res/anim/navbar_fade_in.xml
+++ b/packages/SystemUI/res/anim/navbar_fade_in.xml
@@ -19,4 +19,5 @@
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:interpolator="@android:interpolator/linear_out_slow_in"
+ android:startDelay="32"
android:duration="200"/>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 260d81b1..123ff78 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -260,12 +260,6 @@
<!-- Doze: pulse parameter - how long does it take to fade in after a pickup? -->
<integer name="doze_pulse_duration_in_pickup">300</integer>
- <!-- Doze: pulse parameter - delay to wait for the screen to wake up -->
- <integer name="doze_pulse_delay_in">200</integer>
-
- <!-- Doze: pulse parameter - delay to wait for the screen to wake up after a pickup -->
- <integer name="doze_pulse_delay_in_pickup">200</integer>
-
<!-- Doze: pulse parameter - once faded in, how long does it stay visible? -->
<integer name="doze_pulse_duration_visible">3000</integer>
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 8eef23e..13128b7 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -37,6 +37,7 @@
<item type="id" name="doze_saved_filter_tag"/>
<item type="id" name="qs_icon_tag"/>
<item type="id" name="scrim"/>
+ <item type="id" name="scrim_target"/>
<item type="id" name="hun_scrim_alpha_start"/>
<item type="id" name="hun_scrim_alpha_end"/>
<item type="id" name="notification_power"/>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 89a2c74..82a1bfe 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -29,6 +29,7 @@
void stopDozing();
boolean isPowerSaveActive();
boolean isNotificationLightOn();
+ boolean isPulsingBlocked();
public interface Callback {
void onNewNotifications();
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index 887391c..630d735 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -255,6 +255,11 @@
}
private void continuePulsing(int reason) {
+ if (mHost.isPulsingBlocked()) {
+ mPulsing = false;
+ mWakeLock.release();
+ return;
+ }
mHost.pulseWhileDozing(new DozeHost.PulseCallback() {
@Override
public void onPulseStarted() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index d78800f..d2c60ef 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -80,7 +80,8 @@
@Override // Binder interface
public void keyguardDone(boolean authenticated, boolean wakeup) {
checkPermission();
- mKeyguardViewMediator.keyguardDone(authenticated, wakeup);
+ // TODO: Remove wakeup
+ mKeyguardViewMediator.keyguardDone(authenticated);
}
@Override // Binder interface
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index a1c8b1a..647b272 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -68,6 +68,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.SystemUI;
+import com.android.systemui.statusbar.phone.FingerprintUnlockController;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -176,11 +177,6 @@
*/
private static final String KEYGUARD_ANALYTICS_SETTING = "keyguard_analytics";
- /**
- * How much faster we collapse the lockscreen when authenticating with fingerprint.
- */
- private static final float FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR = 1.3f;
-
/** The stream type that the lock sounds are tied to. */
private int mUiSoundsStreamType;
@@ -458,30 +454,6 @@
break;
}
}
-
- @Override
- public void onFingerprintAuthenticated(int userId, boolean wakeAndUnlocking) {
- boolean unlockingWithFingerprintAllowed =
- mUpdateMonitor.isUnlockingWithFingerprintAllowed();
- if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
- if (unlockingWithFingerprintAllowed) {
- mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated();
- }
- } else {
- if (wakeAndUnlocking && mShowing && unlockingWithFingerprintAllowed) {
- mWakeAndUnlocking = true;
- mStatusBarKeyguardViewManager.setWakeAndUnlocking();
- keyguardDone(true, true);
- } else if (mShowing && mDeviceInteractive) {
- if (wakeAndUnlocking) {
- mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
- }
- mStatusBarKeyguardViewManager.animateCollapsePanels(
- FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
- }
- }
- };
-
};
ViewMediatorCallback mViewMediatorCallback = new ViewMediatorCallback() {
@@ -490,9 +462,12 @@
KeyguardViewMediator.this.userActivity();
}
- public void keyguardDone(boolean authenticated) {
+ public void keyguardDone(boolean strongAuth) {
if (!mKeyguardDonePending) {
- KeyguardViewMediator.this.keyguardDone(authenticated, true);
+ KeyguardViewMediator.this.keyguardDone(true /* authenticated */);
+ }
+ if (strongAuth) {
+ mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
}
}
@@ -506,12 +481,15 @@
}
@Override
- public void keyguardDonePending() {
+ public void keyguardDonePending(boolean strongAuth) {
mKeyguardDonePending = true;
mHideAnimationRun = true;
mStatusBarKeyguardViewManager.startPreHideAnimation(null /* finishRunnable */);
mHandler.sendEmptyMessageDelayed(KEYGUARD_DONE_PENDING_TIMEOUT,
KEYGUARD_DONE_PENDING_TIMEOUT_MS);
+ if (strongAuth) {
+ mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
+ }
}
@Override
@@ -524,7 +502,7 @@
if (mKeyguardDonePending) {
// Somebody has called keyguardDonePending before, which means that we are
// authenticated
- KeyguardViewMediator.this.keyguardDone(true /* authenticated */, true /* wakeUp */);
+ KeyguardViewMediator.this.keyguardDone(true /* authenticated */);
}
}
@@ -552,9 +530,12 @@
public int getBouncerPromptReason() {
int currentUser = ActivityManager.getCurrentUser();
if ((mUpdateMonitor.getUserTrustIsManaged(currentUser)
- || mUpdateMonitor.isUnlockWithFingerPrintPossible(currentUser))
- && !mTrustManager.hasUserAuthenticatedSinceBoot(currentUser)) {
+ || mUpdateMonitor.isUnlockWithFingerprintPossible(currentUser))
+ && !mUpdateMonitor.getStrongAuthTracker().hasUserAuthenticatedSinceBoot()) {
return KeyguardSecurityView.PROMPT_REASON_RESTART;
+ } else if (mUpdateMonitor.isUnlockWithFingerprintPossible(currentUser)
+ && mUpdateMonitor.hasFingerprintUnlockTimedOut(currentUser)) {
+ return KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
}
return KeyguardSecurityView.PROMPT_REASON_NONE;
}
@@ -1189,10 +1170,10 @@
}
};
- public void keyguardDone(boolean authenticated, boolean wakeup) {
- if (DEBUG) Log.d(TAG, "keyguardDone(" + authenticated + ")");
+ public void keyguardDone(boolean authenticated) {
+ if (DEBUG) Log.d(TAG, "keyguardDone(" + authenticated +")");
EventLog.writeEvent(70000, 2);
- Message msg = mHandler.obtainMessage(KEYGUARD_DONE, authenticated ? 1 : 0, wakeup ? 1 : 0);
+ Message msg = mHandler.obtainMessage(KEYGUARD_DONE, authenticated ? 1 : 0);
mHandler.sendMessage(msg);
}
@@ -1235,14 +1216,11 @@
handleNotifyStartedWakingUp();
break;
case KEYGUARD_DONE:
- handleKeyguardDone(msg.arg1 != 0, msg.arg2 != 0);
+ handleKeyguardDone(msg.arg1 != 0);
break;
case KEYGUARD_DONE_DRAWING:
handleKeyguardDoneDrawing();
break;
- case KEYGUARD_DONE_AUTHENTICATING:
- keyguardDone(true, true);
- break;
case SET_OCCLUDED:
handleSetOccluded(msg.arg1 != 0);
break;
@@ -1272,7 +1250,7 @@
* @see #keyguardDone
* @see #KEYGUARD_DONE
*/
- private void handleKeyguardDone(boolean authenticated, boolean wakeup) {
+ private void handleKeyguardDone(boolean authenticated) {
if (DEBUG) Log.d(TAG, "handleKeyguardDone");
synchronized (this) {
resetKeyguardDonePendingLocked();
@@ -1586,6 +1564,7 @@
synchronized (this) {
if (DEBUG) Log.d(TAG, "handleNotifyScreenTurnedOff");
mStatusBarKeyguardViewManager.onScreenTurnedOff();
+ mWakeAndUnlocking = false;
}
}
@@ -1612,11 +1591,17 @@
}
}
+ public void onWakeAndUnlocking() {
+ mWakeAndUnlocking = true;
+ keyguardDone(true /* authenticated */);
+ }
+
public StatusBarKeyguardViewManager registerStatusBar(PhoneStatusBar phoneStatusBar,
ViewGroup container, StatusBarWindowManager statusBarWindowManager,
- ScrimController scrimController) {
+ ScrimController scrimController,
+ FingerprintUnlockController fingerprintUnlockController) {
mStatusBarKeyguardViewManager.registerStatusBar(phoneStatusBar, container,
- statusBarWindowManager, scrimController);
+ statusBarWindowManager, scrimController, fingerprintUnlockController);
return mStatusBarKeyguardViewManager;
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
index 2a84362..adc9b36 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionPermissionActivity.java
@@ -22,6 +22,7 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.graphics.Typeface;
import android.media.projection.MediaProjectionManager;
import android.media.projection.IMediaProjectionManager;
import android.media.projection.IMediaProjection;
@@ -29,7 +30,14 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.text.BidiFormatter;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.style.StyleSpan;
import android.util.Log;
+import android.util.TypedValue;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
@@ -39,6 +47,8 @@
implements DialogInterface.OnClickListener, CheckBox.OnCheckedChangeListener,
DialogInterface.OnCancelListener {
private static final String TAG = "MediaProjectionPermissionActivity";
+ private static final float MAX_APP_NAME_SIZE_PX = 500f;
+ private static final String ELLIPSIS = "\u2026";
private boolean mPermanentGrant;
private String mPackageName;
@@ -84,11 +94,49 @@
return;
}
- String appName = aInfo.loadLabel(packageManager).toString();
+ TextPaint paint = new TextPaint();
+ paint.setTextSize(42);
+
+ String label = aInfo.loadLabel(packageManager).toString();
+
+ // If the label contains new line characters it may push the security
+ // message below the fold of the dialog. Labels shouldn't have new line
+ // characters anyways, so just truncate the message the first time one
+ // is seen.
+ final int labelLength = label.length();
+ int offset = 0;
+ while (offset < labelLength) {
+ final int codePoint = label.codePointAt(offset);
+ final int type = Character.getType(codePoint);
+ if (type == Character.LINE_SEPARATOR
+ || type == Character.CONTROL
+ || type == Character.PARAGRAPH_SEPARATOR) {
+ label = label.substring(0, offset) + ELLIPSIS;
+ break;
+ }
+ offset += Character.charCount(codePoint);
+ }
+
+ if (label.isEmpty()) {
+ label = mPackageName;
+ }
+
+ String unsanitizedAppName = TextUtils.ellipsize(label,
+ paint, MAX_APP_NAME_SIZE_PX, TextUtils.TruncateAt.END).toString();
+ String appName = BidiFormatter.getInstance().unicodeWrap(unsanitizedAppName);
+
+ String actionText = getString(R.string.media_projection_dialog_text, appName);
+ SpannableString message = new SpannableString(actionText);
+
+ int appNameIndex = actionText.indexOf(appName);
+ if (appNameIndex >= 0) {
+ message.setSpan(new StyleSpan(Typeface.BOLD),
+ appNameIndex, appNameIndex + appName.length(), 0);
+ }
mDialog = new AlertDialog.Builder(this)
.setIcon(aInfo.loadIcon(packageManager))
- .setMessage(getString(R.string.media_projection_dialog_text, appName))
+ .setMessage(message)
.setPositiveButton(R.string.media_projection_action_text, this)
.setNegativeButton(android.R.string.cancel, this)
.setView(R.layout.remember_permission_checkbox)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java b/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java
index f36019b..e64f6a0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/UsageTracker.java
@@ -21,7 +21,6 @@
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.SharedPreferences;
import com.android.systemui.Prefs;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 8c2ac88..f1550a0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -20,7 +20,6 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ITaskStackListener;
-import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index b47fb304..d0876fa 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -33,7 +33,6 @@
import android.view.ViewStub;
import android.widget.Toast;
-import com.android.internal.logging.MetricsConstants;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.Prefs;
import com.android.systemui.R;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 298a1cc..d5c9253 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -20,10 +20,8 @@
import android.app.ActivityManagerNative;
import android.app.ActivityOptions;
import android.app.AppGlobals;
-import android.app.IActivityContainer;
import android.app.IActivityManager;
import android.app.ITaskStackListener;
-import android.app.SearchManager;
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
@@ -54,15 +52,12 @@
import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.UserHandle;
-import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;
import android.util.MutableBoolean;
import android.util.Pair;
import android.util.SparseArray;
import android.view.Display;
-import android.view.DisplayInfo;
-import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
@@ -71,7 +66,6 @@
import com.android.systemui.R;
import com.android.systemui.recents.Constants;
import com.android.systemui.recents.Recents;
-import com.android.systemui.recents.RecentsAppWidgetHost;
import java.io.IOException;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 403af70..7f17885 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -350,6 +350,7 @@
} else {
updateBackground();
}
+ setOutlineAlpha(dark ? 0f : 1f);
}
public void setShowingLegacyBackground(boolean showing) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 7065343..c14f2159 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -90,6 +90,7 @@
import com.android.internal.util.NotificationColorUtil;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.DejankUtils;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SwipeHelper;
@@ -167,13 +168,7 @@
// on-screen navigation buttons
protected NavigationBarView mNavigationBarView = null;
- protected Boolean mScreenOn;
-
- // The second field is a bit different from the first one because it only listens to screen on/
- // screen of events from Keyguard. We need this so we don't have a race condition with the
- // broadcast. In the future, we should remove the first field altogether and rename the second
- // field.
- protected boolean mScreenOnFromKeyguard;
+ protected boolean mDeviceInteractive;
protected boolean mVisible;
@@ -1512,6 +1507,15 @@
final PendingIntent intent = sbn.getNotification().contentIntent;
final String notificationKey = sbn.getKey();
+ // Mark notification for one frame.
+ row.setJustClicked(true);
+ DejankUtils.postAfterTraversal(new Runnable() {
+ @Override
+ public void run() {
+ row.setJustClicked(false);
+ }
+ });
+
if (NOTIFICATION_CLICK_DEBUG) {
Log.d(TAG, "Clicked on content of " + notificationKey);
}
@@ -1619,7 +1623,7 @@
protected void updateVisibleToUser() {
boolean oldVisibleToUser = mVisibleToUser;
- mVisibleToUser = mVisible && mScreenOnFromKeyguard;
+ mVisibleToUser = mVisible && mDeviceInteractive;
if (oldVisibleToUser != mVisibleToUser) {
handleVisibleToUserChanged(mVisibleToUser);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index a1b07b5..025451d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -64,6 +64,7 @@
private static final int MSG_APP_TRANSITION_STARTING = 21 << MSG_SHIFT;
private static final int MSG_ASSIST_DISCLOSURE = 22 << MSG_SHIFT;
private static final int MSG_START_ASSIST = 23 << MSG_SHIFT;
+ private static final int MSG_CAMERA_LAUNCH_GESTURE = 24 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -109,6 +110,7 @@
public void appTransitionStarting(long startTime, long duration);
public void showAssistDisclosure();
public void startAssist(Bundle args);
+ public void onCameraLaunchGestureDetected();
}
public CommandQueue(Callbacks callbacks, StatusBarIconList list) {
@@ -293,6 +295,14 @@
}
}
+ @Override
+ public void onCameraLaunchGestureDetected() {
+ synchronized (mList) {
+ mHandler.removeMessages(MSG_CAMERA_LAUNCH_GESTURE);
+ mHandler.obtainMessage(MSG_CAMERA_LAUNCH_GESTURE).sendToTarget();
+ }
+ }
+
private final class H extends Handler {
public void handleMessage(Message msg) {
final int what = msg.what & MSG_MASK;
@@ -391,6 +401,9 @@
case MSG_START_ASSIST:
mCallbacks.startAssist((Bundle) msg.obj);
break;
+ case MSG_CAMERA_LAUNCH_GESTURE:
+ mCallbacks.onCameraLaunchGestureDetected();
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index b88e5ca..5f01306 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -24,6 +24,7 @@
import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
import android.service.notification.StatusBarNotification;
import android.util.AttributeSet;
import android.view.MotionEvent;
@@ -110,6 +111,8 @@
}
};
+ private boolean mJustClicked;
+
public NotificationContentView getPrivateLayout() {
return mPrivateLayout;
}
@@ -301,6 +304,21 @@
return mHeadsUpHeight;
}
+ /**
+ * Mark whether this notification was just clicked, i.e. the user has just clicked this
+ * notification in this frame.
+ */
+ public void setJustClicked(boolean justClicked) {
+ mJustClicked = justClicked;
+ }
+
+ /**
+ * @return true if this notification has been clicked in this frame, false otherwise
+ */
+ public boolean wasJustClicked() {
+ return mJustClicked;
+ }
+
public interface ExpansionLogger {
public void logNotificationExpansion(String key, boolean userAction, boolean expanded);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
index d77e050..a6fc4bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableOutlineView.java
@@ -34,6 +34,7 @@
private final Rect mOutlineRect = new Rect();
protected final int mRoundedRectCornerRadius;
private boolean mCustomOutline;
+ private float mOutlineAlpha = 1f;
public ExpandableOutlineView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -50,6 +51,7 @@
} else {
outline.setRoundRect(mOutlineRect, mRoundedRectCornerRadius);
}
+ outline.setAlpha(mOutlineAlpha);
}
});
}
@@ -66,6 +68,11 @@
invalidateOutline();
}
+ protected void setOutlineAlpha(float alpha) {
+ mOutlineAlpha = alpha;
+ invalidateOutline();
+ }
+
protected void setOutlineRect(RectF rect) {
if (rect != null) {
setOutlineRect(rect.left, rect.top, rect.right, rect.bottom);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 164c496..8058933 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -36,6 +36,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.ImageView;
+
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.KeyguardAffordanceHelper;
import com.android.systemui.statusbar.phone.PhoneStatusBar;
@@ -79,6 +80,7 @@
private float mRestingAlpha = KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT;
private boolean mSupportHardware;
private boolean mFinishing;
+ private boolean mLaunchingAffordance;
private CanvasProperty<Float> mHwCircleRadius;
private CanvasProperty<Float> mHwCenterX;
@@ -152,7 +154,7 @@
@Override
protected void onDraw(Canvas canvas) {
- mSupportHardware = canvas.isHardwareAccelerated();
+ mSupportHardware = false;//canvas.isHardwareAccelerated();
drawBackgroundCircle(canvas);
canvas.save();
canvas.scale(mImageScale, mImageScale, getWidth() / 2, getHeight() / 2);
@@ -161,9 +163,11 @@
}
public void setPreviewView(View v) {
+ View oldPreviewView = mPreviewView;
mPreviewView = v;
if (mPreviewView != null) {
- mPreviewView.setVisibility(INVISIBLE);
+ mPreviewView.setVisibility(mLaunchingAffordance
+ ? oldPreviewView.getVisibility() : INVISIBLE);
}
}
@@ -176,7 +180,7 @@
}
private void drawBackgroundCircle(Canvas canvas) {
- if (mCircleRadius > 0) {
+ if (mCircleRadius > 0 || mFinishing) {
if (mFinishing && mSupportHardware) {
DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
displayListCanvas.drawCircle(mHwCenterX, mHwCenterY, mHwCircleRadius,
@@ -207,11 +211,12 @@
cancelAnimator(mPreviewClipper);
mFinishing = true;
mCircleStartRadius = mCircleRadius;
- float maxCircleSize = getMaxCircleSize();
+ final float maxCircleSize = getMaxCircleSize();
Animator animatorToRadius;
if (mSupportHardware) {
initHwProperties();
animatorToRadius = getRtAnimatorToRadius(maxCircleSize);
+ startRtAlphaFadeIn();
} else {
animatorToRadius = getAnimatorToRadius(maxCircleSize);
}
@@ -222,6 +227,8 @@
public void onAnimationEnd(Animator animation) {
mAnimationEndRunnable.run();
mFinishing = false;
+ mCircleRadius = maxCircleSize;
+ invalidate();
}
});
animatorToRadius.start();
@@ -241,6 +248,36 @@
}
}
+ /**
+ * Fades in the Circle on the RenderThread. It's used when finishing the circle when it had
+ * alpha 0 in the beginning.
+ */
+ private void startRtAlphaFadeIn() {
+ if (mCircleRadius == 0 && mPreviewView == null) {
+ Paint modifiedPaint = new Paint(mCirclePaint);
+ modifiedPaint.setColor(mCircleColor);
+ modifiedPaint.setAlpha(0);
+ mHwCirclePaint = CanvasProperty.createPaint(modifiedPaint);
+ RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
+ RenderNodeAnimator.PAINT_ALPHA, 255);
+ animator.setTarget(this);
+ animator.setInterpolator(PhoneStatusBar.ALPHA_IN);
+ animator.setDuration(250);
+ animator.start();
+ }
+ }
+
+ public void instantFinishAnimation() {
+ cancelAnimator(mPreviewClipper);
+ if (mPreviewView != null) {
+ mPreviewView.setClipBounds(null);
+ mPreviewView.setVisibility(View.VISIBLE);
+ }
+ mCircleRadius = getMaxCircleSize();
+ setImageAlpha(0, false);
+ invalidate();
+ }
+
private void startRtCircleFadeOut(long duration) {
RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
RenderNodeAnimator.PAINT_ALPHA, 0);
@@ -443,6 +480,7 @@
public void setImageAlpha(float alpha, boolean animate, long duration,
Interpolator interpolator, Runnable runnable) {
cancelAnimator(mAlphaAnimator);
+ alpha = mLaunchingAffordance ? 0 : alpha;
int endAlpha = (int) (alpha * 255);
final Drawable background = getBackground();
if (!animate) {
@@ -509,4 +547,8 @@
return false;
}
}
+
+ public void setLaunchingAffordance(boolean launchingAffordance) {
+ mLaunchingAffordance = launchingAffordance;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 07a055c..54f91da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -16,17 +16,13 @@
package com.android.systemui.statusbar;
-import com.android.internal.app.IBatteryStats;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
-
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.res.Resources;
import android.graphics.Color;
+import android.hardware.fingerprint.FingerprintManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
import android.os.Handler;
@@ -39,19 +35,35 @@
import android.util.Log;
import android.view.View;
+import com.android.internal.app.IBatteryStats;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.KeyguardIndicationTextView;
+import com.android.systemui.statusbar.phone.LockIcon;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+
/**
- * Controls the little text indicator on the keyguard.
+ * Controls the indications and error messages shown on the Keyguard
*/
public class KeyguardIndicationController {
private static final String TAG = "KeyguardIndicationController";
+ private static final boolean DEBUG_CHARGING_CURRENT = false;
private static final int MSG_HIDE_TRANSIENT = 1;
+ private static final int MSG_CLEAR_FP_MSG = 2;
+ private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
private final Context mContext;
private final KeyguardIndicationTextView mTextView;
private final IBatteryStats mBatteryInfo;
+ private final int mSlowThreshold;
+ private final int mFastThreshold;
+ private final LockIcon mLockIcon;
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+
private String mRestingIndication;
private String mTransientIndication;
private int mTransientTextColor;
@@ -59,10 +71,20 @@
private boolean mPowerPluggedIn;
private boolean mPowerCharged;
+ private int mChargingSpeed;
+ private int mChargingCurrent;
+ private String mMessageToShowOnScreenOn;
- public KeyguardIndicationController(Context context, KeyguardIndicationTextView textView) {
+ public KeyguardIndicationController(Context context, KeyguardIndicationTextView textView,
+ LockIcon lockIcon) {
mContext = context;
mTextView = textView;
+ mLockIcon = lockIcon;
+
+ Resources res = context.getResources();
+ mSlowThreshold = res.getInteger(R.integer.config_chargingSlowlyThreshold);
+ mFastThreshold = res.getInteger(R.integer.config_chargingFastThreshold);
+
mBatteryInfo = IBatteryStats.Stub.asInterface(
ServiceManager.getService(BatteryStats.SERVICE_NAME));
@@ -150,7 +172,11 @@
return mTransientIndication;
}
if (mPowerPluggedIn) {
- return computePowerIndication();
+ String indication = computePowerIndication();
+ if (DEBUG_CHARGING_CURRENT) {
+ indication += ", " + (mChargingCurrent / 1000) + " mA";
+ }
+ return indication;
}
return mRestingIndication;
}
@@ -174,7 +200,19 @@
}
// Fall back to simple charging label.
- return mContext.getResources().getString(R.string.keyguard_plugged_in);
+ int chargingId;
+ switch (mChargingSpeed) {
+ case KeyguardUpdateMonitor.BatteryStatus.CHARGING_FAST:
+ chargingId = R.string.keyguard_plugged_in_charging_fast;
+ break;
+ case KeyguardUpdateMonitor.BatteryStatus.CHARGING_SLOWLY:
+ chargingId = R.string.keyguard_plugged_in_charging_slowly;
+ break;
+ default:
+ chargingId = R.string.keyguard_plugged_in;
+ break;
+ }
+ return mContext.getResources().getString(chargingId);
}
KeyguardUpdateMonitorCallback mUpdateMonitor = new KeyguardUpdateMonitorCallback() {
@@ -184,8 +222,68 @@
|| status.status == BatteryManager.BATTERY_STATUS_FULL;
mPowerPluggedIn = status.isPluggedIn() && isChargingOrFull;
mPowerCharged = status.isCharged();
+ mChargingCurrent = status.maxChargingCurrent;
+ mChargingSpeed = status.getChargingSpeed(mSlowThreshold, mFastThreshold);
updateIndication();
}
+
+ @Override
+ public void onFingerprintHelp(int msgId, String helpString) {
+ KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+ if (!updateMonitor.isUnlockingWithFingerprintAllowed()) {
+ return;
+ }
+ int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
+ if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+ mStatusBarKeyguardViewManager.showBouncerMessage(helpString, errorColor);
+ } else if (updateMonitor.isDeviceInteractive()) {
+ mLockIcon.setTransientFpError(true);
+ showTransientIndication(helpString, errorColor);
+ mHandler.removeMessages(MSG_CLEAR_FP_MSG);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_FP_MSG),
+ TRANSIENT_FP_ERROR_TIMEOUT);
+ }
+ }
+
+ @Override
+ public void onFingerprintError(int msgId, String errString) {
+ KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+ if (!updateMonitor.isUnlockingWithFingerprintAllowed()
+ || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
+ return;
+ }
+ int errorColor = mContext.getResources().getColor(R.color.system_warning_color, null);
+ if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+ mStatusBarKeyguardViewManager.showBouncerMessage(errString, errorColor);
+ } else if (updateMonitor.isDeviceInteractive()) {
+ showTransientIndication(errString, errorColor);
+ // We want to keep this message around in case the screen was off
+ mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+ hideTransientIndicationDelayed(5000);
+ } else {
+ mMessageToShowOnScreenOn = errString;
+ }
+ }
+
+ @Override
+ public void onScreenTurnedOn() {
+ if (mMessageToShowOnScreenOn != null) {
+ int errorColor = mContext.getResources().getColor(R.color.system_warning_color,
+ null);
+ showTransientIndication(mMessageToShowOnScreenOn, errorColor);
+ // We want to keep this message around in case the screen was off
+ mHandler.removeMessages(MSG_HIDE_TRANSIENT);
+ hideTransientIndicationDelayed(5000);
+ mMessageToShowOnScreenOn = null;
+ }
+ }
+
+ @Override
+ public void onFingerprintRunningStateChanged(boolean running) {
+ if (running) {
+ mMessageToShowOnScreenOn = null;
+ }
+ }
};
BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -203,7 +301,15 @@
if (msg.what == MSG_HIDE_TRANSIENT && mTransientIndication != null) {
mTransientIndication = null;
updateIndication();
+ } else if (msg.what == MSG_CLEAR_FP_MSG) {
+ mLockIcon.setTransientFpError(false);
+ hideTransientIndication();
}
}
};
+
+ public void setStatusBarKeyguardViewManager(
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
index 01aa8d1..8688c28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBackgroundView.java
@@ -86,6 +86,9 @@
if (mBackground != null) {
mBackground.setCallback(this);
}
+ if (mBackground instanceof RippleDrawable) {
+ ((RippleDrawable) mBackground).setForceSoftware(true);
+ }
invalidate();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
index 6b167b4..5a2fa3b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java
@@ -50,8 +50,6 @@
pw.print(" getPulseDuration(pickup=true): "); pw.println(getPulseDuration(true));
pw.print(" getPulseInDuration(pickup=false): "); pw.println(getPulseInDuration(false));
pw.print(" getPulseInDuration(pickup=true): "); pw.println(getPulseInDuration(true));
- pw.print(" getPulseInDelay(pickup=false): "); pw.println(getPulseInDelay(false));
- pw.print(" getPulseInDelay(pickup=true): "); pw.println(getPulseInDelay(true));
pw.print(" getPulseInVisibleDuration(): "); pw.println(getPulseVisibleDuration());
pw.print(" getPulseOutDuration(): "); pw.println(getPulseOutDuration());
pw.print(" getPulseOnSigMotion(): "); pw.println(getPulseOnSigMotion());
@@ -80,12 +78,6 @@
: getInt("doze.pulse.duration.in", R.integer.doze_pulse_duration_in);
}
- public int getPulseInDelay(boolean pickup) {
- return pickup
- ? getInt("doze.pulse.delay.in.pickup", R.integer.doze_pulse_delay_in_pickup)
- : getInt("doze.pulse.delay.in", R.integer.doze_pulse_delay_in);
- }
-
public int getPulseVisibleDuration() {
return getInt("doze.pulse.duration.visible", R.integer.doze_pulse_duration_visible);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 3e17328..3ff69c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -100,10 +100,35 @@
mHandler.post(mPulseIn);
}
+ /**
+ * Aborts pulsing immediately.
+ */
+ public void abortPulsing() {
+ cancelPulsing();
+ if (mDozing) {
+ mScrimController.setDozeBehindAlpha(1f);
+ mScrimController.setDozeInFrontAlpha(1f);
+ }
+ }
+
+ public void onScreenTurnedOn() {
+ if (isPulsing()) {
+ final boolean pickup = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
+ startScrimAnimation(true /* inFront */, 0f,
+ mDozeParameters.getPulseInDuration(pickup),
+ pickup ? mPulseInInterpolatorPickup : mPulseInInterpolator,
+ mPulseInFinished);
+ }
+ }
+
public boolean isPulsing() {
return mPulseCallback != null;
}
+ public boolean isDozing() {
+ return mDozing;
+ }
+
private void cancelPulsing() {
if (DEBUG) Log.d(TAG, "Cancel pulsing");
@@ -138,12 +163,11 @@
private void startScrimAnimation(final boolean inFront, float target, long duration,
Interpolator interpolator) {
- startScrimAnimation(inFront, target, duration, interpolator, 0 /* delay */,
- null /* endRunnable */);
+ startScrimAnimation(inFront, target, duration, interpolator, null /* endRunnable */);
}
private void startScrimAnimation(final boolean inFront, float target, long duration,
- Interpolator interpolator, long delay, final Runnable endRunnable) {
+ Interpolator interpolator, final Runnable endRunnable) {
Animator current = getCurrentAnimator(inFront);
if (current != null) {
float currentTarget = getCurrentTarget(inFront);
@@ -162,7 +186,6 @@
});
anim.setInterpolator(interpolator);
anim.setDuration(duration);
- anim.setStartDelay(delay);
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -222,12 +245,6 @@
+ DozeLog.pulseReasonToString(mPulseReason));
if (!mDozing) return;
DozeLog.tracePulseStart(mPulseReason);
- final boolean pickup = mPulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
- startScrimAnimation(true /* inFront */, 0f,
- mDozeParameters.getPulseInDuration(pickup),
- pickup ? mPulseInInterpolatorPickup : mPulseInInterpolator,
- mDozeParameters.getPulseInDelay(pickup),
- mPulseInFinished);
// Signal that the pulse is ready to turn the screen on and draw.
pulseStarted();
@@ -249,7 +266,7 @@
if (DEBUG) Log.d(TAG, "Pulse out, mDozing=" + mDozing);
if (!mDozing) return;
startScrimAnimation(true /* inFront */, 1f, mDozeParameters.getPulseOutDuration(),
- mPulseOutInterpolator, 0 /* delay */, mPulseOutFinished);
+ mPulseOutInterpolator, mPulseOutFinished);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
new file mode 100644
index 0000000..4e69999
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.keyguard.KeyguardConstants;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+
+/**
+ * Controller which coordinates all the fingerprint unlocking actions with the UI.
+ */
+public class FingerprintUnlockController extends KeyguardUpdateMonitorCallback {
+
+ private static final String TAG = "FingerprintController";
+ private static final boolean DEBUG_FP_WAKELOCK = KeyguardConstants.DEBUG_FP_WAKELOCK;
+ private static final long FINGERPRINT_WAKELOCK_TIMEOUT_MS = 15 * 1000;
+ private static final String FINGERPRINT_WAKE_LOCK_NAME = "wake-and-unlock wakelock";
+
+ /**
+ * Mode in which we don't need to wake up the device when we get a fingerprint.
+ */
+ public static final int MODE_NONE = 0;
+
+ /**
+ * Mode in which we wake up the device, and directly dismiss Keyguard. Active when we acquire
+ * a fingerprint while the screen is off and the device was sleeping.
+ */
+ public static final int MODE_WAKE_AND_UNLOCK = 1;
+
+ /**
+ * Mode in which we wake the device up, and fade out the Keyguard contents because they were
+ * already visible while pulsing in doze mode.
+ */
+ public static final int MODE_WAKE_AND_UNLOCK_PULSING = 2;
+
+ /**
+ * Mode in which we wake up the device, but play the normal dismiss animation. Active when we
+ * acquire a fingerprint pulsing in doze mode.
+ */
+ public static final int MODE_SHOW_BOUNCER = 3;
+
+ /**
+ * Mode in which we only wake up the device, and keyguard was not showing when we acquired a
+ * fingerprint.
+ * */
+ public static final int MODE_ONLY_WAKE = 4;
+
+ /**
+ * Mode in which fingerprint unlocks the device.
+ */
+ public static final int MODE_UNLOCK = 5;
+
+ /**
+ * Mode in which fingerprint brings up the bouncer because fingerprint unlocking is currently
+ * not allowed.
+ */
+ public static final int MODE_DISMISS_BOUNCER = 6;
+
+ /**
+ * How much faster we collapse the lockscreen when authenticating with fingerprint.
+ */
+ private static final float FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR = 1.3f;
+
+ private PowerManager mPowerManager;
+ private Handler mHandler = new Handler();
+ private PowerManager.WakeLock mWakeLock;
+ private KeyguardUpdateMonitor mUpdateMonitor;
+ private int mMode;
+ private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ private StatusBarWindowManager mStatusBarWindowManager;
+ private DozeScrimController mDozeScrimController;
+ private KeyguardViewMediator mKeyguardViewMediator;
+ private ScrimController mScrimController;
+ private PhoneStatusBar mPhoneStatusBar;
+
+ public FingerprintUnlockController(Context context,
+ StatusBarWindowManager statusBarWindowManager,
+ DozeScrimController dozeScrimController,
+ KeyguardViewMediator keyguardViewMediator,
+ ScrimController scrimController,
+ PhoneStatusBar phoneStatusBar) {
+ mPowerManager = context.getSystemService(PowerManager.class);
+ mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
+ mUpdateMonitor.registerCallback(this);
+ mStatusBarWindowManager = statusBarWindowManager;
+ mDozeScrimController = dozeScrimController;
+ mKeyguardViewMediator = keyguardViewMediator;
+ mScrimController = scrimController;
+ mPhoneStatusBar = phoneStatusBar;
+ }
+
+ public void setStatusBarKeyguardViewManager(
+ StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
+ mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
+ }
+
+ private final Runnable mReleaseFingerprintWakeLockRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (DEBUG_FP_WAKELOCK) {
+ Log.i(TAG, "fp wakelock: TIMEOUT!!");
+ }
+ releaseFingerprintWakeLock();
+ }
+ };
+
+ private void releaseFingerprintWakeLock() {
+ if (mWakeLock != null) {
+ mHandler.removeCallbacks(mReleaseFingerprintWakeLockRunnable);
+ if (DEBUG_FP_WAKELOCK) {
+ Log.i(TAG, "releasing fp wakelock");
+ }
+ mWakeLock.release();
+ mWakeLock = null;
+ }
+ }
+
+ @Override
+ public void onFingerprintAcquired() {
+ releaseFingerprintWakeLock();
+ if (!mUpdateMonitor.isDeviceInteractive()) {
+ mWakeLock = mPowerManager.newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, FINGERPRINT_WAKE_LOCK_NAME);
+ mWakeLock.acquire();
+ if (DEBUG_FP_WAKELOCK) {
+ Log.i(TAG, "fingerprint acquired, grabbing fp wakelock");
+ }
+ mHandler.postDelayed(mReleaseFingerprintWakeLockRunnable,
+ FINGERPRINT_WAKELOCK_TIMEOUT_MS);
+ if (mDozeScrimController.isPulsing()) {
+
+ // If we are waking the device up while we are pulsing the clock and the
+ // notifications would light up first, creating an unpleasant animation.
+ // Defer changing the screen brightness by forcing doze brightness on our window
+ // until the clock and the notifications are faded out.
+ mStatusBarWindowManager.setForceDozeBrightness(true);
+ }
+ }
+ }
+
+ @Override
+ public void onFingerprintAuthenticated(int userId) {
+ boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
+ mMode = calculateMode();
+ if (!wasDeviceInteractive) {
+ if (DEBUG_FP_WAKELOCK) {
+ Log.i(TAG, "fp wakelock: Authenticated, waking up...");
+ }
+ mPowerManager.wakeUp(SystemClock.uptimeMillis());
+ }
+ releaseFingerprintWakeLock();
+ switch (mMode) {
+ case MODE_DISMISS_BOUNCER:
+ mStatusBarKeyguardViewManager.notifyKeyguardAuthenticated(
+ false /* strongAuth */);
+ break;
+ case MODE_UNLOCK:
+ case MODE_SHOW_BOUNCER:
+ if (!wasDeviceInteractive) {
+ mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
+ }
+ mStatusBarKeyguardViewManager.animateCollapsePanels(
+ FINGERPRINT_COLLAPSE_SPEEDUP_FACTOR);
+ break;
+ case MODE_WAKE_AND_UNLOCK_PULSING:
+ mPhoneStatusBar.updateMediaMetaData(false /* metaDataChanged */);
+ // Fall through.
+ case MODE_WAKE_AND_UNLOCK:
+ mStatusBarWindowManager.setStatusBarFocusable(false);
+ mDozeScrimController.abortPulsing();
+ mKeyguardViewMediator.onWakeAndUnlocking();
+ mScrimController.setWakeAndUnlocking();
+ if (mPhoneStatusBar.getNavigationBarView() != null) {
+ mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
+ }
+ break;
+ case MODE_ONLY_WAKE:
+ case MODE_NONE:
+ break;
+ }
+ if (mMode != MODE_WAKE_AND_UNLOCK_PULSING) {
+ mStatusBarWindowManager.setForceDozeBrightness(false);
+ }
+ mPhoneStatusBar.notifyFpAuthModeChanged();
+ }
+
+ public int getMode() {
+ return mMode;
+ }
+
+ private int calculateMode() {
+ boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithFingerprintAllowed();
+ if (!mUpdateMonitor.isDeviceInteractive()) {
+ if (!mStatusBarKeyguardViewManager.isShowing()) {
+ return MODE_ONLY_WAKE;
+ } else if (mDozeScrimController.isPulsing() && unlockingAllowed) {
+ return MODE_WAKE_AND_UNLOCK_PULSING;
+ } else if (unlockingAllowed) {
+ return MODE_WAKE_AND_UNLOCK;
+ } else {
+ return MODE_SHOW_BOUNCER;
+ }
+ }
+ if (mStatusBarKeyguardViewManager.isShowing()) {
+ if (mStatusBarKeyguardViewManager.isBouncerShowing() && unlockingAllowed) {
+ return MODE_DISMISS_BOUNCER;
+ } else if (unlockingAllowed) {
+ return MODE_UNLOCK;
+ } else {
+ return MODE_SHOW_BOUNCER;
+ }
+ }
+ return MODE_NONE;
+ }
+
+ @Override
+ public void onFingerprintAuthFailed() {
+ cleanup();
+ }
+
+ @Override
+ public void onFingerprintError(int msgId, String errString) {
+ cleanup();
+ }
+
+ private void cleanup() {
+ mMode = MODE_NONE;
+ releaseFingerprintWakeLock();
+ mStatusBarWindowManager.setForceDozeBrightness(false);
+ mPhoneStatusBar.notifyFpAuthModeChanged();
+ }
+
+ public void startKeyguardFadingAway() {
+
+ // Disable brightness override when the ambient contents are fully invisible.
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mStatusBarWindowManager.setForceDozeBrightness(false);
+ }
+ }, PhoneStatusBar.FADE_KEYGUARD_DURATION_PULSING);
+ }
+
+ public void finishKeyguardFadingAway() {
+ mMode = MODE_NONE;
+ if (mPhoneStatusBar.getNavigationBarView() != null) {
+ mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
+ }
+ mPhoneStatusBar.notifyFpAuthModeChanged();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 10019f9..60ebfdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -86,9 +86,9 @@
mContext = context;
mCallback = callback;
initIcons();
- updateIcon(mLeftIcon, 0.0f, mLeftIcon.getRestingAlpha(), false, false, true);
- updateIcon(mCenterIcon, 0.0f, mCenterIcon.getRestingAlpha(), false, false, true);
- updateIcon(mRightIcon, 0.0f, mRightIcon.getRestingAlpha(), false, false, true);
+ updateIcon(mLeftIcon, 0.0f, mLeftIcon.getRestingAlpha(), false, false, true, false);
+ updateIcon(mCenterIcon, 0.0f, mCenterIcon.getRestingAlpha(), false, false, true, false);
+ updateIcon(mRightIcon, 0.0f, mRightIcon.getRestingAlpha(), false, false, true, false);
initDimens();
}
@@ -144,9 +144,7 @@
} else {
mTouchSlopExeeded = false;
}
- mCallback.onSwipingStarted(targetView == mRightIcon);
- mSwipingInProgress = true;
- mTargetedView = targetView;
+ startSwiping(targetView);
mInitialTouchX = x;
mInitialTouchY = y;
mTranslationOnDown = mTranslation;
@@ -192,6 +190,12 @@
return true;
}
+ private void startSwiping(View targetView) {
+ mCallback.onSwipingStarted(targetView == mRightIcon);
+ mSwipingInProgress = true;
+ mTargetedView = targetView;
+ }
+
private View getIconAtPosition(float x, float y) {
if (leftSwipePossible() && isOnIcon(mLeftIcon, x, y)) {
return mLeftIcon;
@@ -324,7 +328,7 @@
boolean velIsInWrongDirection = vel * mTranslation < 0;
snapBack |= Math.abs(vel) > mMinFlingVelocity && velIsInWrongDirection;
vel = snapBack ^ velIsInWrongDirection ? 0 : vel;
- fling(vel, snapBack || forceSnapBack);
+ fling(vel, snapBack || forceSnapBack, mTranslation < 0);
}
private boolean isBelowFalsingThreshold() {
@@ -336,9 +340,8 @@
return (int) (mMinTranslationAmount * factor);
}
- private void fling(float vel, final boolean snapBack) {
- float target = mTranslation < 0
- ? -mCallback.getMaxTranslationDistance()
+ private void fling(float vel, final boolean snapBack, boolean right) {
+ float target = right ? -mCallback.getMaxTranslationDistance()
: mCallback.getMaxTranslationDistance();
target = snapBack ? 0 : target;
@@ -352,8 +355,8 @@
});
animator.addListener(mFlingEndListener);
if (!snapBack) {
- startFinishingCircleAnimation(vel * 0.375f, mAnimationEndRunnable);
- mCallback.onAnimationToSideStarted(mTranslation < 0, mTranslation, vel);
+ startFinishingCircleAnimation(vel * 0.375f, mAnimationEndRunnable, right);
+ mCallback.onAnimationToSideStarted(right, mTranslation, vel);
} else {
reset(true);
}
@@ -364,8 +367,9 @@
}
}
- private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable) {
- KeyguardAffordanceView targetView = mTranslation > 0 ? mLeftIcon : mRightIcon;
+ private void startFinishingCircleAnimation(float velocity, Runnable mAnimationEndRunnable,
+ boolean right) {
+ KeyguardAffordanceView targetView = right ? mRightIcon : mLeftIcon;
targetView.finishAnimation(velocity, mAnimationEndRunnable);
}
@@ -383,19 +387,20 @@
fadeOutAlpha = Math.max(fadeOutAlpha, 0.0f);
boolean animateIcons = isReset && animateReset;
+ boolean forceNoCircleAnimation = isReset && !animateReset;
float radius = getRadiusFromTranslation(absTranslation);
boolean slowAnimation = isReset && isBelowFalsingThreshold();
if (!isReset) {
updateIcon(targetView, radius, alpha + fadeOutAlpha * targetView.getRestingAlpha(),
- false, false, false);
+ false, false, false, false);
} else {
updateIcon(targetView, 0.0f, fadeOutAlpha * targetView.getRestingAlpha(),
- animateIcons, slowAnimation, false);
+ animateIcons, slowAnimation, false, forceNoCircleAnimation);
}
updateIcon(otherView, 0.0f, fadeOutAlpha * otherView.getRestingAlpha(),
- animateIcons, slowAnimation, false);
+ animateIcons, slowAnimation, false, forceNoCircleAnimation);
updateIcon(mCenterIcon, 0.0f, fadeOutAlpha * mCenterIcon.getRestingAlpha(),
- animateIcons, slowAnimation, false);
+ animateIcons, slowAnimation, false, forceNoCircleAnimation);
mTranslation = translation;
}
@@ -431,16 +436,21 @@
public void animateHideLeftRightIcon() {
cancelAnimation();
- updateIcon(mRightIcon, 0f, 0f, true, false, false);
- updateIcon(mLeftIcon, 0f, 0f, true, false, false);
+ updateIcon(mRightIcon, 0f, 0f, true, false, false, false);
+ updateIcon(mLeftIcon, 0f, 0f, true, false, false, false);
}
private void updateIcon(KeyguardAffordanceView view, float circleRadius, float alpha,
- boolean animate, boolean slowRadiusAnimation, boolean force) {
+ boolean animate, boolean slowRadiusAnimation, boolean force,
+ boolean forceNoCircleAnimation) {
if (view.getVisibility() != View.VISIBLE && !force) {
return;
}
- view.setCircleRadius(circleRadius, slowRadiusAnimation);
+ if (forceNoCircleAnimation) {
+ view.setCircleRadiusWithoutAnimation(circleRadius);
+ } else {
+ view.setCircleRadius(circleRadius, slowRadiusAnimation);
+ }
updateIconAlpha(view, alpha, animate);
}
@@ -503,8 +513,36 @@
mMotionCancelled = true;
if (mSwipingInProgress) {
mCallback.onSwipingAborted();
+ mSwipingInProgress = false;
}
- mSwipingInProgress = false;
+ }
+
+ public boolean isSwipingInProgress() {
+ return mSwipingInProgress;
+ }
+
+ public void launchAffordance(boolean animate, boolean left) {
+ if (mSwipingInProgress) {
+ // We don't want to mess with the state if the user is actually swiping already.
+ return;
+ }
+ KeyguardAffordanceView targetView = left ? mLeftIcon : mRightIcon;
+ KeyguardAffordanceView otherView = left ? mRightIcon : mLeftIcon;
+ startSwiping(targetView);
+ if (animate) {
+ fling(0, false, !left);
+ updateIcon(otherView, 0.0f, 0, true, false, true, false);
+ updateIcon(mCenterIcon, 0.0f, 0, true, false, true, false);
+ } else {
+ mCallback.onAnimationToSideStarted(!left, mTranslation, 0);
+ mTranslation = left ? mCallback.getMaxTranslationDistance()
+ : mCallback.getMaxTranslationDistance();
+ updateIcon(mCenterIcon, 0.0f, 0.0f, false, false, true, false);
+ updateIcon(otherView, 0.0f, 0.0f, false, false, true, false);
+ targetView.instantFinishAnimation();
+ mFlingEndListener.onAnimationEnd(null);
+ mAnimationEndRunnable.run();
+ }
}
public interface Callback {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 1a2fa97..d30411a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -29,7 +29,6 @@
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
-import android.hardware.fingerprint.FingerprintManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
@@ -81,12 +80,11 @@
private static final Intent SECURE_CAMERA_INTENT =
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- private static final Intent INSECURE_CAMERA_INTENT =
+ public static final Intent INSECURE_CAMERA_INTENT =
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
- private static final long TRANSIENT_FP_ERROR_TIMEOUT = 1300;
private KeyguardAffordanceView mCameraImageView;
private KeyguardAffordanceView mLeftAffordanceView;
@@ -528,7 +526,7 @@
return mCameraPreview;
}
- public KeyguardAffordanceView getLockIcon() {
+ public LockIcon getLockIcon() {
return mLockIcon;
}
@@ -613,21 +611,6 @@
}
};
- private final Runnable mTransientFpErrorClearRunnable = new Runnable() {
- @Override
- public void run() {
- mLockIcon.setTransientFpError(false);
- mIndicationController.hideTransientIndication();
- }
- };
-
- private final Runnable mHideTransientIndicationRunnable = new Runnable() {
- @Override
- public void run() {
- mIndicationController.hideTransientIndication();
- }
- };
-
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
@Override
@@ -646,12 +629,18 @@
}
@Override
- public void onKeyguardVisibilityChanged(boolean showing) {
- mLockIcon.update();
+ public void onScreenTurnedOn() {
+ mLockIcon.setScreenOn(true);
}
@Override
- public void onFingerprintAuthenticated(int userId, boolean wakeAndUnlocking) {
+ public void onScreenTurnedOff() {
+ mLockIcon.setScreenOn(false);
+ }
+
+ @Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ mLockIcon.update();
}
@Override
@@ -660,28 +649,8 @@
}
@Override
- public void onFingerprintHelp(int msgId, String helpString) {
- if (!KeyguardUpdateMonitor.getInstance(mContext).isUnlockingWithFingerprintAllowed()) {
- return;
- }
- mLockIcon.setTransientFpError(true);
- mIndicationController.showTransientIndication(helpString,
- getResources().getColor(R.color.system_warning_color, null));
- removeCallbacks(mTransientFpErrorClearRunnable);
- postDelayed(mTransientFpErrorClearRunnable, TRANSIENT_FP_ERROR_TIMEOUT);
- }
-
- @Override
- public void onFingerprintError(int msgId, String errString) {
- if (!KeyguardUpdateMonitor.getInstance(mContext).isUnlockingWithFingerprintAllowed()
- || msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED) {
- return;
- }
- // TODO: Go to bouncer if this is "too many attempts" (lockout) error.
- mIndicationController.showTransientIndication(errString,
- getResources().getColor(R.color.system_warning_color, null));
- removeCallbacks(mHideTransientIndicationRunnable);
- postDelayed(mHideTransientIndicationRunnable, 5000);
+ public void onStrongAuthStateChanged(int userId) {
+ mLockIcon.update();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index e9b2c61..893b352 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.phone;
import android.content.Context;
-import android.view.Choreographer;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@@ -27,6 +26,8 @@
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardHostView;
import com.android.keyguard.KeyguardSecurityView;
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.R;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.DejankUtils;
@@ -48,6 +49,13 @@
private ViewGroup mRoot;
private boolean mShowingSoon;
private int mBouncerPromptReason;
+ private KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onStrongAuthStateChanged(int userId) {
+ mBouncerPromptReason = mCallback.getBouncerPromptReason();
+ }
+ };
public KeyguardBouncer(Context context, ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils, StatusBarWindowManager windowManager,
@@ -57,6 +65,7 @@
mLockPatternUtils = lockPatternUtils;
mContainer = container;
mWindowManager = windowManager;
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallback);
}
public void show(boolean resetSecuritySelection) {
@@ -103,6 +112,10 @@
mKeyguardView.showPromptReason(reason);
}
+ public void showMessage(String message, int color) {
+ mKeyguardView.showMessage(message, color);
+ }
+
private void cancelShowRunnable() {
DejankUtils.removeCallbacks(mShowRunnable);
mShowingSoon = false;
@@ -244,8 +257,8 @@
return mKeyguardView.interceptMediaKey(event);
}
- public void notifyKeyguardAuthenticated() {
+ public void notifyKeyguardAuthenticated(boolean strongAuth) {
ensureView();
- mKeyguardView.finish();
+ mKeyguardView.finish(strongAuth);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 06d2fca..463abfc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -34,12 +34,6 @@
*/
public class LockIcon extends KeyguardAffordanceView {
- /**
- * Delay animations a bit when the screen just turned on as a heuristic to start them after
- * the screen has actually turned on.
- */
- private static final long ANIM_DELAY_AFTER_SCREEN_ON = 250;
-
private static final int STATE_LOCKED = 0;
private static final int STATE_LOCK_OPEN = 1;
private static final int STATE_FACE_UNLOCK = 2;
@@ -50,6 +44,8 @@
private boolean mLastDeviceInteractive;
private boolean mTransientFpError;
private boolean mDeviceInteractive;
+ private boolean mScreenOn;
+ private boolean mLastScreenOn;
private final TrustDrawable mTrustDrawable;
private final UnlockMethodCache mUnlockMethodCache;
private AccessibilityController mAccessibilityController;
@@ -88,6 +84,11 @@
update();
}
+ public void setScreenOn(boolean screenOn) {
+ mScreenOn = screenOn;
+ update();
+ }
+
public void update() {
boolean visible = isShown()
&& KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
@@ -96,20 +97,20 @@
} else {
mTrustDrawable.stop();
}
- if (!visible) {
- return;
- }
// TODO: Real icon for facelock.
int state = getState();
boolean anyFingerprintIcon = state == STATE_FINGERPRINT || state == STATE_FINGERPRINT_ERROR;
- if (state != mLastState || mDeviceInteractive != mLastDeviceInteractive) {
+ if (state != mLastState || mDeviceInteractive != mLastDeviceInteractive
+ || mScreenOn != mLastScreenOn) {
+ boolean isAnim = true;
int iconRes = getAnimationResForTransition(mLastState, state, mLastDeviceInteractive,
- mDeviceInteractive);
+ mDeviceInteractive, mLastScreenOn, mScreenOn);
if (iconRes == R.drawable.lockscreen_fingerprint_draw_off_animation) {
anyFingerprintIcon = true;
}
if (iconRes == -1) {
- iconRes = getIconForState(state);
+ iconRes = getIconForState(state, mScreenOn, mDeviceInteractive);
+ isAnim = false;
}
Drawable icon = mContext.getDrawable(iconRes);
final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
@@ -135,23 +136,12 @@
: R.string.accessibility_unlock_button);
setContentDescription(contentDescription);
mHasFingerPrintIcon = anyFingerprintIcon;
- if (animation != null) {
-
- // If we play the draw on animation, delay it by one frame when the screen is
- // actually turned on.
- if (iconRes == R.drawable.lockscreen_fingerprint_draw_on_animation) {
- postOnAnimationDelayed(new Runnable() {
- @Override
- public void run() {
- animation.start();
- }
- }, ANIM_DELAY_AFTER_SCREEN_ON);
- } else {
- animation.start();
- }
+ if (animation != null && isAnim) {
+ animation.start();
}
mLastState = state;
mLastDeviceInteractive = mDeviceInteractive;
+ mLastScreenOn = mScreenOn;
}
// Hide trust circle when fingerprint is running.
@@ -192,7 +182,7 @@
mAccessibilityController = accessibilityController;
}
- private int getIconForState(int state) {
+ private int getIconForState(int state, boolean screenOn, boolean deviceInteractive) {
switch (state) {
case STATE_LOCKED:
return R.drawable.ic_lock_24dp;
@@ -201,7 +191,11 @@
case STATE_FACE_UNLOCK:
return com.android.internal.R.drawable.ic_account_circle;
case STATE_FINGERPRINT:
- return R.drawable.ic_fingerprint;
+ // If screen is off and device asleep, use the draw on animation so the first frame
+ // gets drawn.
+ return screenOn && deviceInteractive
+ ? R.drawable.ic_fingerprint
+ : R.drawable.lockscreen_fingerprint_draw_on_animation;
case STATE_FINGERPRINT_ERROR:
return R.drawable.ic_fingerprint_error;
default:
@@ -209,8 +203,9 @@
}
}
- private int getAnimationResForTransition(int oldState, int newState, boolean oldScreenOn,
- boolean screenOn) {
+ private int getAnimationResForTransition(int oldState, int newState,
+ boolean oldDeviceInteractive, boolean deviceInteractive,
+ boolean oldScreenOn, boolean screenOn) {
if (oldState == STATE_FINGERPRINT && newState == STATE_FINGERPRINT_ERROR) {
return R.drawable.lockscreen_fingerprint_fp_to_error_state_animation;
} else if (oldState == STATE_FINGERPRINT_ERROR && newState == STATE_FINGERPRINT) {
@@ -218,7 +213,8 @@
} else if (oldState == STATE_FINGERPRINT && newState == STATE_LOCK_OPEN
&& !mUnlockMethodCache.isTrusted()) {
return R.drawable.lockscreen_fingerprint_draw_off_animation;
- } else if (newState == STATE_FINGERPRINT && !oldScreenOn && screenOn) {
+ } else if (newState == STATE_FINGERPRINT && (!oldScreenOn && screenOn && deviceInteractive
+ || screenOn && !oldDeviceInteractive && deviceInteractive)) {
return R.drawable.lockscreen_fingerprint_draw_on_animation;
} else {
return -1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 059ecee..cc31476 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -91,7 +91,8 @@
private OnVerticalChangedListener mOnVerticalChangedListener;
private boolean mIsLayoutRtl;
- private boolean mLayoutTransitionsEnabled;
+ private boolean mLayoutTransitionsEnabled = true;
+ private boolean mWakeAndUnlocking;
private class NavTransitionListener implements TransitionListener {
private boolean mBackTransitioning;
@@ -361,13 +362,19 @@
}
}
- public void setWakeAndUnlocking(boolean wakeAndUnlocking) {
- setUseFadingAnimations(wakeAndUnlocking);
- setLayoutTransitionsEnabled(!wakeAndUnlocking);
+ public void setLayoutTransitionsEnabled(boolean enabled) {
+ mLayoutTransitionsEnabled = enabled;
+ updateLayoutTransitionsEnabled();
}
- private void setLayoutTransitionsEnabled(boolean enabled) {
- mLayoutTransitionsEnabled = enabled;
+ public void setWakeAndUnlocking(boolean wakeAndUnlocking) {
+ setUseFadingAnimations(wakeAndUnlocking);
+ mWakeAndUnlocking = wakeAndUnlocking;
+ updateLayoutTransitionsEnabled();
+ }
+
+ private void updateLayoutTransitionsEnabled() {
+ boolean enabled = !mWakeAndUnlocking && mLayoutTransitionsEnabled;
ViewGroup navButtons = (ViewGroup) mCurrentView.findViewById(R.id.nav_buttons);
LayoutTransition lt = navButtons.getLayoutTransition();
if (lt != null) {
@@ -459,7 +466,7 @@
}
mCurrentView = mRotatedViews[rot];
mCurrentView.setVisibility(View.VISIBLE);
- setLayoutTransitionsEnabled(mLayoutTransitionsEnabled);
+ updateLayoutTransitionsEnabled();
getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 373abe5..5557f9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -203,6 +203,7 @@
private int mLastOrientation = -1;
private boolean mClosingWithAlphaFadeOut;
private boolean mHeadsUpAnimatingAway;
+ private boolean mLaunchingAffordance;
private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
@Override
@@ -488,7 +489,9 @@
mIsLaunchTransitionFinished = false;
mBlockTouches = false;
mUnlockIconActive = false;
- mAfforanceHelper.reset(true);
+ if (!mLaunchingAffordance) {
+ mAfforanceHelper.reset(false);
+ }
closeQs();
mStatusBar.dismissPopups();
mNotificationStackScroller.setOverScrollAmount(0f, true /* onTop */, false /* animate */,
@@ -918,7 +921,7 @@
}
private int getFalsingThreshold() {
- float factor = mStatusBar.isScreenOnComingFromTouch() ? 1.5f : 1.0f;
+ float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
return (int) (mQsFalsingThreshold * factor);
}
@@ -2075,7 +2078,7 @@
@Override
public float getAffordanceFalsingFactor() {
- return mStatusBar.isScreenOnComingFromTouch() ? 1.5f : 1.0f;
+ return mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
}
@Override
@@ -2388,4 +2391,41 @@
protected boolean isPanelVisibleBecauseOfHeadsUp() {
return mHeadsUpManager.hasPinnedHeadsUp() || mHeadsUpAnimatingAway;
}
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return !mDozing;
+ }
+
+ public void launchCamera(boolean animate) {
+ // If we are launching it when we are occluded already we don't want it to animate,
+ // nor setting these flags, since the occluded state doesn't change anymore, hence it's
+ // never reset.
+ if (!isFullyCollapsed()) {
+ mLaunchingAffordance = true;
+ setLaunchingAffordance(true);
+ } else {
+ animate = false;
+ }
+ mAfforanceHelper.launchAffordance(animate, getLayoutDirection() == LAYOUT_DIRECTION_RTL);
+ }
+
+ public void onAffordanceLaunchEnded() {
+ mLaunchingAffordance = false;
+ setLaunchingAffordance(false);
+ }
+
+ /**
+ * Set whether we are currently launching an affordance. This is currently only set when
+ * launched via a camera gesture.
+ */
+ private void setLaunchingAffordance(boolean launchingAffordance) {
+ getLeftIcon().setLaunchingAffordance(launchingAffordance);
+ getRightIcon().setLaunchingAffordance(launchingAffordance);
+ getCenterIcon().setLaunchingAffordance(launchingAffordance);
+ }
+
+ public boolean canCameraGestureBeLaunched() {
+ return !mAfforanceHelper.isSwipingInProgress();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 8b25e08..c6743e7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -382,7 +382,7 @@
|| forceCancel;
DozeLog.traceFling(expand, mTouchAboveFalsingThreshold,
mStatusBar.isFalsingThresholdNeeded(),
- mStatusBar.isScreenOnComingFromTouch());
+ mStatusBar.isWakeUpComingFromTouch());
// Log collapse gesture if on lock screen.
if (!expand && mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
float displayDensity = mStatusBar.getDisplayDensity();
@@ -411,7 +411,7 @@
}
private int getFalsingThreshold() {
- float factor = mStatusBar.isScreenOnComingFromTouch() ? 1.5f : 1.0f;
+ float factor = mStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
return (int) (mUnlockFalsingThreshold * factor);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 18cf95b..8063bbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -66,6 +66,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.Vibrator;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.RankingMap;
@@ -99,6 +100,7 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.keyguard.KeyguardHostView.OnDismissAction;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.BatteryMeterView;
import com.android.systemui.DemoMode;
@@ -153,6 +155,7 @@
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout;
import com.android.systemui.statusbar.stack.NotificationStackScrollLayout.OnChildLocationsChangedListener;
+import com.android.systemui.statusbar.stack.StackStateAnimator;
import com.android.systemui.statusbar.stack.StackViewState;
import com.android.systemui.volume.VolumeComponent;
@@ -230,6 +233,7 @@
public static final int FADE_KEYGUARD_START_DELAY = 100;
public static final int FADE_KEYGUARD_DURATION = 300;
+ public static final int FADE_KEYGUARD_DURATION_PULSING = 96;
/** Allow some time inbetween the long press for back and recents. */
private static final int LOCK_TO_APP_GESTURE_TOLERENCE = 200;
@@ -270,6 +274,7 @@
KeyguardMonitor mKeyguardMonitor;
BrightnessMirrorController mBrightnessMirrorController;
AccessibilityController mAccessibilityController;
+ FingerprintUnlockController mFingerprintUnlockController;
int mNaturalBarHeight = -1;
@@ -282,8 +287,8 @@
private StatusBarWindowManager mStatusBarWindowManager;
private UnlockMethodCache mUnlockMethodCache;
private DozeServiceHost mDozeServiceHost;
- private boolean mScreenOnComingFromTouch;
- private PointF mScreenOnTouchLocation;
+ private boolean mWakeUpComingFromTouch;
+ private PointF mWakeUpTouchLocation;
int mPixelFormat;
Object mQueueLock = new Object();
@@ -478,6 +483,9 @@
private Runnable mLaunchTransitionEndRunnable;
private boolean mLaunchTransitionFadingAway;
private ExpandableNotificationRow mDraggedDownRow;
+ private boolean mLaunchCameraOnScreenTurningOn;
+ private PowerManager.WakeLock mGestureWakeLock;
+ private Vibrator mVibrator;
// Fingerprint (as computed by getLoggingFingerprint() of the last logged state.
private int mLastLoggedStateFingerprint;
@@ -621,6 +629,7 @@
startKeyguard();
mDozeServiceHost = new DozeServiceHost();
+ KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mDozeServiceHost);
putComponent(DozeHost.class, mDozeServiceHost);
putComponent(PhoneStatusBar.class, this);
@@ -780,7 +789,8 @@
mKeyguardBottomArea.setAssistManager(mAssistManager);
mKeyguardIndicationController = new KeyguardIndicationController(mContext,
(KeyguardIndicationTextView) mStatusBarWindow.findViewById(
- R.id.keyguard_indication_text));
+ R.id.keyguard_indication_text),
+ mKeyguardBottomArea.getLockIcon());
mKeyguardBottomArea.setKeyguardIndicationController(mKeyguardIndicationController);
// set the inital view visibility
@@ -897,7 +907,9 @@
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mBroadcastReceiver.onReceive(mContext,
new Intent(pm.isScreenOn() ? Intent.ACTION_SCREEN_ON : Intent.ACTION_SCREEN_OFF));
-
+ mGestureWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK,
+ "GestureWakeLock");
+ mVibrator = mContext.getSystemService(Vibrator.class);
// receive broadcasts
IntentFilter filter = new IntentFilter();
@@ -1009,8 +1021,15 @@
private void startKeyguard() {
KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
+ mFingerprintUnlockController = new FingerprintUnlockController(mContext,
+ mStatusBarWindowManager, mDozeScrimController, keyguardViewMediator,
+ mScrimController, this);
mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
- mStatusBarWindow, mStatusBarWindowManager, mScrimController);
+ mStatusBarWindow, mStatusBarWindowManager, mScrimController,
+ mFingerprintUnlockController);
+ mKeyguardIndicationController.setStatusBarKeyguardViewManager(
+ mStatusBarKeyguardViewManager);
+ mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mKeyguardViewMediatorCallback = keyguardViewMediator.getViewMediatorCallback();
}
@@ -1681,7 +1700,9 @@
final boolean hasArtwork = artworkBitmap != null;
if ((hasArtwork || DEBUG_MEDIA_FAKE_ARTWORK)
- && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
+ && (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
+ && mFingerprintUnlockController.getMode()
+ != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
// time to show some art!
if (mBackdrop.getVisibility() != View.VISIBLE) {
mBackdrop.setVisibility(View.VISIBLE);
@@ -1736,31 +1757,40 @@
if (DEBUG_MEDIA) {
Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
}
- mBackdrop.animate()
- // Never let the alpha become zero - otherwise the RenderNode
- // won't draw anything and uninitialized memory will show through
- // if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in libhwui.
- .alpha(0.002f)
- .setInterpolator(mBackdropInterpolator)
- .setDuration(300)
- .setStartDelay(0)
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mBackdrop.setVisibility(View.GONE);
- mBackdropFront.animate().cancel();
- mBackdropBack.animate().cancel();
- mHandler.post(mHideBackdropFront);
- }
- });
- if (mKeyguardFadingAway) {
- mBackdrop.animate()
+ if (mFingerprintUnlockController.getMode()
+ == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
- // Make it disappear faster, as the focus should be on the activity behind.
- .setDuration(mKeyguardFadingAwayDuration / 2)
- .setStartDelay(mKeyguardFadingAwayDelay)
- .setInterpolator(mLinearInterpolator)
- .start();
+ // We are unlocking directly - no animation!
+ mBackdrop.setVisibility(View.GONE);
+ } else {
+ mBackdrop.animate()
+ // Never let the alpha become zero - otherwise the RenderNode
+ // won't draw anything and uninitialized memory will show through
+ // if mScrimSrcModeEnabled. Note that 0.001 is rounded down to 0 in
+ // libhwui.
+ .alpha(0.002f)
+ .setInterpolator(mBackdropInterpolator)
+ .setDuration(300)
+ .setStartDelay(0)
+ .withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ mBackdrop.setVisibility(View.GONE);
+ mBackdropFront.animate().cancel();
+ mBackdropBack.animate().cancel();
+ mHandler.post(mHideBackdropFront);
+ }
+ });
+ if (mKeyguardFadingAway) {
+ mBackdrop.animate()
+
+ // Make it disappear faster, as the focus should be on the activity
+ // behind.
+ .setDuration(mKeyguardFadingAwayDuration / 2)
+ .setStartDelay(mKeyguardFadingAwayDelay)
+ .setInterpolator(mLinearInterpolator)
+ .start();
+ }
}
}
}
@@ -1910,8 +1940,8 @@
return mNotificationPanel.isQsExpanded();
}
- public boolean isScreenOnComingFromTouch() {
- return mScreenOnComingFromTouch;
+ public boolean isWakeUpComingFromTouch() {
+ return mWakeUpComingFromTouch;
}
public boolean isFalsingThresholdNeeded() {
@@ -2423,8 +2453,12 @@
|| mStatusBarMode == MODE_LIGHTS_OUT_TRANSPARENT);
boolean allowLight = isTransparentBar && !mBatteryController.isPowerSave();
boolean light = (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
-
- mIconController.setIconsDark(allowLight && light);
+ boolean animate = mFingerprintUnlockController == null
+ || (mFingerprintUnlockController.getMode()
+ != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING
+ && mFingerprintUnlockController.getMode()
+ != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK);
+ mIconController.setIconsDark(allowLight && light, animate);
}
// restore the recents bit
if (wasRecentsVisible) {
@@ -2471,7 +2505,7 @@
private void checkBarMode(int mode, int windowState, BarTransitions transitions,
boolean noAnimation) {
final boolean powerSave = mBatteryController.isPowerSave();
- final boolean anim = !noAnimation && (mScreenOn == null || mScreenOn)
+ final boolean anim = !noAnimation && mDeviceInteractive
&& windowState != WINDOW_STATE_HIDDEN && !powerSave;
if (powerSave && getBarState() == StatusBarState.SHADE) {
mode = MODE_WARNING;
@@ -2879,14 +2913,12 @@
}
}
else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
- mScreenOn = false;
notifyNavigationBarScreenOn(false);
notifyHeadsUpScreenOff();
finishBarAnimations();
resetUserExpandedStates();
}
else if (Intent.ACTION_SCREEN_ON.equals(action)) {
- mScreenOn = true;
notifyNavigationBarScreenOn(true);
}
}
@@ -3340,7 +3372,7 @@
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
setBarState(StatusBarState.KEYGUARD);
updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
- if (!mScreenOnFromKeyguard) {
+ if (!mDeviceInteractive) {
// If the screen is off already, we need to disable touch events because these might
// collapse the panel after we expanded it, and thus we would end up with a blank
@@ -3359,6 +3391,8 @@
private void onLaunchTransitionFadingEnded() {
mNotificationPanel.setAlpha(1.0f);
+ mNotificationPanel.onAffordanceLaunchEnded();
+ releaseGestureWakeLock();
runLaunchTransitionEndRunnable();
mLaunchTransitionFadingAway = false;
mScrimController.forceHideScrims(false /* hide */);
@@ -3422,6 +3456,19 @@
}
/**
+ * Fades the content of the Keyguard while we are dozing and makes it invisible when finished
+ * fading.
+ */
+ public void fadeKeyguardWhilePulsing() {
+ mNotificationPanel.animate()
+ .alpha(0f)
+ .setStartDelay(0)
+ .setDuration(FADE_KEYGUARD_DURATION_PULSING)
+ .setInterpolator(ScrimController.KEYGUARD_FADE_OUT_INTERPOLATOR)
+ .start();
+ }
+
+ /**
* Starts the timeout when we try to start the affordances on Keyguard. We usually rely that
* Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen
* because the launched app crashed or something else went wrong.
@@ -3433,6 +3480,8 @@
private void onLaunchTransitionTimeout() {
Log.w(TAG, "Launch transition: Timeout!");
+ mNotificationPanel.onAffordanceLaunchEnded();
+ releaseGestureWakeLock();
mNotificationPanel.resetViews();
}
@@ -3455,11 +3504,24 @@
setBarState(StatusBarState.SHADE);
if (mLeaveOpenOnKeyguardHide) {
mLeaveOpenOnKeyguardHide = false;
- mNotificationPanel.animateToFullShade(calculateGoingToFullShadeDelay());
+ long delay = calculateGoingToFullShadeDelay();
+ mNotificationPanel.animateToFullShade(delay);
if (mDraggedDownRow != null) {
mDraggedDownRow.setUserLocked(false);
mDraggedDownRow = null;
}
+
+ // Disable layout transitions in navbar for this transition because the load is just
+ // too heavy for the CPU and GPU on any device.
+ if (mNavigationBarView != null) {
+ mNavigationBarView.setLayoutTransitionsEnabled(false);
+ mNavigationBarView.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mNavigationBarView.setLayoutTransitionsEnabled(true);
+ }
+ }, delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
+ }
} else {
instantCollapseNotificationPanel();
}
@@ -3471,9 +3533,18 @@
mQSPanel.refreshAllTiles();
}
mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
+ releaseGestureWakeLock();
+ mNotificationPanel.onAffordanceLaunchEnded();
+ mNotificationPanel.setAlpha(1f);
return staying;
}
+ private void releaseGestureWakeLock() {
+ if (mGestureWakeLock.isHeld()) {
+ mGestureWakeLock.release();
+ }
+ }
+
public long calculateGoingToFullShadeDelay() {
return mKeyguardFadingAwayDelay + mKeyguardFadingAwayDuration;
}
@@ -3560,9 +3631,14 @@
private void updateDozingState() {
boolean animate = !mDozing && mDozeScrimController.isPulsing();
mNotificationPanel.setDozing(mDozing, animate);
- mStackScroller.setDark(mDozing, animate, mScreenOnTouchLocation);
+ mStackScroller.setDark(mDozing, animate, mWakeUpTouchLocation);
mScrimController.setDozing(mDozing);
- mDozeScrimController.setDozing(mDozing, animate);
+
+ // Immediately abort the dozing from the doze scrim controller in case of wake-and-unlock
+ // for pulsing so the Keyguard fade-out animation scrim can take over.
+ mDozeScrimController.setDozing(mDozing &&
+ mFingerprintUnlockController.getMode()
+ != FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING, animate);
}
public void updateStackScrollerState(boolean goingToFullShade) {
@@ -3593,6 +3669,11 @@
return mState == StatusBarState.KEYGUARD && mStatusBarKeyguardViewManager.onMenuPressed();
}
+ public void endAffordanceLaunch() {
+ releaseGestureWakeLock();
+ mNotificationPanel.onAffordanceLaunchEnded();
+ }
+
public boolean onBackPressed() {
if (mStatusBarKeyguardViewManager.onBackPressed()) {
return true;
@@ -3613,7 +3694,7 @@
}
public boolean onSpacePressed() {
- if (mScreenOn != null && mScreenOn
+ if (mDeviceInteractive
&& (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)) {
animateCollapsePanels(
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL /* flags */, true /* force */);
@@ -3823,16 +3904,19 @@
disable(mDisabledUnmodified1, mDisabledUnmodified2, true /* animate */);
}
- public void onScreenTurnedOff() {
- mScreenOnFromKeyguard = false;
- mScreenOnComingFromTouch = false;
- mScreenOnTouchLocation = null;
+ public void onFinishedGoingToSleep() {
+ mNotificationPanel.onAffordanceLaunchEnded();
+ releaseGestureWakeLock();
+ mLaunchCameraOnScreenTurningOn = false;
+ mDeviceInteractive = false;
+ mWakeUpComingFromTouch = false;
+ mWakeUpTouchLocation = null;
mStackScroller.setAnimationsEnabled(false);
updateVisibleToUser();
}
- public void onScreenTurnedOn() {
- mScreenOnFromKeyguard = true;
+ public void onStartedWakingUp() {
+ mDeviceInteractive = true;
mStackScroller.setAnimationsEnabled(true);
mNotificationPanel.setTouchDisabled(false);
updateVisibleToUser();
@@ -3840,6 +3924,18 @@
public void onScreenTurningOn() {
mNotificationPanel.onScreenTurningOn();
+ if (mLaunchCameraOnScreenTurningOn) {
+ mNotificationPanel.launchCamera(false);
+ mLaunchCameraOnScreenTurningOn = false;
+ }
+ }
+
+ private void vibrateForCameraGesture() {
+ mVibrator.vibrate(1000L);
+ }
+
+ public void onScreenTurnedOn() {
+ mDozeScrimController.onScreenTurnedOn();
}
/**
@@ -3956,8 +4052,8 @@
if (mDozing && mDozeScrimController.isPulsing()) {
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
pm.wakeUp(time, "com.android.systemui:NODOZE");
- mScreenOnComingFromTouch = true;
- mScreenOnTouchLocation = new PointF(event.getX(), event.getY());
+ mWakeUpComingFromTouch = true;
+ mWakeUpTouchLocation = new PointF(event.getX(), event.getY());
mNotificationPanel.setTouchDisabled(false);
mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
}
@@ -3982,8 +4078,9 @@
public void appTransitionStarting(long startTime, long duration) {
// Use own timings when Keyguard is going away, see keyguardGoingAway and
- // setKeyguardFadingAway
- if (!mKeyguardFadingAway) {
+ // setKeyguardFadingAway. When duration is 0, skip this one because no animation is really
+ // playing.
+ if (!mKeyguardFadingAway && duration > 0) {
mIconController.appTransitionStarting(startTime, duration);
}
if (mIconPolicy != null) {
@@ -3991,8 +4088,48 @@
}
}
+ @Override
+ public void onCameraLaunchGestureDetected() {
+ if (!mNotificationPanel.canCameraGestureBeLaunched()) {
+ return;
+ }
+ if (!mDeviceInteractive) {
+ PowerManager pm = mContext.getSystemService(PowerManager.class);
+ pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:CAMERA_GESTURE");
+ mStatusBarKeyguardViewManager.notifyDeviceWakeUpRequested();
+ }
+ vibrateForCameraGesture();
+ if (!mStatusBarKeyguardViewManager.isShowing()) {
+ startActivity(KeyguardBottomAreaView.INSECURE_CAMERA_INTENT,
+ true /* dismissShade */);
+ } else {
+ if (!mDeviceInteractive) {
+ // Avoid flickering of the scrim when we instant launch the camera and the bouncer
+ // comes on.
+ mScrimController.dontAnimateBouncerChangesUntilNextFrame();
+ mGestureWakeLock.acquire(LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
+ }
+ if (mStatusBarKeyguardViewManager.isScreenTurnedOn()) {
+ mNotificationPanel.launchCamera(mDeviceInteractive /* animate */);
+ } else {
+ // We need to defer the camera launch until the screen comes on, since otherwise
+ // we will dismiss us too early since we are waiting on an activity to be drawn and
+ // incorrectly get notified because of the screen on event (which resumes and pauses
+ // some activities)
+ mLaunchCameraOnScreenTurningOn = true;
+ }
+ }
+ }
+
+ public void notifyFpAuthModeChanged() {
+ updateDozing();
+ }
+
private void updateDozing() {
- mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD;
+ // When in wake-and-unlock while pulsing, keep dozing state until fully unlocked.
+ mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD
+ || mFingerprintUnlockController.getMode()
+ == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
updateDozingState();
}
@@ -4022,7 +4159,7 @@
}
}
- private final class DozeServiceHost implements DozeHost {
+ private final class DozeServiceHost extends KeyguardUpdateMonitorCallback implements DozeHost {
// Amount of time to allow to update the time shown on the screen before releasing
// the wakelock. This timeout is design to compensate for the fact that we don't
// currently have a way to know when time display contents have actually been
@@ -4096,6 +4233,12 @@
}
@Override
+ public boolean isPulsingBlocked() {
+ return mFingerprintUnlockController.getMode()
+ == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK;
+ }
+
+ @Override
public boolean isNotificationLightOn() {
return mNotificationLightOn;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 1a35500..b9e9292 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -22,7 +22,6 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Color;
-import android.util.Log;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.animation.DecelerateInterpolator;
@@ -44,12 +43,15 @@
public class ScrimController implements ViewTreeObserver.OnPreDrawListener,
HeadsUpManager.OnHeadsUpChangedListener {
public static final long ANIMATION_DURATION = 220;
+ public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR
+ = new PathInterpolator(0f, 0, 0.7f, 1f);
private static final float SCRIM_BEHIND_ALPHA = 0.62f;
private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.45f;
private static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f;
private static final float SCRIM_IN_FRONT_ALPHA = 0.75f;
private static final int TAG_KEY_ANIM = R.id.scrim;
+ private static final int TAG_KEY_ANIM_TARGET = R.id.scrim_target;
private static final int TAG_HUN_START_ALPHA = R.id.hun_scrim_alpha_start;
private static final int TAG_HUN_END_ALPHA = R.id.hun_scrim_alpha_end;
@@ -71,9 +73,7 @@
private long mDurationOverride = -1;
private long mAnimationDelay;
private Runnable mOnAnimationFinished;
- private boolean mAnimationStarted;
private final Interpolator mInterpolator = new DecelerateInterpolator();
- private final Interpolator mKeyguardFadeOutInterpolator = new PathInterpolator(0f, 0, 0.7f, 1f);
private BackDropView mBackDropView;
private boolean mScrimSrcEnabled;
private boolean mDozing;
@@ -86,6 +86,8 @@
private float mTopHeadsUpDragAmount;
private View mDraggedHeadsUpView;
private boolean mForceHideScrims;
+ private boolean mSkipFirstFrame;
+ private boolean mDontAnimateBouncerChanges;
public ScrimController(ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
boolean scrimSrcEnabled) {
@@ -124,7 +126,7 @@
public void setBouncerShowing(boolean showing) {
mBouncerShowing = showing;
- mAnimateChange = !mExpanding;
+ mAnimateChange = !mExpanding && !mDontAnimateBouncerChanges;
scheduleUpdate();
}
@@ -133,19 +135,25 @@
scheduleUpdate();
}
- public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished) {
+ public void animateKeyguardFadingOut(long delay, long duration, Runnable onAnimationFinished,
+ boolean skipFirstFrame) {
mWakeAndUnlocking = false;
mAnimateKeyguardFadingOut = true;
mDurationOverride = duration;
mAnimationDelay = delay;
mAnimateChange = true;
+ mSkipFirstFrame = skipFirstFrame;
mOnAnimationFinished = onAnimationFinished;
scheduleUpdate();
+
+ // No need to wait for the next frame to be drawn for this case - onPreDraw will execute
+ // the changes we just scheduled.
+ onPreDraw();
}
public void abortKeyguardFadingOut() {
if (mAnimateKeyguardFadingOut) {
- endAnimateKeyguardFadingOut();
+ endAnimateKeyguardFadingOut(true /* force */);
}
}
@@ -198,8 +206,13 @@
// During wake and unlock, we first hide everything behind a black scrim, which then
// gets faded out from animateKeyguardFadingOut.
- setScrimInFrontColor(1f);
- setScrimBehindColor(0f);
+ if (mDozing) {
+ setScrimInFrontColor(0f);
+ setScrimBehindColor(1f);
+ } else {
+ setScrimInFrontColor(1f);
+ setScrimBehindColor(0f);
+ }
} else if (!mKeyguardShowing && !mBouncerShowing) {
updateScrimNormal();
setScrimInFrontColor(0);
@@ -258,10 +271,14 @@
}
private void setScrimColor(View scrim, float alpha) {
- Object runningAnim = scrim.getTag(TAG_KEY_ANIM);
- if (runningAnim instanceof ValueAnimator) {
- ((ValueAnimator) runningAnim).cancel();
- scrim.setTag(TAG_KEY_ANIM, null);
+ ValueAnimator runningAnim = (ValueAnimator) scrim.getTag(TAG_KEY_ANIM);
+ Float target = (Float) scrim.getTag(TAG_KEY_ANIM_TARGET);
+ if (runningAnim != null && target != null) {
+ if (alpha != target) {
+ runningAnim.cancel();
+ } else {
+ return;
+ }
}
if (mAnimateChange) {
startScrimAnimation(scrim, alpha);
@@ -325,39 +342,51 @@
mOnAnimationFinished = null;
}
scrim.setTag(TAG_KEY_ANIM, null);
+ scrim.setTag(TAG_KEY_ANIM_TARGET, null);
}
});
anim.start();
+ if (mSkipFirstFrame) {
+ anim.setCurrentPlayTime(16);
+ }
scrim.setTag(TAG_KEY_ANIM, anim);
- mAnimationStarted = true;
+ scrim.setTag(TAG_KEY_ANIM_TARGET, target);
}
private Interpolator getInterpolator() {
- return mAnimateKeyguardFadingOut ? mKeyguardFadeOutInterpolator : mInterpolator;
+ return mAnimateKeyguardFadingOut ? KEYGUARD_FADE_OUT_INTERPOLATOR : mInterpolator;
}
@Override
public boolean onPreDraw() {
mScrimBehind.getViewTreeObserver().removeOnPreDrawListener(this);
mUpdatePending = false;
+ if (mDontAnimateBouncerChanges) {
+ mDontAnimateBouncerChanges = false;
+ }
updateScrims();
mDurationOverride = -1;
mAnimationDelay = 0;
+ mSkipFirstFrame = false;
// Make sure that we always call the listener even if we didn't start an animation.
- endAnimateKeyguardFadingOut();
- mAnimationStarted = false;
+ endAnimateKeyguardFadingOut(false /* force */);
return true;
}
- private void endAnimateKeyguardFadingOut() {
+ private void endAnimateKeyguardFadingOut(boolean force) {
mAnimateKeyguardFadingOut = false;
- if (!mAnimationStarted && mOnAnimationFinished != null) {
+ if ((force || (!isAnimating(mScrimInFront) && !isAnimating(mScrimBehind)))
+ && mOnAnimationFinished != null) {
mOnAnimationFinished.run();
mOnAnimationFinished = null;
}
}
+ private boolean isAnimating(View scrim) {
+ return scrim.getTag(TAG_KEY_ANIM) != null;
+ }
+
public void setBackDropView(BackDropView backDropView) {
mBackDropView = backDropView;
mBackDropView.setOnVisibilityChangedRunnable(new Runnable() {
@@ -471,4 +500,8 @@
mAnimateChange = false;
scheduleUpdate();
}
+
+ public void dontAnimateBouncerChangesUntilNextFrame() {
+ mDontAnimateBouncerChanges = true;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
index a1e9ece..18db5b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsButton.java
@@ -19,6 +19,7 @@
import android.animation.Animator.AnimatorListener;
import android.animation.ObjectAnimator;
import android.content.Context;
+import android.graphics.drawable.RippleDrawable;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 7ee47df..e9c4e49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -26,6 +26,7 @@
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.util.MathUtils;
import android.util.TypedValue;
@@ -184,6 +185,12 @@
}
});
requestCaptureValues();
+
+ // RenderThread is doing more harm than good when touching the header (to expand quick
+ // settings), so disable it for this view
+ ((RippleDrawable) getBackground()).setForceSoftware(true);
+ ((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
+ ((RippleDrawable) mSystemIconsSuperContainer.getBackground()).setForceSoftware(true);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 067e50e..5de1c13 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -335,8 +335,10 @@
}
}
- public void setIconsDark(boolean dark) {
- if (mTransitionPending) {
+ public void setIconsDark(boolean dark, boolean animate) {
+ if (!animate) {
+ setIconTintInternal(dark ? 1.0f : 0.0f);
+ } else if (mTransitionPending) {
deferIconTintChange(dark ? 1.0f : 0.0f);
} else if (mTransitionDeferring) {
animateIconTint(dark ? 1.0f : 0.0f,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 7dd3e7c..d0604c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -48,6 +48,8 @@
// with the appear animations of the PIN/pattern/password views.
private static final long NAV_BAR_SHOW_DELAY_BOUNCER = 320;
+ private static final long WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS = 200;
+
private static String TAG = "StatusBarKeyguardViewManager";
private final Context mContext;
@@ -56,6 +58,7 @@
private ViewMediatorCallback mViewMediatorCallback;
private PhoneStatusBar mPhoneStatusBar;
private ScrimController mScrimController;
+ private FingerprintUnlockController mFingerprintUnlockController;
private ViewGroup mContainer;
private StatusBarWindowManager mStatusBarWindowManager;
@@ -71,10 +74,8 @@
private boolean mLastOccluded;
private boolean mLastBouncerShowing;
private boolean mLastBouncerDismissible;
- private boolean mLastDeferScrimFadeOut;
private OnDismissAction mAfterKeyguardGoneAction;
private boolean mDeviceWillWakeUp;
- private boolean mWakeAndUnlocking;
private boolean mDeferScrimFadeOut;
public StatusBarKeyguardViewManager(Context context, ViewMediatorCallback callback,
@@ -86,11 +87,13 @@
public void registerStatusBar(PhoneStatusBar phoneStatusBar,
ViewGroup container, StatusBarWindowManager statusBarWindowManager,
- ScrimController scrimController) {
+ ScrimController scrimController,
+ FingerprintUnlockController fingerprintUnlockController) {
mPhoneStatusBar = phoneStatusBar;
mContainer = container;
mStatusBarWindowManager = statusBarWindowManager;
mScrimController = scrimController;
+ mFingerprintUnlockController = fingerprintUnlockController;
mBouncer = new KeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils,
mStatusBarWindowManager, container);
}
@@ -162,28 +165,33 @@
public void onFinishedGoingToSleep() {
mDeviceInteractive = false;
- mPhoneStatusBar.onScreenTurnedOff();
+ mPhoneStatusBar.onFinishedGoingToSleep();
mBouncer.onScreenTurnedOff();
}
public void onStartedWakingUp() {
mDeviceInteractive = true;
mDeviceWillWakeUp = false;
- mPhoneStatusBar.onScreenTurnedOn();
+ mPhoneStatusBar.onStartedWakingUp();
}
public void onScreenTurningOn() {
mPhoneStatusBar.onScreenTurningOn();
}
+ public boolean isScreenTurnedOn() {
+ return mScreenTurnedOn;
+ }
+
public void onScreenTurnedOn() {
mScreenTurnedOn = true;
- mWakeAndUnlocking = false;
if (mDeferScrimFadeOut) {
mDeferScrimFadeOut = false;
- animateScrimControllerKeyguardFadingOut(0, 200);
+ animateScrimControllerKeyguardFadingOut(0, WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS,
+ true /* skipFirstFrame */);
updateStates();
}
+ mPhoneStatusBar.onScreenTurnedOn();
}
public void onScreenTurnedOff() {
@@ -260,7 +268,8 @@
updateStates();
mScrimController.animateKeyguardFadingOut(
PhoneStatusBar.FADE_KEYGUARD_START_DELAY,
- PhoneStatusBar.FADE_KEYGUARD_DURATION, null);
+ PhoneStatusBar.FADE_KEYGUARD_DURATION, null,
+ false /* skipFirstFrame */);
}
}, new Runnable() {
@Override
@@ -272,18 +281,43 @@
}
});
} else {
- mPhoneStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
- boolean staying = mPhoneStatusBar.hideKeyguard();
- if (!staying) {
+ if (mFingerprintUnlockController.getMode()
+ == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) {
+ mFingerprintUnlockController.startKeyguardFadingAway();
+ mPhoneStatusBar.setKeyguardFadingAway(startTime, 0, 240);
mStatusBarWindowManager.setKeyguardFadingAway(true);
- if (mWakeAndUnlocking && !mScreenTurnedOn) {
- mDeferScrimFadeOut = true;
- } else {
- animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration);
- }
+ mPhoneStatusBar.fadeKeyguardWhilePulsing();
+ animateScrimControllerKeyguardFadingOut(0, 240, new Runnable() {
+ @Override
+ public void run() {
+ mPhoneStatusBar.hideKeyguard();
+ }
+ }, false /* skipFirstFrame */);
} else {
- mScrimController.animateGoingToFullShade(delay, fadeoutDuration);
- mPhoneStatusBar.finishKeyguardFadingAway();
+ mFingerprintUnlockController.startKeyguardFadingAway();
+ mPhoneStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
+ boolean staying = mPhoneStatusBar.hideKeyguard();
+ if (!staying) {
+ mStatusBarWindowManager.setKeyguardFadingAway(true);
+ if (mFingerprintUnlockController.getMode()
+ == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) {
+ if (!mScreenTurnedOn) {
+ mDeferScrimFadeOut = true;
+ } else {
+
+ // Screen is already on, don't defer with fading out.
+ animateScrimControllerKeyguardFadingOut(0,
+ WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS,
+ true /* skipFirstFrame */);
+ }
+ } else {
+ animateScrimControllerKeyguardFadingOut(delay, fadeoutDuration,
+ false /* skipFirstFrame */);
+ }
+ } else {
+ mScrimController.animateGoingToFullShade(delay, fadeoutDuration);
+ mPhoneStatusBar.finishKeyguardFadingAway();
+ }
}
mStatusBarWindowManager.setKeyguardShowing(false);
mBouncer.hide(true /* destroyView */);
@@ -291,24 +325,31 @@
executeAfterKeyguardGoneAction();
updateStates();
}
-
}
- private void animateScrimControllerKeyguardFadingOut(long delay, long duration) {
+ private void animateScrimControllerKeyguardFadingOut(long delay, long duration,
+ boolean skipFirstFrame) {
+ animateScrimControllerKeyguardFadingOut(delay, duration, null /* endRunnable */,
+ skipFirstFrame);
+ }
+
+ private void animateScrimControllerKeyguardFadingOut(long delay, long duration,
+ final Runnable endRunnable, boolean skipFirstFrame) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "Fading out", 0);
mScrimController.animateKeyguardFadingOut(delay, duration, new Runnable() {
@Override
public void run() {
+ if (endRunnable != null) {
+ endRunnable.run();
+ }
mStatusBarWindowManager.setKeyguardFadingAway(false);
mPhoneStatusBar.finishKeyguardFadingAway();
- if (mPhoneStatusBar.getNavigationBarView() != null) {
- mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(false);
- }
+ mFingerprintUnlockController.finishKeyguardFadingAway();
WindowManagerGlobal.getInstance().trimMemory(
ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "Fading out", 0);
}
- });
+ }, skipFirstFrame);
}
private void executeAfterKeyguardGoneAction() {
@@ -348,6 +389,7 @@
*/
public boolean onBackPressed() {
if (mBouncer.isShowing()) {
+ mPhoneStatusBar.endAffordanceLaunch();
reset();
return true;
}
@@ -382,7 +424,6 @@
boolean occluded = mOccluded;
boolean bouncerShowing = mBouncer.isShowing();
boolean bouncerDismissible = !mBouncer.isFullscreenBouncer();
- boolean deferScrimFadeOut = mDeferScrimFadeOut;
if ((bouncerDismissible || !showing) != (mLastBouncerDismissible || !mLastShowing)
|| mFirstUpdate) {
@@ -393,16 +434,18 @@
}
}
- // Hide navigation bar on Keyguard but not on bouncer and also if we are deferring a scrim
- // fade out, i.e. we are waiting for the screen to have turned on.
- boolean navBarVisible = !deferScrimFadeOut && (!(showing && !occluded) || bouncerShowing);
- boolean lastNavBarVisible = !mLastDeferScrimFadeOut && (!(mLastShowing && !mLastOccluded)
- || mLastBouncerShowing);
+ boolean navBarVisible = (!(showing && !occluded) || bouncerShowing);
+ boolean lastNavBarVisible = (!(mLastShowing && !mLastOccluded) || mLastBouncerShowing);
if (navBarVisible != lastNavBarVisible || mFirstUpdate) {
if (mPhoneStatusBar.getNavigationBarView() != null) {
if (navBarVisible) {
- mContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable,
- getNavBarShowDelay());
+ long delay = getNavBarShowDelay();
+ if (delay == 0) {
+ mMakeNavigationBarVisibleRunnable.run();
+ } else {
+ mContainer.postOnAnimationDelayed(mMakeNavigationBarVisibleRunnable,
+ delay);
+ }
} else {
mContainer.removeCallbacks(mMakeNavigationBarVisibleRunnable);
mPhoneStatusBar.getNavigationBarView().setVisibility(View.GONE);
@@ -427,7 +470,6 @@
mFirstUpdate = false;
mLastShowing = showing;
mLastOccluded = occluded;
- mLastDeferScrimFadeOut = deferScrimFadeOut;
mLastBouncerShowing = bouncerShowing;
mLastBouncerDismissible = bouncerDismissible;
@@ -484,15 +526,11 @@
* Notifies that the user has authenticated by other means than using the bouncer, for example,
* fingerprint.
*/
- public void notifyKeyguardAuthenticated() {
- mBouncer.notifyKeyguardAuthenticated();
+ public void notifyKeyguardAuthenticated(boolean strongAuth) {
+ mBouncer.notifyKeyguardAuthenticated(strongAuth);
}
- public void setWakeAndUnlocking() {
- mWakeAndUnlocking = true;
- mScrimController.setWakeAndUnlocking();
- if (mPhoneStatusBar.getNavigationBarView() != null) {
- mPhoneStatusBar.getNavigationBarView().setWakeAndUnlocking(true);
- }
+ public void showBouncerMessage(String message, int color) {
+ mBouncer.showMessage(message, color);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index 038fefb..ccfa0dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -24,6 +24,7 @@
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
+import android.view.Window;
import android.view.WindowManager;
import com.android.keyguard.R;
@@ -47,13 +48,15 @@
private WindowManager.LayoutParams mLpChanged;
private int mBarHeight;
private final boolean mKeyguardScreenRotation;
-
+ private final float mScreenBrightnessDoze;
private final State mCurrentState = new State();
public StatusBarWindowManager(Context context) {
mContext = context;
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
mKeyguardScreenRotation = shouldEnableKeyguardScreenRotation();
+ mScreenBrightnessDoze = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_screenBrightnessDoze) / 255f;
}
private boolean shouldEnableKeyguardScreenRotation() {
@@ -182,6 +185,7 @@
applyInputFeatures(state);
applyFitsSystemWindows(state);
applyModalFlag(state);
+ applyBrightness(state);
if (mLp.copyFrom(mLpChanged) != 0) {
mWindowManager.updateViewLayout(mStatusBarView, mLp);
}
@@ -205,6 +209,14 @@
}
}
+ private void applyBrightness(State state) {
+ if (state.forceDozeBrightness) {
+ mLpChanged.screenBrightness = mScreenBrightnessDoze;
+ } else {
+ mLpChanged.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE;
+ }
+ }
+
public void setKeyguardShowing(boolean showing) {
mCurrentState.keyguardShowing = showing;
apply(mCurrentState);
@@ -279,6 +291,15 @@
apply(mCurrentState);
}
+ /**
+ * Set whether the screen brightness is forced to the value we use for doze mode by the status
+ * bar window.
+ */
+ public void setForceDozeBrightness(boolean forceDozeBrightness) {
+ mCurrentState.forceDozeBrightness = forceDozeBrightness;
+ apply(mCurrentState);
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("StatusBarWindowManager state:");
pw.println(mCurrentState);
@@ -297,6 +318,7 @@
boolean headsUpShowing;
boolean forceStatusBarVisible;
boolean forceCollapsed;
+ boolean forceDozeBrightness;
/**
* The {@link BaseStatusBar} state from the status bar.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
index bd537f7..a91cd51 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockMethodCache.java
@@ -132,7 +132,7 @@
}
@Override
- public void onFingerprintAuthenticated(int userId, boolean wakeAndUnlocking) {
+ public void onFingerprintAuthenticated(int userId) {
if (!mKeyguardUpdateMonitor.isUnlockingWithFingerprintAllowed()) {
return;
}
@@ -143,6 +143,11 @@
public void onFaceUnlockStateChanged(boolean running, int userId) {
update(false /* updateAlways */);
}
+
+ @Override
+ public void onStrongAuthStateChanged(int userId) {
+ update(false /* updateAlways */);
+ }
};
public boolean isTrustManaged() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
index 753a7f7..56f6564 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AnimationFilter.java
@@ -34,6 +34,7 @@
boolean hasDelays;
boolean hasGoToFullShadeEvent;
boolean hasDarkEvent;
+ boolean hasHeadsUpDisappearClickEvent;
int darkAnimationOriginIndex;
public AnimationFilter animateAlpha() {
@@ -106,6 +107,10 @@
hasDarkEvent = true;
darkAnimationOriginIndex = ev.darkAnimationOriginIndex;
}
+ if (ev.animationType == NotificationStackScrollLayout.AnimationEvent
+ .ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
+ hasHeadsUpDisappearClickEvent = true;
+ }
}
}
@@ -135,6 +140,7 @@
hasDelays = false;
hasGoToFullShadeEvent = false;
hasDarkEvent = false;
+ hasHeadsUpDisappearClickEvent = false;
darkAnimationOriginIndex =
NotificationStackScrollLayout.AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
}
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 23d9b9f..a7fe71e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -653,7 +653,7 @@
@Override
public float getFalsingThresholdFactor() {
- return mPhoneStatusBar.isScreenOnComingFromTouch() ? 1.5f : 1.0f;
+ return mPhoneStatusBar.isWakeUpComingFromTouch() ? 1.5f : 1.0f;
}
public View getChildAtPosition(MotionEvent ev) {
@@ -1915,7 +1915,9 @@
boolean onBottom = false;
boolean pinnedAndClosed = row.isPinned() && !mIsExpanded;
if (!mIsExpanded && !isHeadsUp) {
- type = AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
+ type = row.wasJustClicked()
+ ? AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+ : AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR;
} else {
StackViewState viewState = mCurrentStackScrollState.getViewStateForView(row);
if (viewState == null) {
@@ -3016,6 +3018,15 @@
.animateY()
.animateZ(),
+ // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+ new AnimationFilter()
+ .animateAlpha()
+ .animateHeight()
+ .animateTopInset()
+ .animateY()
+ .animateZ()
+ .hasDelays(),
+
// ANIMATION_TYPE_HEADS_UP_OTHER
new AnimationFilter()
.animateAlpha()
@@ -3087,6 +3098,9 @@
// ANIMATION_TYPE_HEADS_UP_DISAPPEAR
StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
+ // ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+ StackStateAnimator.ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
+
// ANIMATION_TYPE_HEADS_UP_OTHER
StackStateAnimator.ANIMATION_DURATION_STANDARD,
@@ -3110,8 +3124,9 @@
static final int ANIMATION_TYPE_GROUP_EXPANSION_CHANGED = 13;
static final int ANIMATION_TYPE_HEADS_UP_APPEAR = 14;
static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR = 15;
- static final int ANIMATION_TYPE_HEADS_UP_OTHER = 16;
- static final int ANIMATION_TYPE_EVERYTHING = 17;
+ static final int ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK = 16;
+ static final int ANIMATION_TYPE_HEADS_UP_OTHER = 17;
+ static final int ANIMATION_TYPE_EVERYTHING = 18;
static final int DARK_ANIMATION_ORIGIN_INDEX_ABOVE = -1;
static final int DARK_ANIMATION_ORIGIN_INDEX_BELOW = -2;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 82064a7..cf696a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -127,7 +127,7 @@
mCollapseSecondCardPadding = context.getResources().getDimensionPixelSize(
R.dimen.notification_collapse_second_card_padding);
mScaleDimmed = context.getResources().getDisplayMetrics().densityDpi
- >= DisplayMetrics.DENSITY_XXHIGH;
+ >= DisplayMetrics.DENSITY_420;
}
public boolean shouldScaleDimmed() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 97c7d30..b4ab48a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -54,6 +54,7 @@
public static final int ANIMATION_DELAY_PER_ELEMENT_DARK = 24;
public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE = 2;
public static final int DELAY_EFFECT_MAX_INDEX_DIFFERENCE_CHILDREN = 3;
+ public static final int ANIMATION_DELAY_HEADS_UP = 120;
private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
@@ -320,6 +321,9 @@
if (mAnimationFilter.hasGoToFullShadeEvent) {
return calculateDelayGoToFullShade(viewState);
}
+ if (mAnimationFilter.hasHeadsUpDisappearClickEvent) {
+ return ANIMATION_DELAY_HEADS_UP;
+ }
long minDelay = 0;
for (NotificationStackScrollLayout.AnimationEvent event : mNewEvents) {
long delayPerElement = ANIMATION_DELAY_PER_ELEMENT_INTERRUPTING;
@@ -890,7 +894,9 @@
mHeadsUpAppearChildren.add(changingView);
finalState.applyState(changingView, mTmpState);
} else if (event.animationType == NotificationStackScrollLayout
- .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR) {
+ .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR ||
+ event.animationType == NotificationStackScrollLayout
+ .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK) {
mHeadsUpDisappearChildren.add(changingView);
if (mHostLayout.indexOfChild(changingView) == -1) {
// This notification was actually removed, so we need to add it to the overlay
@@ -900,7 +906,11 @@
// We temporarily enable Y animations, the real filter will be combined
// afterwards anyway
mAnimationFilter.animateY = true;
- startViewAnimations(changingView, mTmpState, 0,
+ startViewAnimations(changingView, mTmpState,
+ event.animationType == NotificationStackScrollLayout
+ .AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
+ ? ANIMATION_DELAY_HEADS_UP
+ : 0,
ANIMATION_DURATION_HEADS_UP_DISAPPEAR);
mChildrenToClearFromOverlay.add(changingView);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 920b682..2587b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -171,6 +171,10 @@
}
@Override
+ public void onCameraLaunchGestureDetected() {
+ }
+
+ @Override
protected void updateHeadsUp(String key, NotificationData.Entry entry, boolean shouldInterrupt,
boolean alertAgain) {
}
diff --git a/packages/WallpaperCropper/AndroidManifest.xml b/packages/WallpaperCropper/AndroidManifest.xml
index 81d1085..e558d7e 100644
--- a/packages/WallpaperCropper/AndroidManifest.xml
+++ b/packages/WallpaperCropper/AndroidManifest.xml
@@ -21,7 +21,10 @@
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
- <application android:requiredForAllUsers="true">
+ <application
+ android:requiredForAllUsers="true"
+ android:largeHeap="true">
+
<activity
android:name="WallpaperCropActivity"
android:theme="@style/Theme.WallpaperCropper"
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 64b6134..666f2ff 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -9,7 +9,7 @@
java/com/android/server/EventLogTags.logtags \
java/com/android/server/am/EventLogTags.logtags
-LOCAL_JAVA_LIBRARIES := telephony-common
+LOCAL_JAVA_LIBRARIES := services.net telephony-common
LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 56ebed6..c373fb8 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1569,6 +1569,8 @@
if ("-h".equals(arg)) {
dumpHelp(pw);
return;
+ } else if ("-a".equals(arg)) {
+ // dump all data
} else if ("write-settings".equals(arg)) {
long token = Binder.clearCallingIdentity();
try {
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index b3b4651..2eeaec9 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -120,6 +120,7 @@
private int mLastBatteryVoltage;
private int mLastBatteryTemperature;
private boolean mLastBatteryLevelCritical;
+ private int mLastMaxChargingCurrent;
private int mInvalidCharger;
private int mLastInvalidCharger;
@@ -323,6 +324,7 @@
+ "chargerAcOnline=" + mBatteryProps.chargerAcOnline
+ ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline
+ ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline
+ + ", maxChargingCurrent" + mBatteryProps.maxChargingCurrent
+ ", batteryStatus=" + mBatteryProps.batteryStatus
+ ", batteryHealth=" + mBatteryProps.batteryHealth
+ ", batteryPresent=" + mBatteryProps.batteryPresent
@@ -353,6 +355,7 @@
mPlugType != mLastPlugType ||
mBatteryProps.batteryVoltage != mLastBatteryVoltage ||
mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
+ mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent ||
mInvalidCharger != mLastInvalidCharger)) {
if (mPlugType != mLastPlugType) {
@@ -479,6 +482,7 @@
mLastPlugType = mPlugType;
mLastBatteryVoltage = mBatteryProps.batteryVoltage;
mLastBatteryTemperature = mBatteryProps.batteryTemperature;
+ mLastMaxChargingCurrent = mBatteryProps.maxChargingCurrent;
mLastBatteryLevelCritical = mBatteryLevelCritical;
mLastInvalidCharger = mInvalidCharger;
}
@@ -503,17 +507,21 @@
intent.putExtra(BatteryManager.EXTRA_TEMPERATURE, mBatteryProps.batteryTemperature);
intent.putExtra(BatteryManager.EXTRA_TECHNOLOGY, mBatteryProps.batteryTechnology);
intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
+ intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent);
if (DEBUG) {
Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. level:" + mBatteryProps.batteryLevel +
", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +
- ", health:" + mBatteryProps.batteryHealth + ", present:" + mBatteryProps.batteryPresent +
+ ", health:" + mBatteryProps.batteryHealth +
+ ", present:" + mBatteryProps.batteryPresent +
", voltage: " + mBatteryProps.batteryVoltage +
", temperature: " + mBatteryProps.batteryTemperature +
", technology: " + mBatteryProps.batteryTechnology +
- ", AC powered:" + mBatteryProps.chargerAcOnline + ", USB powered:" + mBatteryProps.chargerUsbOnline +
+ ", AC powered:" + mBatteryProps.chargerAcOnline +
+ ", USB powered:" + mBatteryProps.chargerUsbOnline +
", Wireless powered:" + mBatteryProps.chargerWirelessOnline +
- ", icon:" + icon + ", invalid charger:" + mInvalidCharger);
+ ", icon:" + icon + ", invalid charger:" + mInvalidCharger +
+ ", maxChargingCurrent:" + mBatteryProps.maxChargingCurrent);
}
mHandler.post(new Runnable() {
@@ -618,6 +626,7 @@
pw.println(" AC powered: " + mBatteryProps.chargerAcOnline);
pw.println(" USB powered: " + mBatteryProps.chargerUsbOnline);
pw.println(" Wireless powered: " + mBatteryProps.chargerWirelessOnline);
+ pw.println(" Max charging current: " + mBatteryProps.maxChargingCurrent);
pw.println(" status: " + mBatteryProps.batteryStatus);
pw.println(" health: " + mBatteryProps.batteryHealth);
pw.println(" present: " + mBatteryProps.batteryPresent);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 4919bed..e19447d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -47,6 +47,7 @@
import android.content.res.Resources;
import android.database.ContentObserver;
import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.PacketKeepalive;
import android.net.IConnectivityManager;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
@@ -117,6 +118,7 @@
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.DataConnectionStats;
+import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.NetworkDiagnostics;
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.NetworkAgentInfo;
@@ -148,6 +150,8 @@
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.SortedSet;
+import java.util.TreeSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -399,6 +403,8 @@
TelephonyManager mTelephonyManager;
+ private KeepaliveTracker mKeepaliveTracker;
+
// sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
private final static int MIN_NET_ID = 100; // some reserved marks
private final static int MAX_NET_ID = 65535;
@@ -764,6 +770,8 @@
mPacManager = new PacManager(mContext, mHandler, EVENT_PROXY_HAS_CHANGED);
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+
+ mKeepaliveTracker = new KeepaliveTracker(mHandler);
}
private NetworkRequest createInternetRequestForTransport(int transportType) {
@@ -1450,6 +1458,10 @@
"ConnectivityService");
}
+ private void enforceKeepalivePermission() {
+ mContext.enforceCallingPermission(KeepaliveTracker.PERMISSION, "ConnectivityService");
+ }
+
public void sendConnectedBroadcast(NetworkInfo info) {
enforceConnectivityInternalPermission();
sendGeneralBroadcast(info, CONNECTIVITY_ACTION);
@@ -1841,10 +1853,13 @@
pw.println(", last requested never");
}
}
- pw.println();
+ pw.println();
mTethering.dump(fd, pw, args);
+ pw.println();
+ mKeepaliveTracker.dump(pw);
+
if (mInetLog != null && mInetLog.size() > 0) {
pw.println();
pw.println("Inet condition reports:");
@@ -1922,7 +1937,12 @@
(NetworkCapabilities)msg.obj;
if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) ||
networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
- Slog.wtf(TAG, "BUG: " + nai + " has stateful capability.");
+ Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
+ }
+ if (nai.created && !nai.networkCapabilities.equalImmutableCapabilities(
+ networkCapabilities)) {
+ Slog.wtf(TAG, "BUG: " + nai + " changed immutable capabilities: "
+ + nai.networkCapabilities + " -> " + networkCapabilities);
}
updateCapabilities(nai, networkCapabilities);
}
@@ -2006,6 +2026,15 @@
nai.networkMisc.acceptUnvalidated = (boolean) msg.obj;
break;
}
+ case NetworkAgent.EVENT_PACKET_KEEPALIVE: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_PACKET_KEEPALIVE from unknown NetworkAgent");
+ break;
+ }
+ mKeepaliveTracker.handleEventPacketKeepalive(nai, msg);
+ break;
+ }
case NetworkMonitor.EVENT_NETWORK_TESTED: {
NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
if (isLiveNetworkAgent(nai, "EVENT_NETWORK_TESTED")) {
@@ -2148,6 +2177,8 @@
// sending all CALLBACK_LOST messages (for requests, not listens) at the end
// of rematchAllNetworksAndRequests
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
+ mKeepaliveTracker.handleStopAllKeepalives(nai,
+ ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
mNetworkAgentInfos.remove(msg.replyTo);
updateClat(null, nai.linkProperties, nai);
@@ -2226,6 +2257,13 @@
private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
mNetworkRequests.put(nri.request, nri);
mNetworkRequestInfoLogs.log("REGISTER " + nri);
+ if (!nri.isRequest) {
+ for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
+ if (network.satisfiesImmutableCapabilitiesOf(nri.request)) {
+ updateSignalStrengthThresholds(network);
+ }
+ }
+ }
rematchAllNetworksAndRequests(null, 0);
if (nri.isRequest && mNetworkForRequestId.get(nri.request.requestId) == null) {
sendUpdatedScoreToFactories(nri.request, 0);
@@ -2339,6 +2377,9 @@
// if this listen request applies and remove it.
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
nai.networkRequests.remove(nri.request.requestId);
+ if (nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
+ updateSignalStrengthThresholds(nai);
+ }
}
}
callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED);
@@ -2505,6 +2546,19 @@
handleMobileDataAlwaysOn();
break;
}
+ // Sent by KeepaliveTracker to process an app request on the state machine thread.
+ case NetworkAgent.CMD_START_PACKET_KEEPALIVE: {
+ mKeepaliveTracker.handleStartKeepalive(msg);
+ break;
+ }
+ // Sent by KeepaliveTracker to process an app request on the state machine thread.
+ case NetworkAgent.CMD_STOP_PACKET_KEEPALIVE: {
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetwork((Network) msg.obj);
+ int slot = msg.arg1;
+ int reason = msg.arg2;
+ mKeepaliveTracker.handleStopKeepalive(nai, slot, reason);
+ break;
+ }
case EVENT_SYSTEM_READY: {
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
nai.networkMonitor.systemReady = true;
@@ -3554,15 +3608,32 @@
}
}
- private void ensureImmutableCapabilities(NetworkCapabilities networkCapabilities) {
- if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
- throw new IllegalArgumentException(
- "Cannot request network with NET_CAPABILITY_VALIDATED");
+ private void ensureRequestableCapabilities(NetworkCapabilities networkCapabilities) {
+ final String badCapability = networkCapabilities.describeFirstNonRequestableCapability();
+ if (badCapability != null) {
+ throw new IllegalArgumentException("Cannot request network with " + badCapability);
}
- if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL)) {
- throw new IllegalArgumentException(
- "Cannot request network with NET_CAPABILITY_CAPTIVE_PORTAL");
+ }
+
+ private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) {
+ final SortedSet<Integer> thresholds = new TreeSet();
+ synchronized (nai) {
+ for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.request.networkCapabilities.hasSignalStrength() &&
+ nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
+ thresholds.add(nri.request.networkCapabilities.getSignalStrength());
+ }
+ }
}
+ return new ArrayList<Integer>(thresholds);
+ }
+
+ private void updateSignalStrengthThresholds(NetworkAgentInfo nai) {
+ Bundle thresholds = new Bundle();
+ thresholds.putIntegerArrayList("thresholds", getSignalStrengthThresholds(nai));
+ nai.asyncChannel.sendMessage(
+ android.net.NetworkAgent.CMD_SET_SIGNAL_STRENGTH_THRESHOLDS,
+ 0, 0, thresholds);
}
@Override
@@ -3571,7 +3642,7 @@
networkCapabilities = new NetworkCapabilities(networkCapabilities);
enforceNetworkRequestPermissions(networkCapabilities);
enforceMeteredApnPolicy(networkCapabilities);
- ensureImmutableCapabilities(networkCapabilities);
+ ensureRequestableCapabilities(networkCapabilities);
if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) {
throw new IllegalArgumentException("Bad timeout specified");
@@ -3640,7 +3711,7 @@
networkCapabilities = new NetworkCapabilities(networkCapabilities);
enforceNetworkRequestPermissions(networkCapabilities);
enforceMeteredApnPolicy(networkCapabilities);
- ensureImmutableCapabilities(networkCapabilities);
+ ensureRequestableCapabilities(networkCapabilities);
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
nextNetworkRequestId());
@@ -3866,6 +3937,8 @@
notifyIfacesChanged();
notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED);
}
+
+ mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent);
}
private void updateClat(LinkProperties newLp, LinkProperties oldLp, NetworkAgentInfo nai) {
@@ -4532,6 +4605,15 @@
// TODO: support proxy per network.
}
+ // Whether a particular NetworkRequest listen should cause signal strength thresholds to
+ // be communicated to a particular NetworkAgent depends only on the network's immutable,
+ // capabilities, so it only needs to be done once on initial connect, not every time the
+ // network's capabilities change. Note that we do this before rematching the network,
+ // so we could decide to tear it down immediately afterwards. That's fine though - on
+ // disconnection NetworkAgents should stop any signal strength monitoring they have been
+ // doing.
+ updateSignalStrengthThresholds(networkAgent);
+
// Consider network even though it is not yet validated.
rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP);
@@ -4711,6 +4793,22 @@
}
@Override
+ public void startNattKeepalive(Network network, int intervalSeconds, Messenger messenger,
+ IBinder binder, String srcAddr, int srcPort, String dstAddr) {
+ enforceKeepalivePermission();
+ mKeepaliveTracker.startNattKeepalive(
+ getNetworkAgentInfoForNetwork(network),
+ intervalSeconds, messenger, binder,
+ srcAddr, srcPort, dstAddr, ConnectivityManager.PacketKeepalive.NATT_PORT);
+ }
+
+ @Override
+ public void stopKeepalive(Network network, int slot) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ NetworkAgent.CMD_STOP_PACKET_KEEPALIVE, slot, PacketKeepalive.SUCCESS, network));
+ }
+
+ @Override
public void factoryReset() {
enforceConnectivityInternalPermission();
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index 7aebc1a..ab2ea8b 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -244,3 +244,8 @@
# ---------------------------
40000 volume_changed (stream|1), (prev_level|1), (level|1), (max_level|1), (caller|3)
40001 stream_devices_changed (stream|1), (prev_devices|1), (devices|1)
+
+# ---------------------------
+# GestureLauncherService.java
+# ---------------------------
+40100 camera_gesture_triggered (gesture_on_time|2|3), (sensor1_on_time|2|3), (sensor2_on_time|2|3), (event_extra|1|1)
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
new file mode 100644
index 0000000..342a3ef
--- /dev/null
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import android.app.ActivityManager;
+import android.app.KeyguardManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Vibrator;
+import android.provider.Settings;
+import android.util.Slog;
+
+import com.android.server.statusbar.StatusBarManagerInternal;
+
+/**
+ * The service that listens for gestures detected in sensor firmware and starts the intent
+ * accordingly.
+ * <p>For now, only camera launch gesture is supported, and in the future, more gestures can be
+ * added.</p>
+ * @hide
+ */
+class GestureLauncherService extends SystemService {
+ private static final boolean DBG = false;
+ private static final String TAG = "GestureLauncherService";
+
+ /** The listener that receives the gesture event. */
+ private final GestureEventListener mGestureListener = new GestureEventListener();
+
+ private Sensor mCameraLaunchSensor;
+ private Context mContext;
+
+ /** The wake lock held when a gesture is detected. */
+ private WakeLock mWakeLock;
+ private boolean mRegistered;
+ private int mUserId;
+
+ // Below are fields used for event logging only.
+ /** Elapsed real time when the camera gesture is turned on. */
+ private long mCameraGestureOnTimeMs = 0L;
+
+ /** Elapsed real time when the last camera gesture was detected. */
+ private long mCameraGestureLastEventTime = 0L;
+
+ /**
+ * How long the sensor 1 has been turned on since camera launch sensor was
+ * subscribed to and when the last camera launch gesture was detected.
+ * <p>Sensor 1 is the main sensor used to detect camera launch gesture.</p>
+ */
+ private long mCameraGestureSensor1LastOnTimeMs = 0L;
+
+ /**
+ * If applicable, how long the sensor 2 has been turned on since camera
+ * launch sensor was subscribed to and when the last camera launch
+ * gesture was detected.
+ * <p>Sensor 2 is the secondary sensor used to detect camera launch gesture.
+ * This is optional and if only sensor 1 is used for detect camera launch
+ * gesture, this value would always be 0.</p>
+ */
+ private long mCameraGestureSensor2LastOnTimeMs = 0L;
+
+ /**
+ * Extra information about the event when the last camera launch gesture
+ * was detected.
+ */
+ private int mCameraLaunchLastEventExtra = 0;
+
+ public GestureLauncherService(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ public void onStart() {
+ // Nothing to publish.
+ }
+
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ Resources resources = mContext.getResources();
+ if (!isGestureLauncherEnabled(resources)) {
+ if (DBG) Slog.d(TAG, "Gesture launcher is disabled in system properties.");
+ return;
+ }
+
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(
+ Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "GestureLauncherService");
+ updateCameraRegistered();
+
+ mUserId = ActivityManager.getCurrentUser();
+ mContext.registerReceiver(mUserReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED));
+ registerContentObserver();
+ }
+ }
+
+ private void registerContentObserver() {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED),
+ false, mSettingObserver, mUserId);
+ }
+
+ private void updateCameraRegistered() {
+ Resources resources = mContext.getResources();
+ if (isCameraLaunchSettingEnabled(mContext, mUserId)) {
+ registerCameraLaunchGesture(resources);
+ } else {
+ unregisterCameraLaunchGesture();
+ }
+ }
+
+ private void unregisterCameraLaunchGesture() {
+ if (mRegistered) {
+ mRegistered = false;
+ mCameraGestureOnTimeMs = 0L;
+ mCameraGestureLastEventTime = 0L;
+ mCameraGestureSensor1LastOnTimeMs = 0;
+ mCameraGestureSensor2LastOnTimeMs = 0;
+ mCameraLaunchLastEventExtra = 0;
+
+ SensorManager sensorManager = (SensorManager) mContext.getSystemService(
+ Context.SENSOR_SERVICE);
+ sensorManager.unregisterListener(mGestureListener);
+ }
+ }
+
+ /**
+ * Registers for the camera launch gesture.
+ */
+ private void registerCameraLaunchGesture(Resources resources) {
+ if (mRegistered) {
+ return;
+ }
+ mCameraGestureOnTimeMs = SystemClock.elapsedRealtime();
+ mCameraGestureLastEventTime = mCameraGestureOnTimeMs;
+ SensorManager sensorManager = (SensorManager) mContext.getSystemService(
+ Context.SENSOR_SERVICE);
+ int cameraLaunchGestureId = resources.getInteger(
+ com.android.internal.R.integer.config_cameraLaunchGestureSensorType);
+ if (cameraLaunchGestureId != -1) {
+ mRegistered = false;
+ String sensorName = resources.getString(
+ com.android.internal.R.string.config_cameraLaunchGestureSensorStringType);
+ mCameraLaunchSensor = sensorManager.getDefaultSensor(
+ cameraLaunchGestureId,
+ true /*wakeUp*/);
+
+ // Compare the camera gesture string type to that in the resource file to make
+ // sure we are registering the correct sensor. This is redundant check, it
+ // makes the code more robust.
+ if (mCameraLaunchSensor != null) {
+ if (sensorName.equals(mCameraLaunchSensor.getStringType())) {
+ mRegistered = sensorManager.registerListener(mGestureListener,
+ mCameraLaunchSensor, 0);
+ } else {
+ String message = String.format("Wrong configuration. Sensor type and sensor "
+ + "string type don't match: %s in resources, %s in the sensor.",
+ sensorName, mCameraLaunchSensor.getStringType());
+ throw new RuntimeException(message);
+ }
+ }
+ if (DBG) Slog.d(TAG, "Camera launch sensor registered: " + mRegistered);
+ } else {
+ if (DBG) Slog.d(TAG, "Camera launch sensor is not specified.");
+ }
+ }
+
+ public static boolean isCameraLaunchSettingEnabled(Context context, int userId) {
+ return isCameraLaunchEnabled(context.getResources())
+ && (Settings.Secure.getIntForUser(context.getContentResolver(),
+ Settings.Secure.CAMERA_GESTURE_DISABLED, 0, userId) == 0);
+ }
+
+ /**
+ * Whether to enable the camera launch gesture.
+ */
+ public static boolean isCameraLaunchEnabled(Resources resources) {
+ boolean configSet = resources.getInteger(
+ com.android.internal.R.integer.config_cameraLaunchGestureSensorType) != -1;
+ return configSet &&
+ !SystemProperties.getBoolean("gesture.disable_camera_launch", false);
+ }
+
+ /**
+ * Whether GestureLauncherService should be enabled according to system properties.
+ */
+ public static boolean isGestureLauncherEnabled(Resources resources) {
+ // For now, the only supported gesture is camera launch gesture, so whether to enable this
+ // service equals to isCameraLaunchEnabled();
+ return isCameraLaunchEnabled(resources);
+ }
+
+ private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
+ mUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+ mContext.getContentResolver().unregisterContentObserver(mSettingObserver);
+ registerContentObserver();
+ updateCameraRegistered();
+ }
+ }
+ };
+
+ private final ContentObserver mSettingObserver = new ContentObserver(new Handler()) {
+ public void onChange(boolean selfChange, android.net.Uri uri, int userId) {
+ if (userId == mUserId) {
+ updateCameraRegistered();
+ }
+ }
+ };
+
+ private final class GestureEventListener implements SensorEventListener {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (!mRegistered) {
+ if (DBG) Slog.d(TAG, "Ignoring gesture event because it's unregistered.");
+ return;
+ }
+ if (event.sensor == mCameraLaunchSensor) {
+ handleCameraLaunchGesture(event);
+ return;
+ }
+ }
+
+ private void handleCameraLaunchGesture(SensorEvent event) {
+ if (DBG) {
+ float[] values = event.values;
+ Slog.d(TAG, String.format("Received a camera launch event: " +
+ "values=[%.4f, %.4f, %.4f].", values[0], values[1], values[2]));
+ }
+ boolean userSetupComplete = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
+ if (!userSetupComplete) {
+ if (DBG) Slog.d(TAG, String.format(
+ "userSetupComplete = %s, ignoring camera launch gesture.",
+ userSetupComplete));
+ return;
+ }
+ if (DBG) Slog.d(TAG, String.format(
+ "userSetupComplete = %s, performing camera launch gesture.",
+ userSetupComplete));
+
+ // Make sure we don't sleep too early
+ mWakeLock.acquire(500L);
+ StatusBarManagerInternal service = LocalServices.getService(
+ StatusBarManagerInternal.class);
+ service.onCameraLaunchGestureDetected();
+ trackCameraLaunchEvent(event);
+ mWakeLock.release();
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Ignored.
+ }
+
+ private void trackCameraLaunchEvent(SensorEvent event) {
+ long now = SystemClock.elapsedRealtime();
+ long totalDuration = now - mCameraGestureOnTimeMs;
+ // values[0]: ratio between total time duration when accel is turned on and time
+ // duration since camera launch gesture is subscribed.
+ // values[1]: ratio between total time duration when gyro is turned on and time duration
+ // since camera launch gesture is subscribed.
+ // values[2]: extra information
+ float[] values = event.values;
+
+ long sensor1OnTime = (long) (totalDuration * (double) values[0]);
+ long sensor2OnTime = (long) (totalDuration * (double) values[1]);
+ int extra = (int) values[2];
+
+ // We only log the difference in the event log to make aggregation easier.
+ long gestureOnTimeDiff = now - mCameraGestureLastEventTime;
+ long sensor1OnTimeDiff = sensor1OnTime - mCameraGestureSensor1LastOnTimeMs;
+ long sensor2OnTimeDiff = sensor2OnTime - mCameraGestureSensor2LastOnTimeMs;
+ int extraDiff = extra - mCameraLaunchLastEventExtra;
+
+ // Gating against negative time difference. This doesn't usually happen, but it may
+ // happen because of numeric errors.
+ if (gestureOnTimeDiff < 0 || sensor1OnTimeDiff < 0 || sensor2OnTimeDiff < 0) {
+ if (DBG) Slog.d(TAG, "Skipped event logging because negative numbers.");
+ return;
+ }
+
+ if (DBG) Slog.d(TAG, String.format("totalDuration: %d, sensor1OnTime: %s, " +
+ "sensor2OnTime: %d, extra: %d",
+ gestureOnTimeDiff,
+ sensor1OnTimeDiff,
+ sensor2OnTimeDiff,
+ extraDiff));
+ EventLogTags.writeCameraGestureTriggered(
+ gestureOnTimeDiff,
+ sensor1OnTimeDiff,
+ sensor2OnTimeDiff,
+ extraDiff);
+
+ mCameraGestureLastEventTime = now;
+ mCameraGestureSensor1LastOnTimeMs = sensor1OnTime;
+ mCameraGestureSensor2LastOnTimeMs = sensor2OnTime;
+ mCameraLaunchLastEventExtra = extra;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index cae060a..468ead0 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -542,22 +542,25 @@
Slog.e(TAG, "Unable to bind FLP Geofence proxy.");
}
- // bind to the hardware activity recognition if supported
- if (ActivityRecognitionHardware.isSupported()) {
- ActivityRecognitionProxy proxy = ActivityRecognitionProxy.createAndBind(
- mContext,
- mLocationHandler,
- ActivityRecognitionHardware.getInstance(mContext),
- com.android.internal.R.bool.config_enableActivityRecognitionHardwareOverlay,
- com.android.internal.R.string.config_activityRecognitionHardwarePackageName,
- com.android.internal.R.array.config_locationProviderPackageNames);
-
- if (proxy == null) {
- Slog.e(TAG, "Unable to bind ActivityRecognitionProxy.");
- }
+ // bind to hardware activity recognition
+ boolean activityRecognitionHardwareIsSupported = ActivityRecognitionHardware.isSupported();
+ ActivityRecognitionHardware activityRecognitionHardware = null;
+ if (activityRecognitionHardwareIsSupported) {
+ activityRecognitionHardware = ActivityRecognitionHardware.getInstance(mContext);
} else {
Slog.e(TAG, "Hardware Activity-Recognition not supported.");
}
+ ActivityRecognitionProxy proxy = ActivityRecognitionProxy.createAndBind(
+ mContext,
+ mLocationHandler,
+ activityRecognitionHardwareIsSupported,
+ activityRecognitionHardware,
+ com.android.internal.R.bool.config_enableActivityRecognitionHardwareOverlay,
+ com.android.internal.R.string.config_activityRecognitionHardwarePackageName,
+ com.android.internal.R.array.config_locationProviderPackageNames);
+ if (proxy == null) {
+ Slog.e(TAG, "Unable to bind ActivityRecognitionProxy.");
+ }
String[] testProviderStrings = resources.getStringArray(
com.android.internal.R.array.config_testLocationProviders);
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 5e2fe5a..f1d7da4 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -18,6 +18,7 @@
import android.app.admin.DevicePolicyManager;
import android.app.backup.BackupManager;
+import android.app.trust.IStrongAuthTracker;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -28,6 +29,8 @@
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
import static android.content.Context.USER_SERVICE;
import static android.Manifest.permission.READ_CONTACTS;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
+
import android.database.sqlite.SQLiteDatabase;
import android.os.Binder;
import android.os.IBinder;
@@ -70,6 +73,7 @@
private final Context mContext;
private final LockSettingsStorage mStorage;
+ private final LockSettingsStrongAuth mStrongAuth = new LockSettingsStrongAuth();
private LockPatternUtils mLockPatternUtils;
private boolean mFirstCallToVold;
@@ -93,6 +97,7 @@
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_STARTING);
filter.addAction(Intent.ACTION_USER_REMOVED);
+ filter.addAction(Intent.ACTION_USER_PRESENT);
mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
mStorage = new LockSettingsStorage(context, new LockSettingsStorage.Callback() {
@@ -122,6 +127,8 @@
} else if (Intent.ACTION_USER_STARTING.equals(intent.getAction())) {
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
mStorage.prefetchUser(userHandle);
+ } else if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
+ mStrongAuth.reportUnlock(getSendingUserId());
} else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
if (userHandle > 0) {
@@ -659,6 +666,10 @@
if (shouldReEnroll) {
credentialUtil.setCredential(credential, credential, userId);
}
+ } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
+ if (response.getTimeout() > 0) {
+ requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
+ }
}
return response;
@@ -713,6 +724,7 @@
private void removeUser(int userId) {
mStorage.removeUser(userId);
+ mStrongAuth.removeUser(userId);
final KeyStore ks = KeyStore.getInstance();
ks.onUserRemoved(userId);
@@ -727,6 +739,24 @@
}
}
+ @Override
+ public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
+ checkPasswordReadPermission(UserHandle.USER_ALL);
+ mStrongAuth.registerStrongAuthTracker(tracker);
+ }
+
+ @Override
+ public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) {
+ checkPasswordReadPermission(UserHandle.USER_ALL);
+ mStrongAuth.unregisterStrongAuthTracker(tracker);
+ }
+
+ @Override
+ public void requireStrongAuth(int strongAuthReason, int userId) {
+ checkWritePermission(userId);
+ mStrongAuth.requireStrongAuth(strongAuthReason, userId);
+ }
+
private static final String[] VALID_SETTINGS = new String[] {
LockPatternUtils.LOCKOUT_PERMANENT_KEY,
LockPatternUtils.LOCKOUT_ATTEMPT_DEADLINE,
@@ -797,5 +827,4 @@
Slog.e(TAG, "Unable to acquire GateKeeperService");
return null;
}
-
}
diff --git a/services/core/java/com/android/server/LockSettingsStrongAuth.java b/services/core/java/com/android/server/LockSettingsStrongAuth.java
new file mode 100644
index 0000000..c023f4a
--- /dev/null
+++ b/services/core/java/com/android/server/LockSettingsStrongAuth.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
+
+import android.app.trust.IStrongAuthTracker;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.SparseIntArray;
+
+import java.util.ArrayList;
+
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
+
+/**
+ * Keeps track of requests for strong authentication.
+ */
+public class LockSettingsStrongAuth {
+
+ private static final String TAG = "LockSettings";
+
+ private static final int MSG_REQUIRE_STRONG_AUTH = 1;
+ private static final int MSG_REGISTER_TRACKER = 2;
+ private static final int MSG_UNREGISTER_TRACKER = 3;
+ private static final int MSG_REMOVE_USER = 4;
+
+ private final ArrayList<IStrongAuthTracker> mStrongAuthTrackers = new ArrayList<>();
+ private final SparseIntArray mStrongAuthForUser = new SparseIntArray();
+
+ private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) {
+ for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
+ if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) {
+ return;
+ }
+ }
+ mStrongAuthTrackers.add(tracker);
+
+ for (int i = 0; i < mStrongAuthForUser.size(); i++) {
+ int key = mStrongAuthForUser.keyAt(i);
+ int value = mStrongAuthForUser.valueAt(i);
+ try {
+ tracker.onStrongAuthRequiredChanged(value, key);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception while adding StrongAuthTracker.", e);
+ }
+ }
+ }
+
+ private void handleRemoveStrongAuthTracker(IStrongAuthTracker tracker) {
+ for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
+ if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) {
+ mStrongAuthTrackers.remove(i);
+ return;
+ }
+ }
+ }
+
+ private void handleRequireStrongAuth(int strongAuthReason, int userId) {
+ if (userId == UserHandle.USER_ALL) {
+ for (int i = 0; i < mStrongAuthForUser.size(); i++) {
+ int key = mStrongAuthForUser.keyAt(i);
+ handleRequireStrongAuthOneUser(strongAuthReason, key);
+ }
+ } else {
+ handleRequireStrongAuthOneUser(strongAuthReason, userId);
+ }
+ }
+
+ private void handleRequireStrongAuthOneUser(int strongAuthReason, int userId) {
+ int oldValue = mStrongAuthForUser.get(userId, LockPatternUtils.StrongAuthTracker.DEFAULT);
+ int newValue = strongAuthReason == STRONG_AUTH_NOT_REQUIRED
+ ? STRONG_AUTH_NOT_REQUIRED
+ : (oldValue | strongAuthReason);
+ if (oldValue != newValue) {
+ mStrongAuthForUser.put(userId, newValue);
+ notifyStrongAuthTrackers(newValue, userId);
+ }
+ }
+
+ private void handleRemoveUser(int userId) {
+ int index = mStrongAuthForUser.indexOfKey(userId);
+ if (index >= 0) {
+ mStrongAuthForUser.removeAt(index);
+ notifyStrongAuthTrackers(StrongAuthTracker.DEFAULT, userId);
+ }
+ }
+
+ private void notifyStrongAuthTrackers(int strongAuthReason, int userId) {
+ for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
+ try {
+ mStrongAuthTrackers.get(i).onStrongAuthRequiredChanged(strongAuthReason, userId);
+ } catch (DeadObjectException e) {
+ Slog.d(TAG, "Removing dead StrongAuthTracker.");
+ mStrongAuthTrackers.remove(i);
+ i--;
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception while notifying StrongAuthTracker.", e);
+ }
+ }
+ }
+
+ public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
+ mHandler.obtainMessage(MSG_REGISTER_TRACKER, tracker).sendToTarget();
+ }
+
+ public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) {
+ mHandler.obtainMessage(MSG_UNREGISTER_TRACKER, tracker).sendToTarget();
+ }
+
+ public void removeUser(int userId) {
+ mHandler.obtainMessage(MSG_REMOVE_USER, userId, 0).sendToTarget();
+ }
+
+ public void requireStrongAuth(int strongAuthReason, int userId) {
+ if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_OWNER) {
+ mHandler.obtainMessage(MSG_REQUIRE_STRONG_AUTH, strongAuthReason,
+ userId).sendToTarget();
+ } else {
+ throw new IllegalArgumentException(
+ "userId must be an explicit user id or USER_ALL");
+ }
+ }
+
+ public void reportUnlock(int userId) {
+ requireStrongAuth(STRONG_AUTH_NOT_REQUIRED, userId);
+ }
+
+ private final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_REGISTER_TRACKER:
+ handleAddStrongAuthTracker((IStrongAuthTracker) msg.obj);
+ break;
+ case MSG_UNREGISTER_TRACKER:
+ handleRemoveStrongAuthTracker((IStrongAuthTracker) msg.obj);
+ break;
+ case MSG_REQUIRE_STRONG_AUTH:
+ handleRequireStrongAuth(msg.arg1, msg.arg2);
+ break;
+ case MSG_REMOVE_USER:
+ handleRemoveUser(msg.arg1);
+ break;
+ }
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index d10a457..0d64540 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -2582,6 +2582,63 @@
}
@Override
+ public void createNewUserDir(int userHandle, String path) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only SYSTEM_UID can create user directories");
+ }
+
+ waitForReady();
+
+ if (DEBUG_EVENTS) {
+ Slog.i(TAG, "Creating new user dir");
+ }
+
+ try {
+ NativeDaemonEvent event = mCryptConnector.execute(
+ "cryptfs", "createnewuserdir", userHandle, path);
+ if (!"0".equals(event.getMessage())) {
+ String error = "createnewuserdir sent unexpected message: "
+ + event.getMessage();
+ Slog.e(TAG, error);
+ // ext4enc:TODO is this the right exception?
+ throw new RuntimeException(error);
+ }
+ } catch (NativeDaemonConnectorException e) {
+ Slog.e(TAG, "createnewuserdir threw exception", e);
+ throw new RuntimeException("createnewuserdir threw exception", e);
+ }
+ }
+
+ // ext4enc:TODO duplication between this and createNewUserDir is nasty
+ @Override
+ public void deleteUserKey(int userHandle) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ throw new SecurityException("Only SYSTEM_UID can delete user keys");
+ }
+
+ waitForReady();
+
+ if (DEBUG_EVENTS) {
+ Slog.i(TAG, "Deleting user key");
+ }
+
+ try {
+ NativeDaemonEvent event = mCryptConnector.execute(
+ "cryptfs", "deleteuserkey", userHandle);
+ if (!"0".equals(event.getMessage())) {
+ String error = "deleteuserkey sent unexpected message: "
+ + event.getMessage();
+ Slog.e(TAG, error);
+ // ext4enc:TODO is this the right exception?
+ throw new RuntimeException(error);
+ }
+ } catch (NativeDaemonConnectorException e) {
+ Slog.e(TAG, "deleteuserkey threw exception", e);
+ throw new RuntimeException("deleteuserkey threw exception", e);
+ }
+ }
+
+ @Override
public int mkdirs(String callingPkg, String appPath) {
final int userId = UserHandle.getUserId(Binder.getCallingUid());
final UserEnvironment userEnv = new UserEnvironment(userId);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 9bf4f5f..335288d 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1263,8 +1263,8 @@
Slog.v(TAG, "WiFi energy data was reset, new WiFi energy data is " + result);
}
- // There is some accuracy error in reports so allow 30 milliseconds of error.
- final long SAMPLE_ERROR_MILLIS = 30;
+ // There is some accuracy error in reports so allow some slop in the results.
+ final long SAMPLE_ERROR_MILLIS = 750;
final long totalTimeMs = result.mControllerIdleTimeMs + result.mControllerRxTimeMs +
result.mControllerTxTimeMs;
if (totalTimeMs > timePeriodMs + SAMPLE_ERROR_MILLIS) {
diff --git a/services/core/java/com/android/server/camera/CameraService.java b/services/core/java/com/android/server/camera/CameraService.java
index 0be24f4..f82454a 100644
--- a/services/core/java/com/android/server/camera/CameraService.java
+++ b/services/core/java/com/android/server/camera/CameraService.java
@@ -23,13 +23,17 @@
import android.content.pm.UserInfo;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceProxy;
+import android.nfc.INfcAdapter;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Binder;
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserManager;
+import android.os.SystemProperties;
import android.util.Slog;
+import android.util.ArraySet;
import com.android.server.ServiceThread;
import com.android.server.SystemService;
@@ -44,8 +48,10 @@
*
* @hide
*/
-public class CameraService extends SystemService implements Handler.Callback {
+public class CameraService extends SystemService
+ implements Handler.Callback, IBinder.DeathRecipient {
private static final String TAG = "CameraService_proxy";
+ private static final boolean DEBUG = false;
/**
* This must match the ICameraService.aidl definition
@@ -58,6 +64,16 @@
public static final int NO_EVENT = 0; // NOOP
public static final int USER_SWITCHED = 1; // User changed, argument is the new user handle
+ // State arguments to use with the notifyCameraState call from camera service:
+ public static final int CAMERA_STATE_OPEN = 0;
+ public static final int CAMERA_STATE_ACTIVE = 1;
+ public static final int CAMERA_STATE_IDLE = 2;
+ public static final int CAMERA_STATE_CLOSED = 3;
+
+ // Flags arguments to NFC adapter to enable/disable NFC
+ public static final int DISABLE_POLLING_FLAGS = 0x1000;
+ public static final int ENABLE_POLLING_FLAGS = 0x0000;
+
// Handler message codes
private static final int MSG_SWITCH_USER = 1;
@@ -72,6 +88,17 @@
private Set<Integer> mEnabledCameraUsers;
private int mLastUser;
+ private ICameraService mCameraServiceRaw;
+
+ private final ArraySet<String> mActiveCameraIds = new ArraySet<>();
+
+ private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
+ private static final String NFC_SERVICE_BINDER_NAME = "nfc";
+ private static final IBinder nfcInterfaceToken = new Binder();
+
+ private final boolean mNotifyNfc;
+ private int mActiveCameraCount = 0;
+
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -102,6 +129,14 @@
public void pingForUserUpdate() {
notifySwitchWithRetries(30);
}
+
+ @Override
+ public void notifyCameraState(String cameraId, int newCameraState) {
+ String state = cameraStateToString(newCameraState);
+ if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " state now " + state);
+
+ updateActivityCount(cameraId, newCameraState);
+ }
};
public CameraService(Context context) {
@@ -110,6 +145,9 @@
mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DISPLAY, /*allowTo*/false);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper(), this);
+
+ mNotifyNfc = SystemProperties.getInt(NFC_NOTIFICATION_PROP, 0) > 0;
+ if (DEBUG) Slog.v(TAG, "Notify NFC behavior is " + (mNotifyNfc ? "active" : "disabled"));
}
@Override
@@ -161,13 +199,32 @@
}
}
+ /**
+ * Handle the death of the native camera service
+ */
+ @Override
+ public void binderDied() {
+ if (DEBUG) Slog.w(TAG, "Native camera service has died");
+ synchronized(mLock) {
+ mCameraServiceRaw = null;
+
+ // All cameras reset to idle on camera service death
+ boolean wasEmpty = mActiveCameraIds.isEmpty();
+ mActiveCameraIds.clear();
+
+ if ( mNotifyNfc && !wasEmpty ) {
+ notifyNfcService(/*enablePolling*/ true);
+ }
+ }
+ }
+
private void switchUserLocked(int userHandle) {
Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle);
mLastUser = userHandle;
if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) {
// Some user handles have been added or removed, update mediaserver.
mEnabledCameraUsers = currentUserHandles;
- notifyMediaserver(USER_SWITCHED, currentUserHandles);
+ notifyMediaserverLocked(USER_SWITCHED, currentUserHandles);
}
}
@@ -187,7 +244,7 @@
if (mEnabledCameraUsers == null) {
return;
}
- if (notifyMediaserver(USER_SWITCHED, mEnabledCameraUsers)) {
+ if (notifyMediaserverLocked(USER_SWITCHED, mEnabledCameraUsers)) {
retries = 0;
}
}
@@ -199,19 +256,27 @@
RETRY_DELAY_TIME);
}
- private boolean notifyMediaserver(int eventType, Set<Integer> updatedUserHandles) {
+ private boolean notifyMediaserverLocked(int eventType, Set<Integer> updatedUserHandles) {
// Forward the user switch event to the native camera service running in the mediaserver
// process.
- IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
- if (cameraServiceBinder == null) {
- Slog.w(TAG, "Could not notify mediaserver, camera service not available.");
- return false; // Camera service not active, cannot evict user clients.
+ if (mCameraServiceRaw == null) {
+ IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
+ if (cameraServiceBinder == null) {
+ Slog.w(TAG, "Could not notify mediaserver, camera service not available.");
+ return false; // Camera service not active, cannot evict user clients.
+ }
+ try {
+ cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not link to death of native camera service");
+ return false;
+ }
+
+ mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
}
- ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
-
try {
- cameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles));
+ mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles));
} catch (RemoteException e) {
Slog.w(TAG, "Could not notify mediaserver, remote exception: " + e);
// Not much we can do if camera service is dead.
@@ -220,6 +285,44 @@
return true;
}
+ private void updateActivityCount(String cameraId, int newCameraState) {
+ synchronized(mLock) {
+ boolean wasEmpty = mActiveCameraIds.isEmpty();
+ switch (newCameraState) {
+ case CAMERA_STATE_OPEN:
+ break;
+ case CAMERA_STATE_ACTIVE:
+ mActiveCameraIds.add(cameraId);
+ break;
+ case CAMERA_STATE_IDLE:
+ case CAMERA_STATE_CLOSED:
+ mActiveCameraIds.remove(cameraId);
+ break;
+ }
+ boolean isEmpty = mActiveCameraIds.isEmpty();
+ if ( mNotifyNfc && (wasEmpty != isEmpty) ) {
+ notifyNfcService(isEmpty);
+ }
+ }
+ }
+
+ private void notifyNfcService(boolean enablePolling) {
+
+ IBinder nfcServiceBinder = getBinderService(NFC_SERVICE_BINDER_NAME);
+ if (nfcServiceBinder == null) {
+ Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
+ return;
+ }
+ INfcAdapter nfcAdapterRaw = INfcAdapter.Stub.asInterface(nfcServiceBinder);
+ int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
+ if (DEBUG) Slog.v(TAG, "Setting NFC reader mode to flags " + flags);
+ try {
+ nfcAdapterRaw.setReaderMode(nfcInterfaceToken, null, flags, null);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Could not notify NFC service, remote exception: " + e);
+ }
+ }
+
private static int[] toArray(Collection<Integer> c) {
int len = c.size();
int[] ret = new int[len];
@@ -229,4 +332,15 @@
}
return ret;
}
+
+ private static String cameraStateToString(int newCameraState) {
+ switch (newCameraState) {
+ case CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN";
+ case CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE";
+ case CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE";
+ case CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED";
+ default: break;
+ }
+ return "CAMERA_STATE_UNKNOWN";
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/KeepalivePacketData.java b/services/core/java/com/android/server/connectivity/KeepalivePacketData.java
new file mode 100644
index 0000000..64b9399
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/KeepalivePacketData.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.system.OsConstants;
+import android.net.ConnectivityManager;
+import android.net.NetworkUtils;
+import android.net.util.IpUtils;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import static android.net.ConnectivityManager.PacketKeepalive.*;
+
+/**
+ * Represents the actual packets that are sent by the
+ * {@link android.net.ConnectivityManager.PacketKeepalive} API.
+ *
+ * @hide
+ */
+public class KeepalivePacketData {
+ /** Protocol of the packet to send; one of the OsConstants.ETH_P_* values. */
+ public final int protocol;
+
+ /** Source IP address */
+ public final InetAddress srcAddress;
+
+ /** Destination IP address */
+ public final InetAddress dstAddress;
+
+ /** Source port */
+ public final int srcPort;
+
+ /** Destination port */
+ public final int dstPort;
+
+ /** Destination MAC address. Can change if routing changes. */
+ public byte[] dstMac;
+
+ /** Packet data. A raw byte string of packet data, not including the link-layer header. */
+ public final byte[] data;
+
+ private static final int IPV4_HEADER_LENGTH = 20;
+ private static final int UDP_HEADER_LENGTH = 8;
+
+ protected KeepalivePacketData(InetAddress srcAddress, int srcPort,
+ InetAddress dstAddress, int dstPort, byte[] data) throws InvalidPacketException {
+ this.srcAddress = srcAddress;
+ this.dstAddress = dstAddress;
+ this.srcPort = srcPort;
+ this.dstPort = dstPort;
+ this.data = data;
+
+ // Check we have two IP addresses of the same family.
+ if (srcAddress == null || dstAddress == null ||
+ !srcAddress.getClass().getName().equals(dstAddress.getClass().getName())) {
+ }
+
+ // Set the protocol.
+ if (this.dstAddress instanceof Inet4Address) {
+ this.protocol = OsConstants.ETH_P_IP;
+ } else if (this.dstAddress instanceof Inet6Address) {
+ this.protocol = OsConstants.ETH_P_IPV6;
+ } else {
+ throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+ }
+
+ // Check the ports.
+ if (!IpUtils.isValidUdpOrTcpPort(srcPort) || !IpUtils.isValidUdpOrTcpPort(dstPort)) {
+ throw new InvalidPacketException(ERROR_INVALID_PORT);
+ }
+ }
+
+ public static class InvalidPacketException extends Exception {
+ final public int error;
+ public InvalidPacketException(int error) {
+ this.error = error;
+ }
+ }
+
+ /**
+ * Creates an IPsec NAT-T keepalive packet with the specified parameters.
+ */
+ public static KeepalivePacketData nattKeepalivePacket(
+ InetAddress srcAddress, int srcPort,
+ InetAddress dstAddress, int dstPort) throws InvalidPacketException {
+
+ if (!(srcAddress instanceof Inet4Address)) {
+ throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+ }
+
+ if (dstPort != NATT_PORT) {
+ throw new InvalidPacketException(ERROR_INVALID_PORT);
+ }
+
+ int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1;
+ ByteBuffer buf = ByteBuffer.allocate(length);
+ buf.order(ByteOrder.BIG_ENDIAN);
+ buf.putShort((short) 0x4500); // IP version and TOS
+ buf.putShort((short) length);
+ buf.putInt(0); // ID, flags, offset
+ buf.put((byte) 64); // TTL
+ buf.put((byte) OsConstants.IPPROTO_UDP);
+ int ipChecksumOffset = buf.position();
+ buf.putShort((short) 0); // IP checksum
+ buf.put(srcAddress.getAddress());
+ buf.put(dstAddress.getAddress());
+ buf.putShort((short) srcPort);
+ buf.putShort((short) dstPort);
+ buf.putShort((short) (length - 20)); // UDP length
+ int udpChecksumOffset = buf.position();
+ buf.putShort((short) 0); // UDP checksum
+ buf.put((byte) 0xff); // NAT-T keepalive
+ buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
+ buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_LENGTH));
+
+ return new KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
new file mode 100644
index 0000000..c78f347
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import com.android.internal.util.HexDump;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.connectivity.KeepalivePacketData;
+import com.android.server.connectivity.NetworkAgentInfo;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.PacketKeepalive;
+import android.net.LinkAddress;
+import android.net.NetworkAgent;
+import android.net.NetworkUtils;
+import android.net.util.IpUtils;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Process;
+import android.os.RemoteException;
+import android.system.OsConstants;
+import android.util.Log;
+import android.util.Pair;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import static android.net.ConnectivityManager.PacketKeepalive.*;
+import static android.net.NetworkAgent.CMD_START_PACKET_KEEPALIVE;
+import static android.net.NetworkAgent.CMD_STOP_PACKET_KEEPALIVE;
+import static android.net.NetworkAgent.EVENT_PACKET_KEEPALIVE;
+
+/**
+ * Manages packet keepalive requests.
+ *
+ * Provides methods to stop and start keepalive requests, and keeps track of keepalives across all
+ * networks. This class is tightly coupled to ConnectivityService. It is not thread-safe and its
+ * methods must be called only from the ConnectivityService handler thread.
+ */
+public class KeepaliveTracker {
+
+ private static final String TAG = "KeepaliveTracker";
+ private static final boolean DBG = true;
+
+ // TODO: Change this to a system-only permission.
+ public static final String PERMISSION = android.Manifest.permission.CHANGE_NETWORK_STATE;
+
+ /** Keeps track of keepalive requests. */
+ private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
+ new HashMap<> ();
+ private final Handler mConnectivityServiceHandler;
+
+ public KeepaliveTracker(Handler handler) {
+ mConnectivityServiceHandler = handler;
+ }
+
+ /**
+ * Tracks information about a packet keepalive.
+ *
+ * All information about this keepalive is known at construction time except the slot number,
+ * which is only returned when the hardware has successfully started the keepalive.
+ */
+ class KeepaliveInfo implements IBinder.DeathRecipient {
+ // Bookkeping data.
+ private final Messenger mMessenger;
+ private final IBinder mBinder;
+ private final int mUid;
+ private final int mPid;
+ private final NetworkAgentInfo mNai;
+
+ /** Keepalive slot. A small integer that identifies this keepalive among the ones handled
+ * by this network. */
+ private int mSlot = PacketKeepalive.NO_KEEPALIVE;
+
+ // Packet data.
+ private final KeepalivePacketData mPacket;
+ private final int mInterval;
+
+ // Whether the keepalive is started or not.
+ public boolean isStarted;
+
+ public KeepaliveInfo(Messenger messenger, IBinder binder, NetworkAgentInfo nai,
+ KeepalivePacketData packet, int interval) {
+ mMessenger = messenger;
+ mBinder = binder;
+ mPid = Binder.getCallingPid();
+ mUid = Binder.getCallingUid();
+
+ mNai = nai;
+ mPacket = packet;
+ mInterval = interval;
+
+ try {
+ mBinder.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ binderDied();
+ }
+ }
+
+ public NetworkAgentInfo getNai() {
+ return mNai;
+ }
+
+ public String toString() {
+ return new StringBuffer("KeepaliveInfo [")
+ .append(" network=").append(mNai.network)
+ .append(" isStarted=").append(isStarted)
+ .append(" ")
+ .append(IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort))
+ .append("->")
+ .append(IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort))
+ .append(" interval=" + mInterval)
+ .append(" data=" + HexDump.toHexString(mPacket.data))
+ .append(" uid=").append(mUid).append(" pid=").append(mPid)
+ .append(" ]")
+ .toString();
+ }
+
+ /** Sends a message back to the application via its PacketKeepalive.Callback. */
+ void notifyMessenger(int slot, int err) {
+ KeepaliveTracker.this.notifyMessenger(mMessenger, slot, err);
+ }
+
+ /** Called when the application process is killed. */
+ public void binderDied() {
+ // Not called from ConnectivityService handler thread, so send it a message.
+ mConnectivityServiceHandler.obtainMessage(
+ NetworkAgent.CMD_STOP_PACKET_KEEPALIVE,
+ mSlot, PacketKeepalive.BINDER_DIED, mNai.network).sendToTarget();
+ }
+
+ void unlinkDeathRecipient() {
+ if (mBinder != null) {
+ mBinder.unlinkToDeath(this, 0);
+ }
+ }
+
+ private int checkNetworkConnected() {
+ if (!mNai.networkInfo.isConnectedOrConnecting()) {
+ return ERROR_INVALID_NETWORK;
+ }
+ return SUCCESS;
+ }
+
+ private int checkSourceAddress() {
+ // Check that we have the source address.
+ for (InetAddress address : mNai.linkProperties.getAddresses()) {
+ if (address.equals(mPacket.srcAddress)) {
+ return SUCCESS;
+ }
+ }
+ return ERROR_INVALID_IP_ADDRESS;
+ }
+
+ private int checkInterval() {
+ return mInterval >= 20 ? SUCCESS : ERROR_INVALID_INTERVAL;
+ }
+
+ private int isValid() {
+ synchronized (mNai) {
+ int error = checkInterval();
+ if (error == SUCCESS) error = checkNetworkConnected();
+ if (error == SUCCESS) error = checkSourceAddress();
+ return error;
+ }
+ }
+
+ void start(int slot) {
+ int error = isValid();
+ if (error == SUCCESS) {
+ mSlot = slot;
+ Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
+ mNai.asyncChannel.sendMessage(CMD_START_PACKET_KEEPALIVE, slot, mInterval, mPacket);
+ } else {
+ notifyMessenger(NO_KEEPALIVE, error);
+ return;
+ }
+ }
+
+ void stop(int reason) {
+ int uid = Binder.getCallingUid();
+ if (uid != mUid && uid != Process.SYSTEM_UID) {
+ if (DBG) {
+ Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
+ }
+ }
+ if (isStarted) {
+ Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name());
+ mNai.asyncChannel.sendMessage(CMD_STOP_PACKET_KEEPALIVE, mSlot);
+ }
+ notifyMessenger(mSlot, reason);
+ unlinkDeathRecipient();
+ }
+ }
+
+ void notifyMessenger(Messenger messenger, int slot, int err) {
+ Message message = Message.obtain();
+ message.what = EVENT_PACKET_KEEPALIVE;
+ message.arg1 = slot;
+ message.arg2 = err;
+ message.obj = null;
+ try {
+ messenger.send(message);
+ } catch (RemoteException e) {
+ // Process died?
+ }
+ }
+
+ private int findFirstFreeSlot(NetworkAgentInfo nai) {
+ HashMap networkKeepalives = mKeepalives.get(nai);
+ if (networkKeepalives == null) {
+ networkKeepalives = new HashMap<Integer, KeepaliveInfo>();
+ mKeepalives.put(nai, networkKeepalives);
+ }
+
+ // Find the lowest-numbered free slot.
+ int slot;
+ for (slot = 0; slot < networkKeepalives.size(); slot++) {
+ if (networkKeepalives.get(slot) == null) {
+ return slot;
+ }
+ }
+ // No free slot, pick one at the end.
+
+ // HACK for broadcom hardware that does not support slot 0!
+ if (slot == 0) slot = 1;
+ return slot;
+ }
+
+ public void handleStartKeepalive(Message message) {
+ KeepaliveInfo ki = (KeepaliveInfo) message.obj;
+ NetworkAgentInfo nai = ki.getNai();
+ int slot = findFirstFreeSlot(nai);
+ mKeepalives.get(nai).put(slot, ki);
+ ki.start(slot);
+ }
+
+ public void handleStopAllKeepalives(NetworkAgentInfo nai, int reason) {
+ HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
+ if (networkKeepalives != null) {
+ for (KeepaliveInfo ki : networkKeepalives.values()) {
+ ki.stop(reason);
+ }
+ networkKeepalives.clear();
+ mKeepalives.remove(nai);
+ }
+ }
+
+ public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
+ HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
+ if (networkKeepalives == null) {
+ Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + nai.name());
+ return;
+ }
+ KeepaliveInfo ki = networkKeepalives.get(slot);
+ if (ki == null) {
+ Log.e(TAG, "Attempt to stop nonexistent keepalive " + slot + " on " + nai.name());
+ return;
+ }
+ ki.stop(reason);
+ networkKeepalives.remove(slot);
+ if (networkKeepalives.isEmpty()) {
+ mKeepalives.remove(nai);
+ }
+ }
+
+ public void handleCheckKeepalivesStillValid(NetworkAgentInfo nai) {
+ HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
+ if (networkKeepalives != null) {
+ ArrayList<Pair<Integer, Integer>> invalidKeepalives = new ArrayList<>();
+ for (int slot : networkKeepalives.keySet()) {
+ int error = networkKeepalives.get(slot).isValid();
+ if (error != SUCCESS) {
+ invalidKeepalives.add(Pair.create(slot, error));
+ }
+ }
+ for (Pair<Integer, Integer> slotAndError: invalidKeepalives) {
+ handleStopKeepalive(nai, slotAndError.first, slotAndError.second);
+ }
+ }
+ }
+
+ public void handleEventPacketKeepalive(NetworkAgentInfo nai, Message message) {
+ int slot = message.arg1;
+ int reason = message.arg2;
+
+ KeepaliveInfo ki = null;
+ try {
+ ki = mKeepalives.get(nai).get(slot);
+ } catch(NullPointerException e) {}
+ if (ki == null) {
+ Log.e(TAG, "Event for unknown keepalive " + slot + " on " + nai.name());
+ return;
+ }
+
+ if (reason == SUCCESS && !ki.isStarted) {
+ // Keepalive successfully started.
+ if (DBG) Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
+ ki.isStarted = true;
+ ki.notifyMessenger(slot, reason);
+ } else {
+ // Keepalive successfully stopped, or error.
+ ki.isStarted = false;
+ if (reason == SUCCESS) {
+ if (DBG) Log.d(TAG, "Successfully stopped keepalive " + slot + " on " + nai.name());
+ } else {
+ if (DBG) Log.d(TAG, "Keepalive " + slot + " on " + nai.name() + " error " + reason);
+ }
+ handleStopKeepalive(nai, slot, reason);
+ }
+ }
+
+ public void startNattKeepalive(NetworkAgentInfo nai, int intervalSeconds, Messenger messenger,
+ IBinder binder, String srcAddrString, int srcPort, String dstAddrString, int dstPort) {
+ InetAddress srcAddress, dstAddress;
+ try {
+ srcAddress = NetworkUtils.numericToInetAddress(srcAddrString);
+ dstAddress = NetworkUtils.numericToInetAddress(dstAddrString);
+ } catch (IllegalArgumentException e) {
+ notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_IP_ADDRESS);
+ return;
+ }
+
+ KeepalivePacketData packet;
+ try {
+ packet = KeepalivePacketData.nattKeepalivePacket(
+ srcAddress, srcPort, dstAddress, NATT_PORT);
+ } catch (KeepalivePacketData.InvalidPacketException e) {
+ notifyMessenger(messenger, NO_KEEPALIVE, e.error);
+ return;
+ }
+ KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds);
+ Log.d(TAG, "Created keepalive: " + ki.toString());
+ mConnectivityServiceHandler.obtainMessage(
+ NetworkAgent.CMD_START_PACKET_KEEPALIVE, ki).sendToTarget();
+ }
+
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("Packet keepalives:");
+ pw.increaseIndent();
+ for (NetworkAgentInfo nai : mKeepalives.keySet()) {
+ pw.println(nai.name());
+ pw.increaseIndent();
+ for (int slot : mKeepalives.get(nai).keySet()) {
+ KeepaliveInfo ki = mKeepalives.get(nai).get(slot);
+ pw.println(slot + ": " + ki.toString());
+ }
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 39333f6..0029279 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -195,6 +195,12 @@
request.networkCapabilities.satisfiedByNetworkCapabilities(networkCapabilities);
}
+ public boolean satisfiesImmutableCapabilitiesOf(NetworkRequest request) {
+ return created &&
+ request.networkCapabilities.satisfiedByImmutableNetworkCapabilities(
+ networkCapabilities);
+ }
+
public boolean isVPN() {
return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
}
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 2c9d82b..470bd5a 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -16,11 +16,11 @@
package com.android.server.fingerprint;
+import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
import android.app.IUserSwitchObserver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
@@ -29,8 +29,6 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.IRemoteCallback;
-import android.os.Looper;
-import android.os.MessageQueue;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SELinux;
@@ -40,20 +38,28 @@
import android.os.UserManager;
import android.util.Slog;
+import com.android.internal.logging.MetricsLogger;
import com.android.server.SystemService;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.fingerprint.IFingerprintDaemon;
import android.hardware.fingerprint.IFingerprintDaemonCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
+import android.view.Display;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
+import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
import static android.Manifest.permission.USE_FINGERPRINT;
import java.io.File;
-import java.util.ArrayList;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -250,6 +256,9 @@
Slog.v(TAG, "Reset fingerprint lockout");
}
mFailedAttempts = 0;
+ // If we're asked to reset failed attempts externally (i.e. from Keyguard), the runnable
+ // may still be in the queue; remove it.
+ mHandler.removeCallbacks(mLockoutReset);
}
private boolean handleFailedAttempt(ClientMonitor clientMonitor) {
@@ -289,6 +298,7 @@
final int result = daemon.enroll(cryptoToken, groupId, timeout);
if (result != 0) {
Slog.w(TAG, "startEnroll failed, result=" + result);
+ dispatchError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
}
} catch (RemoteException e) {
Slog.e(TAG, "startEnroll failed", e);
@@ -382,6 +392,7 @@
final int result = daemon.authenticate(opId, groupId);
if (result != 0) {
Slog.w(TAG, "startAuthentication failed, result=" + result);
+ dispatchError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
}
} catch (RemoteException e) {
Slog.e(TAG, "startAuthentication failed", e);
@@ -424,12 +435,14 @@
return;
}
+ stopPendingOperations(true);
mRemoveClient = new ClientMonitor(token, receiver, userId, restricted);
// The fingerprint template ids will be removed when we get confirmation from the HAL
try {
final int result = daemon.remove(fingerId, userId);
if (result != 0) {
Slog.w(TAG, "startRemove with id = " + fingerId + " failed, result=" + result);
+ dispatchError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
}
} catch (RemoteException e) {
Slog.e(TAG, "startRemove failed", e);
@@ -557,6 +570,7 @@
private boolean sendEnrollResult(int fpId, int groupId, int remaining) {
if (receiver == null) return true; // client not listening
FingerprintUtils.vibrateFingerprintSuccess(getContext());
+ MetricsLogger.action(mContext, MetricsLogger.ACTION_FINGERPRINT_ENROLL);
try {
receiver.onEnrollResult(mHalDeviceId, fpId, groupId, remaining);
return remaining == 0;
@@ -574,6 +588,8 @@
boolean authenticated = fpId != 0;
if (receiver != null) {
try {
+ MetricsLogger.action(mContext, MetricsLogger.ACTION_FINGERPRINT_AUTH,
+ authenticated);
if (!authenticated) {
receiver.onAuthenticationFailed(mHalDeviceId);
} else {
@@ -589,10 +605,14 @@
result = true; // client not listening
}
if (fpId == 0) {
- FingerprintUtils.vibrateFingerprintError(getContext());
+ if (receiver != null) {
+ FingerprintUtils.vibrateFingerprintError(getContext());
+ }
result |= handleFailedAttempt(this);
} else {
- FingerprintUtils.vibrateFingerprintSuccess(getContext());
+ if (receiver != null) {
+ FingerprintUtils.vibrateFingerprintSuccess(getContext());
+ }
result |= true; // we have a valid fingerprint
mLockoutReset.run();
}
@@ -665,7 +685,6 @@
public void onEnumerate(long deviceId, int[] fingerIds, int[] groupIds) {
dispatchEnumerate(deviceId, fingerIds, groupIds);
}
-
};
private final class FingerprintServiceWrapper extends IFingerprintService.Stub {
@@ -749,6 +768,7 @@
mHandler.post(new Runnable() {
@Override
public void run() {
+ MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
startAuthentication(token, opId, effectiveGroupId, receiver, flags, restricted);
}
});
@@ -849,6 +869,52 @@
return FingerprintService.this.getAuthenticatorId();
}
+
+ @Override // Binder call
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump Fingerprint from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ dumpInternal(pw);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ @Override // Binder call
+ public void resetTimeout(byte [] token) {
+ checkPermission(RESET_FINGERPRINT_LOCKOUT);
+ // TODO: confirm security token when we move timeout management into the HAL layer.
+ mLockoutReset.run();
+ }
+ }
+
+ private void dumpInternal(PrintWriter pw) {
+ JSONObject dump = new JSONObject();
+ try {
+ dump.put("service", "Fingerprint Manager");
+
+ JSONArray sets = new JSONArray();
+ for (UserInfo user : UserManager.get(getContext()).getUsers()) {
+ final int userId = user.getUserHandle().getIdentifier();
+ final int N = mFingerprintUtils.getFingerprintsForUser(mContext, userId).size();
+ JSONObject set = new JSONObject();
+ set.put("id", userId);
+ set.put("count", N);
+ sets.put(set);
+ }
+
+ dump.put("prints", sets);
+ } catch (JSONException e) {
+ Slog.e(TAG, "dump formatting failure", e);
+ }
+ pw.println(dump);
}
@Override
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
index d274412..49dc8e4 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.hardware.fingerprint.Fingerprint;
import android.os.Vibrator;
+import android.text.TextUtils;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -64,6 +65,10 @@
}
public void renameFingerprintForUser(Context ctx, int fingerId, int userId, CharSequence name) {
+ if (TextUtils.isEmpty(name)) {
+ // Don't do the rename if it's empty
+ return;
+ }
getStateForUser(ctx, userId).renameFingerprint(fingerId, name);
}
diff --git a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
index 607805b..2eb58bf 100644
--- a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
+++ b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
@@ -20,8 +20,10 @@
import android.content.Context;
import android.hardware.location.ActivityRecognitionHardware;
+import android.hardware.location.IActivityRecognitionHardwareClient;
import android.hardware.location.IActivityRecognitionHardwareWatcher;
import android.os.Handler;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -34,21 +36,24 @@
private static final String TAG = "ActivityRecognitionProxy";
private final ServiceWatcher mServiceWatcher;
- private final ActivityRecognitionHardware mActivityRecognitionHardware;
+ private final boolean mIsSupported;
+ private final ActivityRecognitionHardware mInstance;
private ActivityRecognitionProxy(
Context context,
Handler handler,
+ boolean activityRecognitionHardwareIsSupported,
ActivityRecognitionHardware activityRecognitionHardware,
int overlaySwitchResId,
int defaultServicePackageNameResId,
int initialPackageNameResId) {
- mActivityRecognitionHardware = activityRecognitionHardware;
+ mIsSupported = activityRecognitionHardwareIsSupported;
+ mInstance = activityRecognitionHardware;
Runnable newServiceWork = new Runnable() {
@Override
public void run() {
- bindProvider(mActivityRecognitionHardware);
+ bindProvider();
}
};
@@ -72,6 +77,7 @@
public static ActivityRecognitionProxy createAndBind(
Context context,
Handler handler,
+ boolean activityRecognitionHardwareIsSupported,
ActivityRecognitionHardware activityRecognitionHardware,
int overlaySwitchResId,
int defaultServicePackageNameResId,
@@ -79,6 +85,7 @@
ActivityRecognitionProxy activityRecognitionProxy = new ActivityRecognitionProxy(
context,
handler,
+ activityRecognitionHardwareIsSupported,
activityRecognitionHardware,
overlaySwitchResId,
defaultServicePackageNameResId,
@@ -89,25 +96,52 @@
Log.e(TAG, "ServiceWatcher could not start.");
return null;
}
-
return activityRecognitionProxy;
}
/**
* Helper function to bind the FusedLocationHardware to the appropriate FusedProvider instance.
*/
- private void bindProvider(ActivityRecognitionHardware activityRecognitionHardware) {
- IActivityRecognitionHardwareWatcher watcher =
- IActivityRecognitionHardwareWatcher.Stub.asInterface(mServiceWatcher.getBinder());
- if (watcher == null) {
- Log.e(TAG, "No provider instance found on connection.");
+ private void bindProvider() {
+ IBinder binder = mServiceWatcher.getBinder();
+ if (binder == null) {
+ Log.e(TAG, "Null binder found on connection.");
+ return;
+ }
+ String descriptor;
+ try {
+ descriptor = binder.getInterfaceDescriptor();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to get interface descriptor.", e);
return;
}
- try {
- watcher.onInstanceChanged(mActivityRecognitionHardware);
- } catch (RemoteException e) {
- Log.e(TAG, "Error delivering hardware interface.", e);
+ if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(descriptor)) {
+ IActivityRecognitionHardwareWatcher watcher =
+ IActivityRecognitionHardwareWatcher.Stub.asInterface(binder);
+ if (watcher == null) {
+ Log.e(TAG, "No watcher found on connection.");
+ return;
+ }
+ try {
+ watcher.onInstanceChanged(mInstance);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error delivering hardware interface to watcher.", e);
+ }
+ } else if (IActivityRecognitionHardwareClient.class.getCanonicalName().equals(descriptor)) {
+ IActivityRecognitionHardwareClient client =
+ IActivityRecognitionHardwareClient.Stub.asInterface(binder);
+ if (client == null) {
+ Log.e(TAG, "No client found on connection.");
+ return;
+ }
+ try {
+ client.onAvailabilityChanged(mIsSupported, mInstance);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error delivering hardware interface to client.", e);
+ }
+ } else {
+ Log.e(TAG, "Invalid descriptor found on connection: " + descriptor);
}
}
}
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index ba5f516..02f2c73 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -766,6 +766,10 @@
&& mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
if (mNetworkAvailable) {
String apnName = info.getExtraInfo();
+ // APN wasn't found in the intent, try to get it from the content provider.
+ if (apnName == null) {
+ apnName = getSelectedApn();
+ }
if (apnName == null) {
/* Assign a dummy value in the case of C2K as otherwise we will have a runtime
exception in the following call to native_agps_data_conn_open*/
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 5c1878e..8845d6e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -108,6 +108,7 @@
import android.net.NetworkIdentity;
import android.net.NetworkInfo;
import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
import android.net.NetworkQuotaInfo;
import android.net.NetworkState;
import android.net.NetworkTemplate;
@@ -201,6 +202,8 @@
private static final int VERSION_LATEST = VERSION_SWITCH_UID;
@VisibleForTesting
+ public static final int TYPE_NONE = 0;
+ @VisibleForTesting
public static final int TYPE_WARNING = 0x1;
@VisibleForTesting
public static final int TYPE_LIMIT = 0x2;
@@ -260,6 +263,9 @@
private PowerManagerInternal mPowerManagerInternal;
private IDeviceIdleController mDeviceIdleController;
+ private final ComponentName mNotificationComponent;
+ private int mNotificationSequenceNumber;
+
final Object mRulesLock = new Object();
volatile boolean mSystemReady;
@@ -357,6 +363,11 @@
mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"));
mAppOps = context.getSystemService(AppOpsManager.class);
+
+ final String notificationComponent = context.getString(
+ R.string.config_networkPolicyNotificationComponent);
+ mNotificationComponent = notificationComponent != null
+ ? ComponentName.unflattenFromString(notificationComponent) : null;
}
public void bindConnectivityManager(IConnectivityManager connManager) {
@@ -778,6 +789,11 @@
final ArraySet<String> beforeNotifs = new ArraySet<String>(mActiveNotifs);
mActiveNotifs.clear();
+ // increment the sequence number so custom components know
+ // this update is new
+ mNotificationSequenceNumber++;
+ boolean hasNotifications = false;
+
// TODO: when switching to kernel notifications, compute next future
// cycle boundary to recompute notifications.
@@ -794,6 +810,7 @@
final long totalBytes = getTotalBytes(policy.template, start, end);
if (policy.isOverLimit(totalBytes)) {
+ hasNotifications = true;
if (policy.lastLimitSnooze >= start) {
enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes);
} else {
@@ -806,10 +823,18 @@
if (policy.isOverWarning(totalBytes) && policy.lastWarningSnooze < start) {
enqueueNotification(policy, TYPE_WARNING, totalBytes);
+ hasNotifications = true;
}
}
}
+ // right now we don't care about restricted background notifications
+ // in the custom notification component, so trigger an update now
+ // if we didn't update anything this pass
+ if (!hasNotifications) {
+ sendNotificationToCustomComponent(null, TYPE_NONE, 0);
+ }
+
// ongoing notification when restricting background data
if (mRestrictBackground) {
enqueueRestrictedNotification(TAG_ALLOW_BACKGROUND);
@@ -856,6 +881,12 @@
* {@link NetworkPolicy#limitBytes}, potentially showing dialog to user.
*/
private void notifyOverLimitLocked(NetworkTemplate template) {
+ if (mNotificationComponent != null) {
+ // It is the job of the notification component to handle UI,
+ // so we do nothing here
+ return;
+ }
+
if (!mOverLimitNotified.contains(template)) {
mContext.startActivity(buildNetworkOverLimitIntent(template));
mOverLimitNotified.add(template);
@@ -874,11 +905,55 @@
return TAG + ":" + policy.template.hashCode() + ":" + type;
}
+ private boolean sendNotificationToCustomComponent(
+ NetworkPolicy policy,
+ int type,
+ long totalBytes) {
+ if (mNotificationComponent == null) {
+ return false;
+ }
+
+ Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.setComponent(mNotificationComponent);
+
+ int notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_NONE;
+ switch (type) {
+ case TYPE_WARNING:
+ notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_USAGE_WARNING;
+ break;
+ case TYPE_LIMIT:
+ notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_USAGE_REACHED_LIMIT;
+ break;
+ case TYPE_LIMIT_SNOOZED:
+ notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_USAGE_EXCEEDED_LIMIT;
+ break;
+ }
+
+ intent.setAction(NetworkPolicyManager.ACTION_SHOW_NETWORK_POLICY_NOTIFICATION);
+ intent.putExtra(NetworkPolicyManager.EXTRA_NOTIFICATION_TYPE, notificationType);
+ intent.putExtra(
+ NetworkPolicyManager.EXTRA_NOTIFICATION_SEQUENCE_NUMBER,
+ mNotificationSequenceNumber);
+
+ if (notificationType != NetworkPolicyManager.NOTIFICATION_TYPE_NONE) {
+ intent.putExtra(NetworkPolicyManager.EXTRA_NETWORK_POLICY, policy);
+ intent.putExtra(NetworkPolicyManager.EXTRA_BYTES_USED, totalBytes);
+ }
+
+ mContext.sendBroadcast(intent);
+ return true;
+ }
+
/**
* Show notification for combined {@link NetworkPolicy} and specific type,
* like {@link #TYPE_LIMIT}. Okay to call multiple times.
*/
private void enqueueNotification(NetworkPolicy policy, int type, long totalBytes) {
+ if (sendNotificationToCustomComponent(policy, type, totalBytes)) {
+ return;
+ }
+
final String tag = buildNotificationTag(policy, type);
final Notification.Builder builder = new Notification.Builder(mContext);
builder.setOnlyAlertOnce(true);
@@ -1738,6 +1813,19 @@
}
}
+ @Override
+ public void snoozeWarning(NetworkTemplate template) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // TODO: this seems like a race condition? (along with snoozeLimit above)
+ performSnooze(template, TYPE_WARNING);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
void performSnooze(NetworkTemplate template, int type) {
maybeRefreshTrustedTime();
final long currentTime = currentTimeMillis();
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 57d7758..2fda745 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -411,8 +411,7 @@
applyRestrictions(muteNotifications, USAGE_NOTIFICATION);
// call restrictions
- final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers
- || mEffectsSuppressed;
+ final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers;
applyRestrictions(muteCalls, USAGE_NOTIFICATION_RINGTONE);
// alarm restrictions
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f67f3f3..e1bd9f9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4626,7 +4626,7 @@
if (result == null) {
result = new CrossProfileDomainInfo();
result.resolveInfo =
- createForwardingResolveInfo(null, sourceUserId, parentUserId);
+ createForwardingResolveInfo(new IntentFilter(), sourceUserId, parentUserId);
result.bestDomainVerificationStatus = status;
} else {
result.bestDomainVerificationStatus = bestDomainVerificationStatus(status,
@@ -15774,7 +15774,7 @@
if (userDir.exists()) continue;
try {
- UserManagerService.prepareUserDirectory(userDir);
+ UserManagerService.prepareUserDirectory(mContext, volumeUuid, user.id);
UserManagerService.enforceSerialNumber(userDir, user.serialNumber);
} catch (IOException e) {
Log.wtf(TAG, "Failed to create user directory on " + volumeUuid, e);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index a762014..943e649 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -38,10 +38,13 @@
import android.os.Build;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.IBinder;
import android.os.Handler;
import android.os.Message;
import android.os.PatternMatcher;
import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 0607525..06c3682 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1285,7 +1285,7 @@
try {
final File userDir = Environment.getDataUserDirectory(volumeUuid,
userId);
- prepareUserDirectory(userDir);
+ prepareUserDirectory(mContext, volumeUuid, userId);
enforceSerialNumber(userDir, userInfo.serialNumber);
} catch (IOException e) {
Log.wtf(LOG_TAG, "Failed to create user directory on " + volumeUuid, e);
@@ -1493,6 +1493,8 @@
}
private void removeUserStateLocked(final int userHandle) {
+ mContext.getSystemService(StorageManager.class)
+ .deleteUserKey(userHandle);
// Cleanup package manager settings
mPm.cleanUpUserLILPw(this, userHandle);
@@ -1899,16 +1901,10 @@
* Create new {@code /data/user/[id]} directory and sets default
* permissions.
*/
- public static void prepareUserDirectory(File file) throws IOException {
- if (!file.exists()) {
- if (!file.mkdir()) {
- throw new IOException("Failed to create " + file);
- }
- }
- if (FileUtils.setPermissions(file.getAbsolutePath(), 0771, Process.SYSTEM_UID,
- Process.SYSTEM_UID) != 0) {
- throw new IOException("Failed to prepare " + file);
- }
+ public static void prepareUserDirectory(Context context, String volumeUuid, int userId) {
+ final StorageManager storage = context.getSystemService(StorageManager.class);
+ final File userDir = Environment.getDataUserDirectory(volumeUuid, userId);
+ storage.createNewUserDir(userId, userDir);
}
/**
diff --git a/services/core/java/com/android/server/policy/BurnInProtectionHelper.java b/services/core/java/com/android/server/policy/BurnInProtectionHelper.java
index fef1e575..e6ec6a6 100644
--- a/services/core/java/com/android/server/policy/BurnInProtectionHelper.java
+++ b/services/core/java/com/android/server/policy/BurnInProtectionHelper.java
@@ -72,6 +72,9 @@
/* 1 means increasing, -1 means decreasing */
private int mYOffsetDirection = 1;
+ private int mAppliedBurnInXOffset = 0;
+ private int mAppliedBurnInYOffset = 0;
+
private final AlarmManager mAlarmManager;
private final PendingIntent mBurnInProtectionIntent;
private final DisplayManagerInternal mDisplayManagerInternal;
@@ -139,6 +142,8 @@
mFirstUpdate = false;
} else {
adjustOffsets();
+ mAppliedBurnInXOffset = mLastBurnInXOffset;
+ mAppliedBurnInYOffset = mLastBurnInYOffset;
mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(),
mLastBurnInXOffset, mLastBurnInYOffset);
}
@@ -258,6 +263,8 @@
@Override
public void onAnimationEnd(Animator animator) {
if (animator == mCenteringAnimator && !mBurnInProtectionActive) {
+ mAppliedBurnInXOffset = 0;
+ mAppliedBurnInYOffset = 0;
// No matter how the animation finishes, we want to zero the offsets.
mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(), 0, 0);
}
@@ -276,7 +283,7 @@
if (!mBurnInProtectionActive) {
final float value = (Float) valueAnimator.getAnimatedValue();
mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(),
- (int) (mLastBurnInXOffset * value), (int) (mLastBurnInYOffset * value));
+ (int) (mAppliedBurnInXOffset * value), (int) (mAppliedBurnInYOffset * value));
}
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 489bcdb..0423aa3 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4410,7 +4410,7 @@
if (mAppsToBeHidden.isEmpty()) {
if (dismissKeyguard && !mKeyguardSecure) {
mAppsThatDismissKeyguard.add(appToken);
- } else {
+ } else if (win.isDrawnLw()) {
mWinShowWhenLocked = win;
mHideLockScreen = true;
mForceStatusBarFromKeyguard = false;
@@ -4444,7 +4444,7 @@
mWinDismissingKeyguard = win;
mSecureDismissingKeyguard = mKeyguardSecure;
mForceStatusBarFromKeyguard = mShowingLockscreen && mKeyguardSecure;
- } else if (mAppsToBeHidden.isEmpty() && showWhenLocked) {
+ } else if (mAppsToBeHidden.isEmpty() && showWhenLocked && win.isDrawnLw()) {
if (DEBUG_LAYOUT) Slog.v(TAG,
"Setting mHideLockScreen to true by win " + win);
mHideLockScreen = true;
@@ -6085,6 +6085,7 @@
}
startedWakingUp();
screenTurningOn(null);
+ screenTurnedOn();
}
ProgressDialog mBootMsgDialog = null;
diff --git a/services/core/java/com/android/server/policy/StatusBarController.java b/services/core/java/com/android/server/policy/StatusBarController.java
index d1b50da..b1ae922 100644
--- a/services/core/java/com/android/server/policy/StatusBarController.java
+++ b/services/core/java/com/android/server/policy/StatusBarController.java
@@ -72,7 +72,9 @@
if (statusbar != null) {
long startTime = calculateStatusBarTransitionStartTime(openAnimation,
closeAnimation);
- statusbar.appTransitionStarting(startTime, TRANSITION_DURATION);
+ long duration = closeAnimation != null || openAnimation != null
+ ? TRANSITION_DURATION : 0;
+ statusbar.appTransitionStarting(startTime, duration);
}
} catch (RemoteException e) {
Slog.e(mTag, "RemoteException when app transition is starting", e);
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index c71b48f..9916223 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -24,10 +24,12 @@
import android.os.Handler;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.text.TextUtils;
import android.util.Slog;
import java.io.PrintWriter;
import java.util.Arrays;
+import java.util.List;
/**
* A special helper class used by the WindowManager
@@ -52,8 +54,9 @@
private SensorManager mSensorManager;
private boolean mEnabled;
private int mRate;
+ private String mSensorType;
private Sensor mSensor;
- private SensorEventListenerImpl mSensorEventListener;
+ private OrientationJudge mOrientationJudge;
private int mCurrentRotation = -1;
private final Object mLock = new Object();
@@ -67,7 +70,7 @@
public WindowOrientationListener(Context context, Handler handler) {
this(context, handler, SensorManager.SENSOR_DELAY_UI);
}
-
+
/**
* Creates a new WindowOrientationListener.
*
@@ -84,11 +87,31 @@
mHandler = handler;
mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
mRate = rate;
- mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR
- ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);
- if (mSensor != null) {
- // Create listener only if sensors do exist
- mSensorEventListener = new SensorEventListenerImpl(context);
+
+ mSensorType = context.getResources().getString(
+ com.android.internal.R.string.config_orientationSensorType);
+ if (!TextUtils.isEmpty(mSensorType)) {
+ List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+ final int N = sensors.size();
+ for (int i = 0; i < N; i++) {
+ Sensor sensor = sensors.get(i);
+ if (mSensorType.equals(sensor.getStringType())) {
+ mSensor = sensor;
+ break;
+ }
+ }
+ if (mSensor != null) {
+ mOrientationJudge = new OrientationSensorJudge();
+ }
+ }
+
+ if (mOrientationJudge == null) {
+ mSensor = mSensorManager.getDefaultSensor(USE_GRAVITY_SENSOR
+ ? Sensor.TYPE_GRAVITY : Sensor.TYPE_ACCELEROMETER);
+ if (mSensor != null) {
+ // Create listener only if sensors do exist
+ mOrientationJudge = new AccelSensorJudge(context);
+ }
}
}
@@ -106,8 +129,8 @@
if (LOG) {
Slog.d(TAG, "WindowOrientationListener enabled");
}
- mSensorEventListener.resetLocked();
- mSensorManager.registerListener(mSensorEventListener, mSensor, mRate, mHandler);
+ mOrientationJudge.resetLocked();
+ mSensorManager.registerListener(mOrientationJudge, mSensor, mRate, mHandler);
mEnabled = true;
}
}
@@ -126,7 +149,7 @@
if (LOG) {
Slog.d(TAG, "WindowOrientationListener disabled");
}
- mSensorManager.unregisterListener(mSensorEventListener);
+ mSensorManager.unregisterListener(mOrientationJudge);
mEnabled = false;
}
}
@@ -134,8 +157,8 @@
public void onTouchStart() {
synchronized (mLock) {
- if (mSensorEventListener != null) {
- mSensorEventListener.onTouchStartLocked();
+ if (mOrientationJudge != null) {
+ mOrientationJudge.onTouchStartLocked();
}
}
}
@@ -144,8 +167,8 @@
long whenElapsedNanos = SystemClock.elapsedRealtimeNanos();
synchronized (mLock) {
- if (mSensorEventListener != null) {
- mSensorEventListener.onTouchEndLocked(whenElapsedNanos);
+ if (mOrientationJudge != null) {
+ mOrientationJudge.onTouchEndLocked(whenElapsedNanos);
}
}
}
@@ -172,7 +195,7 @@
public int getProposedRotation() {
synchronized (mLock) {
if (mEnabled) {
- return mSensorEventListener.getProposedRotationLocked();
+ return mOrientationJudge.getProposedRotationLocked();
}
return -1;
}
@@ -194,6 +217,8 @@
* It is called each time the orientation determination transitions from being
* uncertain to being certain again, even if it is the same orientation as before.
*
+ * This should only be called on the Handler thread.
+ *
* @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants.
* @see android.view.Surface
*/
@@ -205,15 +230,77 @@
prefix += " ";
pw.println(prefix + "mEnabled=" + mEnabled);
pw.println(prefix + "mCurrentRotation=" + mCurrentRotation);
+ pw.println(prefix + "mSensorType=" + mSensorType);
pw.println(prefix + "mSensor=" + mSensor);
pw.println(prefix + "mRate=" + mRate);
- if (mSensorEventListener != null) {
- mSensorEventListener.dumpLocked(pw, prefix);
+ if (mOrientationJudge != null) {
+ mOrientationJudge.dumpLocked(pw, prefix);
}
}
}
+ abstract class OrientationJudge implements SensorEventListener {
+ // Number of nanoseconds per millisecond.
+ protected static final long NANOS_PER_MS = 1000000;
+
+ // Number of milliseconds per nano second.
+ protected static final float MILLIS_PER_NANO = 0.000001f;
+
+ // The minimum amount of time that must have elapsed since the screen was last touched
+ // before the proposed rotation can change.
+ protected static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS =
+ 500 * NANOS_PER_MS;
+
+ /**
+ * Gets the proposed rotation.
+ *
+ * This method only returns a rotation if the orientation listener is certain
+ * of its proposal. If the rotation is indeterminate, returns -1.
+ *
+ * Should only be called when holding WindowOrientationListener lock.
+ *
+ * @return The proposed rotation, or -1 if unknown.
+ */
+ public abstract int getProposedRotationLocked();
+
+ /**
+ * Notifies the orientation judge that the screen is being touched.
+ *
+ * Should only be called when holding WindowOrientationListener lock.
+ */
+ public abstract void onTouchStartLocked();
+
+ /**
+ * Notifies the orientation judge that the screen is no longer being touched.
+ *
+ * Should only be called when holding WindowOrientationListener lock.
+ *
+ * @param whenElapsedNanos Given in the elapsed realtime nanos time base.
+ */
+ public abstract void onTouchEndLocked(long whenElapsedNanos);
+
+ /**
+ * Resets the state of the judge.
+ *
+ * Should only be called when holding WindowOrientationListener lock.
+ */
+ public abstract void resetLocked();
+
+ /**
+ * Dumps internal state of the orientation judge.
+ *
+ * Should only be called when holding WindowOrientationListener lock.
+ */
+ public abstract void dumpLocked(PrintWriter pw, String prefix);
+
+ @Override
+ public abstract void onAccuracyChanged(Sensor sensor, int accuracy);
+
+ @Override
+ public abstract void onSensorChanged(SensorEvent event);
+ }
+
/**
* This class filters the raw accelerometer data and tries to detect actual changes in
* orientation. This is a very ill-defined problem so there are a lot of tweakable parameters,
@@ -252,13 +339,10 @@
* See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for
* signal processing background.
*/
- final class SensorEventListenerImpl implements SensorEventListener {
+ final class AccelSensorJudge extends OrientationJudge {
// We work with all angles in degrees in this class.
private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI);
- // Number of nanoseconds per millisecond.
- private static final long NANOS_PER_MS = 1000000;
-
// Indices into SensorEvent.values for the accelerometer sensor.
private static final int ACCELEROMETER_DATA_X = 0;
private static final int ACCELEROMETER_DATA_Y = 1;
@@ -286,11 +370,6 @@
private static final long PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS =
500 * NANOS_PER_MS;
- // The minimum amount of time that must have elapsed since the screen was last touched
- // before the proposed rotation can change.
- private static final long PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS =
- 500 * NANOS_PER_MS;
-
// If the tilt angle remains greater than the specified angle for a minimum of
// the specified time, then the device is deemed to be lying flat
// (just chillin' on a table).
@@ -434,7 +513,7 @@
private long[] mTiltHistoryTimestampNanos = new long[TILT_HISTORY_SIZE];
private int mTiltHistoryIndex;
- public SensorEventListenerImpl(Context context) {
+ public AccelSensorJudge(Context context) {
// Load tilt tolerance configuration.
int[] tiltTolerance = context.getResources().getIntArray(
com.android.internal.R.array.config_autoRotationTiltTolerance);
@@ -455,11 +534,15 @@
}
}
+ @Override
public int getProposedRotationLocked() {
return mProposedRotation;
}
+ @Override
public void dumpLocked(PrintWriter pw, String prefix) {
+ pw.println(prefix + "AccelSensorJudge");
+ prefix += " ";
pw.println(prefix + "mProposedRotation=" + mProposedRotation);
pw.println(prefix + "mPredictedRotation=" + mPredictedRotation);
pw.println(prefix + "mLastFilteredX=" + mLastFilteredX);
@@ -689,6 +772,33 @@
}
}
+ @Override
+ public void onTouchStartLocked() {
+ mTouched = true;
+ }
+
+ @Override
+ public void onTouchEndLocked(long whenElapsedNanos) {
+ mTouched = false;
+ mTouchEndedTimestampNanos = whenElapsedNanos;
+ }
+
+ @Override
+ public void resetLocked() {
+ mLastFilteredTimestampNanos = Long.MIN_VALUE;
+ mProposedRotation = -1;
+ mFlatTimestampNanos = Long.MIN_VALUE;
+ mFlat = false;
+ mSwingTimestampNanos = Long.MIN_VALUE;
+ mSwinging = false;
+ mAccelerationTimestampNanos = Long.MIN_VALUE;
+ mAccelerating = false;
+ mOverhead = false;
+ clearPredictedRotationLocked();
+ clearTiltHistoryLocked();
+ }
+
+
/**
* Returns true if the tilt angle is acceptable for a given predicted rotation.
*/
@@ -787,20 +897,6 @@
return true;
}
- private void resetLocked() {
- mLastFilteredTimestampNanos = Long.MIN_VALUE;
- mProposedRotation = -1;
- mFlatTimestampNanos = Long.MIN_VALUE;
- mFlat = false;
- mSwingTimestampNanos = Long.MIN_VALUE;
- mSwinging = false;
- mAccelerationTimestampNanos = Long.MIN_VALUE;
- mAccelerating = false;
- mOverhead = false;
- clearPredictedRotationLocked();
- clearTiltHistoryLocked();
- }
-
private void clearPredictedRotationLocked() {
mPredictedRotation = -1;
mPredictedRotationTimestampNanos = Long.MIN_VALUE;
@@ -869,14 +965,147 @@
private float remainingMS(long now, long until) {
return now >= until ? 0 : (until - now) * 0.000001f;
}
+ }
- private void onTouchStartLocked() {
- mTouched = true;
+ final class OrientationSensorJudge extends OrientationJudge {
+ private boolean mTouching;
+ private long mTouchEndedTimestampNanos = Long.MIN_VALUE;
+ private int mProposedRotation = -1;
+ private int mDesiredRotation = -1;
+ private boolean mRotationEvaluationScheduled;
+
+ @Override
+ public int getProposedRotationLocked() {
+ return mProposedRotation;
}
- private void onTouchEndLocked(long whenElapsedNanos) {
- mTouched = false;
+ @Override
+ public void onTouchStartLocked() {
+ mTouching = true;
+ }
+
+ @Override
+ public void onTouchEndLocked(long whenElapsedNanos) {
+ mTouching = false;
mTouchEndedTimestampNanos = whenElapsedNanos;
+ if (mDesiredRotation != mProposedRotation) {
+ final long now = SystemClock.elapsedRealtimeNanos();
+ scheduleRotationEvaluationIfNecessaryLocked(now);
+ }
}
+
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ int newRotation;
+ synchronized (mLock) {
+ mDesiredRotation = (int) event.values[0];
+ newRotation = evaluateRotationChangeLocked();
+ }
+ if (newRotation >=0) {
+ onProposedRotationChanged(newRotation);
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) { }
+
+ @Override
+ public void dumpLocked(PrintWriter pw, String prefix) {
+ pw.println(prefix + "OrientationSensorJudge");
+ prefix += " ";
+ pw.println(prefix + "mDesiredRotation=" + mDesiredRotation);
+ pw.println(prefix + "mProposedRotation=" + mProposedRotation);
+ pw.println(prefix + "mTouching=" + mTouching);
+ pw.println(prefix + "mTouchEndedTimestampNanos=" + mTouchEndedTimestampNanos);
+ }
+
+ @Override
+ public void resetLocked() {
+ mProposedRotation = -1;
+ mDesiredRotation = -1;
+ mTouching = false;
+ mTouchEndedTimestampNanos = Long.MIN_VALUE;
+ unscheduleRotationEvaluationLocked();
+ }
+
+ public int evaluateRotationChangeLocked() {
+ unscheduleRotationEvaluationLocked();
+ if (mDesiredRotation == mProposedRotation) {
+ return -1;
+ }
+ final long now = SystemClock.elapsedRealtimeNanos();
+ if (isDesiredRotationAcceptableLocked(now)) {
+ mProposedRotation = mDesiredRotation;
+ return mProposedRotation;
+ } else {
+ scheduleRotationEvaluationIfNecessaryLocked(now);
+ }
+ return -1;
+ }
+
+ private boolean isDesiredRotationAcceptableLocked(long now) {
+ if (mTouching) {
+ return false;
+ }
+ if (now < mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS) {
+ return false;
+ }
+ return true;
+ }
+
+ private void scheduleRotationEvaluationIfNecessaryLocked(long now) {
+ if (mRotationEvaluationScheduled || mDesiredRotation == mProposedRotation) {
+ if (LOG) {
+ Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
+ "ignoring, an evaluation is already scheduled or is unnecessary.");
+ }
+ return;
+ }
+ if (mTouching) {
+ if (LOG) {
+ Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
+ "ignoring, user is still touching the screen.");
+ }
+ return;
+ }
+ long timeOfNextPossibleRotationNanos =
+ mTouchEndedTimestampNanos + PROPOSAL_MIN_TIME_SINCE_TOUCH_END_NANOS;
+ if (now >= timeOfNextPossibleRotationNanos) {
+ if (LOG) {
+ Slog.d(TAG, "scheduleRotationEvaluationLocked: " +
+ "ignoring, already past the next possible time of rotation.");
+ }
+ return;
+ }
+ // Use a delay instead of an absolute time since handlers are in uptime millis and we
+ // use elapsed realtime.
+ final long delayMs =
+ (long) Math.ceil((timeOfNextPossibleRotationNanos - now) * MILLIS_PER_NANO);
+ mHandler.postDelayed(mRotationEvaluator, delayMs);
+ mRotationEvaluationScheduled = true;
+ }
+
+ private void unscheduleRotationEvaluationLocked() {
+ if (!mRotationEvaluationScheduled) {
+ return;
+ }
+ mHandler.removeCallbacks(mRotationEvaluator);
+ mRotationEvaluationScheduled = false;
+ }
+
+ private Runnable mRotationEvaluator = new Runnable() {
+ @Override
+ public void run() {
+ int newRotation;
+ synchronized (mLock) {
+ mRotationEvaluationScheduled = false;
+ newRotation = evaluateRotationChangeLocked();
+ }
+ if (newRotation >= 0) {
+ onProposedRotationChanged(newRotation);
+ }
+ }
+ };
}
}
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 5d52307..6b45941 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -32,6 +32,14 @@
private static final String TAG = "KeyguardServiceDelegate";
private static final boolean DEBUG = true;
+ private static final int SCREEN_STATE_OFF = 0;
+ private static final int SCREEN_STATE_TURNING_ON = 1;
+ private static final int SCREEN_STATE_ON = 2;
+
+ private static final int INTERACTIVE_STATE_SLEEP = 0;
+ private static final int INTERACTIVE_STATE_AWAKE = 1;
+ private static final int INTERACTIVE_STATE_GOING_TO_SLEEP = 2;
+
protected KeyguardServiceWrapper mKeyguardService;
private final Context mContext;
private final View mScrim; // shown if keyguard crashes
@@ -61,6 +69,8 @@
public int offReason;
public int currentUser;
public boolean bootCompleted;
+ public int screenState;
+ public int interactiveState;
};
public interface DrawnListener {
@@ -144,10 +154,17 @@
// If the system is ready, it means keyguard crashed and restarted.
mKeyguardService.onSystemReady();
// This is used to hide the scrim once keyguard displays.
- mKeyguardService.onStartedWakingUp();
- mKeyguardService.onScreenTurningOn(
- new KeyguardShowDelegate(mDrawnListenerWhenConnect));
- mKeyguardService.onScreenTurnedOn();
+ if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE) {
+ mKeyguardService.onStartedWakingUp();
+ }
+ if (mKeyguardState.screenState == SCREEN_STATE_ON
+ || mKeyguardState.screenState == SCREEN_STATE_TURNING_ON) {
+ mKeyguardService.onScreenTurningOn(
+ new KeyguardShowDelegate(mDrawnListenerWhenConnect));
+ }
+ if (mKeyguardState.screenState == SCREEN_STATE_ON) {
+ mKeyguardService.onScreenTurnedOn();
+ }
mDrawnListenerWhenConnect = null;
}
if (mKeyguardState.bootCompleted) {
@@ -231,6 +248,7 @@
if (DEBUG) Log.v(TAG, "onStartedWakingUp()");
mKeyguardService.onStartedWakingUp();
}
+ mKeyguardState.interactiveState = INTERACTIVE_STATE_AWAKE;
}
public void onScreenTurnedOff() {
@@ -238,6 +256,7 @@
if (DEBUG) Log.v(TAG, "onScreenTurnedOff()");
mKeyguardService.onScreenTurnedOff();
}
+ mKeyguardState.screenState = SCREEN_STATE_OFF;
}
public void onScreenTurningOn(final DrawnListener drawnListener) {
@@ -252,6 +271,7 @@
mDrawnListenerWhenConnect = drawnListener;
showScrim();
}
+ mKeyguardState.screenState = SCREEN_STATE_TURNING_ON;
}
public void onScreenTurnedOn() {
@@ -259,6 +279,7 @@
if (DEBUG) Log.v(TAG, "onScreenTurnedOn()");
mKeyguardService.onScreenTurnedOn();
}
+ mKeyguardState.screenState = SCREEN_STATE_ON;
}
public void onStartedGoingToSleep(int why) {
@@ -266,12 +287,14 @@
mKeyguardService.onStartedGoingToSleep(why);
}
mKeyguardState.offReason = why;
+ mKeyguardState.interactiveState = INTERACTIVE_STATE_GOING_TO_SLEEP;
}
public void onFinishedGoingToSleep(int why) {
if (mKeyguardService != null) {
mKeyguardService.onFinishedGoingToSleep(why);
}
+ mKeyguardState.interactiveState = INTERACTIVE_STATE_SLEEP;
}
public void setKeyguardEnabled(boolean enabled) {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 130815e..5d01931 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -28,4 +28,5 @@
void showScreenPinningRequest();
void showAssistDisclosure();
void startAssist(Bundle args);
+ void onCameraLaunchGestureDetected();
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 2a817ea..0fb1169 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -176,6 +176,16 @@
}
}
}
+
+ @Override
+ public void onCameraLaunchGestureDetected() {
+ if (mBar != null) {
+ try {
+ mBar.onCameraLaunchGestureDetected();
+ } catch (RemoteException e) {
+ }
+ }
+ }
};
// ================================================================================
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 15da829..6c70fe9 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -19,6 +19,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
import com.android.server.SystemService;
import org.xmlpull.v1.XmlPullParser;
@@ -59,6 +60,7 @@
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.util.Xml;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
@@ -96,16 +98,15 @@
private static final int MSG_UNREGISTER_LISTENER = 2;
private static final int MSG_DISPATCH_UNLOCK_ATTEMPT = 3;
private static final int MSG_ENABLED_AGENTS_CHANGED = 4;
- private static final int MSG_REQUIRE_CREDENTIAL_ENTRY = 5;
private static final int MSG_KEYGUARD_SHOWING_CHANGED = 6;
private static final int MSG_START_USER = 7;
private static final int MSG_CLEANUP_USER = 8;
private static final int MSG_SWITCH_USER = 9;
- private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<AgentInfo>();
- private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<ITrustListener>();
+ private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<>();
+ private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<>();
private final Receiver mReceiver = new Receiver();
- private final SparseBooleanArray mUserHasAuthenticated = new SparseBooleanArray();
+
/* package */ final TrustArchive mArchive = new TrustArchive();
private final Context mContext;
private final LockPatternUtils mLockPatternUtils;
@@ -118,9 +119,6 @@
@GuardedBy("mDeviceLockedForUser")
private final SparseBooleanArray mDeviceLockedForUser = new SparseBooleanArray();
- @GuardedBy("mUserHasAuthenticatedSinceBoot")
- private final SparseBooleanArray mUserHasAuthenticatedSinceBoot = new SparseBooleanArray();
-
private boolean mTrustAgentsCanRun = false;
private int mCurrentUser = UserHandle.USER_OWNER;
@@ -146,6 +144,7 @@
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
mReceiver.register(mContext);
+ mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker);
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
mTrustAgentsCanRun = true;
refreshAgentList(UserHandle.USER_ALL);
@@ -230,7 +229,7 @@
if (!userInfo.supportsSwitchTo()) continue;
if (!mActivityManager.isUserRunning(userInfo.id)) continue;
if (!lockPatternUtils.isSecure(userInfo.id)) continue;
- if (!getUserHasAuthenticated(userInfo.id)) continue;
+ if (!mStrongAuthTracker.isTrustAllowedForUser(userInfo.id)) continue;
DevicePolicyManager dpm = lockPatternUtils.getDevicePolicyManager();
int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, userInfo.id);
final boolean disableTrustAgents =
@@ -509,7 +508,7 @@
// Agent dispatch and aggregation
private boolean aggregateIsTrusted(int userId) {
- if (!getUserHasAuthenticated(userId)) {
+ if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
return false;
}
for (int i = 0; i < mActiveAgents.size(); i++) {
@@ -524,7 +523,7 @@
}
private boolean aggregateIsTrustManaged(int userId) {
- if (!getUserHasAuthenticated(userId)) {
+ if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
return false;
}
for (int i = 0; i < mActiveAgents.size(); i++) {
@@ -545,54 +544,6 @@
info.agent.onUnlockAttempt(successful);
}
}
-
- if (successful) {
- updateUserHasAuthenticated(userId);
- }
- }
-
- private void updateUserHasAuthenticated(int userId) {
- boolean changed = setUserHasAuthenticated(userId);
- if (changed) {
- refreshAgentList(userId);
- }
- }
-
- private boolean getUserHasAuthenticated(int userId) {
- return mUserHasAuthenticated.get(userId);
- }
-
- /**
- * @return whether the value has changed
- */
- private boolean setUserHasAuthenticated(int userId) {
- if (!mUserHasAuthenticated.get(userId)) {
- mUserHasAuthenticated.put(userId, true);
- synchronized (mUserHasAuthenticatedSinceBoot) {
- mUserHasAuthenticatedSinceBoot.put(userId, true);
- }
- return true;
- }
- return false;
- }
-
- private void clearUserHasAuthenticated(int userId) {
- if (userId == UserHandle.USER_ALL) {
- mUserHasAuthenticated.clear();
- } else {
- mUserHasAuthenticated.put(userId, false);
- }
- }
-
- private boolean getUserHasAuthenticatedSinceBoot(int userId) {
- synchronized (mUserHasAuthenticatedSinceBoot) {
- return mUserHasAuthenticatedSinceBoot.get(userId);
- }
- }
-
- private void requireCredentialEntry(int userId) {
- clearUserHasAuthenticated(userId);
- refreshAgentList(userId);
}
// Listeners
@@ -681,17 +632,6 @@
}
@Override
- public void reportRequireCredentialEntry(int userId) throws RemoteException {
- enforceReportPermission();
- if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_OWNER) {
- mHandler.obtainMessage(MSG_REQUIRE_CREDENTIAL_ENTRY, userId, 0).sendToTarget();
- } else {
- throw new IllegalArgumentException(
- "userId must be an explicit user id or USER_ALL");
- }
- }
-
- @Override
public void reportKeyguardShowingChanged() throws RemoteException {
enforceReportPermission();
// coalesce refresh messages.
@@ -734,18 +674,6 @@
}
}
- @Override
- public boolean hasUserAuthenticatedSinceBoot(int userId) throws RemoteException {
- mContext.enforceCallingOrSelfPermission(
- Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, null);
- long token = Binder.clearCallingIdentity();
- try {
- return getUserHasAuthenticatedSinceBoot(userId);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
-
private void enforceReportPermission() {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, "reporting trust events");
@@ -794,9 +722,8 @@
fout.print(": trusted=" + dumpBool(aggregateIsTrusted(user.id)));
fout.print(", trustManaged=" + dumpBool(aggregateIsTrustManaged(user.id)));
fout.print(", deviceLocked=" + dumpBool(isDeviceLockedInner(user.id)));
- fout.print(", hasAuthenticated=" + dumpBool(getUserHasAuthenticated(user.id)));
- fout.print(", hasAuthenticatedSinceBoot="
- + dumpBool(getUserHasAuthenticatedSinceBoot(user.id)));
+ fout.print(", strongAuthRequired=" + dumpHex(
+ mStrongAuthTracker.getStrongAuthForUser(user.id)));
fout.println();
fout.println(" Enabled agents:");
boolean duplicateSimpleNames = false;
@@ -831,6 +758,10 @@
private String dumpBool(boolean b) {
return b ? "1" : "0";
}
+
+ private String dumpHex(int i) {
+ return "0x" + Integer.toHexString(i);
+ }
};
private int resolveProfileParent(int userId) {
@@ -864,9 +795,6 @@
// This is also called when the security mode of a user changes.
refreshDeviceLockedForUser(UserHandle.USER_ALL);
break;
- case MSG_REQUIRE_CREDENTIAL_ENTRY:
- requireCredentialEntry(msg.arg1);
- break;
case MSG_KEYGUARD_SHOWING_CHANGED:
refreshDeviceLockedForUser(mCurrentUser);
break;
@@ -900,6 +828,13 @@
}
};
+ private final StrongAuthTracker mStrongAuthTracker = new StrongAuthTracker() {
+ @Override
+ public void onStrongAuthRequiredChanged(int userId) {
+ refreshAgentList(userId);
+ }
+ };
+
private class Receiver extends BroadcastReceiver {
@Override
@@ -908,8 +843,6 @@
if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) {
refreshAgentList(getSendingUserId());
updateDevicePolicyFeatures();
- } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
- updateUserHasAuthenticated(getSendingUserId());
} else if (Intent.ACTION_USER_ADDED.equals(action)) {
int userId = getUserId(intent);
if (userId > 0) {
@@ -918,7 +851,6 @@
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
int userId = getUserId(intent);
if (userId > 0) {
- mUserHasAuthenticated.delete(userId);
synchronized (mUserIsTrusted) {
mUserIsTrusted.delete(userId);
}
diff --git a/services/core/java/com/android/server/wm/DisplaySettings.java b/services/core/java/com/android/server/wm/DisplaySettings.java
index 01f878c..80526f2 100644
--- a/services/core/java/com/android/server/wm/DisplaySettings.java
+++ b/services/core/java/com/android/server/wm/DisplaySettings.java
@@ -79,17 +79,20 @@
}
}
- public void setOverscanLocked(String name, int left, int top, int right, int bottom) {
+ public void setOverscanLocked(String uniqueId, String name, int left, int top, int right,
+ int bottom) {
if (left == 0 && top == 0 && right == 0 && bottom == 0) {
// Right now all we are storing is overscan; if there is no overscan,
// we have no need for the entry.
+ mEntries.remove(uniqueId);
+ // Legacy name might have been in used, so we need to clear it.
mEntries.remove(name);
return;
}
- Entry entry = mEntries.get(name);
+ Entry entry = mEntries.get(uniqueId);
if (entry == null) {
- entry = new Entry(name);
- mEntries.put(name, entry);
+ entry = new Entry(uniqueId);
+ mEntries.put(uniqueId, entry);
}
entry.overscanLeft = left;
entry.overscanTop = top;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 794b49c..3da9b44 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -389,7 +389,9 @@
void resetAnimationBackgroundAnimator() {
mAnimationBackgroundAnimator = null;
- mAnimationBackgroundSurface.hide();
+ if (mAnimationBackgroundSurface != null) {
+ mAnimationBackgroundSurface.hide();
+ }
}
private long getDimBehindFadeDuration(long duration) {
@@ -454,11 +456,14 @@
}
boolean isDimming() {
+ if (mDimLayer == null) {
+ return false;
+ }
return mDimLayer.isDimming();
}
boolean isDimming(WindowStateAnimator winAnimator) {
- return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
+ return mDimWinAnimator == winAnimator && isDimming();
}
void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 806f241..f4e2e75 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8702,7 +8702,8 @@
displayInfo.overscanBottom = bottom;
}
- mDisplaySettings.setOverscanLocked(displayInfo.uniqueId, left, top, right, bottom);
+ mDisplaySettings.setOverscanLocked(displayInfo.uniqueId, displayInfo.name, left, top,
+ right, bottom);
mDisplaySettings.writeSettingsLocked();
reconfigureDisplayLocked(displayContent);
@@ -10411,8 +10412,8 @@
": removed=" + win.mRemoved + " visible=" + win.isVisibleLw() +
" mHasSurface=" + win.mHasSurface +
" drawState=" + win.mWinAnimator.mDrawState);
- if (win.mRemoved || !win.mHasSurface) {
- // Window has been removed; no draw will now happen, so stop waiting.
+ if (win.mRemoved || !win.mHasSurface || !win.mPolicyVisibility) {
+ // Window has been removed or hidden; no draw will now happen, so stop waiting.
if (DEBUG_SCREEN_ON) Slog.w(TAG, "Aborted waiting for drawn: " + win);
mWaitingForDrawn.remove(win);
} else if (win.hasDrawnLw()) {
@@ -11955,12 +11956,18 @@
final WindowList windows = getDefaultWindowListLocked();
for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
final WindowState win = windows.get(winNdx);
+ final boolean isForceHiding = mPolicy.isForceHiding(win.mAttrs);
if (win.isVisibleLw()
- && (win.mAppToken != null || mPolicy.isForceHiding(win.mAttrs))) {
+ && (win.mAppToken != null || isForceHiding)) {
win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
// Force add to mResizingWindows.
win.mLastContentInsets.set(-1, -1, -1, -1);
mWaitingForDrawn.add(win);
+
+ // No need to wait for the windows below Keyguard.
+ if (isForceHiding) {
+ break;
+ }
}
}
requestTraversalLocked();
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index ab56d5e..726d29d 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1589,8 +1589,6 @@
final int left = ((int) shownFrame.left) - attrs.surfaceInsets.left;
final int top = ((int) shownFrame.top) - attrs.surfaceInsets.top;
if (mSurfaceX != left || mSurfaceY != top) {
- mSurfaceX = left;
- mSurfaceY = top;
if (mAnimating) {
// If this window (or its app token) is animating, then the position
// of the surface will be re-computed on the next animation frame.
@@ -1598,6 +1596,8 @@
// transformation is being applied by the animation.
return;
}
+ mSurfaceX = left;
+ mSurfaceY = top;
if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setWallpaperOffset");
SurfaceControl.openTransaction();
try {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index cd2885b..dedf1d9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -21,6 +21,7 @@
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.content.pm.PackageManager.GET_UNINSTALLED_PACKAGES;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.TEXT;
@@ -44,6 +45,7 @@
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SystemUpdatePolicy;
import android.app.backup.IBackupManager;
+import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -2957,7 +2959,8 @@
}
boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0;
if (requireEntry) {
- utils.requireCredentialEntry(UserHandle.USER_ALL);
+ utils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW,
+ UserHandle.USER_ALL);
}
synchronized (this) {
int newOwner = requireEntry ? callingUid : -1;
@@ -3089,7 +3092,8 @@
mPowerManager.goToSleep(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN, 0);
// Ensure the device is locked
- new LockPatternUtils(mContext).requireCredentialEntry(UserHandle.USER_ALL);
+ new LockPatternUtils(mContext).requireStrongAuth(
+ STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW, UserHandle.USER_ALL);
getWindowManager().lockNow(null);
} catch (RemoteException e) {
} finally {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d28411e..7a8d4ef 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -875,6 +875,11 @@
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
}
+
+ if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) {
+ Slog.i(TAG, "Gesture Launcher Service");
+ mSystemServiceManager.startService(GestureLauncherService.class);
+ }
}
try {
diff --git a/services/net/java/android/net/util/IpUtils.java b/services/net/java/android/net/util/IpUtils.java
new file mode 100644
index 0000000..e037c40
--- /dev/null
+++ b/services/net/java/android/net/util/IpUtils.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.BufferOverflowException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ShortBuffer;
+
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.IPPROTO_UDP;
+
+/**
+ * @hide
+ */
+public class IpUtils {
+ /**
+ * Converts a signed short value to an unsigned int value. Needed
+ * because Java does not have unsigned types.
+ */
+ private static int intAbs(short v) {
+ return v & 0xFFFF;
+ }
+
+ /**
+ * Performs an IP checksum (used in IP header and across UDP
+ * payload) on the specified portion of a ByteBuffer. The seed
+ * allows the checksum to commence with a specified value.
+ */
+ private static int checksum(ByteBuffer buf, int seed, int start, int end) {
+ int sum = seed;
+ final int bufPosition = buf.position();
+
+ // set position of original ByteBuffer, so that the ShortBuffer
+ // will be correctly initialized
+ buf.position(start);
+ ShortBuffer shortBuf = buf.asShortBuffer();
+
+ // re-set ByteBuffer position
+ buf.position(bufPosition);
+
+ final int numShorts = (end - start) / 2;
+ for (int i = 0; i < numShorts; i++) {
+ sum += intAbs(shortBuf.get(i));
+ }
+ start += numShorts * 2;
+
+ // see if a singleton byte remains
+ if (end != start) {
+ short b = buf.get(start);
+
+ // make it unsigned
+ if (b < 0) {
+ b += 256;
+ }
+
+ sum += b * 256;
+ }
+
+ sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF);
+ sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF);
+ int negated = ~sum;
+ return intAbs((short) negated);
+ }
+
+ private static int pseudoChecksumIPv4(
+ ByteBuffer buf, int headerOffset, int protocol, int transportLen) {
+ int partial = protocol + transportLen;
+ partial += intAbs(buf.getShort(headerOffset + 12));
+ partial += intAbs(buf.getShort(headerOffset + 14));
+ partial += intAbs(buf.getShort(headerOffset + 16));
+ partial += intAbs(buf.getShort(headerOffset + 18));
+ return partial;
+ }
+
+ private static int pseudoChecksumIPv6(
+ ByteBuffer buf, int headerOffset, int protocol, int transportLen) {
+ int partial = protocol + transportLen;
+ for (int offset = 8; offset < 40; offset += 2) {
+ partial += intAbs(buf.getShort(headerOffset + offset));
+ }
+ return partial;
+ }
+
+ private static byte ipversion(ByteBuffer buf, int headerOffset) {
+ return (byte) ((buf.get(headerOffset) & (byte) 0xf0) >> 4);
+ }
+
+ public static short ipChecksum(ByteBuffer buf, int headerOffset) {
+ byte ihl = (byte) (buf.get(headerOffset) & 0x0f);
+ return (short) checksum(buf, 0, headerOffset, headerOffset + ihl * 4);
+ }
+
+ private static short transportChecksum(ByteBuffer buf, int protocol,
+ int ipOffset, int transportOffset, int transportLen) {
+ if (transportLen < 0) {
+ throw new IllegalArgumentException("Transport length < 0: " + transportLen);
+ }
+ int sum;
+ byte ver = ipversion(buf, ipOffset);
+ if (ver == 4) {
+ sum = pseudoChecksumIPv4(buf, ipOffset, protocol, transportLen);
+ } else if (ver == 6) {
+ sum = pseudoChecksumIPv6(buf, ipOffset, protocol, transportLen);
+ } else {
+ throw new UnsupportedOperationException("Checksum must be IPv4 or IPv6");
+ }
+
+ sum = checksum(buf, sum, transportOffset, transportOffset + transportLen);
+ if (protocol == IPPROTO_UDP && sum == 0) {
+ sum = (short) 0xffff;
+ }
+ return (short) sum;
+ }
+
+ public static short udpChecksum(ByteBuffer buf, int ipOffset, int transportOffset) {
+ int transportLen = intAbs(buf.getShort(transportOffset + 4));
+ return transportChecksum(buf, IPPROTO_UDP, ipOffset, transportOffset, transportLen);
+ }
+
+ public static short tcpChecksum(ByteBuffer buf, int ipOffset, int transportOffset,
+ int transportLen) {
+ return transportChecksum(buf, IPPROTO_TCP, ipOffset, transportOffset, transportLen);
+ }
+
+ public static String addressAndPortToString(InetAddress address, int port) {
+ return String.format(
+ (address instanceof Inet6Address) ? "[%s]:%d" : "%s:%d",
+ address.getHostAddress(), port);
+ }
+
+ public static boolean isValidUdpOrTcpPort(int port) {
+ return port > 0 && port < 65536;
+ }
+}
diff --git a/services/tests/servicestests/src/android/net/IpUtilsTest.java b/services/tests/servicestests/src/android/net/IpUtilsTest.java
new file mode 100644
index 0000000..c2d1608
--- /dev/null
+++ b/services/tests/servicestests/src/android/net/IpUtilsTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.util;
+
+import android.net.util.IpUtils;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.nio.ByteBuffer;
+
+import junit.framework.TestCase;
+
+
+public class IpUtilsTest extends TestCase {
+
+ private static final int IPV4_HEADER_LENGTH = 20;
+ private static final int IPV6_HEADER_LENGTH = 40;
+ private static final int TCP_HEADER_LENGTH = 20;
+ private static final int UDP_HEADER_LENGTH = 8;
+ private static final int IP_CHECKSUM_OFFSET = 10;
+ private static final int TCP_CHECKSUM_OFFSET = 16;
+ private static final int UDP_CHECKSUM_OFFSET = 6;
+
+ private int getUnsignedByte(ByteBuffer buf, int offset) {
+ return buf.get(offset) & 0xff;
+ }
+
+ private int getChecksum(ByteBuffer buf, int offset) {
+ return getUnsignedByte(buf, offset) * 256 + getUnsignedByte(buf, offset + 1);
+ }
+
+ private void assertChecksumEquals(int expected, short actual) {
+ assertEquals(Integer.toHexString(expected), Integer.toHexString(actual & 0xffff));
+ }
+
+ // Generate test packets using Python code like this::
+ //
+ // from scapy import all as scapy
+ //
+ // def JavaPacketDefinition(bytes):
+ // out = " ByteBuffer packet = ByteBuffer.wrap(new byte[] {\n "
+ // for i in xrange(len(bytes)):
+ // out += "(byte) 0x%02x" % ord(bytes[i])
+ // if i < len(bytes) - 1:
+ // if i % 4 == 3:
+ // out += ",\n "
+ // else:
+ // out += ", "
+ // out += "\n });"
+ // return out
+ //
+ // packet = (scapy.IPv6(src="2001:db8::1", dst="2001:db8::2") /
+ // scapy.UDP(sport=12345, dport=7) /
+ // "hello")
+ // print JavaPacketDefinition(str(packet))
+
+ @SmallTest
+ public void testIpv6TcpChecksum() throws Exception {
+ // packet = (scapy.IPv6(src="2001:db8::1", dst="2001:db8::2", tc=0x80) /
+ // scapy.TCP(sport=12345, dport=7,
+ // seq=1692871236, ack=128376451, flags=16,
+ // window=32768) /
+ // "hello, world")
+ ByteBuffer packet = ByteBuffer.wrap(new byte[] {
+ (byte) 0x68, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x20, (byte) 0x06, (byte) 0x40,
+ (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01,
+ (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02,
+ (byte) 0x30, (byte) 0x39, (byte) 0x00, (byte) 0x07,
+ (byte) 0x64, (byte) 0xe7, (byte) 0x2a, (byte) 0x44,
+ (byte) 0x07, (byte) 0xa6, (byte) 0xde, (byte) 0x83,
+ (byte) 0x50, (byte) 0x10, (byte) 0x80, (byte) 0x00,
+ (byte) 0xee, (byte) 0x71, (byte) 0x00, (byte) 0x00,
+ (byte) 0x68, (byte) 0x65, (byte) 0x6c, (byte) 0x6c,
+ (byte) 0x6f, (byte) 0x2c, (byte) 0x20, (byte) 0x77,
+ (byte) 0x6f, (byte) 0x72, (byte) 0x6c, (byte) 0x64
+ });
+
+ // Check that a valid packet has checksum 0.
+ int transportLen = packet.limit() - IPV6_HEADER_LENGTH;
+ assertEquals(0, IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
+
+ // Check that we can calculate the checksum from scratch.
+ int sumOffset = IPV6_HEADER_LENGTH + TCP_CHECKSUM_OFFSET;
+ int sum = getUnsignedByte(packet, sumOffset) * 256 + getUnsignedByte(packet, sumOffset + 1);
+ assertEquals(0xee71, sum);
+
+ packet.put(sumOffset, (byte) 0);
+ packet.put(sumOffset + 1, (byte) 0);
+ assertChecksumEquals(sum, IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
+
+ // Check that writing the checksum back into the packet results in a valid packet.
+ packet.putShort(
+ sumOffset,
+ IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
+ assertEquals(0, IpUtils.tcpChecksum(packet, 0, IPV6_HEADER_LENGTH, transportLen));
+ }
+
+ @SmallTest
+ public void testIpv4UdpChecksum() {
+ // packet = (scapy.IP(src="192.0.2.1", dst="192.0.2.2", tos=0x40) /
+ // scapy.UDP(sport=32012, dport=4500) /
+ // "\xff")
+ ByteBuffer packet = ByteBuffer.wrap(new byte[] {
+ (byte) 0x45, (byte) 0x40, (byte) 0x00, (byte) 0x1d,
+ (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00,
+ (byte) 0x40, (byte) 0x11, (byte) 0xf6, (byte) 0x8b,
+ (byte) 0xc0, (byte) 0x00, (byte) 0x02, (byte) 0x01,
+ (byte) 0xc0, (byte) 0x00, (byte) 0x02, (byte) 0x02,
+ (byte) 0x7d, (byte) 0x0c, (byte) 0x11, (byte) 0x94,
+ (byte) 0x00, (byte) 0x09, (byte) 0xee, (byte) 0x36,
+ (byte) 0xff
+ });
+
+ // Check that a valid packet has IP checksum 0 and UDP checksum 0xffff (0 is not a valid
+ // UDP checksum, so the udpChecksum rewrites 0 to 0xffff).
+ assertEquals(0, IpUtils.ipChecksum(packet, 0));
+ assertEquals((short) 0xffff, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
+
+ // Check that we can calculate the checksums from scratch.
+ final int ipSumOffset = IP_CHECKSUM_OFFSET;
+ final int ipSum = getChecksum(packet, ipSumOffset);
+ assertEquals(0xf68b, ipSum);
+
+ packet.put(ipSumOffset, (byte) 0);
+ packet.put(ipSumOffset + 1, (byte) 0);
+ assertChecksumEquals(ipSum, IpUtils.ipChecksum(packet, 0));
+
+ final int udpSumOffset = IPV4_HEADER_LENGTH + UDP_CHECKSUM_OFFSET;
+ final int udpSum = getChecksum(packet, udpSumOffset);
+ assertEquals(0xee36, udpSum);
+
+ packet.put(udpSumOffset, (byte) 0);
+ packet.put(udpSumOffset + 1, (byte) 0);
+ assertChecksumEquals(udpSum, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
+
+ // Check that writing the checksums back into the packet results in a valid packet.
+ packet.putShort(ipSumOffset, IpUtils.ipChecksum(packet, 0));
+ packet.putShort(udpSumOffset, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
+ assertEquals(0, IpUtils.ipChecksum(packet, 0));
+ assertEquals((short) 0xffff, IpUtils.udpChecksum(packet, 0, IPV4_HEADER_LENGTH));
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 09e15a8..fb9a3a3 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -19,15 +19,19 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
+import android.hardware.usb.UsbPort;
+import android.hardware.usb.UsbPortStatus;
import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
@@ -105,6 +109,7 @@
private static final int MSG_USER_SWITCHED = 5;
private static final int MSG_SET_USB_DATA_UNLOCKED = 6;
private static final int MSG_UPDATE_USER_RESTRICTIONS = 7;
+ private static final int MSG_UPDATE_HOST_STATE = 8;
private static final int AUDIO_MODE_SOURCE = 1;
@@ -175,6 +180,15 @@
}
};
+ private final BroadcastReceiver mHostReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ UsbPort port = intent.getParcelableExtra(UsbManager.EXTRA_PORT);
+ UsbPortStatus status = intent.getParcelableExtra(UsbManager.EXTRA_PORT_STATUS);
+ mHandler.updateHostState(port, status);
+ }
+ };
+
public UsbDeviceManager(Context context, UsbAlsaManager alsaManager) {
mContext = context;
mUsbAlsaManager = alsaManager;
@@ -197,6 +211,8 @@
if (secureAdbEnabled && !dataEncrypted) {
mDebuggingManager = new UsbDebuggingManager(context);
}
+ mContext.registerReceiver(mHostReceiver,
+ new IntentFilter(UsbManager.ACTION_USB_PORT_CHANGED));
}
private UsbSettingsManager getCurrentSettings() {
@@ -299,6 +315,7 @@
// current USB state
private boolean mConnected;
+ private boolean mHostConnected;
private boolean mConfigured;
private boolean mUsbDataUnlocked;
private String mCurrentFunctions;
@@ -377,6 +394,11 @@
sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0);
}
+ public void updateHostState(UsbPort port, UsbPortStatus status) {
+ boolean hostConnected = status.getCurrentDataRole() == UsbPort.DATA_ROLE_HOST;
+ obtainMessage(MSG_UPDATE_HOST_STATE, hostConnected ? 1 :0, 0).sendToTarget();
+ }
+
private boolean waitForState(String state) {
// wait for the transition to complete.
// give up after 1 second.
@@ -652,6 +674,10 @@
updateUsbFunctions();
}
break;
+ case MSG_UPDATE_HOST_STATE:
+ mHostConnected = (msg.arg1 == 1);
+ updateUsbNotification();
+ break;
case MSG_ENABLE_ADB:
setAdbEnabled(msg.arg1 == 1);
break;
@@ -707,7 +733,7 @@
if (mNotificationManager == null || !mUseUsbNotification) return;
int id = 0;
Resources r = mContext.getResources();
- if (mConnected) {
+ if (mConnected || mHostConnected) {
if (!mUsbDataUnlocked) {
id = com.android.internal.R.string.usb_charging_notification_title;
} else if (UsbManager.containsFunction(mCurrentFunctions,
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 52abcfe..7f182a4 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -26,8 +26,13 @@
import android.hardware.usb.UsbPortStatus;
import android.os.Handler;
import android.os.Message;
+import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UEventObserver;
import android.os.UserHandle;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
@@ -89,6 +94,9 @@
private static final String PORT_DATA_ROLE_HOST = "host";
private static final String PORT_DATA_ROLE_DEVICE = "device";
+ private static final String USB_TYPEC_PROP_PREFIX = "sys.usb.typec.";
+ private static final String USB_TYPEC_STATE = "sys.usb.typec.state";
+
// All non-trivial role combinations.
private static final int COMBO_SOURCE_HOST =
UsbPort.combineRolesAsBit(UsbPort.POWER_ROLE_SOURCE, UsbPort.DATA_ROLE_HOST);
@@ -621,16 +629,25 @@
return 0;
}
+ private static boolean fileIsRootWritable(String path) {
+ try {
+ // If the file is user writable, then it is root writable.
+ return (Os.stat(path).st_mode & OsConstants.S_IWUSR) != 0;
+ } catch (ErrnoException e) {
+ return false;
+ }
+ }
+
private static boolean canChangeMode(File portDir) {
- return new File(portDir, SYSFS_PORT_MODE).canWrite();
+ return fileIsRootWritable(new File(portDir, SYSFS_PORT_MODE).getPath());
}
private static boolean canChangePowerRole(File portDir) {
- return new File(portDir, SYSFS_PORT_POWER_ROLE).canWrite();
+ return fileIsRootWritable(new File(portDir, SYSFS_PORT_POWER_ROLE).getPath());
}
private static boolean canChangeDataRole(File portDir) {
- return new File(portDir, SYSFS_PORT_DATA_ROLE).canWrite();
+ return fileIsRootWritable(new File(portDir, SYSFS_PORT_DATA_ROLE).getPath());
}
private static String readFile(File dir, String filename) {
@@ -642,16 +659,29 @@
}
}
- private static boolean writeFile(File dir, String filename, String contents) {
- final File file = new File(dir, filename);
- try {
- try (FileWriter writer = new FileWriter(file)) {
- writer.write(contents);
- }
- return true;
- } catch (IOException ex) {
- return false;
+ private static boolean waitForState(String property, String state) {
+ // wait for the transition to complete.
+ // give up after 5 seconds.
+ // 5 seconds is probably too long, but we have seen hardware that takes
+ // over 3 seconds to change states.
+ String value = null;
+ for (int i = 0; i < 100; i++) {
+ // State transition is done when property is set to the new configuration
+ value = SystemProperties.get(property);
+ if (state.equals(value)) return true;
+ SystemClock.sleep(50);
}
+ Slog.e(TAG, "waitForState(" + state + ") for " + property + " FAILED: got " + value);
+ return false;
+ }
+
+ private static String propertyFromFilename(String filename) {
+ return USB_TYPEC_PROP_PREFIX + filename;
+ }
+
+ private static boolean writeFile(File dir, String filename, String contents) {
+ SystemProperties.set(propertyFromFilename(filename), contents);
+ return waitForState(USB_TYPEC_STATE, contents);
}
private static void logAndPrint(int priority, IndentingPrintWriter pw, String msg) {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 0a1dbf6..f149f24 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -970,7 +970,6 @@
unregisterCallback(listener);
}
-
/** {@hide} */
Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) {
mPhone = phone;
@@ -980,6 +979,14 @@
}
/** {@hide} */
+ Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state) {
+ mPhone = phone;
+ mTelecomCallId = telecomCallId;
+ mInCallAdapter = inCallAdapter;
+ mState = state;
+ }
+
+ /** {@hide} */
final String internalGetCallId() {
return mTelecomCallId;
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 96f44b9..383e45b 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -656,6 +656,9 @@
connection.getDisconnectCause(),
createIdList(connection.getConferenceables()),
connection.getExtras()));
+ if (isUnknown) {
+ triggerConferenceRecalculate();
+ }
}
private void abort(String callId) {
@@ -1016,6 +1019,16 @@
}
/**
+ * Trigger recalculate functinality for conference calls. This is used when a Telephony
+ * Connection is part of a conference controller but is not yet added to Connection
+ * Service and hence cannot be added to the conference call.
+ *
+ * @hide
+ */
+ public void triggerConferenceRecalculate() {
+ }
+
+ /**
* Create a {@code Connection} given an outgoing request. This is used to initiate new
* outgoing calls.
*
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 8eb091b..47154da 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -122,7 +122,8 @@
}
final void internalAddCall(ParcelableCall parcelableCall) {
- Call call = new Call(this, parcelableCall.getId(), mInCallAdapter);
+ Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,
+ parcelableCall.getState());
mCallByTelecomCallId.put(parcelableCall.getId(), call);
mCalls.add(call);
checkCallTree(parcelableCall);
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 067e734..8779462 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -367,6 +367,48 @@
"android.telecom.intent.extra.TTY_PREFERRED";
/**
+ * Broadcast intent action for letting custom component know to show the missed call
+ * notification.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_SHOW_MISSED_CALLS_NOTIFICATION =
+ "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
+
+ /**
+ * The number of calls associated with the notification.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_NOTIFICATION_COUNT =
+ "android.telecom.extra.NOTIFICATION_COUNT";
+
+ /**
+ * The number associated with the missed calls. This number is only relevant
+ * when EXTRA_NOTIFICATION_COUNT is 1.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_NOTIFICATION_PHONE_NUMBER =
+ "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
+
+ /**
+ * The intent to clear missed calls.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT =
+ "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
+
+ /**
+ * The intent to call back a missed call.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_CALL_BACK_INTENT =
+ "android.telecom.extra.CALL_BACK_INTENT";
+
+ /**
* The following 4 constants define how properties such as phone numbers and names are
* displayed to the user.
*/
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e9c41a1..9dc94c8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -259,6 +259,14 @@
= "carrier_allow_turnoff_ims_bool";
/**
+ * Flag specifying whether IMS instant lettering is available for the carrier. {@code True} if
+ * instant lettering is available for the carrier, {@code false} otherwise.
+ * @hide
+ */
+ public static final String KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL =
+ "carrier_instant_lettering_available_bool";
+
+ /**
* If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0
* this is the value that should be used instead. A configuration value of
* RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default
@@ -334,6 +342,21 @@
"ci_action_on_sys_update_extra_val_string";
/**
+ * Specifies the amount of gap to be added in millis between DTMF tones. When a non-zero value
+ * is specified, the UE shall wait for the specified amount of time before it sends out
+ * successive DTMF tones on the network.
+ * @hide
+ */
+ public static final String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int";
+
+ /**
+ * Determines whether conference calls are supported by a carrier. When {@code true},
+ * conference calling is supported, {@code false otherwise}.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
+
+ /**
* If this is true, the SIM card (through Customer Service Profile EF file) will be able to
* prevent manual operator selection. If false, this SIM setting will be ignored and manual
* operator selection will always be available. See CPHS4_2.WW6, CPHS B.4.7.1 for more
@@ -393,6 +416,7 @@
sDefaults.putBoolean(KEY_CARRIER_VOLTE_PROVISIONING_REQUIRED_BOOL, false);
sDefaults.putBoolean(KEY_CARRIER_VOLTE_TTY_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL, true);
+ sDefaults.putBoolean(KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL, false);
sDefaults.putBoolean(KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL, false);
sDefaults.putBoolean(KEY_DTMF_TYPE_ENABLED_BOOL, false);
sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true);
@@ -430,6 +454,8 @@
sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
sDefaults.putStringArray(KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_FORCE_HOME_NETWORK_BOOL, false);
+ sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0);
+ sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
// MMS defaults
sDefaults.putBoolean(KEY_MMS_ALIAS_ENABLED_BOOL, false);
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 79146f3..273cc93 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1135,6 +1135,8 @@
"VI", // U.S. Virgin Islands
};
+ private static final String KOREA_ISO_COUNTRY_CODE = "KR";
+
/**
* Breaks the given number down and formats it according to the rules
* for the country the number is from.
@@ -1455,7 +1457,14 @@
String result = null;
try {
PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
- result = util.formatInOriginalFormat(pn, defaultCountryIso);
+ if (KOREA_ISO_COUNTRY_CODE.equals(defaultCountryIso) &&
+ (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE))) {
+ // Format local Korean phone numbers with country code to corresponding national
+ // format which would replace the leading +82 with 0.
+ result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+ } else {
+ result = util.formatInOriginalFormat(pn, defaultCountryIso);
+ }
} catch (NumberParseException e) {
}
return result;
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2415165..e104b38 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -602,6 +602,46 @@
public static final String EXTRA_DATA_FAILURE_CAUSE = PhoneConstants.DATA_FAILURE_CAUSE_KEY;
/**
+ * Broadcast intent action for letting custom component know to show voicemail notification.
+ * @hide
+ */
+ @SystemApi
+ public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION =
+ "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
+
+ /**
+ * The number of voice messages associated with the notification.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_NOTIFICATION_COUNT =
+ "android.telephony.extra.NOTIFICATION_COUNT";
+
+ /**
+ * The voicemail number.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_VOICEMAIL_NUMBER =
+ "android.telephony.extra.VOICEMAIL_NUMBER";
+
+ /**
+ * The intent to call voicemail.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_CALL_VOICEMAIL_INTENT =
+ "android.telephony.extra.CALL_VOICEMAIL_INTENT";
+
+ /**
+ * The intent to launch voicemail settings.
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT =
+ "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
+
+ /**
* Response codes for sim activation. Activation completed successfully.
* @hide
*/
@@ -4352,13 +4392,13 @@
}
}
- /**
- * Returns the Status of Volte
- *@hide
- */
- public boolean isVolteEnabled() {
+ /**
+ * Returns the Status of Volte
+ * @hide
+ */
+ public boolean isVolteAvailable() {
try {
- return getITelephony().isVolteEnabled();
+ return getITelephony().isVolteAvailable();
} catch (RemoteException ex) {
return false;
} catch (NullPointerException ex) {
@@ -4366,13 +4406,27 @@
}
}
- /**
- * Returns the Status of Wi-Fi Calling
- *@hide
- */
- public boolean isWifiCallingEnabled() {
+ /**
+ * Returns the Status of video telephony (VT)
+ * @hide
+ */
+ public boolean isVideoTelephonyAvailable() {
+ try {
+ return getITelephony().isVideoTelephonyAvailable();
+ } catch (RemoteException ex) {
+ return false;
+ } catch (NullPointerException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Returns the Status of Wi-Fi Calling
+ * @hide
+ */
+ public boolean isWifiCallingAvailable() {
try {
- return getITelephony().isWifiCallingEnabled();
+ return getITelephony().isWifiCallingAvailable();
} catch (RemoteException ex) {
return false;
} catch (NullPointerException ex) {
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index 2c4354b..f263b4d 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -174,11 +174,19 @@
* cna : Calling name
* ussd : For network-initiated USSD, MT only
* remote_uri : Connected user identity (it can be used for the conference)
+ * ChildNum: Child number info.
+ * Codec: Codec info.
+ * DisplayText: Display text for the call.
+ * AdditionalCallInfo: Additional call info.
*/
public static final String EXTRA_OI = "oi";
public static final String EXTRA_CNA = "cna";
public static final String EXTRA_USSD = "ussd";
public static final String EXTRA_REMOTE_URI = "remote_uri";
+ public static final String EXTRA_CHILD_NUMBER = "ChildNum";
+ public static final String EXTRA_CODEC = "Codec";
+ public static final String EXTRA_DISPLAY_TEXT = "DisplayText";
+ public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo";
public int mServiceType;
public int mCallType;
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index 9628915..2769a2b 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -99,6 +99,9 @@
// MT : No action from user after alerting the call
public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203;
+ //Call failures for FDN
+ public static final int CODE_FDN_BLOCKED = 241;
+
/**
* STATUSCODE (SIP response code) (IMS -> Telephony)
*/
@@ -227,6 +230,12 @@
public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100;
/**
+ * MT call has ended due to a release from the network
+ * because the call was answered elsewhere
+ */
+ public static final int CODE_ANSWERED_ELSEWHERE = 1014;
+
+ /**
* Network string error messages.
* mExtraMessage may have these values.
*/
diff --git a/telephony/java/com/android/ims/ImsSsInfo.java b/telephony/java/com/android/ims/ImsSsInfo.java
index dbde1c6..7acc3bf 100644
--- a/telephony/java/com/android/ims/ImsSsInfo.java
+++ b/telephony/java/com/android/ims/ImsSsInfo.java
@@ -34,6 +34,7 @@
// 0: disabled, 1: enabled
public int mStatus;
+ public String mIcbNum;
public ImsSsInfo() {
}
@@ -50,6 +51,7 @@
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mStatus);
+ out.writeString(mIcbNum);
}
@Override
@@ -59,6 +61,7 @@
private void readFromParcel(Parcel in) {
mStatus = in.readInt();
+ mIcbNum = in.readString();
}
public static final Creator<ImsSsInfo> CREATOR =
diff --git a/telephony/java/com/android/ims/ImsSuppServiceNotification.aidl b/telephony/java/com/android/ims/ImsSuppServiceNotification.aidl
new file mode 100644
index 0000000..6b4479f
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsSuppServiceNotification.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.ims;
+
+parcelable ImsSuppServiceNotification;
diff --git a/telephony/java/com/android/ims/ImsSuppServiceNotification.java b/telephony/java/com/android/ims/ImsSuppServiceNotification.java
new file mode 100644
index 0000000..faf7499
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsSuppServiceNotification.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+
+/**
+ * Parcelable object to handle IMS supplementary service notifications.
+ *
+ * @hide
+ */
+public class ImsSuppServiceNotification implements Parcelable {
+ private static final String TAG = "ImsSuppServiceNotification";
+
+ /** Type of notification: 0 = MO; 1 = MT */
+ public int notificationType;
+ /** TS 27.007 7.17 "code1" or "code2" */
+ public int code;
+ /** TS 27.007 7.17 "index" - Not used currently*/
+ public int index;
+ /** TS 27.007 7.17 "type" (MT only) - Not used currently */
+ public int type;
+ /** TS 27.007 7.17 "number" (MT only) */
+ public String number;
+ /** List of forwarded numbers, if any */
+ public String[] history;
+
+ public ImsSuppServiceNotification() {
+ }
+
+ public ImsSuppServiceNotification(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public String toString() {
+ return "{ notificationType=" + notificationType +
+ ", code=" + code +
+ ", index=" + index +
+ ", type=" + type +
+ ", number=" + number +
+ ", history=" + Arrays.toString(history) +
+ " }";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(notificationType);
+ out.writeInt(code);
+ out.writeInt(index);
+ out.writeInt(type);
+ out.writeString(number);
+ out.writeStringArray(history);
+ }
+
+ private void readFromParcel(Parcel in) {
+ notificationType = in.readInt();
+ code = in.readInt();
+ index = in.readInt();
+ type = in.readInt();
+ number = in.readString();
+ history = in.createStringArray();
+ }
+
+ public static final Creator<ImsSuppServiceNotification> CREATOR =
+ new Creator<ImsSuppServiceNotification>() {
+ @Override
+ public ImsSuppServiceNotification createFromParcel(Parcel in) {
+ return new ImsSuppServiceNotification(in);
+ }
+
+ @Override
+ public ImsSuppServiceNotification[] newArray(int size) {
+ return new ImsSuppServiceNotification[size];
+ }
+ };
+}
diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
index 0443c3e..d562ecc 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
@@ -21,6 +21,7 @@
import com.android.ims.ImsReasonInfo;
import com.android.ims.ImsConferenceState;
import com.android.ims.internal.IImsCallSession;
+import com.android.ims.ImsSuppServiceNotification;
/**
* A listener type for receiving notification on IMS call session events.
@@ -123,4 +124,10 @@
* @param isMultiParty {@code true} if the session became multiparty, {@code false} otherwise.
*/
void callSessionMultipartyStateChanged(in IImsCallSession session, in boolean isMultiParty);
+
+ /**
+ * Notifies the supplementary service information for the current session.
+ */
+ void callSessionSuppServiceReceived(in IImsCallSession session,
+ in ImsSuppServiceNotification suppSrvNotification);
}
diff --git a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
index c910600..a6a2658 100644
--- a/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsRegistrationListener.aidl
@@ -72,4 +72,10 @@
*/
void registrationFeatureCapabilityChanged(int serviceClass,
out int[] enabledFeatures, out int[] disabledFeatures);
+
+ /**
+ * Updates the application with the waiting voice message count.
+ * @param count The number of waiting voice messages.
+ */
+ void voiceMessageCountUpdate(int count);
}
diff --git a/telephony/java/com/android/ims/internal/IImsUt.aidl b/telephony/java/com/android/ims/internal/IImsUt.aidl
index c531ea5..4ab5ee3 100644
--- a/telephony/java/com/android/ims/internal/IImsUt.aidl
+++ b/telephony/java/com/android/ims/internal/IImsUt.aidl
@@ -74,7 +74,7 @@
/**
* Updates the configuration of the call barring.
*/
- int updateCallBarring(int cbType, boolean enable, in String[] barrList);
+ int updateCallBarring(int cbType, int action, in String[] barrList);
/**
* Updates the configuration of the call forward.
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index a93e465..661f12d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -951,6 +951,7 @@
* @return {@code true} if the device supports hearing aid compatibility.
*/
boolean isHearingAidCompatibilitySupported();
+
/**
* Get IMS Registration Status
*/
@@ -958,15 +959,18 @@
/**
* Returns the Status of Wi-Fi Calling
- *@hide
*/
- boolean isWifiCallingEnabled();
+ boolean isWifiCallingAvailable();
+
+ /**
+ * Returns the Status of Volte
+ */
+ boolean isVolteAvailable();
/**
- * Returns the Status of Volte
- *@hide
+ * Returns the Status of VT (video telephony)
*/
- boolean isVolteEnabled();
+ boolean isVideoTelephonyAvailable();
/**
* Returns the unique device ID of phone, for example, the IMEI for
@@ -995,7 +999,6 @@
/**
* Return the modem activity info.
- *@hide
*/
ModemActivityInfo getModemActivityInfo();
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index b76ec17..567002e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -33,6 +33,7 @@
import org.xmlpull.v1.XmlPullParserException;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.res.ColorStateList;
import android.graphics.Bitmap;
import android.graphics.Bitmap_Delegate;
@@ -227,16 +228,18 @@
* Find the background color for this bar from the theme attributes. Only relevant to StatusBar
* and NavigationBar.
* <p/>
- * Returns 0 if not found.
+ * Returns null if not found.
*
* @param colorAttrName the attribute name for the background color
* @param translucentAttrName the attribute name for the translucency property of the bar.
*
* @throws NumberFormatException if color resolved to an invalid string.
*/
- protected int getBarColor(@NonNull String colorAttrName, @NonNull String translucentAttrName) {
+ @Nullable
+ protected Integer getBarColor(@NonNull String colorAttrName,
+ @NonNull String translucentAttrName) {
if (!Config.isGreaterOrEqual(mSimulatedPlatformVersion, LOLLIPOP)) {
- return 0;
+ return null;
}
RenderResources renderResources = getContext().getRenderResources();
// First check if the bar is translucent.
@@ -251,10 +254,11 @@
if (transparent) {
return getColor(renderResources, colorAttrName);
}
- return 0;
+ return null;
}
- private static int getColor(RenderResources renderResources, String attr) {
+ @Nullable
+ private static Integer getColor(RenderResources renderResources, String attr) {
// From ?attr/foo to @color/bar. This is most likely an ItemResourceValue.
ResourceValue resource = renderResources.findItemInTheme(attr, true);
// Form @color/bar to the #AARRGGBB
@@ -275,7 +279,7 @@
}
}
}
- return 0;
+ return null;
}
private ResourceValue getResourceValue(String reference) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index 9c89bfe..d50ce23 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -65,8 +65,8 @@
super(context, orientation, getShortestWidth(context)>= 600 ? LAYOUT_600DP_XML : LAYOUT_XML,
"navigation_bar.xml", simulatedPlatformVersion);
- int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
- setBackgroundColor(color == 0 ? 0xFF000000 : color);
+ Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
+ setBackgroundColor(color == null ? 0xFF000000 : color);
// Cannot access the inside items through id because no R.id values have been
// created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index 2dc7c65..95a5a58 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -71,8 +71,9 @@
// FIXME: use FILL_H?
setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
- int color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
- setBackgroundColor(color == 0 ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
+ Integer color = getBarColor(ATTR_COLOR, ATTR_TRANSLUCENT);
+ setBackgroundColor(
+ color == null ? Config.getStatusBarColor(simulatedPlatformVersion) : color);
// Cannot access the inside items through id because no R.id values have been
// created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index ac7c409..24e1ce7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -421,8 +421,7 @@
gc.setComposite(AlphaComposite.Src);
gc.setColor(new Color(0x00000000, true));
- gc.fillRect(0, 0,
- mMeasuredScreenWidth, mMeasuredScreenHeight);
+ gc.fillRect(0, 0, mMeasuredScreenWidth, mMeasuredScreenHeight);
// done
gc.dispose();