diff options
Diffstat (limited to 'services')
33 files changed, 2248 insertions, 231 deletions
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 @@ LOCAL_SRC_FILES += \ 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/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 @@ public final class BatteryService extends SystemService { private int mLastBatteryVoltage; private int mLastBatteryTemperature; private boolean mLastBatteryLevelCritical; + private int mLastMaxChargingCurrent; private int mInvalidCharger; private int mLastInvalidCharger; @@ -323,6 +324,7 @@ public final class BatteryService extends SystemService { + "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 @@ public final class BatteryService extends SystemService { mPlugType != mLastPlugType || mBatteryProps.batteryVoltage != mLastBatteryVoltage || mBatteryProps.batteryTemperature != mLastBatteryTemperature || + mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent || mInvalidCharger != mLastInvalidCharger)) { if (mPlugType != mLastPlugType) { @@ -479,6 +482,7 @@ public final class BatteryService extends SystemService { mLastPlugType = mPlugType; mLastBatteryVoltage = mBatteryProps.batteryVoltage; mLastBatteryTemperature = mBatteryProps.batteryTemperature; + mLastMaxChargingCurrent = mBatteryProps.maxChargingCurrent; mLastBatteryLevelCritical = mBatteryLevelCritical; mLastInvalidCharger = mInvalidCharger; } @@ -503,17 +507,21 @@ public final class BatteryService extends SystemService { 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 @@ public final class BatteryService extends SystemService { 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.Configuration; 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.IndentingPrintWriter; 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.Arrays; 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub "ConnectivityService"); } + private void enforceKeepalivePermission() { + mContext.enforceCallingPermission(KeepaliveTracker.PERMISSION, "ConnectivityService"); + } + public void sendConnectedBroadcast(NetworkInfo info) { enforceConnectivityInternalPermission(); sendGeneralBroadcast(info, CONNECTIVITY_ACTION); @@ -1841,10 +1853,13 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub (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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - 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 @@ public class ConnectivityService extends IConnectivityManager.Stub 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 @@ public class ConnectivityService extends IConnectivityManager.Stub networkCapabilities = new NetworkCapabilities(networkCapabilities); enforceNetworkRequestPermissions(networkCapabilities); enforceMeteredApnPolicy(networkCapabilities); - ensureImmutableCapabilities(networkCapabilities); + ensureRequestableCapabilities(networkCapabilities); NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE, nextNetworkRequestId()); @@ -3866,6 +3937,8 @@ public class ConnectivityService extends IConnectivityManager.Stub notifyIfacesChanged(); notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_IP_CHANGED); } + + mKeepaliveTracker.handleCheckKeepalivesStillValid(networkAgent); } private void updateClat(LinkProperties newLp, LinkProperties oldLp, NetworkAgentInfo nai) { @@ -4532,6 +4605,15 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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 @@ public class ConnectivityService extends IConnectivityManager.Stub } @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 @@ option java_package com.android.server # --------------------------- 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ package com.android.server; 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 android.content.pm.UserInfo; 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 @@ public class LockSettingsService extends ILockSettings.Stub { private final Context mContext; private final LockSettingsStorage mStorage; + private final LockSettingsStrongAuth mStrongAuth = new LockSettingsStrongAuth(); private LockPatternUtils mLockPatternUtils; private boolean mFirstCallToVold; @@ -93,6 +97,7 @@ public class LockSettingsService extends ILockSettings.Stub { 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 @@ public class LockSettingsService extends ILockSettings.Stub { } 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 @@ public class LockSettingsService extends ILockSettings.Stub { 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 @@ public class LockSettingsService extends ILockSettings.Stub { private void removeUser(int userId) { mStorage.removeUser(userId); + mStrongAuth.removeUser(userId); final KeyStore ks = KeyStore.getInstance(); ks.onUserRemoved(userId); @@ -727,6 +739,24 @@ public class LockSettingsService extends ILockSettings.Stub { } } + @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 @@ public class LockSettingsService extends ILockSettings.Stub { 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 @@ class MountService extends IMountService.Stub } @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/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.IntentFilter; 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 @@ import java.util.Set; * * @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 class CameraService extends SystemService implements Handler.Callback { 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 @@ public class CameraService extends SystemService implements Handler.Callback { 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 class CameraService extends SystemService implements Handler.Callback { 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 @@ public class CameraService extends SystemService implements Handler.Callback { 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 @@ public class CameraService extends SystemService implements Handler.Callback { } } + /** + * 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 @@ public class CameraService extends SystemService implements Handler.Callback { if (mEnabledCameraUsers == null) { return; } - if (notifyMediaserver(USER_SWITCHED, mEnabledCameraUsers)) { + if (notifyMediaserverLocked(USER_SWITCHED, mEnabledCameraUsers)) { retries = 0; } } @@ -199,19 +256,27 @@ public class CameraService extends SystemService implements Handler.Callback { 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; + } - ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder); + mCameraServiceRaw = 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 @@ public class CameraService extends SystemService implements Handler.Callback { 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 @@ public class CameraService extends SystemService implements Handler.Callback { } 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 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> { 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.Environment; 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.UserHandle; 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 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe 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 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe 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 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe 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 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe 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 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe 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 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe 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 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe 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 class FingerprintService extends SystemService implements IBinder.DeathRe public void onEnumerate(long deviceId, int[] fingerIds, int[] groupIds) { dispatchEnumerate(deviceId, fingerIds, groupIds); } - }; private final class FingerprintServiceWrapper extends IFingerprintService.Stub { @@ -749,6 +768,7 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe 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 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe 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 @@ package com.android.server.fingerprint; 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 class FingerprintUtils { } 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..55222dc 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 com.android.server.ServiceWatcher; 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 @@ public class ActivityRecognitionProxy { 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 class ActivityRecognitionProxy { public static ActivityRecognitionProxy createAndBind( Context context, Handler handler, + boolean activityRecognitionHardwareIsSupported, ActivityRecognitionHardware activityRecognitionHardware, int overlaySwitchResId, int defaultServicePackageNameResId, @@ -79,6 +85,7 @@ public class ActivityRecognitionProxy { ActivityRecognitionProxy activityRecognitionProxy = new ActivityRecognitionProxy( context, handler, + activityRecognitionHardwareIsSupported, activityRecognitionHardware, overlaySwitchResId, defaultServicePackageNameResId, @@ -89,25 +96,58 @@ public class ActivityRecognitionProxy { 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 { - watcher.onInstanceChanged(mActivityRecognitionHardware); + descriptor = binder.getInterfaceDescriptor(); } catch (RemoteException e) { - Log.e(TAG, "Error delivering hardware interface.", e); + Log.e(TAG, "Unable to get interface descriptor.", e); + return; + } + + if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(descriptor)) { + IActivityRecognitionHardwareWatcher watcher = + IActivityRecognitionHardwareWatcher.Stub.asInterface(binder); + if (watcher == null) { + Log.e(TAG, "No watcher found on connection."); + return; + } + if (mInstance == null) { + // to keep backwards compatibility do not update the watcher when there is no + // instance available, or it will cause an NPE + Log.d(TAG, "AR HW instance not available, binding will be a no-op."); + 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/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6d4d900..3f5c270 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -15779,7 +15779,7 @@ public class PackageManagerService extends IPackageManager.Stub { 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.Binder; 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 @@ public class UserManagerService extends IUserManager.Stub { 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 @@ public class UserManagerService extends IUserManager.Stub { } private void removeUserStateLocked(final int userHandle) { + mContext.getSystemService(StorageManager.class) + .deleteUserKey(userHandle); // Cleanup package manager settings mPm.cleanUpUserLILPw(this, userHandle); @@ -1899,16 +1901,10 @@ public class UserManagerService extends IUserManager.Stub { * 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/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 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mAppsToBeHidden.isEmpty()) { if (dismissKeyguard && !mKeyguardSecure) { mAppsThatDismissKeyguard.add(appToken); - } else { + } else if (win.isDrawnLw()) { mWinShowWhenLocked = win; mHideLockScreen = true; mForceStatusBarFromKeyguard = false; @@ -4444,7 +4444,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { } 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 @@ public class StatusBarController extends BarController { 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.hardware.SensorManager; 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 @@ public abstract class WindowOrientationListener { 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 abstract class WindowOrientationListener { public WindowOrientationListener(Context context, Handler handler) { this(context, handler, SensorManager.SENSOR_DELAY_UI); } - + /** * Creates a new WindowOrientationListener. * @@ -84,11 +87,31 @@ public abstract class WindowOrientationListener { 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 @@ public abstract class WindowOrientationListener { 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 @@ public abstract class WindowOrientationListener { if (LOG) { Slog.d(TAG, "WindowOrientationListener disabled"); } - mSensorManager.unregisterListener(mSensorEventListener); + mSensorManager.unregisterListener(mOrientationJudge); mEnabled = false; } } @@ -134,8 +157,8 @@ public abstract class WindowOrientationListener { public void onTouchStart() { synchronized (mLock) { - if (mSensorEventListener != null) { - mSensorEventListener.onTouchStartLocked(); + if (mOrientationJudge != null) { + mOrientationJudge.onTouchStartLocked(); } } } @@ -144,8 +167,8 @@ public abstract class WindowOrientationListener { long whenElapsedNanos = SystemClock.elapsedRealtimeNanos(); synchronized (mLock) { - if (mSensorEventListener != null) { - mSensorEventListener.onTouchEndLocked(whenElapsedNanos); + if (mOrientationJudge != null) { + mOrientationJudge.onTouchEndLocked(whenElapsedNanos); } } } @@ -172,7 +195,7 @@ public abstract class WindowOrientationListener { public int getProposedRotation() { synchronized (mLock) { if (mEnabled) { - return mSensorEventListener.getProposedRotationLocked(); + return mOrientationJudge.getProposedRotationLocked(); } return -1; } @@ -194,6 +217,8 @@ public abstract class WindowOrientationListener { * 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 @@ public abstract class WindowOrientationListener { 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 @@ public abstract class WindowOrientationListener { * 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 @@ public abstract class WindowOrientationListener { 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 @@ public abstract class WindowOrientationListener { 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 @@ public abstract class WindowOrientationListener { } } + @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 @@ public abstract class WindowOrientationListener { } } + @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 @@ public abstract class WindowOrientationListener { 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 @@ public abstract class WindowOrientationListener { 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 @@ public class KeyguardServiceDelegate { 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 class KeyguardServiceDelegate { public int offReason; public int currentUser; public boolean bootCompleted; + public int screenState; + public int interactiveState; }; public interface DrawnListener { @@ -144,10 +154,17 @@ public class KeyguardServiceDelegate { // 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 @@ public class KeyguardServiceDelegate { if (DEBUG) Log.v(TAG, "onStartedWakingUp()"); mKeyguardService.onStartedWakingUp(); } + mKeyguardState.interactiveState = INTERACTIVE_STATE_AWAKE; } public void onScreenTurnedOff() { @@ -238,6 +256,7 @@ public class KeyguardServiceDelegate { if (DEBUG) Log.v(TAG, "onScreenTurnedOff()"); mKeyguardService.onScreenTurnedOff(); } + mKeyguardState.screenState = SCREEN_STATE_OFF; } public void onScreenTurningOn(final DrawnListener drawnListener) { @@ -252,6 +271,7 @@ public class KeyguardServiceDelegate { mDrawnListenerWhenConnect = drawnListener; showScrim(); } + mKeyguardState.screenState = SCREEN_STATE_TURNING_ON; } public void onScreenTurnedOn() { @@ -259,6 +279,7 @@ public class KeyguardServiceDelegate { if (DEBUG) Log.v(TAG, "onScreenTurnedOn()"); mKeyguardService.onScreenTurnedOn(); } + mKeyguardState.screenState = SCREEN_STATE_ON; } public void onStartedGoingToSleep(int why) { @@ -266,12 +287,14 @@ public class KeyguardServiceDelegate { 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 @@ public interface StatusBarManagerInternal { 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 @@ public class StatusBarManagerService extends IStatusBarService.Stub { } } } + + @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 @@ package com.android.server.trust; 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.AttributeSet; 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 @@ public class TrustManagerService extends SystemService { 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 @@ public class TrustManagerService extends SystemService { @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 @@ public class TrustManagerService extends SystemService { 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 @@ public class TrustManagerService extends SystemService { 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 @@ public class TrustManagerService extends SystemService { // 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 @@ public class TrustManagerService extends SystemService { } 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 @@ public class TrustManagerService extends SystemService { 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 @@ public class TrustManagerService extends SystemService { } @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 @@ public class TrustManagerService extends SystemService { } } - @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 @@ public class TrustManagerService extends SystemService { 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 @@ public class TrustManagerService extends SystemService { 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 @@ public class TrustManagerService extends SystemService { // 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 @@ public class TrustManagerService extends SystemService { } }; + private final StrongAuthTracker mStrongAuthTracker = new StrongAuthTracker() { + @Override + public void onStrongAuthRequiredChanged(int userId) { + refreshAgentList(userId); + } + }; + private class Receiver extends BroadcastReceiver { @Override @@ -908,8 +843,6 @@ public class TrustManagerService extends SystemService { 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 @@ public class TrustManagerService extends SystemService { } 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/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index bc5ef36..36cb7f2 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -10408,8 +10408,8 @@ public class WindowManagerService extends IWindowManager.Stub ": 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()) { @@ -11952,12 +11952,18 @@ public class WindowManagerService extends IWindowManager.Stub 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/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.PASSWORD_QUALITY_COMPLEX; 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.DevicePolicyManagerInternal; 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 bd72860..7dd16d1 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -875,6 +875,11 @@ public final class SystemServer { 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 @@ package com.android.server.usb; 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 @@ public class UsbDeviceManager { 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 @@ public class UsbDeviceManager { } }; + 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 @@ public class UsbDeviceManager { if (secureAdbEnabled && !dataEncrypted) { mDebuggingManager = new UsbDebuggingManager(context); } + mContext.registerReceiver(mHostReceiver, + new IntentFilter(UsbManager.ACTION_USB_PORT_CHANGED)); } private UsbSettingsManager getCurrentSettings() { @@ -299,6 +315,7 @@ public class UsbDeviceManager { // current USB state private boolean mConnected; + private boolean mHostConnected; private boolean mConfigured; private boolean mUsbDataUnlocked; private String mCurrentFunctions; @@ -377,6 +394,11 @@ public class UsbDeviceManager { 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 @@ public class UsbDeviceManager { 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 @@ public class UsbDeviceManager { 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.UsbPort; 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 @@ public class UsbPortManager { 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 @@ public class UsbPortManager { 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 @@ public class UsbPortManager { } } - 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) { |