summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/Android.mk7
-rw-r--r--services/accessibility/Android.mk2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java1
-rw-r--r--services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java7
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java3
-rw-r--r--services/backup/java/com/android/server/backup/BackupManagerService.java35
-rw-r--r--services/core/Android.mk4
-rw-r--r--services/core/java/com/android/server/AlarmManagerService.java304
-rw-r--r--services/core/java/com/android/server/AnyMotionDetector.java14
-rw-r--r--services/core/java/com/android/server/AppOpsPolicy.java446
-rw-r--r--services/core/java/com/android/server/AppOpsService.java610
-rw-r--r--services/core/java/com/android/server/AssetAtlasService.java5
-rw-r--r--services/core/java/com/android/server/BasePermissionDialog.java84
-rw-r--r--services/core/java/com/android/server/BatteryService.java569
-rw-r--r--services/core/java/com/android/server/BluetoothManagerService.java345
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java84
-rw-r--r--services/core/java/com/android/server/DeviceIdleController.java190
-rw-r--r--services/core/java/com/android/server/EventLogTags.logtags10
-rw-r--r--services/core/java/com/android/server/InputMethodManagerService.java157
-rw-r--r--services/core/java/com/android/server/LocationManagerService.java149
-rw-r--r--services/core/java/com/android/server/LockSettingsService.java63
-rw-r--r--services/core/java/com/android/server/LockSettingsStorage.java32
-rw-r--r--services/core/java/com/android/server/LockSettingsStrongAuth.java12
-rw-r--r--services/core/java/com/android/server/MasterClearReceiver.java6
-rw-r--r--services/core/java/com/android/server/MountService.java46
-rw-r--r--services/core/java/com/android/server/NetPluginDelegate.java113
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java137
-rw-r--r--services/core/java/com/android/server/PermissionDialog.java145
-rw-r--r--services/core/java/com/android/server/PermissionDialogReqQueue.java88
-rw-r--r--services/core/java/com/android/server/SystemConfig.java91
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java65
-rw-r--r--services/core/java/com/android/server/ThermalObserver.java146
-rw-r--r--services/core/java/com/android/server/VibratorService.java34
-rw-r--r--services/core/java/com/android/server/Watchdog.java42
-rw-r--r--services/core/java/com/android/server/WiredAccessoryManager.java41
-rw-r--r--services/core/java/com/android/server/accounts/AccountManagerService.java8
-rw-r--r--services/core/java/com/android/server/accounts/TokenCache.java10
-rwxr-xr-xservices/core/java/com/android/server/am/ActiveServices.java69
-rwxr-xr-x[-rw-r--r--]services/core/java/com/android/server/am/ActivityManagerService.java634
-rw-r--r--services/core/java/com/android/server/am/ActivityStack.java98
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java104
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java114
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java13
-rw-r--r--services/core/java/com/android/server/am/EventLogTags.logtags2
-rw-r--r--services/core/java/com/android/server/am/LockTaskNotify.java28
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java127
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java30
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java377
-rw-r--r--services/core/java/com/android/server/audio/MediaFocusControl.java79
-rw-r--r--services/core/java/com/android/server/connectivity/Nat464Xlat.java17
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java272
-rw-r--r--services/core/java/com/android/server/content/SyncManager.java3
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java71
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java26
-rw-r--r--services/core/java/com/android/server/display/ExtendedRemoteDisplayHelper.java163
-rw-r--r--services/core/java/com/android/server/display/LocalDisplayAdapter.java4
-rw-r--r--services/core/java/com/android/server/display/WifiDisplayController.java124
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java89
-rw-r--r--services/core/java/com/android/server/fingerprint/FingerprintService.java20
-rw-r--r--services/core/java/com/android/server/gesture/GestureInputFilter.java333
-rw-r--r--services/core/java/com/android/server/gesture/GestureService.java63
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java221
-rw-r--r--services/core/java/com/android/server/lights/Light.java2
-rw-r--r--services/core/java/com/android/server/lights/LightsManager.java4
-rw-r--r--services/core/java/com/android/server/lights/LightsService.java33
-rw-r--r--services/core/java/com/android/server/location/GeoFencerBase.java147
-rw-r--r--services/core/java/com/android/server/location/GeoFencerProxy.java149
-rw-r--r--services/core/java/com/android/server/location/GpsLocationProvider.java42
-rw-r--r--services/core/java/com/android/server/media/MediaSessionRecord.java182
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java153
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsCollection.java15
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsRecorder.java61
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsService.java42
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java553
-rw-r--r--services/core/java/com/android/server/notification/RankingConfig.java5
-rw-r--r--services/core/java/com/android/server/notification/RankingHelper.java32
-rw-r--r--services/core/java/com/android/server/notification/ZenModeHelper.java36
-rw-r--r--services/core/java/com/android/server/pm/BasePermission.java2
-rw-r--r--services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java61
-rw-r--r--services/core/java/com/android/server/pm/Installer.java41
-rw-r--r--services/core/java/com/android/server/pm/MultiTaskDealer.java141
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java12
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerSession.java10
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java1766
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java38
-rwxr-xr-x[-rw-r--r--]services/core/java/com/android/server/pm/Settings.java391
-rw-r--r--services/core/java/com/android/server/pm/UserContentObserver.java92
-rw-r--r--services/core/java/com/android/server/policy/BarController.java3
-rw-r--r--services/core/java/com/android/server/policy/BurnInProtectionHelper.java9
-rw-r--r--services/core/java/com/android/server/policy/GlobalActions.java468
-rw-r--r--services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java3
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java1359
-rw-r--r--services/core/java/com/android/server/policy/PolicyControl.java4
-rw-r--r--services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java10
-rw-r--r--services/core/java/com/android/server/policy/WindowOrientationListener.java7
-rw-r--r--services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java27
-rw-r--r--services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java12
-rw-r--r--services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java11
-rw-r--r--services/core/java/com/android/server/power/Notifier.java35
-rw-r--r--services/core/java/com/android/server/power/PowerManagerService.java611
-rw-r--r--services/core/java/com/android/server/power/ShutdownThread.java343
-rw-r--r--services/core/java/com/android/server/statusbar/StatusBarManagerService.java2
-rw-r--r--services/core/java/com/android/server/tv/TvInputHal.java2
-rw-r--r--services/core/java/com/android/server/twilight/TwilightService.java7
-rw-r--r--services/core/java/com/android/server/wallpaper/WallpaperManagerService.java323
-rw-r--r--services/core/java/com/android/server/wm/BlurLayer.java313
-rw-r--r--services/core/java/com/android/server/wm/CircularDisplayMask.java2
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java45
-rw-r--r--services/core/java/com/android/server/wm/DisplaySettings.java11
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java28
-rw-r--r--services/core/java/com/android/server/wm/Session.java28
-rw-r--r--services/core/java/com/android/server/wm/TaskStack.java107
-rw-r--r--services/core/java/com/android/server/wm/Watermark.java52
-rw-r--r--services/core/java/com/android/server/wm/WindowAnimator.java64
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java219
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java148
-rw-r--r--services/core/jni/Android.mk7
-rw-r--r--services/core/jni/com_android_server_AlarmManagerService.cpp85
-rw-r--r--services/core/jni/com_android_server_PersistentDataBlockService.cpp21
-rw-r--r--services/core/jni/com_android_server_SystemServer.cpp16
-rw-r--r--services/core/jni/com_android_server_UsbMidiDevice.cpp22
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp62
-rw-r--r--services/core/jni/com_android_server_lights_LightsService.cpp24
-rw-r--r--services/core/jni/com_android_server_location_GpsLocationProvider.cpp9
-rw-r--r--services/core/jni/com_android_server_power_PowerManagerService.cpp18
-rw-r--r--services/core/jni/com_android_server_tv_TvInputHal.cpp85
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java32
-rw-r--r--services/java/com/android/server/SystemServer.java94
-rw-r--r--services/libtvextensions/Android.mk21
-rw-r--r--services/libtvextensions/common/ExtensionsLoader.hpp91
-rw-r--r--services/libtvextensions/common/TvInputHalExtensionsCommon.h69
-rw-r--r--services/libtvextensions/jni/BufferProducerThread.h89
-rw-r--r--services/libtvextensions/jni/TvInputHalExtensions.h54
-rw-r--r--services/libtvextensions/jni/TvInputHalFactory.cpp56
-rw-r--r--services/net/java/android/net/dhcp/DhcpPacket.java7
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java130
-rw-r--r--services/usb/Android.mk2
-rw-r--r--services/usb/java/com/android/server/usb/UsbAlsaManager.java2
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java171
-rw-r--r--services/usb/java/com/android/server/usb/UsbSettingsManager.java2
141 files changed, 15071 insertions, 1552 deletions
diff --git a/services/Android.mk b/services/Android.mk
index 1918db5..b15f575 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -35,6 +35,8 @@ services := \
# The convention is to name each service module 'services.$(module_name)'
LOCAL_STATIC_JAVA_LIBRARIES := $(addprefix services.,$(services))
+LOCAL_JAVA_LIBRARIES += org.cyanogenmod.platform.internal
+
include $(BUILD_JAVA_LIBRARY)
# native library
@@ -48,6 +50,11 @@ LOCAL_SHARED_LIBRARIES :=
# include all the jni subdirs to collect their sources
include $(wildcard $(LOCAL_PATH)/*/jni/Android.mk)
+LOCAL_C_INCLUDES += \
+ $(TOP)/frameworks/base/services/libtvextensions \
+
+LOCAL_WHOLE_STATIC_LIBRARIES := libTvInputHalExtensions
+
LOCAL_CFLAGS += -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
LOCAL_MODULE:= libandroid_servers
diff --git a/services/accessibility/Android.mk b/services/accessibility/Android.mk
index d98fc28..39355a7 100644
--- a/services/accessibility/Android.mk
+++ b/services/accessibility/Android.mk
@@ -7,4 +7,6 @@ LOCAL_MODULE := services.accessibility
LOCAL_SRC_FILES += \
$(call all-java-files-under,java)
+LOCAL_JAVA_LIBRARIES += org.cyanogenmod.platform.internal
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
index d0b5898..4e4c249 100644
--- a/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/DisplayAdjustmentUtils.java
@@ -26,6 +26,7 @@ import android.os.ServiceManager;
import android.provider.Settings;
import android.util.Slog;
import android.view.accessibility.AccessibilityManager;
+import cyanogenmod.providers.CMSettings;
/**
* Utility methods for performing accessibility display adjustments.
diff --git a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
index b4613d6..9be29c2 100644
--- a/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
+++ b/services/accessibility/java/com/android/server/accessibility/ScreenMagnifier.java
@@ -790,12 +790,7 @@ public final class ScreenMagnifier implements WindowManagerInternal.Magnificatio
while (mDelayedEventQueue != null) {
MotionEventInfo info = mDelayedEventQueue;
mDelayedEventQueue = info.mNext;
- final long offset = SystemClock.uptimeMillis() - info.mCachedTimeMillis;
- MotionEvent event = obtainEventWithOffsetTimeAndDownTime(info.mEvent, offset);
- MotionEvent rawEvent = obtainEventWithOffsetTimeAndDownTime(info.mRawEvent, offset);
- ScreenMagnifier.this.onMotionEvent(event, rawEvent, info.mPolicyFlags);
- event.recycle();
- rawEvent.recycle();
+ ScreenMagnifier.this.onMotionEvent(info.mEvent, info.mRawEvent, info.mPolicyFlags);
info.recycle();
}
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index fa87270..bc84676 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -2009,6 +2009,9 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
if (widget.views != null) {
pw.print(" views="); pw.println(widget.views);
}
+ if (widget.options != null) {
+ pw.print(" options="); pw.println(widget.options);
+ }
}
private static void serializeProvider(XmlSerializer out, Provider p) throws IOException {
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index 30d90af..d16c78a 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -2231,11 +2231,12 @@ public class BackupManagerService {
// fire off a backup agent, blocking until it attaches or times out
IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
IBackupAgent agent = null;
- synchronized(mAgentConnectLock) {
- mConnecting = true;
- mConnectedAgent = null;
- try {
- if (mActivityManager.bindBackupAgent(app, mode)) {
+ try {
+ synchronized(mAgentConnectLock) {
+ mConnecting = true;
+ mConnectedAgent = null;
+ if (mActivityManager.bindBackupAgent(app.packageName, mode,
+ UserHandle.USER_OWNER)) {
Slog.d(TAG, "awaiting agent for " + app);
// success; wait for the agent to arrive
@@ -2248,7 +2249,6 @@ public class BackupManagerService {
} catch (InterruptedException e) {
// just bail
Slog.w(TAG, "Interrupted: " + e);
- mActivityManager.clearPendingBackup();
return null;
}
}
@@ -2256,14 +2256,22 @@ public class BackupManagerService {
// if we timed out with no connect, abort and move on
if (mConnecting == true) {
Slog.w(TAG, "Timeout waiting for agent " + app);
- mActivityManager.clearPendingBackup();
return null;
}
if (DEBUG) Slog.i(TAG, "got agent " + mConnectedAgent);
agent = mConnectedAgent;
}
- } catch (RemoteException e) {
+ }
+ } catch (RemoteException e) {
// can't happen - ActivityManager is local
+ } finally {
+ // failed to bind backup agent, clear pending backup
+ if (agent == null) {
+ try {
+ mActivityManager.clearPendingBackup();
+ } catch (RemoteException e) {
+ // can't happen - ActivityManager is local
+ }
}
}
return agent;
@@ -2944,6 +2952,12 @@ public class BackupManagerService {
final String pkgName = mCurrentPackage.packageName;
final long filepos = mBackupDataName.length();
+ if (mBackupData == null) {
+ failAgent(mAgentBinder, "Backup data was null: " + mBackupDataName);
+ addBackupTrace("backup data was null: " + mBackupDataName);
+ agentErrorCleanup();
+ return;
+ }
FileDescriptor fd = mBackupData.getFileDescriptor();
try {
// If it's a 3rd party app, see whether they wrote any protected keys
@@ -7948,6 +7962,11 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
}
int toCopy = result;
while (toCopy > 0) {
+ if (!mEngine.isRunning() && RestoreEngine.SUCCESS != mEngine.getResult()) {
+ Slog.e(TAG, "RestoreEngine fail");
+ // throw IOException to abandon this package's restore
+ throw new IOException();
+ }
int n = transportIn.read(buffer, 0, toCopy);
engineOut.write(buffer, 0, n);
toCopy -= n;
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 666f2ff..d2ecac9 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -12,4 +12,8 @@ LOCAL_SRC_FILES += \
LOCAL_JAVA_LIBRARIES := services.net telephony-common
LOCAL_STATIC_JAVA_LIBRARIES := tzdata_update
+LOCAL_JAVA_LIBRARIES += services.accessibility
+
+LOCAL_JAVA_LIBRARIES += org.cyanogenmod.platform.internal
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 130a234..5875ca9 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -28,7 +28,9 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Binder;
@@ -42,6 +44,7 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.provider.AlarmClock;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.DateFormat;
@@ -66,25 +69,38 @@ import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
+import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.TimeZone;
import java.util.TreeSet;
+import java.util.Timer;
+import java.util.TimerTask;
import static android.app.AlarmManager.RTC_WAKEUP;
import static android.app.AlarmManager.RTC;
import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
import static android.app.AlarmManager.ELAPSED_REALTIME;
+import static android.app.AlarmManager.RTC_POWEROFF_WAKEUP;
import com.android.internal.util.LocalLog;
+import cyanogenmod.app.CMStatusBarManager;
+import cyanogenmod.app.CustomTile;
+
+import org.cyanogenmod.internal.util.QSUtils;
+import org.cyanogenmod.internal.util.QSUtils.OnQSChanged;
+import org.cyanogenmod.internal.util.QSConstants;
+
class AlarmManagerService extends SystemService {
private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP;
private static final int RTC_MASK = 1 << RTC;
private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP;
private static final int ELAPSED_REALTIME_MASK = 1 << ELAPSED_REALTIME;
+ private static final int RTC_POWEROFF_WAKEUP_MASK = 1 << RTC_POWEROFF_WAKEUP;
static final int TIME_CHANGED_MASK = 1 << 16;
- static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK|ELAPSED_REALTIME_WAKEUP_MASK;
+ static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK|ELAPSED_REALTIME_WAKEUP_MASK
+ |RTC_POWEROFF_WAKEUP_MASK;
// Mask for testing whether a given alarm type is wakeup vs non-wakeup
static final int TYPE_NONWAKEUP_MASK = 0x1; // low bit => non-wakeup
@@ -111,8 +127,13 @@ class AlarmManagerService extends SystemService {
final Object mLock = new Object();
+ private final ArrayList<Integer> mTriggeredUids = new ArrayList<Integer>();
+ private final ArrayList<Integer> mBlockedUids = new ArrayList<Integer>();
+ private static final int BLOCKED_UID_CHECK_INTERVAL = 1000; // 1 sec.
+
long mNativeData;
private long mNextWakeup;
+ private long mNextRtcWakeup;
private long mNextNonWakeup;
int mBroadcastRefCount = 0;
PowerManager.WakeLock mWakeLock;
@@ -286,6 +307,14 @@ class AlarmManagerService extends SystemService {
final Constants mConstants;
+ private final OnQSChanged mQSListener = new OnQSChanged() {
+ @Override
+ public void onQSChanged() {
+ processQSChangedLocked();
+ }
+ };
+ private ContentObserver mQSObserver;
+
// Alarm delivery ordering bookkeeping
static final int PRIO_TICK = 0;
static final int PRIO_WAKEUP = 1;
@@ -347,6 +376,14 @@ class AlarmManagerService extends SystemService {
return alarms.get(index);
}
+ long getWhenByElapsedTime(long whenElapsed) {
+ for(int i=0;i< alarms.size();i++) {
+ if(alarms.get(i).whenElapsed == whenElapsed)
+ return alarms.get(i).when;
+ }
+ return 0;
+ }
+
boolean canHold(long whenElapsed, long maxWhen) {
return (end >= whenElapsed) && (start <= maxWhen);
}
@@ -496,6 +533,17 @@ class AlarmManagerService extends SystemService {
return false;
}
+ boolean isRtcPowerOffWakeup() {
+ final int N = alarms.size();
+ for (int i = 0; i < N; i++) {
+ Alarm a = alarms.get(i);
+ if (a.type == RTC_POWEROFF_WAKEUP) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public String toString() {
StringBuilder b = new StringBuilder(40);
@@ -602,7 +650,8 @@ class AlarmManagerService extends SystemService {
}
static long convertToElapsed(long when, int type) {
- final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
+ final boolean isRtc = (type == RTC || type == RTC_WAKEUP
+ || type == RTC_POWEROFF_WAKEUP);
if (isRtc) {
when -= System.currentTimeMillis() - SystemClock.elapsedRealtime();
}
@@ -732,9 +781,10 @@ class AlarmManagerService extends SystemService {
final BroadcastStats mBroadcastStats;
final FilterStats mFilterStats;
final int mAlarmType;
+ final int mUid;
InFlight(AlarmManagerService service, PendingIntent pendingIntent, WorkSource workSource,
- int alarmType, String tag, long nowELAPSED) {
+ int alarmType, String tag, long nowELAPSED, int uid) {
mPendingIntent = pendingIntent;
mWorkSource = workSource;
mTag = tag;
@@ -747,6 +797,7 @@ class AlarmManagerService extends SystemService {
fs.lastTime = nowELAPSED;
mFilterStats = fs;
mAlarmType = alarmType;
+ mUid = uid;
}
}
@@ -794,7 +845,7 @@ class AlarmManagerService extends SystemService {
@Override
public void onStart() {
mNativeData = init();
- mNextWakeup = mNextNonWakeup = 0;
+ mNextWakeup = mNextRtcWakeup = mNextNonWakeup = 0;
// We have to set current TimeZone info to kernel
// because kernel doesn't keep this after reboot
@@ -828,6 +879,8 @@ class AlarmManagerService extends SystemService {
}
publishBinderService(Context.ALARM_SERVICE, mService);
+
+ mQSObserver = QSUtils.registerObserverForQSChanges(getContext(), mQSListener);
}
@Override
@@ -840,6 +893,12 @@ class AlarmManagerService extends SystemService {
@Override
protected void finalize() throws Throwable {
try {
+ QSUtils.unregisterObserverForQSChanges(getContext(), mQSObserver);
+ } catch (Exception ex) {
+ // Ignore
+ }
+
+ try {
close(mNativeData);
} finally {
super.finalize();
@@ -916,7 +975,7 @@ class AlarmManagerService extends SystemService {
interval = minInterval;
}
- if (type < RTC_WAKEUP || type > ELAPSED_REALTIME) {
+ if (type < RTC_WAKEUP || type > RTC_POWEROFF_WAKEUP) {
throw new IllegalArgumentException("Invalid alarm type " + type);
}
@@ -1023,6 +1082,8 @@ class AlarmManagerService extends SystemService {
if (a.alarmClock != null) {
mNextAlarmClockMayChange = true;
+ //Publish as system user
+ publishNextAlarmCustomTile(UserHandle.myUserId());
}
boolean needRebatch = false;
@@ -1174,8 +1235,52 @@ class AlarmManagerService extends SystemService {
dumpImpl(pw);
}
+
+ @Override
+ /* updates the blocked uids, so if a wake lock is acquired to only fire
+ * alarm for it, it can be released.
+ */
+ public void updateBlockedUids(int uid, boolean isBlocked) {
+
+ if (localLOGV) Slog.v(TAG, "UpdateBlockedUids: uid = " + uid +
+ " isBlocked = " + isBlocked);
+
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ if (localLOGV) Slog.v(TAG, "UpdateBlockedUids is not allowed");
+ return;
+ }
+
+ synchronized(mLock) {
+ if(isBlocked) {
+ mBlockedUids.add(new Integer(uid));
+ Timer checkBlockedUidTimer = new Timer();
+ checkBlockedUidTimer.schedule( new CheckBlockedUidTimerTask(uid),
+ BLOCKED_UID_CHECK_INTERVAL);
+ } else {
+ mBlockedUids.clear();
+ }
+ }
+ }
};
+ class CheckBlockedUidTimerTask extends TimerTask {
+ private int mUid;
+ CheckBlockedUidTimerTask(int uid) {
+ mUid = uid;
+ }
+ @Override
+ public void run(){
+ if (mBlockedUids.contains(mUid) && mTriggeredUids.contains(mUid)) {
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ if (localLOGV)
+ Slog.v(TAG, "AM WakeLock Released Internally!!");
+ return;
+ }
+ }
+ return;
+ }
+ }
void dumpImpl(PrintWriter pw) {
synchronized (mLock) {
pw.println("Current Alarm Manager state:");
@@ -1469,6 +1574,19 @@ class AlarmManagerService extends SystemService {
return null;
}
+ private Batch findFirstRtcWakeupBatchLocked() {
+ long elapsed = SystemClock.elapsedRealtime();
+ final int N = mAlarmBatches.size();
+ for (int i = 0; i < N; i++) {
+ Batch b = mAlarmBatches.get(i);
+ long intervalTime = b.start - elapsed;
+ if (b.isRtcPowerOffWakeup()) {
+ return b;
+ }
+ }
+ return null;
+ }
+
long getNextWakeFromIdleTimeImpl() {
synchronized (mLock) {
return mNextWakeFromIdle != null ? mNextWakeFromIdle.whenElapsed : Long.MAX_VALUE;
@@ -1536,6 +1654,9 @@ class AlarmManagerService extends SystemService {
updateNextAlarmInfoForUserLocked(userId, null);
}
}
+
+ // Process dynamic custom tile
+ processQSChangedLocked();
}
private void updateNextAlarmInfoForUserLocked(int userId,
@@ -1611,10 +1732,18 @@ class AlarmManagerService extends SystemService {
if (mAlarmBatches.size() > 0) {
final Batch firstWakeup = findFirstWakeupBatchLocked();
final Batch firstBatch = mAlarmBatches.get(0);
+ final Batch firstRtcWakeup = findFirstRtcWakeupBatchLocked();
if (firstWakeup != null && mNextWakeup != firstWakeup.start) {
mNextWakeup = firstWakeup.start;
setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
}
+ if (firstRtcWakeup != null && mNextRtcWakeup != firstRtcWakeup.start) {
+ mNextRtcWakeup = firstRtcWakeup.start;
+ long when = firstRtcWakeup.getWhenByElapsedTime(mNextRtcWakeup);
+ if (when != 0) {
+ setLocked(RTC_POWEROFF_WAKEUP, when);
+ }
+ }
if (firstBatch != firstWakeup) {
nextNonWakeup = firstBatch.start;
}
@@ -1634,6 +1763,18 @@ class AlarmManagerService extends SystemService {
boolean didRemove = false;
for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
Batch b = mAlarmBatches.get(i);
+ ArrayList<Alarm> alarmList = b.alarms;
+ Alarm alarm = null;
+ for (int j = alarmList.size() - 1; j >= 0; j--) {
+ alarm = alarmList.get(j);
+ if (alarm.type == RTC_POWEROFF_WAKEUP && alarm.operation.equals(operation)) {
+ long alarmSeconds, alarmNanoseconds;
+ alarmSeconds = alarm.when / 1000;
+ alarmNanoseconds = (alarm.when % 1000) * 1000 * 1000;
+ clear(mNativeData, alarm.type, alarmSeconds, alarmNanoseconds);
+ mNextRtcWakeup = 0;
+ }
+ }
didRemove |= b.remove(operation);
if (b.size() == 0) {
mAlarmBatches.remove(i);
@@ -1804,6 +1945,7 @@ class AlarmManagerService extends SystemService {
case RTC_WAKEUP : return "RTC_WAKEUP";
case ELAPSED_REALTIME : return "ELAPSED";
case ELAPSED_REALTIME_WAKEUP: return "ELAPSED_WAKEUP";
+ case RTC_POWEROFF_WAKEUP : return "RTC_POWEROFF_WAKEUP";
default:
break;
}
@@ -1824,6 +1966,7 @@ class AlarmManagerService extends SystemService {
private native long init();
private native void close(long nativeData);
private native void set(long nativeData, int type, long seconds, long nanoseconds);
+ private native void clear(long nativeData, int type, long seconds, long nanoseconds);
private native int waitForAlarm(long nativeData);
private native int setKernelTime(long nativeData, long millis);
private native int setKernelTimezone(long nativeData, int minuteswest);
@@ -1959,6 +2102,7 @@ class AlarmManagerService extends SystemService {
public long maxWhenElapsed; // also in the elapsed time base
public long repeatInterval;
public PriorityClass priorityClass;
+ public int pid;
public Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen,
long _interval, PendingIntent _op, WorkSource _ws, int _flags,
@@ -1966,7 +2110,8 @@ class AlarmManagerService extends SystemService {
type = _type;
origWhen = _when;
wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP
- || _type == AlarmManager.RTC_WAKEUP;
+ || _type == AlarmManager.RTC_WAKEUP
+ || _type == AlarmManager.RTC_POWEROFF_WAKEUP;
when = _when;
whenElapsed = _whenElapsed;
windowLength = _windowLength;
@@ -1977,12 +2122,13 @@ class AlarmManagerService extends SystemService {
workSource = _ws;
flags = _flags;
alarmClock = _info;
- uid = _uid;
+ uid = operation.getCreatorUid();
+ pid = Binder.getCallingPid();
}
public static String makeTag(PendingIntent pi, int type) {
return pi.getTag(type == ELAPSED_REALTIME_WAKEUP || type == RTC_WAKEUP
- ? "*walarm*:" : "*alarm*:");
+ || type == RTC_POWEROFF_WAKEUP ? "*walarm*:" : "*alarm*:");
}
@Override
@@ -2002,7 +2148,8 @@ class AlarmManagerService extends SystemService {
public void dump(PrintWriter pw, String prefix, long nowRTC, long nowELAPSED,
SimpleDateFormat sdf) {
- final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
+ final boolean isRtc = (type == RTC || type == RTC_WAKEUP
+ || type == RTC_POWEROFF_WAKEUP);
pw.print(prefix); pw.print("tag="); pw.println(tag);
pw.print(prefix); pw.print("type="); pw.print(type);
pw.print(" whenElapsed="); TimeUtils.formatDuration(whenElapsed,
@@ -2117,15 +2264,19 @@ class AlarmManagerService extends SystemService {
mResultReceiver, mHandler, null, allowWhileIdle ? mIdleOptions : null);
// we have an active broadcast so stay awake.
- if (mBroadcastRefCount == 0) {
+ if (mBroadcastRefCount == 0 || !mWakeLock.isHeld()) {
setWakelockWorkSource(alarm.operation, alarm.workSource,
alarm.type, alarm.tag, true);
mWakeLock.acquire();
}
final InFlight inflight = new InFlight(AlarmManagerService.this,
- alarm.operation, alarm.workSource, alarm.type, alarm.tag, nowELAPSED);
+ alarm.operation,
+ alarm.workSource,
+ alarm.type, alarm.tag,
+ nowELAPSED, alarm.uid);
mInFlight.add(inflight);
mBroadcastRefCount++;
+ mTriggeredUids.add(new Integer(alarm.uid));
if (allowWhileIdle) {
// Record the last time this uid handled an ALLOW_WHILE_IDLE alarm.
@@ -2149,7 +2300,8 @@ class AlarmManagerService extends SystemService {
fs.nesting++;
}
if (alarm.type == ELAPSED_REALTIME_WAKEUP
- || alarm.type == RTC_WAKEUP) {
+ || alarm.type == RTC_WAKEUP
+ || alarm.type == RTC_POWEROFF_WAKEUP) {
bs.numWakeup++;
fs.numWakeup++;
if (alarm.workSource != null && alarm.workSource.size() > 0) {
@@ -2321,11 +2473,10 @@ class AlarmManagerService extends SystemService {
mWakeLock.setWorkSource(new WorkSource(uid));
return;
}
+ // Something went wrong; fall back to attributing the lock to the OS
+ mWakeLock.setWorkSource(null);
} catch (Exception e) {
}
-
- // Something went wrong; fall back to attributing the lock to the OS
- mWakeLock.setWorkSource(null);
}
private class AlarmHandler extends Handler {
@@ -2529,13 +2680,124 @@ class AlarmManagerService extends SystemService {
return bs;
}
+ private void publishNextAlarmCustomTile(int userId) {
+ // This action should be performed as system
+ long token = Binder.clearCallingIdentity();
+ try {
+ final UserHandle user = new UserHandle(userId);
+ final int icon = QSUtils.getDynamicQSTileResIconId(getContext(), userId,
+ QSConstants.DYNAMIC_TILE_NEXT_ALARM);
+ final String contentDesc = QSUtils.getDynamicQSTileLabel(getContext(), userId,
+ QSConstants.DYNAMIC_TILE_NEXT_ALARM);
+ final Context resourceContext = QSUtils.getQSTileContext(getContext(), userId);
+
+ // Create the expanded view with all the user alarms
+ AlarmManager.AlarmClockInfo nextAlarm = null;
+ CustomTile.ListExpandedStyle style = new CustomTile.ListExpandedStyle();
+ ArrayList<CustomTile.ExpandedListItem> items = new ArrayList<>();
+ for (Alarm alarm : getAllUserAlarmsLocked(userId)) {
+ if (nextAlarm == null) {
+ nextAlarm = alarm.alarmClock;
+ }
+
+ final String pkg = alarm.operation.getCreatorPackage();
+ CustomTile.ExpandedListItem item = new CustomTile.ExpandedListItem();
+ item.setExpandedListItemDrawable(icon);
+ item.setExpandedListItemTitle(formatNextAlarm(getContext(), alarm.alarmClock,
+ userId));
+ item.setExpandedListItemSummary(getAlarmApkLabel(pkg));
+ item.setExpandedListItemOnClickIntent(getCustomTilePendingIntent(user, pkg));
+ items.add(item);
+ }
+ style.setListItems(items);
+
+ // Don't bother posting a tile if no "next alarms" are available.
+ if (nextAlarm == null) {
+ return;
+ }
+ // Build the custom tile
+ CMStatusBarManager statusBarManager = CMStatusBarManager.getInstance(getContext());
+ CustomTile tile = new CustomTile.Builder(resourceContext)
+ .setLabel(formatNextAlarm(getContext(), nextAlarm, userId))
+ .setContentDescription(contentDesc)
+ .setIcon(icon)
+ .setExpandedStyle(style)
+ .build();
+ statusBarManager.publishTileAsUser(QSConstants.DYNAMIC_TILE_NEXT_ALARM,
+ AlarmManagerService.class.hashCode(), tile, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void unpublishNextAlarmCustomTile(int userId) {
+ // This action should be performed as system
+ long token = Binder.clearCallingIdentity();
+ try {
+ CMStatusBarManager statusBarManager = CMStatusBarManager.getInstance(getContext());
+ statusBarManager.removeTileAsUser(QSConstants.DYNAMIC_TILE_NEXT_ALARM,
+ AlarmManagerService.class.hashCode(), new UserHandle(userId));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private List<Alarm> getAllUserAlarmsLocked(int userId) {
+ List<Alarm> userAlarms = new ArrayList<>();
+ synchronized (mLock) {
+ final int N = mAlarmBatches.size();
+ for (int i = 0; i < N; i++) {
+ ArrayList<Alarm> alarms = mAlarmBatches.get(i).alarms;
+ final int M = alarms.size();
+ for (int j = 0; j < M; j++) {
+ Alarm a = alarms.get(j);
+ if (a.alarmClock != null && userId == a.uid) {
+ userAlarms.add(a);
+ }
+ }
+ }
+ }
+ return userAlarms;
+ }
+
+ private String getAlarmApkLabel(String pkg) {
+ final PackageManager pm = getContext().getPackageManager();
+ ApplicationInfo ai = null;
+ try {
+ ai = pm.getApplicationInfo(pkg, 0);
+ } catch (final NameNotFoundException e) {
+ // Ignore
+ }
+ return (String) (ai != null ? pm.getApplicationLabel(ai) : pkg);
+ }
+
+ private PendingIntent getCustomTilePendingIntent(UserHandle user, String pkg) {
+ Intent i = new Intent(AlarmClock.ACTION_SHOW_ALARMS);
+ i.setPackage(pkg);
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return PendingIntent.getActivityAsUser(getContext(), 0, i,
+ PendingIntent.FLAG_UPDATE_CURRENT, null, user);
+ }
+
+ private void processQSChangedLocked() {
+ synchronized (mLock) {
+ int count = mNextAlarmClockForUser.size();
+ for (int i = 0; i < count; i++) {
+ int userId = mNextAlarmClockForUser.keyAt(i);
+ publishNextAlarmCustomTile(userId);
+ }
+ }
+ }
+
class ResultReceiver implements PendingIntent.OnFinished {
public void onSendFinished(PendingIntent pi, Intent intent, int resultCode,
String resultData, Bundle resultExtras) {
synchronized (mLock) {
+ int uid = 0;
InFlight inflight = null;
for (int i=0; i<mInFlight.size(); i++) {
if (mInFlight.get(i).mPendingIntent == pi) {
+ uid = mInFlight.get(i).mUid;
inflight = mInFlight.remove(i);
break;
}
@@ -2569,8 +2831,12 @@ class AlarmManagerService extends SystemService {
mLog.w("No in-flight alarm for " + pi + " " + intent);
}
mBroadcastRefCount--;
+ mTriggeredUids.remove(new Integer(uid));
+
if (mBroadcastRefCount == 0) {
- mWakeLock.release();
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
if (mInFlight.size() > 0) {
mLog.w("Finished all broadcasts with " + mInFlight.size()
+ " remaining inflights");
@@ -2581,8 +2847,12 @@ class AlarmManagerService extends SystemService {
}
} else {
// the next of our alarms is now in flight. reattribute the wakelock.
+ InFlight inFlight = null;
if (mInFlight.size() > 0) {
- InFlight inFlight = mInFlight.get(0);
+ for(int index = 0; index < mInFlight.size(); index++){
+ inFlight = mInFlight.get(index);
+ if(!mBlockedUids.contains(inFlight.mUid)) break;
+ }
setWakelockWorkSource(inFlight.mPendingIntent, inFlight.mWorkSource,
inFlight.mAlarmType, inFlight.mTag, false);
} else {
diff --git a/services/core/java/com/android/server/AnyMotionDetector.java b/services/core/java/com/android/server/AnyMotionDetector.java
index 6a67316..a0b5c15 100644
--- a/services/core/java/com/android/server/AnyMotionDetector.java
+++ b/services/core/java/com/android/server/AnyMotionDetector.java
@@ -58,9 +58,6 @@ public class AnyMotionDetector {
/** Current measurement state. */
private int mState;
- /** Threshold angle in degrees beyond which the device is considered moving. */
- private final float THRESHOLD_ANGLE = 2f;
-
/** Threshold energy above which the device is considered moving. */
private final float THRESHOLD_ENERGY = 5f;
@@ -88,6 +85,9 @@ public class AnyMotionDetector {
private SensorManager mSensorManager;
private PowerManager.WakeLock mWakeLock;
+ /** Threshold angle in degrees beyond which the device is considered moving. */
+ private final float mThresholdAngle;
+
/** The minimum number of samples required to detect AnyMotion. */
private int mNumSufficientSamples;
@@ -106,7 +106,7 @@ public class AnyMotionDetector {
private DeviceIdleCallback mCallback = null;
public AnyMotionDetector(PowerManager pm, Handler handler, SensorManager sm,
- DeviceIdleCallback callback) {
+ DeviceIdleCallback callback, float thresholdAngle) {
if (DEBUG) Slog.d(TAG, "AnyMotionDetector instantiated.");
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mWakeLock.setReferenceCounted(false);
@@ -116,6 +116,7 @@ public class AnyMotionDetector {
mMeasurementInProgress = false;
mState = STATE_INACTIVE;
mCallback = callback;
+ mThresholdAngle = thresholdAngle;
mRunningStats = new RunningSignalStats();
mNumSufficientSamples = (int) Math.ceil(
((double)ORIENTATION_MEASUREMENT_DURATION_MILLIS / SAMPLING_INTERVAL_MILLIS));
@@ -224,8 +225,9 @@ public class AnyMotionDetector {
Vector3 previousGravityVectorNormalized = mPreviousGravityVector.normalized();
Vector3 currentGravityVectorNormalized = mCurrentGravityVector.normalized();
float angle = previousGravityVectorNormalized.angleBetween(currentGravityVectorNormalized);
- if (DEBUG) Slog.d(TAG, "getStationaryStatus: angle = " + angle);
- if ((angle < THRESHOLD_ANGLE) && (mRunningStats.getEnergy() < THRESHOLD_ENERGY)) {
+ if (DEBUG) Slog.d(TAG, "getStationaryStatus: angle = " + angle
+ + " energy = " + mRunningStats.getEnergy());
+ if ((angle < mThresholdAngle) && (mRunningStats.getEnergy() < THRESHOLD_ENERGY)) {
return RESULT_STATIONARY;
} else if (Float.isNaN(angle)) {
/**
diff --git a/services/core/java/com/android/server/AppOpsPolicy.java b/services/core/java/com/android/server/AppOpsPolicy.java
new file mode 100644
index 0000000..d51983f
--- /dev/null
+++ b/services/core/java/com/android/server/AppOpsPolicy.java
@@ -0,0 +1,446 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.server;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+
+public class AppOpsPolicy {
+ static final String TAG = "AppOpsPolicy";
+ static final boolean DEBUG = false;
+ final File mFile;
+ final Context mContext;
+ public static final int CONTROL_SHOW = 0;
+
+ public static final int CONTROL_NOSHOW = 1;
+
+ public static final int CONTROL_UNKNOWN = 2;
+
+ // Rate limiting thresholds for ask operations
+ public static final int RATE_LIMIT_OP_COUNT = 3;
+ public static final int RATE_LIMIT_OPS_TOTAL_PKG_COUNT = 4;
+ public static final int RATE_LIMIT_OP_DELAY_CEILING = 10;
+
+ public static int stringToControl(String show) {
+ if ("true".equalsIgnoreCase(show)) {
+ return CONTROL_SHOW;
+ } else if ("false".equalsIgnoreCase(show)) {
+ return CONTROL_NOSHOW;
+ }
+ return CONTROL_UNKNOWN;
+ }
+
+ HashMap<String, PolicyPkg> mPolicy = new HashMap<String, PolicyPkg>();
+
+ public AppOpsPolicy(File file, Context context) {
+ super();
+ mFile = file;
+ mContext = context;
+ }
+
+ public final static class PolicyPkg extends SparseArray<PolicyOp> {
+ public String packageName;
+ public int mode;
+ public int show;
+ public String type;
+
+ public PolicyPkg(String packageName, int mode, int show, String type) {
+ this.packageName = packageName;
+ this.mode = mode;
+ this.show = show;
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ return "PolicyPkg [packageName=" + packageName + ", mode=" + mode
+ + ", show=" + show + ", type=" + type + "]";
+ }
+
+ }
+
+ public final static class PolicyOp {
+ public int op;
+ public int mode;
+ public int show;
+
+ public PolicyOp(int op, int mode, int show) {
+ this.op = op;
+ this.mode = mode;
+ this.show = show;
+ }
+
+ @Override
+ public String toString() {
+ return "PolicyOp [op=" + op + ", mode=" + mode + ", show=" + show
+ + "]";
+ }
+ }
+
+ void readPolicy() {
+ FileInputStream stream;
+ synchronized (mFile) {
+ try {
+ stream = new FileInputStream(mFile);
+ } catch (FileNotFoundException e) {
+ Slog.i(TAG, "App ops policy file (" + mFile.getPath()
+ + ") not found; Skipping.");
+ return;
+ }
+ boolean success = false;
+ try {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+ int type;
+ success = true;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ ;
+ }
+ if (type != XmlPullParser.START_TAG) {
+ throw new IllegalStateException("no start tag found");
+ }
+
+ int outerDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG
+ || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String tagName = parser.getName();
+ if (tagName.equals("user-app")
+ || tagName.equals("system-app")) {
+ readDefaultPolicy(parser, tagName);
+ } else if (tagName.equals("application")) {
+ readApplicationPolicy(parser);
+ } else {
+ Slog.w(TAG, "Unknown element under <appops-policy>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ } catch (IllegalStateException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (NullPointerException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (XmlPullParserException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (IOException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } catch (IndexOutOfBoundsException e) {
+ Slog.w(TAG, "Failed parsing " + e);
+ } finally {
+ if (!success) {
+ mPolicy.clear();
+ }
+ try {
+ stream.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private void readDefaultPolicy(XmlPullParser parser, String packageName)
+ throws NumberFormatException, XmlPullParserException, IOException {
+ if (!"user-app".equalsIgnoreCase(packageName)
+ && !"system-app".equalsIgnoreCase(packageName)) {
+ return;
+ }
+ int mode = AppOpsManager.stringToMode(parser.getAttributeValue(null,
+ "permission"));
+ int show = stringToControl(parser.getAttributeValue(null, "show"));
+ if (mode == AppOpsManager.MODE_ERRORED && show == CONTROL_UNKNOWN) {
+ return;
+ }
+ PolicyPkg pkg = this.mPolicy.get(packageName);
+ if (pkg == null) {
+ pkg = new PolicyPkg(packageName, mode, show, packageName);
+ this.mPolicy.put(packageName, pkg);
+ } else {
+ Slog.w(TAG, "Duplicate policy found for package: " + packageName
+ + " of type: " + packageName);
+ pkg.mode = mode;
+ pkg.show = show;
+ }
+ }
+
+ private void readApplicationPolicy(XmlPullParser parser)
+ throws NumberFormatException, XmlPullParserException, IOException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tagName = parser.getName();
+ if (tagName.equals("pkg")) {
+ readPkgPolicy(parser);
+ } else {
+ Slog.w(TAG,
+ "Unknown element under <application>: "
+ + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
+ private void readPkgPolicy(XmlPullParser parser)
+ throws NumberFormatException, XmlPullParserException, IOException {
+ String packageName = parser.getAttributeValue(null, "name");
+ if (packageName == null)
+ return;
+ String appType = parser.getAttributeValue(null, "type");
+ if (appType == null)
+ return;
+ int mode = AppOpsManager.stringToMode(parser.getAttributeValue(null,
+ "permission"));
+ int show = stringToControl(parser.getAttributeValue(null, "show"));
+ String key = packageName + "." + appType;
+ PolicyPkg pkg = this.mPolicy.get(key);
+ if (pkg == null) {
+ pkg = new PolicyPkg(packageName, mode, show, appType);
+ this.mPolicy.put(key, pkg);
+ } else {
+ Slog.w(TAG, "Duplicate policy found for package: " + packageName
+ + " of type: " + appType);
+ pkg.mode = mode;
+ pkg.show = show;
+ }
+
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+ String tagName = parser.getName();
+ if (tagName.equals("op")) {
+ readOpPolicy(parser, pkg);
+ } else {
+ Slog.w(TAG, "Unknown element under <pkg>: " + parser.getName());
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
+ private void readOpPolicy(XmlPullParser parser, PolicyPkg pkg)
+ throws NumberFormatException, XmlPullParserException, IOException {
+ if (pkg == null) {
+ return;
+ }
+ String opName = parser.getAttributeValue(null, "name");
+ if (opName == null) {
+ Slog.w(TAG, "Op name is null");
+ return;
+ }
+ int code = AppOpsManager.stringOpToOp(opName);
+ if (code == AppOpsManager.OP_NONE) {
+ Slog.w(TAG, "Unknown Op: " + opName);
+ return;
+ }
+ int mode = AppOpsManager.stringToMode(parser.getAttributeValue(null,
+ "permission"));
+ int show = stringToControl(parser.getAttributeValue(null, "show"));
+ if (mode == AppOpsManager.MODE_ERRORED && show == CONTROL_UNKNOWN) {
+ return;
+ }
+ PolicyOp op = pkg.get(code);
+ if (op == null) {
+ op = new PolicyOp(code, mode, show);
+ pkg.put(code, op);
+ } else {
+ Slog.w(TAG, "Duplicate policy found for package: "
+ + pkg.packageName + " type: " + pkg.type + " op: " + op.op);
+ op.mode = mode;
+ op.show = show;
+ }
+ }
+
+ void debugPoilcy() {
+ Iterator<Map.Entry<String, PolicyPkg>> iterator = mPolicy.entrySet()
+ .iterator();
+ while (iterator.hasNext()) {
+ String key = iterator.next().getKey();
+ if (DEBUG)
+ Slog.d(TAG, "Key: " + key);
+ PolicyPkg pkg = mPolicy.get(key);
+ if (pkg == null) {
+ if (DEBUG)
+ Slog.d(TAG, "Pkg is null for key: " + key);
+ continue;
+ }
+ if (DEBUG)
+ Slog.d(TAG, pkg.toString());
+ for (int i = 0; i < pkg.size(); i++) {
+ PolicyOp op = pkg.valueAt(i);
+ if (DEBUG)
+ Slog.d(TAG, op.toString());
+ }
+ }
+ }
+
+ private String getAppType(String packageName) {
+ String appType = null;
+ ApplicationInfo appInfo = null;
+ if (mContext != null) {
+ try {
+ appInfo = mContext.getPackageManager().getApplicationInfo(
+ packageName, 0);
+ } catch (NameNotFoundException e) {
+ appInfo = null;
+ }
+ if (appInfo != null) {
+ if ((appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ appType = "system-app";
+ } else {
+ appType = "user-app";
+ }
+ }
+ } else {
+ Slog.e(TAG, "Context is null");
+ }
+ return appType;
+ }
+
+ public boolean isControlAllowed(int code, String packageName) {
+ boolean isShow = true;
+ int show = CONTROL_UNKNOWN;
+ PolicyPkg pkg;
+ String key;
+ String type;
+
+ if (mPolicy == null) {
+ return isShow;
+ }
+
+ type = getAppType(packageName);
+ if (type != null) {
+ key = type;
+ pkg = mPolicy.get(key);
+ if (pkg != null && pkg.show != CONTROL_UNKNOWN) {
+ show = pkg.show;
+ }
+ }
+ key = packageName;
+ if (type != null) {
+ key = key + "." + type;
+ }
+ pkg = mPolicy.get(key);
+ if (pkg != null) {
+ if (pkg.show != CONTROL_UNKNOWN) {
+ show = pkg.show;
+ }
+ PolicyOp op = pkg.get(code);
+ if (op != null) {
+ if (op.show != CONTROL_UNKNOWN) {
+ show = op.show;
+ }
+ }
+ }
+ if (show == CONTROL_NOSHOW) {
+ isShow = false;
+ }
+ return isShow;
+ }
+
+ public int getDefualtMode(int code, String packageName) {
+ int mode = AppOpsManager.MODE_ERRORED;
+ PolicyPkg pkg;
+ String key;
+ String type;
+
+ if (mPolicy == null) {
+ return mode;
+ }
+ if (DEBUG)
+ Slog.d(TAG, "Default mode requested for op=" + code + " package="
+ + packageName);
+ type = getAppType(packageName);
+ if (type != null) {
+ // Get value based on 'type'
+ key = type;
+ pkg = mPolicy.get(key);
+ if (pkg != null && pkg.mode != AppOpsManager.MODE_ERRORED) {
+ if (DEBUG)
+ Slog.d(TAG, "Setting value based on type: " + pkg);
+ mode = pkg.mode;
+ }
+ }
+ // Get value based on 'pkg'.
+ key = packageName;
+ if (type != null) {
+ key = key + "." + type;
+ }
+ pkg = mPolicy.get(key);
+ if (pkg != null) {
+ if (pkg.mode != AppOpsManager.MODE_ERRORED) {
+ if (DEBUG)
+ Slog.d(TAG, "Setting value based on packageName: " + pkg);
+ mode = pkg.mode;
+ }
+ // Get value base on 'op'
+ PolicyOp op = pkg.get(code);
+ if (op != null) {
+ if (op.mode != AppOpsManager.MODE_ERRORED) {
+ if (DEBUG)
+ Slog.d(TAG, "Setting value based on op: " + op);
+ mode = op.mode;
+ }
+ }
+ }
+ if (DEBUG)
+ Slog.d(TAG, "Returning mode=" + mode);
+ return mode;
+ }
+}
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 96c1e2a..3b12ed6 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1,4 +1,7 @@
/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -35,7 +38,11 @@ import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.AppOpsManager;
+import android.app.Dialog;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
@@ -45,6 +52,8 @@ import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
+import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -67,6 +76,7 @@ import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
+import com.android.server.PermissionDialogReqQueue.PermissionDialogReq;
import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParser;
@@ -80,9 +90,24 @@ public class AppOpsService extends IAppOpsService.Stub {
// Write at most every 30 minutes.
static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
+ // Location of policy file.
+ static final String DEFAULT_POLICY_FILE = "/system/etc/appops_policy.xml";
+
Context mContext;
final AtomicFile mFile;
final Handler mHandler;
+ final Looper mLooper;
+ final boolean mStrictEnable;
+ AppOpsPolicy mPolicy;
+ private PowerManager mPowerManager;
+
+ private static final int[] PRIVACY_GUARD_OP_STATES = new int[] {
+ AppOpsManager.OP_COARSE_LOCATION,
+ AppOpsManager.OP_READ_CALL_LOG,
+ AppOpsManager.OP_READ_CONTACTS,
+ AppOpsManager.OP_READ_CALENDAR,
+ AppOpsManager.OP_READ_SMS
+ };
boolean mWriteScheduled;
boolean mFastWriteScheduled;
@@ -104,6 +129,14 @@ public class AppOpsService extends IAppOpsService.Stub {
final SparseArray<UidState> mUidStates = new SparseArray<>();
+ private Runnable mSuSessionChangedRunner = new Runnable() {
+ @Override
+ public void run() {
+ mContext.sendBroadcastAsUser(new Intent(AppOpsManager.ACTION_SU_SESSION_CHANGED),
+ UserHandle.ALL);
+ }
+ };
+
private final SparseArray<boolean[]> mOpRestrictions = new SparseArray<boolean[]>();
private static final class UidState {
@@ -149,12 +182,21 @@ public class AppOpsService extends IAppOpsService.Stub {
public long time;
public long rejectTime;
public int nesting;
-
- public Op(int _uid, String _packageName, int _op) {
+ public int noteOpCount;
+ public int startOpCount;
+ public PermissionDialogReqQueue dialogReqQueue;
+ final ArrayList<IBinder> clientTokens;
+ public int allowedCount;
+ public int ignoredCount;
+ public int delayedCount;
+
+ public Op(int _uid, String _packageName, int _op, int _mode) {
uid = _uid;
packageName = _packageName;
op = _op;
- mode = AppOpsManager.opToDefaultMode(op);
+ mode = _mode;
+ dialogReqQueue = new PermissionDialogReqQueue();
+ clientTokens = new ArrayList<IBinder>();
}
}
@@ -226,17 +268,27 @@ public class AppOpsService extends IAppOpsService.Stub {
}
mClients.remove(mAppToken);
}
+
+ // We cannot broadcast on the synchronized block above because the broadcast might
+ // trigger another appop call that eventually arrives here from a different thread,
+ // causing a deadlock.
+ for (int i=mStartedOps.size()-1; i>=0; i--) {
+ broadcastOpIfNeeded(mStartedOps.get(i).op);
+ }
}
}
public AppOpsService(File storagePath, Handler handler) {
mFile = new AtomicFile(storagePath);
mHandler = handler;
+ mLooper = Looper.myLooper();
+ mStrictEnable = AppOpsManager.isStrictEnable();
readState();
}
public void publish(Context context) {
mContext = context;
+ readPolicy();
ServiceManager.addService(Context.APP_OPS_SERVICE, asBinder());
}
@@ -313,8 +365,45 @@ public class AppOpsService extends IAppOpsService.Stub {
|| mountMode == Zygote.MOUNT_EXTERNAL_WRITE;
}
});
+
+ mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_SCREEN_OFF);
+ mContext.registerReceiver(mIntentReceiver, filter);
}
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(Intent.ACTION_SCREEN_OFF)) {
+ synchronized (this) {
+ for (int i = mUidStates.size() - 1; i >= 0; i--) {
+ UidState uidState = mUidStates.valueAt(i);
+
+ ArrayMap<String, Ops> packages = uidState.pkgOps;
+ if (packages == null) {
+ continue;
+ }
+
+ Iterator<Map.Entry<String, Ops>> it = packages.entrySet().iterator();
+ while (it.hasNext()) {
+ Map.Entry<String, Ops> ent = it.next();
+ Ops pkgOps = ent.getValue();
+ for (int j = pkgOps.size() - 1; j >= 0; j--) {
+ Op curOp = pkgOps.valueAt(j);
+ if (DEBUG)
+ Log.d(TAG, "Ignoring " + curOp.packageName + " request "
+ + curOp.op);
+ curOp.dialogReqQueue.ignore();
+ }
+ }
+ }
+ }
+ }
+ }
+ };
+
public void packageRemoved(int uid, String packageName) {
synchronized (this) {
UidState uidState = mUidStates.get(uid);
@@ -372,7 +461,7 @@ public class AppOpsService extends IAppOpsService.Stub {
Op curOp = pkgOps.valueAt(j);
resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
curOp.rejectTime, curOp.duration, curOp.proxyUid,
- curOp.proxyPackageName));
+ curOp.proxyPackageName, curOp.allowedCount, curOp.ignoredCount));
}
} else {
for (int j=0; j<ops.length; j++) {
@@ -383,7 +472,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
curOp.rejectTime, curOp.duration, curOp.proxyUid,
- curOp.proxyPackageName));
+ curOp.proxyPackageName, curOp.allowedCount, curOp.ignoredCount));
}
}
}
@@ -475,7 +564,8 @@ public class AppOpsService extends IAppOpsService.Stub {
code = AppOpsManager.opToSwitch(code);
synchronized (this) {
- final int defaultMode = AppOpsManager.opToDefaultMode(code);
+ final int defaultMode = AppOpsManager.opToDefaultMode(code,
+ AppOpsManager.isStrictOp(code));
UidState uidState = getUidStateLocked(uid, false);
if (uidState == null) {
@@ -512,33 +602,35 @@ public class AppOpsService extends IAppOpsService.Stub {
String[] uidPackageNames = getPackagesForUid(uid);
ArrayMap<Callback, ArraySet<String>> callbackSpecs = null;
- ArrayList<Callback> callbacks = mOpModeWatchers.get(code);
- if (callbacks != null) {
- final int callbackCount = callbacks.size();
- for (int i = 0; i < callbackCount; i++) {
- Callback callback = callbacks.get(i);
- ArraySet<String> changedPackages = new ArraySet<>();
- Collections.addAll(changedPackages, uidPackageNames);
- callbackSpecs = new ArrayMap<>();
- callbackSpecs.put(callback, changedPackages);
- }
- }
-
- for (String uidPackageName : uidPackageNames) {
- callbacks = mPackageModeWatchers.get(uidPackageName);
+ synchronized (this) {
+ ArrayList<Callback> callbacks = mOpModeWatchers.get(code);
if (callbacks != null) {
- if (callbackSpecs == null) {
- callbackSpecs = new ArrayMap<>();
- }
final int callbackCount = callbacks.size();
for (int i = 0; i < callbackCount; i++) {
Callback callback = callbacks.get(i);
- ArraySet<String> changedPackages = callbackSpecs.get(callback);
- if (changedPackages == null) {
- changedPackages = new ArraySet<>();
- callbackSpecs.put(callback, changedPackages);
+ ArraySet<String> changedPackages = new ArraySet<>();
+ Collections.addAll(changedPackages, uidPackageNames);
+ callbackSpecs = new ArrayMap<>();
+ callbackSpecs.put(callback, changedPackages);
+ }
+ }
+
+ for (String uidPackageName : uidPackageNames) {
+ callbacks = mPackageModeWatchers.get(uidPackageName);
+ if (callbacks != null) {
+ if (callbackSpecs == null) {
+ callbackSpecs = new ArrayMap<>();
+ }
+ final int callbackCount = callbacks.size();
+ for (int i = 0; i < callbackCount; i++) {
+ Callback callback = callbacks.get(i);
+ ArraySet<String> changedPackages = callbackSpecs.get(callback);
+ if (changedPackages == null) {
+ changedPackages = new ArraySet<>();
+ callbackSpecs.put(callback, changedPackages);
+ }
+ changedPackages.add(uidPackageName);
}
- changedPackages.add(uidPackageName);
}
}
}
@@ -603,7 +695,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
repCbs.addAll(cbs);
}
- if (mode == AppOpsManager.opToDefaultMode(op.op)) {
+ if (mode == getDefaultMode(code, uid, packageName)) {
// If going into the default mode, prune this op
// if there is nothing else interesting in it.
pruneOp(op, uid, packageName);
@@ -730,9 +822,11 @@ public class AppOpsService extends IAppOpsService.Stub {
Ops pkgOps = ent.getValue();
for (int j=pkgOps.size()-1; j>=0; j--) {
Op curOp = pkgOps.valueAt(j);
+ int defaultMode = getDefaultMode(curOp.op, curOp.uid,
+ curOp.packageName);
if (AppOpsManager.opAllowsReset(curOp.op)
- && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
- curOp.mode = AppOpsManager.opToDefaultMode(curOp.op);
+ && curOp.mode != defaultMode) {
+ curOp.mode = defaultMode;
changed = true;
callbacks = addCallbacks(callbacks, packageName, curOp.op,
mOpModeWatchers.get(curOp.op));
@@ -853,7 +947,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
Op op = getOpLocked(code, uid, packageName, false);
if (op == null) {
- return AppOpsManager.opToDefaultMode(code);
+ return getDefaultMode(code, uid, packageName);
}
return op.mode;
}
@@ -944,6 +1038,7 @@ public class AppOpsService extends IAppOpsService.Stub {
private int noteOperationUnchecked(int code, int uid, String packageName,
int proxyUid, String proxyPackageName) {
+ final PermissionDialogReq req;
synchronized (this) {
Ops ops = getOpsLocked(uid, packageName, true);
if (ops == null) {
@@ -953,6 +1048,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
Op op = getOpLocked(ops, code, true);
if (isOpRestricted(uid, code, packageName)) {
+ op.ignoredCount++;
return AppOpsManager.MODE_IGNORED;
}
if (op.duration == -1) {
@@ -973,24 +1069,93 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
- if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
- if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
- + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
+ if (switchOp.mode != AppOpsManager.MODE_ALLOWED
+ && switchOp.mode != AppOpsManager.MODE_ASK) {
+ if (DEBUG)
+ Log.d(TAG, "noteOperation: reject #" + op.mode
+ + " for code " + switchCode + " (" + code
+ + ") uid " + uid + " package " + packageName);
op.rejectTime = System.currentTimeMillis();
+ op.ignoredCount++;
return switchOp.mode;
+ } else if (switchOp.mode == AppOpsManager.MODE_ALLOWED) {
+ if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
+ + " package " + packageName);
+ op.time = System.currentTimeMillis();
+ op.rejectTime = 0;
+ op.proxyUid = proxyUid;
+ op.proxyPackageName = proxyPackageName;
+ op.allowedCount++;
+ broadcastOpIfNeeded(code);
+ return AppOpsManager.MODE_ALLOWED;
+
+ } else {
+ if (Looper.myLooper() == mLooper) {
+ Log.e(TAG,
+ "noteOperation: This method will deadlock if called from the main thread. (Code: "
+ + code
+ + " uid: "
+ + uid
+ + " package: "
+ + packageName + ")");
+ return switchOp.mode;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Package " + op.packageName + " has " + op.noteOpCount
+ + " requests and " + op.startOpCount + " start requests with "
+ + op.ignoredCount + " ignored at " + op.time +
+ " with a duration of "
+ + op.duration + " while being delayed " + op.delayedCount +
+ " times");
+ Log.d(TAG, "Total pkops for " + ops.packageName + " "
+ + ops.uidState.pkgOps.size());
+ }
+
+ // First drop all request events if the device is not interactive, next
+ // check what the global pkg ops count for the package,
+ // then check op scoped count. High frequency request ops will be delayed until
+ // their delay count ceiling is met. This is to mitigate the overloading the
+ // main activity manager service handler and having watchdog kill our service.
+ // Google play services likes to share its uid with numerous packages to avoid
+ // having to grant permissions from the users perspective and thus is the worst
+ // example of overloading this queue -- so, to not encourage bad behavior,
+ // we move them to the back of the line. NOTE: these values are magic, and may need
+ // tuning. Ideally we'd want a ringbuffer or token bucket here to do proper rate
+ // limiting.
+ final boolean isInteractive = mPowerManager.isInteractive();
+ if (isInteractive &&
+ (ops.uidState.pkgOps.size() < AppOpsPolicy.RATE_LIMIT_OPS_TOTAL_PKG_COUNT
+ && op.noteOpCount < AppOpsPolicy.RATE_LIMIT_OP_COUNT
+ || op.delayedCount > AppOpsPolicy.RATE_LIMIT_OP_DELAY_CEILING)) {
+
+ // Reset delayed count, most ops will never need this
+ if (op.delayedCount > 0) {
+ if (DEBUG) Log.d(TAG, "Resetting delayed count for " + op.packageName);
+ op.delayedCount = 0;
+ }
+
+ op.noteOpCount++;
+ req = askOperationLocked(code, uid, packageName, switchOp);
+ } else {
+ if (isInteractive) {
+ op.delayedCount++;
+ }
+ op.ignoredCount++;
+ return AppOpsManager.MODE_IGNORED;
+ }
}
- if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
- + " package " + packageName);
- op.time = System.currentTimeMillis();
- op.rejectTime = 0;
- op.proxyUid = proxyUid;
- op.proxyPackageName = proxyPackageName;
- return AppOpsManager.MODE_ALLOWED;
}
+
+ int result = req.get();
+ broadcastOpIfNeeded(code);
+ return result;
}
@Override
- public int startOperation(IBinder token, int code, int uid, String packageName) {
+ public int startOperation(IBinder token, int code, int uid,
+ String packageName) {
+ final PermissionDialogReq req;
verifyIncomingUid(uid);
verifyIncomingOp(code);
ClientState client = (ClientState)token;
@@ -1003,6 +1168,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
Op op = getOpLocked(ops, code, true);
if (isOpRestricted(uid, code, packageName)) {
+ op.ignoredCount++;
return AppOpsManager.MODE_IGNORED;
}
final int switchCode = AppOpsManager.opToSwitch(code);
@@ -1018,25 +1184,51 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
- if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
- if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
- + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
+ if (switchOp.mode != AppOpsManager.MODE_ALLOWED
+ && switchOp.mode != AppOpsManager.MODE_ASK) {
+ if (DEBUG)
+ Log.d(TAG, "startOperation: reject #" + op.mode
+ + " for code " + switchCode + " (" + code
+ + ") uid " + uid + " package " + packageName);
op.rejectTime = System.currentTimeMillis();
+ op.ignoredCount++;
return switchOp.mode;
+ } else if (switchOp.mode == AppOpsManager.MODE_ALLOWED) {
+ if (DEBUG)
+ Log.d(TAG, "startOperation: allowing code " + code
+ + " uid " + uid + " package " + packageName);
+ if (op.nesting == 0) {
+ op.time = System.currentTimeMillis();
+ op.rejectTime = 0;
+ op.duration = -1;
+ op.allowedCount++;
+ }
+ op.nesting++;
+ if (client.mStartedOps != null) {
+ client.mStartedOps.add(op);
+ }
+ broadcastOpIfNeeded(code);
+ return AppOpsManager.MODE_ALLOWED;
+ } else {
+ if (Looper.myLooper() == mLooper) {
+ Log.e(TAG,
+ "startOperation: This method will deadlock if called from the main thread. (Code: "
+ + code
+ + " uid: "
+ + uid
+ + " package: "
+ + packageName + ")");
+ return switchOp.mode;
+ }
+ op.startOpCount++;
+ IBinder clientToken = client.mAppToken;
+ op.clientTokens.add(clientToken);
+ req = askOperationLocked(code, uid, packageName, switchOp);
}
- if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
- + " package " + packageName);
- if (op.nesting == 0) {
- op.time = System.currentTimeMillis();
- op.rejectTime = 0;
- op.duration = -1;
- }
- op.nesting++;
- if (client.mStartedOps != null) {
- client.mStartedOps.add(op);
- }
- return AppOpsManager.MODE_ALLOWED;
}
+ int result = req.get();
+ broadcastOpIfNeeded(code);
+ return result;
}
@Override
@@ -1057,6 +1249,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
finishOperationLocked(op);
}
+ broadcastOpIfNeeded(code);
}
@Override
@@ -1081,6 +1274,10 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private void verifyIncomingUid(int uid) {
+ if (Binder.getCallingUid() == 0) {
+ // Allow root to delegate uid operations.
+ return;
+ }
if (uid == Binder.getCallingUid()) {
return;
}
@@ -1115,6 +1312,9 @@ public class AppOpsService extends IAppOpsService.Stub {
packageName = "root";
} else if (uid == Process.SHELL_UID) {
packageName = "com.android.shell";
+ } else if (uid == Process.SYSTEM_UID) {
+ if (packageName == null)
+ packageName = "android";
}
return getOpsRawLocked(uid, packageName, edit);
}
@@ -1202,12 +1402,14 @@ public class AppOpsService extends IAppOpsService.Stub {
}
private Op getOpLocked(Ops ops, int code, boolean edit) {
+ int mode;
Op op = ops.get(code);
if (op == null) {
if (!edit) {
return null;
}
- op = new Op(ops.uidState.uid, ops.packageName, code);
+ mode = getDefaultMode(code, ops.uidState.uid, ops.packageName);
+ op = new Op(ops.uidState.uid, ops.packageName, code, mode);
ops.put(code, op);
}
if (edit) {
@@ -1387,10 +1589,32 @@ public class AppOpsService extends IAppOpsService.Stub {
String tagName = parser.getName();
if (tagName.equals("op")) {
- Op op = new Op(uid, pkgName, Integer.parseInt(parser.getAttributeValue(null, "n")));
+ int code = Integer
+ .parseInt(parser.getAttributeValue(null, "n"));
+ // use op name string if it exists
+ String codeNameStr = parser.getAttributeValue(null, "ns");
+ if (codeNameStr != null) {
+ // returns OP_NONE if it could not be mapped
+ code = AppOpsManager.nameToOp(codeNameStr);
+ }
+ // skip op codes that are out of bounds
+ if (code == AppOpsManager.OP_NONE
+ || code >= AppOpsManager._NUM_OP) {
+ continue;
+ }
+ Op op = new Op(uid, pkgName, code, AppOpsManager.MODE_ERRORED);
String mode = parser.getAttributeValue(null, "m");
if (mode != null) {
op.mode = Integer.parseInt(mode);
+ } else {
+ String sDefualtMode = parser.getAttributeValue(null, "dm");
+ int defaultMode;
+ if (sDefualtMode != null) {
+ defaultMode = Integer.parseInt(sDefualtMode);
+ } else {
+ defaultMode = getDefaultMode(code, uid, pkgName);
+ }
+ op.mode = defaultMode;
}
String time = parser.getAttributeValue(null, "t");
if (time != null) {
@@ -1412,7 +1636,14 @@ public class AppOpsService extends IAppOpsService.Stub {
if (proxyPackageName != null) {
op.proxyPackageName = proxyPackageName;
}
-
+ String allowed = parser.getAttributeValue(null, "ac");
+ if (allowed != null) {
+ op.allowedCount = Integer.parseInt(allowed);
+ }
+ String ignored = parser.getAttributeValue(null, "ic");
+ if (ignored != null) {
+ op.ignoredCount = Integer.parseInt(ignored);
+ }
UidState uidState = getUidStateLocked(uid, true);
if (uidState.pkgOps == null) {
uidState.pkgOps = new ArrayMap<>();
@@ -1499,8 +1730,13 @@ public class AppOpsService extends IAppOpsService.Stub {
AppOpsManager.OpEntry op = ops.get(j);
out.startTag(null, "op");
out.attribute(null, "n", Integer.toString(op.getOp()));
- if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
+ out.attribute(null, "ns", AppOpsManager.opToName(op.getOp()));
+ int defaultMode = getDefaultMode(op.getOp(),
+ pkg.getUid(), pkg.getPackageName());
+ if (op.getMode() != defaultMode) {
out.attribute(null, "m", Integer.toString(op.getMode()));
+ } else {
+ out.attribute(null, "dm", Integer.toString(defaultMode));
}
long time = op.getTime();
if (time != 0) {
@@ -1522,6 +1758,14 @@ public class AppOpsService extends IAppOpsService.Stub {
if (proxyPackageName != null) {
out.attribute(null, "pp", proxyPackageName);
}
+ int allowed = op.getAllowedCount();
+ if (allowed != 0) {
+ out.attribute(null, "ac", Integer.toString(allowed));
+ }
+ int ignored = op.getIgnoredCount();
+ if (ignored != 0) {
+ out.attribute(null, "ic", Integer.toString(ignored));
+ }
out.endTag(null, "op");
}
out.endTag(null, "uid");
@@ -1765,14 +2009,181 @@ public class AppOpsService extends IAppOpsService.Stub {
private void checkSystemUid(String function) {
int uid = Binder.getCallingUid();
if (uid != Process.SYSTEM_UID) {
- throw new SecurityException(function + " must by called by the system");
+ throw new SecurityException(function
+ + " must by called by the system");
+ }
+ }
+
+ final class AskRunnable implements Runnable {
+ final int code;
+ final int uid;
+ final String packageName;
+ final Op op;
+ final PermissionDialogReq request;
+
+ public AskRunnable(int code, int uid, String packageName, Op op,
+ PermissionDialogReq request) {
+ super();
+ this.code = code;
+ this.uid = uid;
+ this.packageName = packageName;
+ this.op = op;
+ this.request = request;
+ }
+
+ @Override
+ public void run() {
+ PermissionDialog permDialog = null;
+ synchronized (AppOpsService.this) {
+ Log.e(TAG, "Creating dialog box");
+ op.dialogReqQueue.register(request);
+ if (op.dialogReqQueue.getDialog() == null) {
+ permDialog = new PermissionDialog(mContext,
+ AppOpsService.this, code, uid, packageName);
+ op.dialogReqQueue.setDialog(permDialog);
+ }
+ }
+ if (permDialog != null) {
+ permDialog.show();
+ }
+ }
+ }
+
+ private PermissionDialogReq askOperationLocked(int code, int uid,
+ String packageName, Op op) {
+ PermissionDialogReq request = new PermissionDialogReq();
+ mHandler.post(new AskRunnable(code, uid, packageName, op, request));
+ return request;
+ }
+
+ private int getDefaultMode(int code, int uid, String packageName) {
+ int mode = AppOpsManager.opToDefaultMode(code,
+ isStrict(code, uid, packageName));
+ if (AppOpsManager.isStrictOp(code) && mPolicy != null) {
+ int policyMode = mPolicy.getDefualtMode(code, packageName);
+ if (policyMode != AppOpsManager.MODE_ERRORED) {
+ mode = policyMode;
+ }
+ }
+ return mode;
+ }
+
+ private boolean isStrict(int code, int uid, String packageName) {
+ if (!mStrictEnable)
+ return false;
+
+ return UserHandle.isApp(uid);
+ }
+
+ private void printOperationLocked(Op op, int mode, String operation) {
+ if(op != null) {
+ int switchCode = AppOpsManager.opToSwitch(op.op);
+ if (mode == AppOpsManager.MODE_IGNORED) {
+ if (DEBUG) Log.d(TAG, operation + ": reject #" + mode + " for code "
+ + switchCode + " (" + op.op + ") uid " + op.uid + " package "
+ + op.packageName);
+ } else if (mode == AppOpsManager.MODE_ALLOWED) {
+ if (DEBUG) Log.d(TAG, operation + ": allowing code " + op.op + " uid "
+ + op.uid
+ + " package " + op.packageName);
+ }
+ }
+ }
+
+ private void recordOperationLocked(int code, int uid, String packageName,
+ int mode) {
+ Op op = getOpLocked(code, uid, packageName, false);
+ if(op != null) {
+ if(op.noteOpCount != 0)
+ printOperationLocked(op, mode, "noteOperartion");
+ if(op.startOpCount != 0)
+ printOperationLocked(op, mode, "startOperation");
+ if (mode == AppOpsManager.MODE_IGNORED) {
+ op.rejectTime = System.currentTimeMillis();
+ } else if (mode == AppOpsManager.MODE_ALLOWED) {
+ if(op.noteOpCount != 0) {
+ op.time = System.currentTimeMillis();
+ op.rejectTime = 0;
+ }
+ if(op.startOpCount != 0) {
+ if(op.nesting == 0) {
+ op.time = System.currentTimeMillis();
+ op.rejectTime = 0;
+ op.duration = -1;
+ }
+ op.nesting = op.nesting + op.startOpCount;
+ while(op.clientTokens.size() != 0) {
+ IBinder clientToken = op.clientTokens.get(0);
+ ClientState client = mClients.get(clientToken);
+ if (client != null) {
+ if (client.mStartedOps != null) {
+ client.mStartedOps.add(op);
+ }
+ }
+ op.clientTokens.remove(0);
+ }
+ }
+ }
+ op.clientTokens.clear();
+ op.startOpCount = 0;
+ op.noteOpCount = 0;
+ }
+ }
+
+ public void notifyOperation(int code, int uid, String packageName,
+ int mode, boolean remember) {
+ verifyIncomingUid(uid);
+ verifyIncomingOp(code);
+ ArrayList<Callback> repCbs = null;
+ int switchCode = AppOpsManager.opToSwitch(code);
+ synchronized (this) {
+ recordOperationLocked(code, uid, packageName, mode);
+ Op op = getOpLocked(switchCode, uid, packageName, true);
+ if (op != null) {
+ // Send result to all waiting client
+ if (op.dialogReqQueue.getDialog() != null) {
+ op.dialogReqQueue.notifyAll(mode);
+ op.dialogReqQueue.setDialog(null);
+ }
+ if (remember && op.mode != mode) {
+ op.mode = mode;
+ ArrayList<Callback> cbs = mOpModeWatchers.get(switchCode);
+ if (cbs != null) {
+ if (repCbs == null) {
+ repCbs = new ArrayList<Callback>();
+ }
+ repCbs.addAll(cbs);
+ }
+ cbs = mPackageModeWatchers.get(packageName);
+ if (cbs != null) {
+ if (repCbs == null) {
+ repCbs = new ArrayList<Callback>();
+ }
+ repCbs.addAll(cbs);
+ }
+ if (mode == getDefaultMode(op.op, op.uid, op.packageName)) {
+ // If going into the default mode, prune this op
+ // if there is nothing else interesting in it.
+ pruneOp(op, uid, packageName);
+ }
+ scheduleWriteLocked();
+ }
+ }
+ }
+ if (repCbs != null) {
+ for (int i = 0; i < repCbs.size(); i++) {
+ try {
+ repCbs.get(i).mCallback.opChanged(switchCode, packageName);
+ } catch (RemoteException e) {
+ }
+ }
}
}
private static String[] getPackagesForUid(int uid) {
String[] packageNames = null;
try {
- packageNames= AppGlobals.getPackageManager().getPackagesForUid(uid);
+ packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid);
} catch (RemoteException e) {
/* ignore - local call */
}
@@ -1781,4 +2192,75 @@ public class AppOpsService extends IAppOpsService.Stub {
}
return packageNames;
}
+
+ private void broadcastOpIfNeeded(int op) {
+ switch (op) {
+ case AppOpsManager.OP_SU:
+ mHandler.post(mSuSessionChangedRunner);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void readPolicy() {
+ if (mStrictEnable) {
+ mPolicy = new AppOpsPolicy(new File(DEFAULT_POLICY_FILE), mContext);
+ mPolicy.readPolicy();
+ mPolicy.debugPoilcy();
+ } else {
+ mPolicy = null;
+ }
+ }
+
+ public boolean isControlAllowed(int code, String packageName) {
+ boolean isShow = true;
+ if (mPolicy != null) {
+ isShow = mPolicy.isControlAllowed(code, packageName);
+ }
+ return isShow;
+ }
+
+ @Override
+ public boolean getPrivacyGuardSettingForPackage(int uid, String packageName) {
+ for (int op : PRIVACY_GUARD_OP_STATES) {
+ int switchOp = AppOpsManager.opToSwitch(op);
+ int mode = checkOperation(op, uid, packageName);
+ if (mode != AppOpsManager.MODE_ALLOWED && mode != AppOpsManager.MODE_IGNORED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void setPrivacyGuardSettingForPackage(int uid, String packageName, boolean state) {
+ for (int op : PRIVACY_GUARD_OP_STATES) {
+ int switchOp = AppOpsManager.opToSwitch(op);
+ setMode(switchOp, uid, packageName, state
+ ? AppOpsManager.MODE_ASK : AppOpsManager.MODE_ALLOWED);
+ }
+ }
+
+ @Override
+ public void resetCounters() {
+ mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
+ Binder.getCallingPid(), Binder.getCallingUid(), null);
+ synchronized (this) {
+ for (int i=0; i<mUidStates.size(); i++) {
+ final UidState uidState = mUidStates.valueAt(i);
+ for (Map.Entry<String, Ops> ent : uidState.pkgOps.entrySet()) {
+ String packageName = ent.getKey();
+ Ops pkgOps = ent.getValue();
+ for (int j=0; j<pkgOps.size(); j++) {
+ Op curOp = pkgOps.valueAt(j);
+ curOp.allowedCount = 0;
+ curOp.ignoredCount = 0;
+ }
+ }
+ }
+ // ensure the counter reset persists
+ scheduleWriteLocked();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/AssetAtlasService.java b/services/core/java/com/android/server/AssetAtlasService.java
index 4569dae..ff4456e 100644
--- a/services/core/java/com/android/server/AssetAtlasService.java
+++ b/services/core/java/com/android/server/AssetAtlasService.java
@@ -391,6 +391,11 @@ public class AssetAtlasService extends IAssetAtlas.Stub {
}
}
+ if (results.size() == 0) {
+ if (DEBUG_ATLAS) Log.w(LOG_TAG, "No atlas configuration found!");
+ return null;
+ }
+
// Maximize the number of packed bitmaps, minimize the texture size
Collections.sort(results, new Comparator<WorkerResult>() {
@Override
diff --git a/services/core/java/com/android/server/BasePermissionDialog.java b/services/core/java/com/android/server/BasePermissionDialog.java
new file mode 100644
index 0000000..e3dbcda
--- /dev/null
+++ b/services/core/java/com/android/server/BasePermissionDialog.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2006 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.AlertDialog;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.view.KeyEvent;
+import android.view.WindowManager;
+import android.widget.Button;
+
+import com.android.internal.R;
+
+public class BasePermissionDialog extends AlertDialog {
+ public BasePermissionDialog(Context context) {
+ super(context, com.android.internal.R.style.Theme_Dialog_AppError);
+ getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
+ WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ WindowManager.LayoutParams attrs = getWindow().getAttributes();
+ attrs.setTitle("Permission Dialog");
+ getWindow().setAttributes(attrs);
+ setIconAttribute(R.attr.alertDialogIcon);
+ }
+
+ public void onStart() {
+ super.onStart();
+ setEnabled(false);
+ mHandler.sendMessage(mHandler.obtainMessage(0));
+ }
+
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (mConsuming) {
+ // Slog.i(TAG, "Consuming: " + event);
+ return true;
+ }
+ // Slog.i(TAG, "Dispatching: " + event);
+ return super.dispatchKeyEvent(event);
+ }
+
+ private void setEnabled(boolean enabled) {
+ Button b = (Button) findViewById(R.id.button1);
+ if (b != null) {
+ b.setEnabled(enabled);
+ }
+ b = (Button) findViewById(R.id.button2);
+ if (b != null) {
+ b.setEnabled(enabled);
+ }
+ b = (Button) findViewById(R.id.button3);
+ if (b != null) {
+ b.setEnabled(enabled);
+ }
+ }
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ if (msg.what == 0) {
+ mConsuming = false;
+ setEnabled(true);
+ }
+ }
+ };
+
+ private boolean mConsuming = true;
+}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 2eeaec9..839ba6e 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006 The Android Open Source Project
+ * Copyright (C) 2016 The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,10 +26,16 @@ import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import android.app.ActivityManagerNative;
+import android.app.IBatteryService;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.database.ContentObserver;
import android.os.BatteryManager;
import android.os.BatteryManagerInternal;
import android.os.BatteryProperties;
@@ -51,9 +58,14 @@ import android.util.Slog;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import cyanogenmod.providers.CMSettings;
/**
* <p>BatteryService monitors the charging status, and charge level of the device
@@ -77,6 +89,21 @@ import java.io.PrintWriter;
* a degree Centigrade</p>
* <p>&quot;technology&quot; - String, the type of battery installed, e.g. "Li-ion"</p>
*
+ * <p>If a dock battery is present, then this Intent data will be present too related
+ * to dock battery information:</p>
+ * <p>&quot;dock_scale&quot; - int, the maximum value for the charge level</p>
+ * <p>&quot;dock_level&quot; - int, charge level, from 0 through &quot;scale&quot; inclusive</p>
+ * <p>&quot;dock_status&quot; - String, the current charging status.<br />
+ * <p>&quot;dock_health&quot; - String, the current battery health.<br />
+ * <p>&quot;dock_present&quot; - boolean, true if the battery is present<br />
+ * <p>&quot;dock_icon-small&quot; - int, suggested small icon to use for this state</p>
+ * <p>&quot;dock_plugged&quot; - int, 0 if the device is not plugged in; 1 if plugged
+ * into an AC power adapter; 2 if plugged in via USB.</p>
+ * <p>&quot;dock_voltage&quot; - int, current battery voltage in millivolts</p>
+ * <p>&quot;dock_temperature&quot; - int, current battery temperature in tenths of
+ * a degree Centigrade</p>
+ * <p>&quot;dock_technology&quot; - String, the type of battery installed, e.g. "Li-ion"</p>
+ *
* <p>
* The battery service may be called by the power manager while holding its locks so
* we take care to post all outcalls into the activity manager to a handler.
@@ -92,6 +119,9 @@ public final class BatteryService extends SystemService {
private static final int BATTERY_SCALE = 100; // battery capacity is a percentage
+ // notification light maximum brightness value to use
+ private static final int LIGHT_BRIGHTNESS_MAXIMUM = 255;
+
// Used locally for determining when to make a last ditch effort to log
// discharge stats before the device dies.
private int mCriticalBatteryLevel;
@@ -122,17 +152,35 @@ public final class BatteryService extends SystemService {
private boolean mLastBatteryLevelCritical;
private int mLastMaxChargingCurrent;
+ private boolean mDockBatterySupported;
+ private int mLastDockBatteryStatus;
+ private int mLastDockBatteryHealth;
+ private boolean mLastDockBatteryPresent;
+ private int mLastDockBatteryLevel;
+ private int mLastDockBatteryVoltage;
+ private int mLastDockBatteryTemperature;
+
private int mInvalidCharger;
private int mLastInvalidCharger;
+ private boolean mAdjustableNotificationLedBrightness;
+ private int mNotificationLedBrightnessLevel = LIGHT_BRIGHTNESS_MAXIMUM;
+ private boolean mUseSegmentedBatteryLed = false;
+
+ private boolean mMultipleNotificationLeds;
+ private boolean mMultipleLedsEnabled = false;
+
private int mLowBatteryWarningLevel;
private int mLowBatteryCloseWarningLevel;
private int mShutdownBatteryTemperature;
private int mPlugType;
private int mLastPlugType = -1; // Extra state so we can detect first run
+ private int mDockPlugType;
+ private int mLastDockPlugType = -1; // Extra state so we can detect first run
private boolean mBatteryLevelLow;
+ private boolean mDockBatteryLevelLow;
private long mDischargeStartTime;
private int mDischargeStartLevel;
@@ -140,9 +188,19 @@ public final class BatteryService extends SystemService {
private boolean mUpdatesStopped;
private Led mLed;
+ // Disable LED until SettingsObserver can be started
+ private boolean mLightEnabled = false;
+ private boolean mLedPulseEnabled;
+ private int mBatteryLowARGB;
+ private int mBatteryMediumARGB;
+ private int mBatteryFullARGB;
+ private boolean mMultiColorLed;
private boolean mSentLowBatteryBroadcast = false;
+ private boolean mShowBatteryFullyChargedNotification;
+ private boolean mIsShowingBatteryFullyChargedNotification;
+
public BatteryService(Context context) {
super(context);
@@ -151,6 +209,10 @@ public final class BatteryService extends SystemService {
mLed = new Led(context, getLocalService(LightsManager.class));
mBatteryStats = BatteryStatsService.getService();
+ // By default dock battery are not supported. The first events will refresh
+ // this status from the battery property bag
+ mDockBatterySupported = false;
+
mCriticalBatteryLevel = mContext.getResources().getInteger(
com.android.internal.R.integer.config_criticalBatteryWarningLevel);
mLowBatteryWarningLevel = mContext.getResources().getInteger(
@@ -159,6 +221,8 @@ public final class BatteryService extends SystemService {
com.android.internal.R.integer.config_lowBatteryCloseWarningBump);
mShutdownBatteryTemperature = mContext.getResources().getInteger(
com.android.internal.R.integer.config_shutdownBatteryTemperature);
+ mShowBatteryFullyChargedNotification = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_showBatteryFullyChargedNotification);
// watch for invalid charger messages if the invalid_charger switch exists
if (new File("/sys/devices/virtual/switch/invalid_charger/state").exists()) {
@@ -178,7 +242,7 @@ public final class BatteryService extends SystemService {
// Should never happen.
}
- publishBinderService("battery", new BinderService());
+ publishBinderService(Context.BATTERY_SERVICE, new BinderService());
publishLocalService(BatteryManagerInternal.class, new LocalService());
}
@@ -201,6 +265,9 @@ public final class BatteryService extends SystemService {
false, obs, UserHandle.USER_ALL);
updateBatteryWarningLevelLocked();
}
+ } else if (phase == PHASE_BOOT_COMPLETED) {
+ SettingsObserver observer = new SettingsObserver(new Handler());
+ observer.observe();
}
}
@@ -257,8 +324,10 @@ public final class BatteryService extends SystemService {
private void shutdownIfNoPowerLocked() {
// shut down gracefully if our battery is critically low and we are not powered.
+ // or the battery voltage is decreasing (consumption rate higher than charging rate)
// wait until the system has booted before attempting to display the shutdown dialog.
- if (mBatteryProps.batteryLevel == 0 && !isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)) {
+ if (mBatteryProps.batteryLevel == 0 && (!isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY) ||
+ mBatteryProps.batteryVoltage < mLastBatteryVoltage) ) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -308,21 +377,31 @@ public final class BatteryService extends SystemService {
boolean logOutlier = false;
long dischargeDuration = 0;
+ mDockBatterySupported = mBatteryProps.dockBatterySupported;
+
mBatteryLevelCritical = (mBatteryProps.batteryLevel <= mCriticalBatteryLevel);
+ mPlugType = BATTERY_PLUGGED_NONE;
if (mBatteryProps.chargerAcOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
} else if (mBatteryProps.chargerUsbOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_USB;
} else if (mBatteryProps.chargerWirelessOnline) {
mPlugType = BatteryManager.BATTERY_PLUGGED_WIRELESS;
- } else {
- mPlugType = BATTERY_PLUGGED_NONE;
+ }
+ mDockPlugType = BATTERY_PLUGGED_NONE;
+ if (mBatteryProps.chargerDockAcOnline && mBatteryProps.chargerAcOnline) {
+ mDockPlugType = BatteryManager.BATTERY_DOCK_PLUGGED_AC;
+ } else if (mBatteryProps.chargerDockAcOnline && mBatteryProps.chargerUsbOnline) {
+ mDockPlugType = BatteryManager.BATTERY_DOCK_PLUGGED_USB;
}
if (DEBUG) {
- Slog.d(TAG, "Processing new values: "
- + "chargerAcOnline=" + mBatteryProps.chargerAcOnline
- + ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline
+ String msg = "Processing new values: "
+ + "chargerAcOnline=" + mBatteryProps.chargerAcOnline;
+ if (mDockBatterySupported) {
+ msg += ", chargerDockAcOnline=" + mBatteryProps.chargerDockAcOnline;
+ }
+ msg += ", chargerUsbOnline=" + mBatteryProps.chargerUsbOnline
+ ", chargerWirelessOnline=" + mBatteryProps.chargerWirelessOnline
+ ", maxChargingCurrent" + mBatteryProps.maxChargingCurrent
+ ", batteryStatus=" + mBatteryProps.batteryStatus
@@ -332,8 +411,22 @@ public final class BatteryService extends SystemService {
+ ", batteryTechnology=" + mBatteryProps.batteryTechnology
+ ", batteryVoltage=" + mBatteryProps.batteryVoltage
+ ", batteryTemperature=" + mBatteryProps.batteryTemperature
- + ", mBatteryLevelCritical=" + mBatteryLevelCritical
- + ", mPlugType=" + mPlugType);
+ + ", mBatteryLevelCritical=" + mBatteryLevelCritical;
+ if (mDockBatterySupported) {
+ msg += ", dockBatteryStatus=" + mBatteryProps.dockBatteryStatus
+ + ", dockBatteryHealth=" + mBatteryProps.dockBatteryHealth
+ + ", dockBatteryPresent=" + mBatteryProps.dockBatteryPresent
+ + ", dockBatteryLevel=" + mBatteryProps.dockBatteryLevel
+ + ", dockBatteryTechnology=" + mBatteryProps.dockBatteryTechnology
+ + ", dockBatteryVoltage=" + mBatteryProps.dockBatteryVoltage
+ + ", dockBatteryTemperature=" + mBatteryProps.dockBatteryTemperature;
+ }
+ msg += ", mPlugType=" + mPlugType;
+ if (mDockBatterySupported) {
+ msg += ", mDockPlugType=" + mDockPlugType;
+ }
+
+ Slog.d(TAG, msg);
}
// Let the battery stats keep track of the current level.
@@ -344,19 +437,40 @@ public final class BatteryService extends SystemService {
} catch (RemoteException e) {
// Should never happen.
}
+ if (mDockBatterySupported) {
+ try {
+ mBatteryStats.setDockBatteryState(mBatteryProps.dockBatteryStatus,
+ mBatteryProps.dockBatteryHealth, mDockPlugType,
+ mBatteryProps.dockBatteryLevel, mBatteryProps.dockBatteryTemperature,
+ mBatteryProps.dockBatteryVoltage);
+ } catch (RemoteException e) {
+ // Should never happen.
+ }
+ }
shutdownIfNoPowerLocked();
shutdownIfOverTempLocked();
- if (force || (mBatteryProps.batteryStatus != mLastBatteryStatus ||
+ final boolean batteryChanged = mBatteryProps.batteryStatus != mLastBatteryStatus ||
mBatteryProps.batteryHealth != mLastBatteryHealth ||
mBatteryProps.batteryPresent != mLastBatteryPresent ||
mBatteryProps.batteryLevel != mLastBatteryLevel ||
mPlugType != mLastPlugType ||
mBatteryProps.batteryVoltage != mLastBatteryVoltage ||
mBatteryProps.batteryTemperature != mLastBatteryTemperature ||
- mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent ||
- mInvalidCharger != mLastInvalidCharger)) {
+ mBatteryProps.maxChargingCurrent != mLastMaxChargingCurrent;
+
+ final boolean dockBatteryChanged = mDockBatterySupported &&
+ (mBatteryProps.dockBatteryStatus != mLastDockBatteryStatus ||
+ mBatteryProps.dockBatteryHealth != mLastDockBatteryHealth ||
+ mBatteryProps.dockBatteryPresent != mLastDockBatteryPresent ||
+ mBatteryProps.dockBatteryLevel != mLastDockBatteryLevel ||
+ mDockPlugType != mLastDockPlugType ||
+ mBatteryProps.dockBatteryVoltage != mLastDockBatteryVoltage ||
+ mBatteryProps.dockBatteryTemperature != mLastDockBatteryTemperature);
+
+ if (force || batteryChanged || dockBatteryChanged ||
+ mInvalidCharger != mLastInvalidCharger) {
if (mPlugType != mLastPlugType) {
if (mLastPlugType == BATTERY_PLUGGED_NONE) {
@@ -386,12 +500,30 @@ public final class BatteryService extends SystemService {
mBatteryProps.batteryStatus, mBatteryProps.batteryHealth, mBatteryProps.batteryPresent ? 1 : 0,
mPlugType, mBatteryProps.batteryTechnology);
}
+ if (mDockBatterySupported &&
+ (mBatteryProps.dockBatteryStatus != mLastDockBatteryStatus ||
+ mBatteryProps.dockBatteryHealth != mLastDockBatteryHealth ||
+ mBatteryProps.dockBatteryPresent != mLastDockBatteryPresent ||
+ mDockPlugType != mLastDockPlugType)) {
+ EventLog.writeEvent(EventLogTags.DOCK_BATTERY_STATUS,
+ mBatteryProps.dockBatteryStatus, mBatteryProps.dockBatteryHealth,
+ mBatteryProps.dockBatteryPresent ? 1 : 0,
+ mDockPlugType, mBatteryProps.dockBatteryTechnology);
+ }
if (mBatteryProps.batteryLevel != mLastBatteryLevel) {
// Don't do this just from voltage or temperature changes, that is
// too noisy.
EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
mBatteryProps.batteryLevel, mBatteryProps.batteryVoltage, mBatteryProps.batteryTemperature);
}
+ if (mDockBatterySupported &&
+ (mBatteryProps.dockBatteryLevel != mLastDockBatteryLevel)) {
+ // Don't do this just from voltage or temperature changes, that is
+ // too noisy.
+ EventLog.writeEvent(EventLogTags.DOCK_BATTERY_LEVEL,
+ mBatteryProps.dockBatteryLevel, mBatteryProps.dockBatteryVoltage,
+ mBatteryProps.dockBatteryTemperature);
+ }
if (mBatteryLevelCritical && !mLastBatteryLevelCritical &&
mPlugType == BATTERY_PLUGGED_NONE) {
// We want to make sure we log discharge cycle outliers
@@ -418,13 +550,34 @@ public final class BatteryService extends SystemService {
mBatteryLevelLow = false;
}
}
+ if (mDockBatterySupported) {
+ if (!mDockBatteryLevelLow) {
+ // Should we now switch in to low battery mode?
+ if (mDockPlugType == BATTERY_PLUGGED_NONE
+ && mBatteryProps.dockBatteryLevel <= mLowBatteryWarningLevel) {
+ mDockBatteryLevelLow = true;
+ }
+ } else {
+ // Should we now switch out of low battery mode?
+ if (mDockPlugType != BATTERY_PLUGGED_NONE) {
+ mDockBatteryLevelLow = false;
+ } else if (mBatteryProps.dockBatteryLevel >= mLowBatteryCloseWarningLevel) {
+ mDockBatteryLevelLow = false;
+ } else if (force && mBatteryProps.batteryLevel >= mLowBatteryWarningLevel) {
+ // If being forced, the previous state doesn't matter, we will just
+ // absolutely check to see if we are now above the warning level.
+ mDockBatteryLevelLow = false;
+ }
+ }
+ }
sendIntentLocked();
// Separate broadcast is sent for power connected / not connected
// since the standard intent will not wake any applications and some
// applications may want to have smart behavior based on this.
- if (mPlugType != 0 && mLastPlugType == 0) {
+ if (mPlugType != 0 && mLastPlugType == 0 ||
+ (mLastPlugType == 0 && mDockPlugType != 0 && mLastDockPlugType == 0)) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -434,7 +587,8 @@ public final class BatteryService extends SystemService {
}
});
}
- else if (mPlugType == 0 && mLastPlugType != 0) {
+ else if (mPlugType == 0 && mLastPlugType != 0 ||
+ (mLastPlugType != 0 && mDockPlugType == 0 && mLastDockPlugType != 0)) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -470,6 +624,12 @@ public final class BatteryService extends SystemService {
// Update the battery LED
mLed.updateLightsLocked();
+ if (shouldShowBatteryFullyChargedNotificationLocked()) {
+ showBatteryFullyChargedNotificationLocked();
+ } else if (shouldClearBatteryFullyChargedNotificationLocked()) {
+ clearBatteryFullyChargedNotificationLocked();
+ }
+
// This needs to be done after sendIntent() so that we get the lastest battery stats.
if (logOutlier && dischargeDuration != 0) {
logOutlierLocked(dischargeDuration);
@@ -484,6 +644,14 @@ public final class BatteryService extends SystemService {
mLastBatteryTemperature = mBatteryProps.batteryTemperature;
mLastMaxChargingCurrent = mBatteryProps.maxChargingCurrent;
mLastBatteryLevelCritical = mBatteryLevelCritical;
+ mLastDockBatteryStatus = mBatteryProps.dockBatteryStatus;
+ mLastDockBatteryHealth = mBatteryProps.dockBatteryHealth;
+ mLastDockBatteryPresent = mBatteryProps.dockBatteryPresent;
+ mLastDockBatteryLevel = mBatteryProps.dockBatteryLevel;
+ mLastDockPlugType = mDockPlugType;
+ mLastDockBatteryVoltage = mBatteryProps.dockBatteryVoltage;
+ mLastDockBatteryTemperature = mBatteryProps.dockBatteryTemperature;
+
mLastInvalidCharger = mInvalidCharger;
}
}
@@ -495,6 +663,7 @@ public final class BatteryService extends SystemService {
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
int icon = getIconLocked(mBatteryProps.batteryLevel);
+ int dockIcon = 0;
intent.putExtra(BatteryManager.EXTRA_STATUS, mBatteryProps.batteryStatus);
intent.putExtra(BatteryManager.EXTRA_HEALTH, mBatteryProps.batteryHealth);
@@ -509,19 +678,62 @@ public final class BatteryService extends SystemService {
intent.putExtra(BatteryManager.EXTRA_INVALID_CHARGER, mInvalidCharger);
intent.putExtra(BatteryManager.EXTRA_MAX_CHARGING_CURRENT, mBatteryProps.maxChargingCurrent);
+ if (mDockBatterySupported) {
+ dockIcon = getDockIconLocked(mBatteryProps.dockBatteryLevel);
+
+ intent.putExtra(BatteryManager.EXTRA_DOCK_STATUS, mBatteryProps.dockBatteryStatus);
+ intent.putExtra(BatteryManager.EXTRA_DOCK_HEALTH, mBatteryProps.dockBatteryHealth);
+ intent.putExtra(BatteryManager.EXTRA_DOCK_PRESENT, mBatteryProps.dockBatteryPresent);
+ intent.putExtra(BatteryManager.EXTRA_DOCK_LEVEL, mBatteryProps.dockBatteryLevel);
+ intent.putExtra(BatteryManager.EXTRA_DOCK_SCALE, BATTERY_SCALE);
+ intent.putExtra(BatteryManager.EXTRA_DOCK_ICON_SMALL, dockIcon);
+ intent.putExtra(BatteryManager.EXTRA_DOCK_PLUGGED, mDockPlugType);
+ intent.putExtra(BatteryManager.EXTRA_DOCK_VOLTAGE, mBatteryProps.dockBatteryVoltage);
+ intent.putExtra(BatteryManager.EXTRA_DOCK_TEMPERATURE,
+ mBatteryProps.dockBatteryTemperature);
+ intent.putExtra(BatteryManager.EXTRA_DOCK_TECHNOLOGY,
+ mBatteryProps.dockBatteryTechnology);
+
+ // EEPAD legacy data
+ intent.putExtra("usb_wakeup", mBatteryProps.chargerUsbOnline);
+ intent.putExtra("ac_online", mBatteryProps.chargerAcOnline);
+ intent.putExtra("dock_ac_online", mBatteryProps.chargerDockAcOnline);
+ }
+
+
if (DEBUG) {
- Slog.d(TAG, "Sending ACTION_BATTERY_CHANGED. level:" + mBatteryProps.batteryLevel +
- ", scale:" + BATTERY_SCALE + ", status:" + mBatteryProps.batteryStatus +
+ String msg = "Sending ACTION_BATTERY_CHANGED. level:" + mBatteryProps.batteryLevel +
+ ", scale:" + BATTERY_SCALE +
+ ", status:" + mBatteryProps.batteryStatus +
", health:" + mBatteryProps.batteryHealth +
", present:" + mBatteryProps.batteryPresent +
", voltage: " + mBatteryProps.batteryVoltage +
", temperature: " + mBatteryProps.batteryTemperature +
", technology: " + mBatteryProps.batteryTechnology +
- ", AC powered:" + mBatteryProps.chargerAcOnline +
- ", USB powered:" + mBatteryProps.chargerUsbOnline +
- ", Wireless powered:" + mBatteryProps.chargerWirelessOnline +
- ", icon:" + icon + ", invalid charger:" + mInvalidCharger +
- ", maxChargingCurrent:" + mBatteryProps.maxChargingCurrent);
+ ", maxChargingCurrent:" + mBatteryProps.maxChargingCurrent;
+
+ if (mDockBatterySupported) {
+ msg += ", dock_level:" + mBatteryProps.dockBatteryLevel +
+ ", dock_status:" + mBatteryProps.dockBatteryStatus +
+ ", dock_health:" + mBatteryProps.dockBatteryHealth +
+ ", dock_present:" + mBatteryProps.dockBatteryPresent +
+ ", dock_voltage: " + mBatteryProps.dockBatteryVoltage +
+ ", dock_temperature: " + mBatteryProps.dockBatteryTemperature +
+ ", dock_technology: " + mBatteryProps.dockBatteryTechnology;
+ }
+ msg += ", AC powered:" + mBatteryProps.chargerAcOnline;
+ if (mDockBatterySupported) {
+ msg += ", Dock AC powered:" + mBatteryProps.chargerDockAcOnline;
+ }
+ msg += ", USB powered:" + mBatteryProps.chargerUsbOnline +
+ ", Wireless powered:" + mBatteryProps.chargerWirelessOnline;
+ msg += ", icon:" + icon;
+ if (mDockBatterySupported) {
+ msg += ", dock_icon:" + dockIcon;
+ }
+ msg += ", invalid charger:" + mInvalidCharger;
+
+ Slog.d(TAG, msg);
}
mHandler.post(new Runnable() {
@@ -616,6 +828,22 @@ public final class BatteryService extends SystemService {
}
}
+ private int getDockIconLocked(int level) {
+ if (mBatteryProps.dockBatteryStatus == BatteryManager.BATTERY_STATUS_CHARGING) {
+ return com.android.internal.R.drawable.stat_sys_battery_charge;
+ } else if (mBatteryProps.dockBatteryStatus == BatteryManager.BATTERY_STATUS_DISCHARGING) {
+ return com.android.internal.R.drawable.stat_sys_battery;
+ } else if (mBatteryProps.dockBatteryStatus == BatteryManager.BATTERY_STATUS_NOT_CHARGING
+ || mBatteryProps.dockBatteryStatus == BatteryManager.BATTERY_STATUS_FULL) {
+ if (isPoweredLocked(BatteryManager.BATTERY_PLUGGED_ANY)
+ && mBatteryProps.dockBatteryLevel >= 100) {
+ return com.android.internal.R.drawable.stat_sys_battery_charge;
+ }
+ return com.android.internal.R.drawable.stat_sys_battery;
+ }
+ return com.android.internal.R.drawable.stat_sys_battery_unknown;
+ }
+
private void dumpInternal(PrintWriter pw, String[] args) {
synchronized (mLock) {
if (args == null || args.length == 0 || "-a".equals(args[0])) {
@@ -624,6 +852,9 @@ public final class BatteryService extends SystemService {
pw.println(" (UPDATES STOPPED -- use 'reset' to restart)");
}
pw.println(" AC powered: " + mBatteryProps.chargerAcOnline);
+ if (mDockBatterySupported) {
+ pw.println(" Dock AC powered: " + mBatteryProps.chargerDockAcOnline);
+ }
pw.println(" USB powered: " + mBatteryProps.chargerUsbOnline);
pw.println(" Wireless powered: " + mBatteryProps.chargerWirelessOnline);
pw.println(" Max charging current: " + mBatteryProps.maxChargingCurrent);
@@ -635,7 +866,15 @@ public final class BatteryService extends SystemService {
pw.println(" voltage: " + mBatteryProps.batteryVoltage);
pw.println(" temperature: " + mBatteryProps.batteryTemperature);
pw.println(" technology: " + mBatteryProps.batteryTechnology);
-
+ if (mDockBatterySupported) {
+ pw.println(" dock_status: " + mBatteryProps.dockBatteryStatus);
+ pw.println(" dock_health: " + mBatteryProps.dockBatteryHealth);
+ pw.println(" dock_present: " + mBatteryProps.dockBatteryPresent);
+ pw.println(" dock_level: " + mBatteryProps.dockBatteryLevel);
+ pw.println(" dock_voltage: " + mBatteryProps.dockBatteryVoltage);
+ pw.println(" dock_temperature: " + mBatteryProps.dockBatteryTemperature);
+ pw.println(" dock_technology: " + mBatteryProps.dockBatteryTechnology);
+ }
} else if ("unplug".equals(args[0])) {
if (!mUpdatesStopped) {
mLastBatteryProps.set(mBatteryProps);
@@ -661,6 +900,8 @@ public final class BatteryService extends SystemService {
boolean update = true;
if ("ac".equals(key)) {
mBatteryProps.chargerAcOnline = Integer.parseInt(value) != 0;
+ } else if (mDockBatterySupported && "dockac".equals(key)) {
+ mBatteryProps.chargerDockAcOnline = Integer.parseInt(value) != 0;
} else if ("usb".equals(key)) {
mBatteryProps.chargerUsbOnline = Integer.parseInt(value) != 0;
} else if ("wireless".equals(key)) {
@@ -669,6 +910,10 @@ public final class BatteryService extends SystemService {
mBatteryProps.batteryStatus = Integer.parseInt(value);
} else if ("level".equals(key)) {
mBatteryProps.batteryLevel = Integer.parseInt(value);
+ } else if (mDockBatterySupported && "dockstatus".equals(key)) {
+ mBatteryProps.dockBatteryStatus = Integer.parseInt(value);
+ } else if (mDockBatterySupported && "docklevel".equals(key)) {
+ mBatteryProps.dockBatteryLevel = Integer.parseInt(value);
} else if ("invalid".equals(key)) {
mInvalidCharger = Integer.parseInt(value);
} else {
@@ -701,7 +946,12 @@ public final class BatteryService extends SystemService {
}
} else {
pw.println("Dump current battery state, or:");
- pw.println(" set [ac|usb|wireless|status|level|invalid] <value>");
+ if (mDockBatterySupported) {
+ pw.println(" set [ac|dockac|usb|wireless|status|level|dockstatus" +
+ "|docklevel|invalid] <value>");
+ } else {
+ pw.println(" set [ac|usb|wireless|status|level|invalid] <value>");
+ }
pw.println(" unplug");
pw.println(" reset");
}
@@ -720,53 +970,167 @@ public final class BatteryService extends SystemService {
}
};
+ private synchronized void updateLedPulse() {
+ mLed.updateLightsLocked();
+ }
+
+ private boolean shouldShowBatteryFullyChargedNotificationLocked() {
+ return mShowBatteryFullyChargedNotification && mPlugType != 0
+ && mBatteryProps.batteryLevel == BATTERY_SCALE
+ && !mIsShowingBatteryFullyChargedNotification;
+ }
+
+ private void showBatteryFullyChargedNotificationLocked() {
+ NotificationManager nm = mContext.getSystemService(NotificationManager.class);
+ Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY);
+ PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
+ intent, 0, null, UserHandle.CURRENT);
+
+ CharSequence title = mContext.getText(
+ com.android.internal.R.string.notify_battery_fully_charged_title);
+ CharSequence message = mContext.getText(
+ com.android.internal.R.string.notify_battery_fully_charged_text);
+
+ Notification notification = new Notification.Builder(mContext)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_battery_charge)
+ .setWhen(0)
+ .setOngoing(false)
+ .setAutoCancel(true)
+ .setTicker(title)
+ .setDefaults(0) // please be quiet
+ .setPriority(Notification.PRIORITY_DEFAULT)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentText(message)
+ .setStyle(new Notification.BigTextStyle().bigText(message))
+ .setContentIntent(pi)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .build();
+
+ nm.notifyAsUser(null, com.android.internal.R.string.notify_battery_fully_charged_title,
+ notification, UserHandle.ALL);
+ mIsShowingBatteryFullyChargedNotification = true;
+ }
+
+ private boolean shouldClearBatteryFullyChargedNotificationLocked() {
+ return mIsShowingBatteryFullyChargedNotification &&
+ (mPlugType == 0 || mBatteryProps.batteryLevel < BATTERY_SCALE);
+ }
+
+ private void clearBatteryFullyChargedNotificationLocked() {
+ NotificationManager nm = mContext.getSystemService(NotificationManager.class);
+ nm.cancel(com.android.internal.R.string.notify_battery_fully_charged_title);
+ mIsShowingBatteryFullyChargedNotification = false;
+ }
+
private final class Led {
private final Light mBatteryLight;
- private final int mBatteryLowARGB;
- private final int mBatteryMediumARGB;
- private final int mBatteryFullARGB;
private final int mBatteryLedOn;
private final int mBatteryLedOff;
public Led(Context context, LightsManager lights) {
mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY);
- mBatteryLowARGB = context.getResources().getInteger(
- com.android.internal.R.integer.config_notificationsBatteryLowARGB);
- mBatteryMediumARGB = context.getResources().getInteger(
- com.android.internal.R.integer.config_notificationsBatteryMediumARGB);
- mBatteryFullARGB = context.getResources().getInteger(
- com.android.internal.R.integer.config_notificationsBatteryFullARGB);
+ // Does the Device support changing battery LED colors?
+ mMultiColorLed = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_multiColorBatteryLed);
+
+ // Is the notification LED brightness changeable ?
+ mAdjustableNotificationLedBrightness = context.getResources().getBoolean(
+ org.cyanogenmod.platform.internal.R.bool.config_adjustableNotificationLedBrightness);
+
+ // Does the Device have multiple LEDs ?
+ mMultipleNotificationLeds = context.getResources().getBoolean(
+ org.cyanogenmod.platform.internal.R.bool.config_multipleNotificationLeds);
+
mBatteryLedOn = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLedOn);
mBatteryLedOff = context.getResources().getInteger(
com.android.internal.R.integer.config_notificationsBatteryLedOff);
+
+ // Does the Device have segmented battery LED support? In this case, we send the level
+ // in the alpha channel of the color and let the HAL sort it out.
+ mUseSegmentedBatteryLed = context.getResources().getBoolean(
+ org.cyanogenmod.platform.internal.R.bool.config_useSegmentedBatteryLed);
+ }
+
+ private boolean isHvdcpPresent() {
+ File mChargerTypeFile = new File("/sys/class/power_supply/usb/type");
+ FileReader fileReader;
+ BufferedReader br;
+ String type;
+ boolean ret = false;
+
+ if (!mChargerTypeFile.exists()) {
+ // Device does not support HVDCP
+ return ret;
+ }
+
+ try {
+ fileReader = new FileReader(mChargerTypeFile);
+ br = new BufferedReader(fileReader);
+ type = br.readLine();
+ if (type.regionMatches(true, 0, "USB_HVDCP", 0, 9))
+ ret = true;
+ br.close();
+ fileReader.close();
+ } catch (IOException e) {
+ Slog.e(TAG, "Failure in reading charger type", e);
+ }
+
+ return ret;
}
/**
* Synchronize on BatteryService.
*/
public void updateLightsLocked() {
+ // mBatteryProps could be null on startup (called by SettingsObserver)
+ if (mBatteryProps == null) {
+ Slog.w(TAG, "updateLightsLocked: mBatteryProps is null; skipping");
+ return;
+ }
+
final int level = mBatteryProps.batteryLevel;
final int status = mBatteryProps.batteryStatus;
- if (level < mLowBatteryWarningLevel) {
+ mNotificationLedBrightnessLevel = mUseSegmentedBatteryLed ? level :
+ LIGHT_BRIGHTNESS_MAXIMUM;
+
+ if (!mLightEnabled) {
+ // No lights if explicitly disabled
+ mBatteryLight.turnOff();
+ } else if (level < mLowBatteryWarningLevel) {
+ mBatteryLight.setModes(mNotificationLedBrightnessLevel,
+ mMultipleLedsEnabled);
if (status == BatteryManager.BATTERY_STATUS_CHARGING) {
- // Solid red when battery is charging
+ // Battery is charging and low
mBatteryLight.setColor(mBatteryLowARGB);
- } else {
- // Flash red when battery is low and not charging
+ } else if (mLedPulseEnabled) {
+ // Battery is low and not charging
mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED,
mBatteryLedOn, mBatteryLedOff);
+ } else {
+ // "Pulse low battery light" is disabled, no lights.
+ mBatteryLight.turnOff();
}
} else if (status == BatteryManager.BATTERY_STATUS_CHARGING
|| status == BatteryManager.BATTERY_STATUS_FULL) {
+ mBatteryLight.setModes(mNotificationLedBrightnessLevel,
+ mMultipleLedsEnabled);
if (status == BatteryManager.BATTERY_STATUS_FULL || level >= 90) {
- // Solid green when full or charging and nearly full
+ // Battery is full or charging and nearly full
mBatteryLight.setColor(mBatteryFullARGB);
} else {
- // Solid orange when charging and halfway full
- mBatteryLight.setColor(mBatteryMediumARGB);
+ if (isHvdcpPresent()) {
+ // Blinking orange if HVDCP charger
+ mBatteryLight.setFlashing(mBatteryMediumARGB, Light.LIGHT_FLASH_TIMED,
+ mBatteryLedOn, mBatteryLedOn);
+ } else {
+ // Battery is charging and halfway full
+ mBatteryLight.setColor(mBatteryMediumARGB);
+ }
}
} else {
// No lights if not charging and not low
@@ -787,7 +1151,12 @@ public final class BatteryService extends SystemService {
}
}
- private final class BinderService extends Binder {
+ private final class BinderService extends IBatteryService.Stub {
+ @Override
+ public boolean isDockBatterySupported() throws RemoteException {
+ return getLocalService(BatteryManagerInternal.class).isDockBatterySupported();
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -833,10 +1202,130 @@ public final class BatteryService extends SystemService {
}
@Override
+ public int getDockPlugType() {
+ synchronized (mLock) {
+ return mDockPlugType;
+ }
+ }
+
+ @Override
+ public int getDockBatteryLevel() {
+ synchronized (mLock) {
+ return mBatteryProps.dockBatteryLevel;
+ }
+ }
+
+ @Override
+ public boolean getDockBatteryLevelLow() {
+ synchronized (mLock) {
+ return mDockBatteryLevelLow;
+ }
+ }
+
+ @Override
public int getInvalidCharger() {
synchronized (mLock) {
return mInvalidCharger;
}
}
+
+ @Override
+ public boolean isDockBatterySupported() {
+ synchronized (mLock) {
+ return mDockBatterySupported;
+ }
+ }
+ }
+
+ class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ void observe() {
+ ContentResolver resolver = mContext.getContentResolver();
+
+ // Battery light enabled
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.BATTERY_LIGHT_ENABLED), false, this, UserHandle.USER_ALL);
+
+ // Low battery pulse
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.BATTERY_LIGHT_PULSE), false, this, UserHandle.USER_ALL);
+
+ // Notification LED brightness
+ if (mAdjustableNotificationLedBrightness) {
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.NOTIFICATION_LIGHT_BRIGHTNESS_LEVEL),
+ false, this, UserHandle.USER_ALL);
+ }
+
+ // Multiple LEDs enabled
+ if (mMultipleNotificationLeds) {
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.NOTIFICATION_LIGHT_MULTIPLE_LEDS_ENABLE),
+ false, this, UserHandle.USER_ALL);
+ }
+
+ // Light colors
+ if (mMultiColorLed) {
+ // Register observer if we have a multi color led
+ resolver.registerContentObserver(
+ CMSettings.System.getUriFor(CMSettings.System.BATTERY_LIGHT_LOW_COLOR),
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(
+ CMSettings.System.getUriFor(CMSettings.System.BATTERY_LIGHT_MEDIUM_COLOR),
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(
+ CMSettings.System.getUriFor(CMSettings.System.BATTERY_LIGHT_FULL_COLOR),
+ false, this, UserHandle.USER_ALL);
+ }
+
+ update();
+ }
+
+ @Override public void onChange(boolean selfChange) {
+ update();
+ }
+
+ public void update() {
+ ContentResolver resolver = mContext.getContentResolver();
+ Resources res = mContext.getResources();
+
+ // Battery light enabled
+ mLightEnabled = CMSettings.System.getInt(resolver,
+ CMSettings.System.BATTERY_LIGHT_ENABLED, 1) != 0;
+
+ // Low battery pulse
+ mLedPulseEnabled = CMSettings.System.getInt(resolver,
+ CMSettings.System.BATTERY_LIGHT_PULSE, 1) != 0;
+
+ // Light colors
+ mBatteryLowARGB = CMSettings.System.getInt(resolver,
+ CMSettings.System.BATTERY_LIGHT_LOW_COLOR, res.getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryLowARGB));
+ mBatteryMediumARGB = CMSettings.System.getInt(resolver,
+ CMSettings.System.BATTERY_LIGHT_MEDIUM_COLOR, res.getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryMediumARGB));
+ mBatteryFullARGB = CMSettings.System.getInt(resolver,
+ CMSettings.System.BATTERY_LIGHT_FULL_COLOR, res.getInteger(
+ com.android.internal.R.integer.config_notificationsBatteryFullARGB));
+
+ // Notification LED brightness
+ if (mAdjustableNotificationLedBrightness) {
+ mNotificationLedBrightnessLevel = CMSettings.System.getInt(resolver,
+ CMSettings.System.NOTIFICATION_LIGHT_BRIGHTNESS_LEVEL,
+ LIGHT_BRIGHTNESS_MAXIMUM);
+ }
+
+ // Multiple LEDs enabled
+ if (mMultipleNotificationLeds) {
+ mMultipleLedsEnabled = CMSettings.System.getInt(resolver,
+ CMSettings.System.NOTIFICATION_LIGHT_MULTIPLE_LEDS_ENABLE,
+ mMultipleNotificationLeds ? 1 : 0) != 0;
+ }
+
+ updateLedPulse();
+ }
}
}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 50bd544..dbdffef 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -1,4 +1,7 @@
/*
+ * Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,16 +21,14 @@ package com.android.server;
import android.Manifest;
import android.app.ActivityManager;
+import android.app.AppOpsManager;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothProfile;
import android.bluetooth.IBluetooth;
import android.bluetooth.IBluetoothCallback;
import android.bluetooth.IBluetoothGatt;
-import android.bluetooth.IBluetoothHeadset;
import android.bluetooth.IBluetoothManager;
import android.bluetooth.IBluetoothManagerCallback;
-import android.bluetooth.IBluetoothProfileServiceConnection;
import android.bluetooth.IBluetoothStateChangeCallback;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -63,7 +64,7 @@ import java.util.Map;
class BluetoothManagerService extends IBluetoothManager.Stub {
private static final String TAG = "BluetoothManagerService";
- private static final boolean DBG = false;
+ private static final boolean DBG = true;
private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
@@ -80,8 +81,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private static final int ERROR_RESTART_TIME_MS = 3000;
//Maximum msec to delay MESSAGE_USER_SWITCHED
private static final int USER_SWITCHED_TIME_MS = 200;
- // Delay for the addProxy function in msec
- private static final int ADD_PROXY_DELAY_MS = 100;
private static final int MESSAGE_ENABLE = 1;
private static final int MESSAGE_DISABLE = 2;
@@ -98,8 +97,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private static final int MESSAGE_GET_NAME_AND_ADDRESS=200;
private static final int MESSAGE_SAVE_NAME_AND_ADDRESS=201;
private static final int MESSAGE_USER_SWITCHED = 300;
- private static final int MESSAGE_ADD_PROXY_DELAYED = 400;
- private static final int MESSAGE_BIND_PROFILE_SERVICE = 401;
private static final int MAX_SAVE_RETRIES=3;
private static final int MAX_ERROR_RESTART_RETRIES=6;
@@ -151,11 +148,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
private final BluetoothHandler mHandler;
private int mErrorRecoveryRetryCounter;
private final int mSystemUiUid;
-
- // Save a ProfileServiceConnections object for each of the bound
- // bluetooth profile services
- private final Map <Integer, ProfileServiceConnections> mProfileServices =
- new HashMap <Integer, ProfileServiceConnections>();
+ private boolean mIntentPending = false;
private void registerForAirplaneMode(IntentFilter filter) {
final ContentResolver resolver = mContext.getContentResolver();
@@ -273,7 +266,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
sysUiUid = mContext.getPackageManager().getPackageUid("com.android.systemui",
UserHandle.USER_OWNER);
} catch (PackageManager.NameNotFoundException e) {
- Log.wtf(TAG, "Unable to resolve SystemUI's UID.", e);
+ // Some platforms, such as wearables do not have a system ui.
+ Log.w(TAG, "Unable to resolve SystemUI's UID.", e);
}
mSystemUiUid = sysUiUid;
}
@@ -618,7 +612,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
return true;
}
- public boolean enable() {
+ public boolean enable(String callingPackage) {
if ((Binder.getCallingUid() != Process.SYSTEM_UID) &&
(!checkIfCallerIsForegroundUser())) {
Log.w(TAG,"enable(): not allowed for non-active and non system user");
@@ -632,6 +626,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
" mBinding = " + mBinding);
}
+ AppOpsManager appOps = (AppOpsManager) mContext
+ .getSystemService(Context.APP_OPS_SERVICE);
+ int callingUid = Binder.getCallingUid();
+ if (appOps.noteOp(AppOpsManager.OP_BLUETOOTH_CHANGE, callingUid,
+ callingPackage) != AppOpsManager.MODE_ALLOWED)
+ return false;
+
synchronized(mReceiver) {
mQuietEnableExternal = false;
mEnableExternal = true;
@@ -706,69 +707,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
return mBluetoothGatt;
}
- @Override
- public boolean bindBluetoothProfileService(int bluetoothProfile,
- IBluetoothProfileServiceConnection proxy) {
- if (!mEnable) {
- if (DBG) {
- Log.d(TAG, "Trying to bind to profile: " + bluetoothProfile +
- ", while Bluetooth was disabled");
- }
- return false;
- }
- synchronized (mProfileServices) {
- ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
- if (psc == null) {
- if (DBG) {
- Log.d(TAG, "Creating new ProfileServiceConnections object for"
- + " profile: " + bluetoothProfile);
- }
-
- if (bluetoothProfile != BluetoothProfile.HEADSET) return false;
-
- Intent intent = new Intent(IBluetoothHeadset.class.getName());
- psc = new ProfileServiceConnections(intent);
- if (!psc.bindService()) return false;
-
- mProfileServices.put(new Integer(bluetoothProfile), psc);
- }
- }
-
- // Introducing a delay to give the client app time to prepare
- Message addProxyMsg = mHandler.obtainMessage(MESSAGE_ADD_PROXY_DELAYED);
- addProxyMsg.arg1 = bluetoothProfile;
- addProxyMsg.obj = proxy;
- mHandler.sendMessageDelayed(addProxyMsg, ADD_PROXY_DELAY_MS);
- return true;
- }
-
- @Override
- public void unbindBluetoothProfileService(int bluetoothProfile,
- IBluetoothProfileServiceConnection proxy) {
- synchronized (mProfileServices) {
- ProfileServiceConnections psc = mProfileServices.get(new Integer(bluetoothProfile));
- if (psc == null) {
- return;
- }
- psc.removeProxy(proxy);
- }
- }
-
- private void unbindAllBluetoothProfileServices() {
- synchronized (mProfileServices) {
- for (Integer i : mProfileServices.keySet()) {
- ProfileServiceConnections psc = mProfileServices.get(i);
- try {
- mContext.unbindService(psc);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "Unable to unbind service with intent: " + psc.mIntent, e);
- }
- psc.removeAllProxies();
- }
- mProfileServices.clear();
- }
- }
-
/**
* Send enable message and set adapter name and address. Called when the boot phase becomes
* PHASE_SYSTEM_SERVICES_READY.
@@ -794,148 +732,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_USER_SWITCHED, userHandle, 0));
}
- /**
- * This class manages the clients connected to a given ProfileService
- * and maintains the connection with that service.
- */
- final private class ProfileServiceConnections implements ServiceConnection,
- IBinder.DeathRecipient {
- final RemoteCallbackList<IBluetoothProfileServiceConnection> mProxies =
- new RemoteCallbackList <IBluetoothProfileServiceConnection>();
- IBinder mService;
- ComponentName mClassName;
- Intent mIntent;
- boolean mInvokingProxyCallbacks = false;
-
- ProfileServiceConnections(Intent intent) {
- mService = null;
- mClassName = null;
- mIntent = intent;
- }
-
- private boolean bindService() {
- if (mIntent != null && mService == null &&
- doBind(mIntent, this, 0, UserHandle.CURRENT_OR_SELF)) {
- Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
- msg.obj = this;
- mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
- return true;
- }
- Log.w(TAG, "Unable to bind with intent: " + mIntent);
- return false;
- }
-
- private void addProxy(IBluetoothProfileServiceConnection proxy) {
- mProxies.register(proxy);
- if (mService != null) {
- try{
- proxy.onServiceConnected(mClassName, mService);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to connect to proxy", e);
- }
- } else {
- if (!mHandler.hasMessages(MESSAGE_BIND_PROFILE_SERVICE, this)) {
- Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
- msg.obj = this;
- mHandler.sendMessage(msg);
- }
- }
- }
-
- private void removeProxy(IBluetoothProfileServiceConnection proxy) {
- if (proxy != null) {
- if (mProxies.unregister(proxy)) {
- try {
- proxy.onServiceDisconnected(mClassName);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to disconnect proxy", e);
- }
- }
- } else {
- Log.w(TAG, "Trying to remove a null proxy");
- }
- }
-
- private void removeAllProxies() {
- onServiceDisconnected(mClassName);
- mProxies.kill();
- }
-
- @Override
- public void onServiceConnected(ComponentName className, IBinder service) {
- // remove timeout message
- mHandler.removeMessages(MESSAGE_BIND_PROFILE_SERVICE, this);
- mService = service;
- mClassName = className;
- try {
- mService.linkToDeath(this, 0);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to linkToDeath", e);
- }
-
- if (mInvokingProxyCallbacks) {
- Log.e(TAG, "Proxy callbacks already in progress.");
- return;
- }
- mInvokingProxyCallbacks = true;
-
- final int n = mProxies.beginBroadcast();
- try {
- for (int i = 0; i < n; i++) {
- try {
- mProxies.getBroadcastItem(i).onServiceConnected(className, service);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to connect to proxy", e);
- }
- }
- } finally {
- mProxies.finishBroadcast();
- mInvokingProxyCallbacks = false;
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName className) {
- if (mService == null) return;
- mService.unlinkToDeath(this, 0);
- mService = null;
- mClassName = null;
-
- if (mInvokingProxyCallbacks) {
- Log.e(TAG, "Proxy callbacks already in progress.");
- return;
- }
- mInvokingProxyCallbacks = true;
-
- final int n = mProxies.beginBroadcast();
- try {
- for (int i = 0; i < n; i++) {
- try {
- mProxies.getBroadcastItem(i).onServiceDisconnected(className);
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to disconnect from proxy", e);
- }
- }
- } finally {
- mProxies.finishBroadcast();
- mInvokingProxyCallbacks = false;
- }
- }
-
- @Override
- public void binderDied() {
- if (DBG) {
- Log.w(TAG, "Profile service for profile: " + mClassName
- + " died.");
- }
- onServiceDisconnected(mClassName);
- // Trigger rebind
- Message msg = mHandler.obtainMessage(MESSAGE_BIND_PROFILE_SERVICE);
- msg.obj = this;
- mHandler.sendMessageDelayed(msg, TIMEOUT_BIND_MS);
- }
- }
-
private void sendBluetoothStateCallback(boolean isUp) {
try {
int n = mStateChangeCallbacks.beginBroadcast();
@@ -967,6 +763,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
} catch (RemoteException e) {
Log.e(TAG, "Unable to call onBluetoothServiceUp() on callback #" + i, e);
}
+ Log.d(TAG, "Broadcasted onBluetoothServiceUp() to " + mCallbacks.getBroadcastItem(i));
}
} finally {
mCallbacks.finishBroadcast();
@@ -988,6 +785,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
} catch (RemoteException e) {
Log.e(TAG, "Unable to call onBluetoothServiceDown() on callback #" + i, e);
}
+ Log.d(TAG, "Broadcasted onBluetoothServiceDown() to " + mCallbacks.getBroadcastItem(i));
}
} finally {
mCallbacks.finishBroadcast();
@@ -1257,28 +1055,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
}
break;
}
- case MESSAGE_ADD_PROXY_DELAYED:
- {
- ProfileServiceConnections psc = mProfileServices.get(
- new Integer(msg.arg1));
- if (psc == null) {
- break;
- }
- IBluetoothProfileServiceConnection proxy =
- (IBluetoothProfileServiceConnection) msg.obj;
- psc.addProxy(proxy);
- break;
- }
- case MESSAGE_BIND_PROFILE_SERVICE:
- {
- ProfileServiceConnections psc = (ProfileServiceConnections) msg.obj;
- removeMessages(MESSAGE_BIND_PROFILE_SERVICE, msg.obj);
- if (psc == null) {
- break;
- }
- psc.bindService();
- break;
- }
case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
{
if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
@@ -1371,8 +1147,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
recoverBluetoothServiceFromError();
}
if ((prevState == BluetoothAdapter.STATE_TURNING_ON) &&
- (newState == BluetoothAdapter.STATE_BLE_ON) &&
- (mBluetooth != null) && mEnable) {
+ (newState == BluetoothAdapter.STATE_OFF) &&
+ (mBluetooth != null) && mEnable) {
+ persistBluetoothSetting(BLUETOOTH_OFF);
+ }
+ if ((prevState == BluetoothAdapter.STATE_TURNING_ON) &&
+ (newState == BluetoothAdapter.STATE_BLE_ON) &&
+ (mBluetooth != null) && mEnable) {
recoverBluetoothServiceFromError();
}
if (newState == BluetoothAdapter.STATE_ON ||
@@ -1482,20 +1263,19 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
mState = BluetoothAdapter.STATE_TURNING_ON;
}
- waitForOnOff(true, false);
+ waitForMonitoredOnOff(true, false);
if (mState == BluetoothAdapter.STATE_TURNING_ON) {
bluetoothStateChangeHandler(mState, BluetoothAdapter.STATE_ON);
}
- unbindAllBluetoothProfileServices();
// disable
handleDisable();
// Pbap service need receive STATE_TURNING_OFF intent to close
bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON,
BluetoothAdapter.STATE_TURNING_OFF);
- waitForOnOff(false, true);
+ waitForMonitoredOnOff(false, true);
bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
BluetoothAdapter.STATE_OFF);
@@ -1665,7 +1445,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
unbindAndFinish();
sendBleStateChanged(prevState, newState);
// Don't broadcast as it has already been broadcast before
- isStandardBroadcast = false;
+ if(!mIntentPending) {
+ isStandardBroadcast = false;
+ } else {
+ mIntentPending = false;
+ }
}
} else if (!intermediate_off) {
@@ -1694,6 +1478,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
// Broadcast as STATE_OFF
newState = BluetoothAdapter.STATE_OFF;
sendBrEdrDownCallback();
+ if(!isBleAppPresent()){
+ isStandardBroadcast = false;
+ mIntentPending = true;
+ } else {
+ mIntentPending = false;
+ isStandardBroadcast = true;
+ }
}
} else if (newState == BluetoothAdapter.STATE_ON) {
boolean isUp = (newState==BluetoothAdapter.STATE_ON);
@@ -1710,15 +1501,25 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
sendBleStateChanged(prevState, newState);
}
+ if( newState == BluetoothAdapter.STATE_TURNING_ON
+ && prevState == BluetoothAdapter.STATE_BLE_ON) {
+ mEnable = true;
+ }
+
if (isStandardBroadcast) {
if (prevState == BluetoothAdapter.STATE_BLE_ON) {
// Show prevState of BLE_ON as OFF to standard users
prevState = BluetoothAdapter.STATE_OFF;
}
+ else if (prevState == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
+ // show prevState to TURNING_OFF
+ prevState = BluetoothAdapter.STATE_TURNING_OFF;
+ }
Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
}
}
@@ -1758,6 +1559,48 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
return false;
}
+ /**
+ * if on is true, wait for state become ON
+ * if off is true, wait for state become OFF
+ * if both on and off are false, wait for state not ON
+ */
+ private boolean waitForMonitoredOnOff(boolean on, boolean off) {
+ int i = 0;
+ while (i < 10) {
+ synchronized(mConnection) {
+ try {
+ if (mBluetooth == null) break;
+ if (on) {
+ if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true;
+ if (mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
+ bluetoothStateChangeHandler(BluetoothAdapter.STATE_BLE_TURNING_ON,
+ BluetoothAdapter.STATE_BLE_ON);
+ }
+ } else if (off) {
+ if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true;
+ if (mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
+ bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF,
+ BluetoothAdapter.STATE_BLE_ON);
+ }
+ } else {
+ if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getState()", e);
+ break;
+ }
+ }
+ if (on || off) {
+ SystemClock.sleep(800);
+ } else {
+ SystemClock.sleep(50);
+ }
+ i++;
+ }
+ Log.e(TAG,"waitForOnOff time out");
+ return false;
+ }
+
private void sendDisableMsg() {
mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_DISABLE));
}
@@ -1810,7 +1653,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub {
if (mBluetooth != null) {
mBluetooth = null;
//Unbind
- mContext.unbindService(mConnection);
+ try {
+ mContext.unbindService(mConnection);
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to unbind",e);
+ }
}
mBluetoothGatt = null;
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 327fb8a..1489fd8 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -71,6 +71,7 @@ import android.net.ProxyInfo;
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.Uri;
+import android.net.wifi.WifiDevice;
import android.os.Binder;
import android.os.Bundle;
import android.os.FileUtils;
@@ -132,6 +133,7 @@ import com.android.server.net.LockdownVpnTracker;
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
+import cyanogenmod.providers.CMSettings;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -157,6 +159,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.Random;
/**
* @hide
@@ -169,6 +172,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final boolean VDBG = false;
private static final boolean LOGD_RULES = false;
+ private static final boolean LOGD_BLOCKED_NETWORKINFO = true;
// TODO: create better separation between radio types and network types
@@ -220,6 +224,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
private INetworkPolicyManager mPolicyManager;
private String mCurrentTcpBufferSizes;
+ private int mCurrentTcpDelayedAckSegments;
+ private int mCurrentTcpUserCfg;
private static final int ENABLED = 1;
private static final int DISABLED = 0;
@@ -641,12 +647,22 @@ public class ConnectivityService extends IConnectivityManager.Stub
mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper());
// setup our unique device name
+ // either to (in order): current net.hostname
+ // DEVICE_HOSTNAME
+ // android-ANDROID_ID
+ // android-r-RANDOM_NUMBER
if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) {
+ String hostname = CMSettings.Secure.getString(context.getContentResolver(),
+ CMSettings.Secure.DEVICE_HOSTNAME);
String id = Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.ANDROID_ID);
- if (id != null && id.length() > 0) {
+ if (!TextUtils.isEmpty(hostname)) {
+ SystemProperties.set("net.hostname", hostname);
+ } else if (!TextUtils.isEmpty(id)) {
String name = new String("android-").concat(id);
SystemProperties.set("net.hostname", name);
+ } else {
+ SystemProperties.set("net.hostname", "android-r-" + new Random().nextInt());
}
}
@@ -955,6 +971,21 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ private void maybeLogBlockedNetworkInfo(NetworkInfo ni, int uid) {
+ if (ni == null || !LOGD_BLOCKED_NETWORKINFO) return;
+ boolean removed = false;
+ boolean added = false;
+ synchronized (mBlockedAppUids) {
+ if (ni.getDetailedState() == DetailedState.BLOCKED && mBlockedAppUids.add(uid)) {
+ added = true;
+ } else if (ni.isConnected() && mBlockedAppUids.remove(uid)) {
+ removed = true;
+ }
+ }
+ if (added) log("Returning blocked NetworkInfo to uid=" + uid);
+ else if (removed) log("Returning unblocked NetworkInfo to uid=" + uid);
+ }
+
/**
* Return a filtered {@link NetworkInfo}, potentially marked
* {@link DetailedState#BLOCKED} based on
@@ -965,10 +996,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
// network is blocked; clone and override state
info = new NetworkInfo(info);
info.setDetailedState(DetailedState.BLOCKED, null, null);
- if (VDBG) {
- log("returning Blocked NetworkInfo for ifname=" +
- lp.getInterfaceName() + ", uid=" + uid);
- }
}
if (info != null && mLockdownTracker != null) {
info = mLockdownTracker.augmentNetworkInfo(info);
@@ -989,7 +1016,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
enforceAccessPermission();
final int uid = Binder.getCallingUid();
NetworkState state = getUnfilteredActiveNetworkState(uid);
- return getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid);
+ NetworkInfo ni = getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid);
+ maybeLogBlockedNetworkInfo(ni, uid);
+ return ni;
}
@Override
@@ -1728,6 +1757,34 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ private void updateTcpDelayedAck(NetworkAgentInfo nai) {
+ if (isDefaultNetwork(nai) == false) {
+ return;
+ }
+
+ int segments = nai.linkProperties.getTcpDelayedAckSegments();
+ int usercfg = nai.linkProperties.getTcpUserCfg();
+
+ if (segments != mCurrentTcpDelayedAckSegments) {
+ try {
+ FileUtils.stringToFile("/sys/kernel/ipv4/tcp_delack_seg",
+ String.valueOf(segments));
+ mCurrentTcpDelayedAckSegments = segments;
+ } catch (IOException e) {
+ // optional
+ }
+ }
+
+ if (usercfg != mCurrentTcpUserCfg) {
+ try {
+ FileUtils.stringToFile("/sys/kernel/ipv4/tcp_use_usercfg",
+ String.valueOf(usercfg));
+ mCurrentTcpUserCfg = usercfg;
+ } catch (IOException e) {
+ // optional
+ }
+ }
+ }
private void flushVmDnsCache() {
/*
* Tell the VMs to toss their DNS caches
@@ -2579,6 +2636,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
}
+ public List<WifiDevice> getTetherConnectedSta() {
+ if (isTetheringSupported()) {
+ return mTethering.getTetherConnectedSta();
+ } else {
+ return null;
+ }
+ }
+
// javadoc from interface
public int tether(String iface) {
ConnectivityManager.enforceTetherChangePermission(mContext);
@@ -3882,6 +3947,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
private final HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos =
new HashMap<Messenger, NetworkAgentInfo>();
+ @GuardedBy("mBlockedAppUids")
+ private final HashSet<Integer> mBlockedAppUids = new HashSet();
+
// Note: if mDefaultRequest is changed, NetworkMonitor needs to be updated.
private final NetworkRequest mDefaultRequest;
@@ -3946,6 +4014,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// updateMtu(lp, null);
// }
updateTcpBufferSizes(networkAgent);
+ updateTcpDelayedAck(networkAgent);
// TODO: deprecate and remove mDefaultDns when we can do so safely. See http://b/18327075
// In L, we used it only when the network had Internet access but provided no DNS servers.
@@ -4270,6 +4339,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
notifyLockdownVpn(newNetwork);
handleApplyDefaultProxy(newNetwork.linkProperties.getHttpProxy());
updateTcpBufferSizes(newNetwork);
+ updateTcpDelayedAck(newNetwork);
setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
}
@@ -4503,7 +4573,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
if (reapUnvalidatedNetworks == ReapUnvalidatedNetworks.REAP) {
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
- if (unneeded(nai)) {
+ if (unneeded(nai) && nai != newNetwork) {
if (DBG) log("Reaping " + nai.name());
teardownUnneededNetwork(nai);
}
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index b88658b..71650c3 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -31,6 +31,8 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorManager;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.hardware.display.DisplayManager;
@@ -111,7 +113,7 @@ public class DeviceIdleController extends SystemService
private INetworkPolicyManager mNetworkPolicyManager;
private DisplayManager mDisplayManager;
private SensorManager mSensorManager;
- private Sensor mSigMotionSensor;
+ private Sensor mMotionSensor;
private LocationManager mLocationManager;
private LocationRequest mLocationRequest;
private PendingIntent mSensingAlarmIntent;
@@ -123,12 +125,12 @@ public class DeviceIdleController extends SystemService
private boolean mForceIdle;
private boolean mScreenOn;
private boolean mCharging;
- private boolean mSigMotionActive;
private boolean mSensing;
private boolean mNotMoving;
private boolean mLocating;
private boolean mLocated;
- private boolean mHaveGps;
+ private boolean mHasGps;
+ private boolean mHasNetworkLocation;
private Location mLastGenericLocation;
private Location mLastGpsLocation;
@@ -275,13 +277,57 @@ public class DeviceIdleController extends SystemService
}
};
- private final TriggerEventListener mSigMotionListener = new TriggerEventListener() {
- @Override public void onTrigger(TriggerEvent event) {
+ private final class MotionListener extends TriggerEventListener
+ implements SensorEventListener {
+
+ boolean active = false;
+
+ @Override
+ public void onTrigger(TriggerEvent event) {
synchronized (DeviceIdleController.this) {
- significantMotionLocked();
+ active = false;
+ motionLocked();
+ }
+ }
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ synchronized (DeviceIdleController.this) {
+ mSensorManager.unregisterListener(this, mMotionSensor);
+ active = false;
+ motionLocked();
}
}
- };
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+ public boolean registerLocked() {
+ boolean success = false;
+ if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+ success = mSensorManager.requestTriggerSensor(mMotionListener, mMotionSensor);
+ } else {
+ success = mSensorManager.registerListener(
+ mMotionListener, mMotionSensor, SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ if (success) {
+ active = true;
+ } else {
+ Slog.e(TAG, "Unable to register for " + mMotionSensor);
+ }
+ return success;
+ }
+
+ public void unregisterLocked() {
+ if (mMotionSensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+ mSensorManager.cancelTriggerSensor(mMotionListener, mMotionSensor);
+ } else {
+ mSensorManager.unregisterListener(mMotionListener);
+ }
+ active = false;
+ }
+ }
+ private final MotionListener mMotionListener = new MotionListener();
private final LocationListener mGenericLocationListener = new LocationListener() {
@Override
@@ -356,7 +402,7 @@ public class DeviceIdleController extends SystemService
* This is the time, after becoming inactive, at which we start looking at the
* motion sensor to determine if the device is being left alone. We don't do this
* immediately after going inactive just because we don't want to be continually running
- * the significant motion sensor whenever the screen is off.
+ * the motion sensor whenever the screen is off.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
* @see #KEY_INACTIVE_TIMEOUT
*/
@@ -399,7 +445,7 @@ public class DeviceIdleController extends SystemService
/**
* This is the time, after the inactive timeout elapses, that we will wait looking
- * for significant motion until we truly consider the device to be idle.
+ * for motion until we truly consider the device to be idle.
* @see Settings.Global#DEVICE_IDLE_CONSTANTS
* @see #KEY_IDLE_AFTER_INACTIVE_TIMEOUT
*/
@@ -750,6 +796,12 @@ public class DeviceIdleController extends SystemService
return isPowerSaveWhitelistExceptIdleAppInternal(name);
}
+ @Override public int getIdleStateDetailed() {
+ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER,
+ null);
+ return mState;
+ }
+
@Override public boolean isPowerSaveWhitelistApp(String name) {
return isPowerSaveWhitelistAppInternal(name);
}
@@ -890,17 +942,38 @@ public class DeviceIdleController extends SystemService
mDisplayManager = (DisplayManager) getContext().getSystemService(
Context.DISPLAY_SERVICE);
mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
- mSigMotionSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
- mLocationManager = (LocationManager) getContext().getSystemService(
- Context.LOCATION_SERVICE);
- mLocationRequest = new LocationRequest()
- .setQuality(LocationRequest.ACCURACY_FINE)
- .setInterval(0)
- .setFastestInterval(0)
- .setNumUpdates(1);
+ int sigMotionSensorId = getContext().getResources().getInteger(
+ com.android.internal.R.integer.config_autoPowerModeAnyMotionSensor);
+ if (sigMotionSensorId > 0) {
+ mMotionSensor = mSensorManager.getDefaultSensor(sigMotionSensorId, true);
+ }
+ if (mMotionSensor == null && getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_autoPowerModePreferWristTilt)) {
+ mMotionSensor = mSensorManager.getDefaultSensor(
+ Sensor.TYPE_WRIST_TILT_GESTURE, true);
+ }
+ if (mMotionSensor == null) {
+ // As a last ditch, fall back to SMD.
+ mMotionSensor = mSensorManager.getDefaultSensor(
+ Sensor.TYPE_SIGNIFICANT_MOTION, true);
+ }
+
+ if (getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
+ mLocationManager = (LocationManager) getContext().getSystemService(
+ Context.LOCATION_SERVICE);
+ mLocationRequest = new LocationRequest()
+ .setQuality(LocationRequest.ACCURACY_FINE)
+ .setInterval(0)
+ .setFastestInterval(0)
+ .setNumUpdates(1);
+ }
+
+ float angleThreshold = getContext().getResources().getInteger(
+ com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f;
mAnyMotionDetector = new AnyMotionDetector(
(PowerManager) getContext().getSystemService(Context.POWER_SERVICE),
- mHandler, mSensorManager, this);
+ mHandler, mSensorManager, this, angleThreshold);
Intent intent = new Intent(ACTION_STEP_IDLE_STATE)
.setPackage("android")
@@ -1202,7 +1275,7 @@ public class DeviceIdleController extends SystemService
void scheduleReportActiveLocked(String activeReason, int activeUid) {
Message msg = mHandler.obtainMessage(MSG_REPORT_ACTIVE, activeUid,
- mState == STATE_IDLE ? 1 : 0, activeReason);
+ ((mState == STATE_IDLE) || (mState == STATE_IDLE_MAINTENANCE)) ? 1 : 0, activeReason);
mHandler.sendMessage(msg);
}
@@ -1236,7 +1309,7 @@ public class DeviceIdleController extends SystemService
cancelAlarmLocked();
cancelSensingAlarmLocked();
cancelLocatingLocked();
- stopMonitoringSignificantMotion();
+ stopMonitoringMotionLocked();
mAnyMotionDetector.stop();
}
@@ -1265,8 +1338,8 @@ public class DeviceIdleController extends SystemService
switch (mState) {
case STATE_INACTIVE:
// We have now been inactive long enough, it is time to start looking
- // for significant motion and sleep some more while doing so.
- startMonitoringSignificantMotion();
+ // for motion and sleep some more while doing so.
+ startMonitoringMotionLocked();
scheduleAlarmLocked(mConstants.IDLE_AFTER_INACTIVE_TIMEOUT, false);
// Reset the upcoming idle delays.
mNextIdlePendingDelay = mConstants.IDLE_PENDING_TIMEOUT;
@@ -1292,17 +1365,30 @@ public class DeviceIdleController extends SystemService
if (DEBUG) Slog.d(TAG, "Moved from STATE_SENSING to STATE_LOCATING.");
EventLogTags.writeDeviceIdle(mState, "step");
scheduleSensingAlarmLocked(mConstants.LOCATING_TIMEOUT);
- mLocating = true;
- mLocationManager.requestLocationUpdates(mLocationRequest, mGenericLocationListener,
- mHandler.getLooper());
- if (mLocationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
- mHaveGps = true;
+ if (mLocationManager != null
+ && mLocationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
+ mLocationManager.requestLocationUpdates(mLocationRequest,
+ mGenericLocationListener, mHandler.getLooper());
+ mLocating = true;
+ } else {
+ mHasNetworkLocation = false;
+ }
+ if (mLocationManager != null
+ && mLocationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
+ mHasGps = true;
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 5,
mGpsLocationListener, mHandler.getLooper());
+ mLocating = true;
} else {
- mHaveGps = false;
+ mHasGps = false;
}
- break;
+ // If we have a location provider, we're all set, the listeners will move state
+ // forward.
+ if (mLocating) {
+ break;
+ }
+
+ // Otherwise, we have to move from locating into idle maintenance.
case STATE_LOCATING:
cancelSensingAlarmLocked();
cancelLocatingLocked();
@@ -1332,17 +1418,16 @@ public class DeviceIdleController extends SystemService
}
}
- void significantMotionLocked() {
- if (DEBUG) Slog.d(TAG, "significantMotionLocked()");
- // When the sensor goes off, its trigger is automatically removed.
- mSigMotionActive = false;
+ void motionLocked() {
+ if (DEBUG) Slog.d(TAG, "motionLocked()");
+ // The motion sensor will have been disabled at this point
handleMotionDetectedLocked(mConstants.MOTION_INACTIVE_TIMEOUT, "motion");
}
void handleMotionDetectedLocked(long timeout, String type) {
// The device is not yet active, so we want to go back to the pending idle
- // state to wait again for no motion. Note that we only monitor for significant
- // motion after moving out of the inactive state, so no need to worry about that.
+ // state to wait again for no motion. Note that we only monitor for motion
+ // after moving out of the inactive state, so no need to worry about that.
if (mState != STATE_ACTIVE) {
scheduleReportActiveLocked(type, Process.myUid());
mState = STATE_ACTIVE;
@@ -1360,7 +1445,7 @@ public class DeviceIdleController extends SystemService
}
if (DEBUG) Slog.d(TAG, "Generic location: " + location);
mLastGenericLocation = new Location(location);
- if (location.getAccuracy() > mConstants.LOCATION_ACCURACY && mHaveGps) {
+ if (location.getAccuracy() > mConstants.LOCATION_ACCURACY && mHasGps) {
return;
}
mLocated = true;
@@ -1385,19 +1470,17 @@ public class DeviceIdleController extends SystemService
}
}
- void startMonitoringSignificantMotion() {
- if (DEBUG) Slog.d(TAG, "startMonitoringSignificantMotion()");
- if (mSigMotionSensor != null && !mSigMotionActive) {
- mSensorManager.requestTriggerSensor(mSigMotionListener, mSigMotionSensor);
- mSigMotionActive = true;
+ void startMonitoringMotionLocked() {
+ if (DEBUG) Slog.d(TAG, "startMonitoringMotionLocked()");
+ if (mMotionSensor != null && !mMotionListener.active) {
+ mMotionListener.registerLocked();
}
}
- void stopMonitoringSignificantMotion() {
- if (DEBUG) Slog.d(TAG, "stopMonitoringSignificantMotion()");
- if (mSigMotionActive) {
- mSensorManager.cancelTriggerSensor(mSigMotionListener, mSigMotionSensor);
- mSigMotionActive = false;
+ void stopMonitoringMotionLocked() {
+ if (DEBUG) Slog.d(TAG, "stopMonitoringMotionLocked()");
+ if (mMotionSensor != null && mMotionListener.active) {
+ mMotionListener.unregisterLocked();
}
}
@@ -1426,10 +1509,10 @@ public class DeviceIdleController extends SystemService
void scheduleAlarmLocked(long delay, boolean idleUntil) {
if (DEBUG) Slog.d(TAG, "scheduleAlarmLocked(" + delay + ", " + idleUntil + ")");
- if (mSigMotionSensor == null) {
- // If there is no significant motion sensor on this device, then we won't schedule
+ if (mMotionSensor == null) {
+ // If there is no motion sensor on this device, then we won't schedule
// alarms, because we can't determine if the device is not moving. This effectively
- // turns off normal exeuction of device idling, although it is still possible to
+ // turns off normal execution of device idling, although it is still possible to
// manually poke it by pretending like the alarm is going off.
return;
}
@@ -1911,15 +1994,16 @@ public class DeviceIdleController extends SystemService
pw.print(" mEnabled="); pw.println(mEnabled);
pw.print(" mForceIdle="); pw.println(mForceIdle);
- pw.print(" mSigMotionSensor="); pw.println(mSigMotionSensor);
+ pw.print(" mMotionSensor="); pw.println(mMotionSensor);
pw.print(" mCurDisplay="); pw.println(mCurDisplay);
pw.print(" mScreenOn="); pw.println(mScreenOn);
pw.print(" mCharging="); pw.println(mCharging);
- pw.print(" mSigMotionActive="); pw.println(mSigMotionActive);
+ pw.print(" mMotionActive="); pw.println(mMotionListener.active);
pw.print(" mSensing="); pw.print(mSensing); pw.print(" mNotMoving=");
pw.println(mNotMoving);
- pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHaveGps=");
- pw.print(mHaveGps); pw.print(" mLocated="); pw.println(mLocated);
+ pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
+ pw.print(mHasGps); pw.print(" mHasNetwork=");
+ pw.print(mHasNetworkLocation); pw.print(" mLocated="); pw.println(mLocated);
if (mLastGenericLocation != null) {
pw.print(" mLastGenericLocation="); pw.println(mLastGenericLocation);
}
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index ab2ea8b..8bb158e 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -11,6 +11,10 @@ option java_package com.android.server
# It lets us count the total amount of time between charges and the discharge level
2730 battery_discharge (duration|2|3),(minLevel|1|6),(maxLevel|1|6)
+# dock battery
+2738 dock_battery_level (level|1|6),(voltage|1|1),(temperature|1|1)
+2739 dock_battery_status (status|1|5),(health|1|5),(present|1|5),(plugged|1|5),(technology|3)
+
# ---------------------------
# PowerManagerService.java
@@ -29,9 +33,13 @@ option java_package com.android.server
# This is logged when the partial wake lock (keeping the device awake
# regardless of whether the screen is off) is acquired or released.
2729 power_partial_wake_state (releasedorAcquired|1|5),(tag|3)
+# The device is being asked to go into a soft sleep (typically by the ungaze gesture).
+# It logs the time remaining before the device would've normally gone to sleep without the request.
+2731 power_soft_sleep_requested (savedwaketimems|2)
#
-# Leave IDs through 2739 for more power logs (2730 used by battery_discharge above)
+# Leave IDs through 2739 for more power logs (2730 used by battery_discharge and
+# 2738-2739 used by dock battery above)
#
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 6d07a57..a9f6e40 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -36,6 +36,9 @@ import com.android.server.pm.UserManagerService;
import com.android.server.statusbar.StatusBarManagerService;
import com.android.server.wm.WindowManagerService;
+import cyanogenmod.app.CMStatusBarManager;
+import cyanogenmod.app.CustomTile;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -90,6 +93,7 @@ import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
import android.text.style.SuggestionSpan;
@@ -139,6 +143,12 @@ import java.util.HashSet;
import java.util.List;
import java.util.Locale;
+import cyanogenmod.hardware.CMHardwareManager;
+import cyanogenmod.providers.CMSettings;
+
+import org.cyanogenmod.internal.util.QSUtils;
+import org.cyanogenmod.internal.util.QSUtils.OnQSChanged;
+import org.cyanogenmod.internal.util.QSConstants;
/**
* This class provides a system service that manages input methods.
*/
@@ -196,6 +206,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
final InputBindResult mNoBinding = new InputBindResult(null, null, null, -1, -1);
+ private final OnQSChanged mQSListener = new OnQSChanged() {
+ @Override
+ public void onQSChanged() {
+ processQSChangedLocked();
+ }
+ };
+
// All known input methods. mMethodMap also serves as the global
// lock for this class.
final ArrayList<InputMethodInfo> mMethodList = new ArrayList<InputMethodInfo>();
@@ -223,6 +240,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
private boolean mShowOngoingImeSwitcherForPhones;
private boolean mNotificationShown;
private final boolean mImeSelectedOnBoot;
+ private CMHardwareManager mCMHardware;
static class SessionState {
final ClientState client;
@@ -482,15 +500,40 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this, userId);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, userId);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.STATUS_BAR_IME_SWITCHER),
+ false, new ContentObserver(mHandler) {
+ public void onChange(boolean selfChange) {
+ updateFromSettingsLocked(true);
+ }
+ }, userId);
+
+ if (mCMHardware.isSupported(CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY)) {
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.HIGH_TOUCH_SENSITIVITY_ENABLE), false, this, userId);
+ }
+ if (mCMHardware.isSupported(CMHardwareManager.FEATURE_TOUCH_HOVERING)) {
+ resolver.registerContentObserver(CMSettings.Secure.getUriFor(
+ CMSettings.Secure.FEATURE_TOUCH_HOVERING), false, this, userId);
+ }
+
mRegistered = true;
}
@Override public void onChange(boolean selfChange, Uri uri) {
final Uri showImeUri =
Settings.Secure.getUriFor(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD);
+ final Uri touchSensitivityUri =
+ CMSettings.System.getUriFor(CMSettings.System.HIGH_TOUCH_SENSITIVITY_ENABLE);
+ final Uri touchHoveringUri =
+ CMSettings.Secure.getUriFor(CMSettings.Secure.FEATURE_TOUCH_HOVERING);
synchronized (mMethodMap) {
if (showImeUri.equals(uri)) {
updateKeyboardFromSettingsLocked();
+ } else if (touchSensitivityUri.equals(uri)) {
+ updateTouchSensitivity();
+ } else if (touchHoveringUri.equals(uri)) {
+ updateTouchHovering();
} else {
boolean enabledChanged = false;
String newEnabled = mSettings.getEnabledInputMethodsStr();
@@ -919,11 +962,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
- synchronized (mMethodMap) {
- mSettingsObserver.registerContentObserverLocked(userId);
- updateFromSettingsLocked(true);
- }
-
// IMMS wants to receive Intent.ACTION_LOCALE_CHANGED in order to update the current IME
// according to the new system locale.
final IntentFilter filter = new IntentFilter();
@@ -938,6 +976,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}, filter);
LocalServices.addService(InputMethodManagerInternal.class, new LocalServiceImpl(mHandler));
+ QSUtils.registerObserverForQSChanges(mContext, mQSListener);
}
private void resetDefaultImeLocked(Context context) {
@@ -1047,6 +1086,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mContext.getBasePackageName());
}
+ updateTouchHovering();
+ updateTouchSensitivity();
+
if (DEBUG) Slog.d(TAG, "Switching user stage 3/3. newUserId=" + newUserId
+ " selectedIme=" + mSettings.getSelectedInputMethod());
}
@@ -1083,6 +1125,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
if (!mSystemReady) {
mSystemReady = true;
+ // Must happen before registerContentObserverLocked
+ mCMHardware = CMHardwareManager.getInstance(mContext);
+
+ mSettingsObserver.registerContentObserverLocked(
+ mSettings.getCurrentUserId());
+ updateFromSettingsLocked(true);
+
+ updateTouchHovering();
+ updateTouchSensitivity();
+
mKeyguardManager =
(KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
mNotificationManager = (NotificationManager)
@@ -1090,8 +1142,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mStatusBar = statusBar;
statusBar.setIconVisibility("ime", false);
updateSystemUiLocked(mCurToken, mImeWindowVis, mBackDisposition);
- mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
- com.android.internal.R.bool.show_ongoing_ime_switcher);
if (mShowOngoingImeSwitcherForPhones) {
mWindowManagerService.setOnHardKeyboardStatusChangeListener(
mHardKeyboardListener);
@@ -1814,6 +1864,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
mImeSwitcherNotification.build(), UserHandle.ALL);
mNotificationShown = true;
}
+ publishImeSelectorCustomTile(imi);
} else {
if (mNotificationShown && mNotificationManager != null) {
if (DEBUG) {
@@ -1823,6 +1874,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
com.android.internal.R.string.select_input_method, UserHandle.ALL);
mNotificationShown = false;
}
+ unpublishImeSelectorCustomTile();
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -1929,6 +1981,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
// There is no longer an input method set, so stop any current one.
unbindCurrentMethodLocked(true, false);
}
+ // code to disable the CM Phone IME switcher with config_show_cmIMESwitcher set = false
+ try {
+ mShowOngoingImeSwitcherForPhones = CMSettings.System.getInt(mContext.getContentResolver(),
+ CMSettings.System.STATUS_BAR_IME_SWITCHER) == 1;
+ } catch (CMSettings.CMSettingNotFoundException e) {
+ mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
+ com.android.internal.R.bool.config_show_cmIMESwitcher);
+ }
// Here is not the perfect place to reset the switching controller. Ideally
// mSwitchingController and mSettings should be able to share the same state.
// TODO: Make sure that mSwitchingController and mSettings are sharing the
@@ -1937,6 +1997,26 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
+ private void updateTouchSensitivity() {
+ if (!mCMHardware.isSupported(CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY)) {
+ return;
+ }
+ boolean touchSensitivityEnable = CMSettings.System.getInt(mContext.getContentResolver(),
+ CMSettings.System.HIGH_TOUCH_SENSITIVITY_ENABLE, 0) == 1;
+ mCMHardware.set(CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY,
+ touchSensitivityEnable);
+ }
+
+ private void updateTouchHovering() {
+ if (!mCMHardware.isSupported(CMHardwareManager.FEATURE_TOUCH_HOVERING)) {
+ return;
+ }
+ boolean touchHovering = CMSettings.Secure.getInt(mContext.getContentResolver(),
+ CMSettings.Secure.FEATURE_TOUCH_HOVERING, 0) == 1;
+ mCMHardware.set(CMHardwareManager.FEATURE_TOUCH_HOVERING,
+ touchHovering);
+ }
+
public void updateKeyboardFromSettingsLocked() {
mShowImeWithHardKeyboard = mSettings.isShowImeWithHardKeyboardEnabled();
if (mSwitchingDialog != null
@@ -3544,6 +3624,69 @@ public class InputMethodManagerService extends IInputMethodManager.Stub
}
}
+ private void publishImeSelectorCustomTile(InputMethodInfo imi) {
+ // This action should be performed as system
+ final int userId = UserHandle.myUserId();
+ long token = Binder.clearCallingIdentity();
+ try {
+ final UserHandle user = new UserHandle(userId);
+ final int icon = QSUtils.getDynamicQSTileResIconId(mContext, userId,
+ QSConstants.DYNAMIC_TILE_IME_SELECTOR);
+ final String contentDesc = QSUtils.getDynamicQSTileLabel(mContext, userId,
+ QSConstants.DYNAMIC_TILE_IME_SELECTOR);
+ final Context resourceContext = QSUtils.getQSTileContext(mContext, userId);
+ CharSequence inputMethodName = null;
+ if (mCurrentSubtype != null) {
+ inputMethodName = mCurrentSubtype.getDisplayName(mContext,
+ imi.getPackageName(), imi.getServiceInfo().applicationInfo);
+ }
+ final CharSequence label = inputMethodName == null ? contentDesc : inputMethodName;
+
+ CMStatusBarManager statusBarManager = CMStatusBarManager.getInstance(mContext);
+ CustomTile tile = new CustomTile.Builder(resourceContext)
+ .setLabel(label.toString())
+ .setContentDescription(contentDesc)
+ .setIcon(icon)
+ .setOnClickIntent(mImeSwitchPendingIntent)
+ .build();
+ statusBarManager.publishTileAsUser(QSConstants.DYNAMIC_TILE_IME_SELECTOR,
+ InputMethodManagerService.class.hashCode(), tile, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void unpublishImeSelectorCustomTile() {
+ // This action should be performed as system
+ final int userId = UserHandle.myUserId();
+ long token = Binder.clearCallingIdentity();
+ try {
+ CMStatusBarManager statusBarManager = CMStatusBarManager.getInstance(mContext);
+ statusBarManager.removeTileAsUser(QSConstants.DYNAMIC_TILE_IME_SELECTOR,
+ InputMethodManagerService.class.hashCode(), new UserHandle(userId));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void processQSChangedLocked() {
+ final int userId = UserHandle.myUserId();
+ final boolean isIMEVisible = ((mImeWindowVis & (InputMethodService.IME_ACTIVE)) != 0)
+ && (mWindowManagerService.isHardKeyboardAvailable()
+ || (mImeWindowVis & (InputMethodService.IME_VISIBLE)) != 0);
+ InputMethodInfo imi = null;
+ synchronized (mMethodMap) {
+ if (mCurMethodId != null) {
+ imi = mMethodMap.get(mCurMethodId);
+ }
+ }
+ if (shouldShowImeSwitcherLocked(isIMEVisible ? 1 : 0)) {
+ publishImeSelectorCustomTile(imi);
+ } else {
+ unpublishImeSelectorCustomTile();
+ }
+ }
+
// TODO: Cache the state for each user and reset when the cached user is removed.
private static class InputMethodFileManager {
private static final String SYSTEM_PATH = "system";
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 468ead0..6cd8e10 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -64,6 +64,7 @@ import android.location.Geofence;
import android.location.IGpsGeofenceHardware;
import android.location.IGpsMeasurementsListener;
import android.location.IGpsNavigationMessageListener;
+import android.location.GeoFenceParams;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
import android.location.ILocationListener;
@@ -90,6 +91,28 @@ import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+import com.android.internal.os.BackgroundThread;
+import com.android.server.location.FlpHardwareProvider;
+import com.android.server.location.FusedProxy;
+import com.android.server.location.GeocoderProxy;
+import com.android.server.location.GeofenceProxy;
+import com.android.server.location.GeofenceManager;
+import com.android.server.location.GeoFencerBase;
+import com.android.server.location.GeoFencerProxy;
+import com.android.server.location.GpsLocationProvider;
+import com.android.server.location.LocationBlacklist;
+import com.android.server.location.LocationFudger;
+import com.android.server.location.LocationProviderInterface;
+import com.android.server.location.LocationProviderProxy;
+import com.android.server.location.LocationRequestStatistics;
+import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
+import com.android.server.location.LocationRequestStatistics.PackageStatistics;
+import com.android.server.location.MockProvider;
+import com.android.server.location.PassiveProvider;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -154,6 +177,11 @@ public class LocationManagerService extends ILocationManager.Stub {
private LocationFudger mLocationFudger;
private GeofenceManager mGeofenceManager;
private PackageManager mPackageManager;
+ private String mComboNlpPackageName;
+ private String mComboNlpReadyMarker;
+ private String mComboNlpScreenMarker;
+ private String mGeoFencerPackageName;
+ private GeoFencerBase mGeoFencer;
private PowerManager mPowerManager;
private UserManager mUserManager;
private GeocoderProxy mGeocodeProvider;
@@ -509,6 +537,15 @@ public class LocationManagerService extends ILocationManager.Stub {
Slog.e(TAG, "no geocoder provider found");
}
+ mGeoFencerPackageName = resources.getString(
+ com.android.internal.R.string.config_geofenceProvider);
+ if (mGeoFencerPackageName != null &&
+ mPackageManager.resolveService(new Intent(mGeoFencerPackageName), 0) != null){
+ mGeoFencer = GeoFencerProxy.getGeoFencerProxy(mContext, mGeoFencerPackageName);
+ } else {
+ mGeoFencer = null;
+ }
+
// bind to fused hardware provider if supported
// in devices without support, requesting an instance of FlpHardwareProvider will raise an
// exception, so make sure we only do that when supported
@@ -562,6 +599,13 @@ public class LocationManagerService extends ILocationManager.Stub {
Slog.e(TAG, "Unable to bind ActivityRecognitionProxy.");
}
+ mComboNlpPackageName = resources.getString(
+ com.android.internal.R.string.config_comboNetworkLocationProvider);
+ if (mComboNlpPackageName != null) {
+ mComboNlpReadyMarker = mComboNlpPackageName + ".nlp:ready";
+ mComboNlpScreenMarker = mComboNlpPackageName + ".nlp:screen";
+ }
+
String[] testProviderStrings = resources.getStringArray(
com.android.internal.R.array.config_testLocationProviders);
for (String testProviderString : testProviderStrings) {
@@ -1614,9 +1658,11 @@ public class LocationManagerService extends ILocationManager.Stub {
checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
synchronized (mLock) {
- Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
+ Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
packageName, workSource, hideFromAppOps);
- requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
+ if (receiver != null) {
+ requestLocationUpdatesLocked(sanitizedRequest, receiver, pid, uid, packageName);
+ }
}
} finally {
Binder.restoreCallingIdentity(identity);
@@ -1675,7 +1721,9 @@ public class LocationManagerService extends ILocationManager.Stub {
// providers may use public location API's, need to clear identity
long identity = Binder.clearCallingIdentity();
try {
- removeUpdatesLocked(receiver);
+ if (receiver != null) {
+ removeUpdatesLocked(receiver);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1814,8 +1862,20 @@ public class LocationManagerService extends ILocationManager.Stub {
}
long identity = Binder.clearCallingIdentity();
try {
- mGeofenceManager.addFence(sanitizedRequest, geofence, intent, allowedResolutionLevel,
- uid, packageName);
+ if (mGeoFencer != null) {
+ long expiration;
+ if (sanitizedRequest.getExpireAt() == Long.MAX_VALUE) {
+ expiration = -1; // -1 means forever
+ } else {
+ expiration = sanitizedRequest.getExpireAt() - SystemClock.elapsedRealtime();
+ }
+ mGeoFencer.add(new GeoFenceParams(uid, geofence.getLatitude(),
+ geofence.getLongitude(), geofence.getRadius(),
+ expiration, intent, packageName));
+ } else {
+ mGeofenceManager.addFence(sanitizedRequest, geofence, intent,
+ allowedResolutionLevel, uid, packageName);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -1831,7 +1891,11 @@ public class LocationManagerService extends ILocationManager.Stub {
// geo-fence manager uses the public location API, need to clear identity
long identity = Binder.clearCallingIdentity();
try {
- mGeofenceManager.removeFence(geofence, intent);
+ if (mGeoFencer != null) {
+ mGeoFencer.remove(intent);
+ } else {
+ mGeofenceManager.removeFence(geofence, intent);
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
@@ -2332,6 +2396,71 @@ public class LocationManagerService extends ILocationManager.Stub {
synchronized (mLock) {
return mMockProviders.containsKey(provider);
}
+
+ }
+
+ private Location screenLocationLocked(Location location, String provider) {
+ if (isMockProvider(LocationManager.NETWORK_PROVIDER)) {
+ return location;
+ }
+ LocationProviderProxy providerProxy =
+ (LocationProviderProxy)mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
+ if (mComboNlpPackageName == null || providerProxy == null ||
+ false == provider.equals(LocationManager.NETWORK_PROVIDER) ||
+ isMockProvider(LocationManager.NETWORK_PROVIDER)) {
+ return location;
+ }
+
+ String connectedNlpPackage = providerProxy.getConnectedPackageName();
+ if (connectedNlpPackage == null || !connectedNlpPackage.equals(mComboNlpPackageName)) {
+ return location;
+ }
+
+ Bundle extras = location.getExtras();
+ boolean isBeingScreened = false;
+
+ if (extras == null || !extras.containsKey(mComboNlpReadyMarker)) {
+ // see if Combo Nlp is a passive listener
+ ArrayList<UpdateRecord> records =
+ mRecordsByProvider.get(LocationManager.PASSIVE_PROVIDER);
+ if (records != null) {
+ for (UpdateRecord r : records) {
+ if (r.mReceiver.mPackageName.equals(mComboNlpPackageName)) {
+ if (!isBeingScreened) {
+ isBeingScreened = true;
+ if (extras == null) {
+ location.setExtras(new Bundle());
+ extras = location.getExtras();
+ }
+ extras.putBoolean(mComboNlpScreenMarker, true);
+ }
+ // send location to Combo Nlp for screening
+ if (!r.mReceiver.callLocationChangedLocked(location)) {
+ Slog.w(TAG, "RemoteException calling onLocationChanged on "
+ + r.mReceiver);
+ } else {
+ if (D) {
+ Log.d(TAG, "Sending location for screening");
+ }
+ }
+ }
+ }
+ }
+ if (isBeingScreened) {
+ return null;
+ }
+ if (D) {
+ Log.d(TAG, "Not screening locations");
+ }
+ } else {
+ if (D) {
+ Log.d(TAG, "This location is marked as ready for broadcast");
+ }
+ // clear the ready marker
+ extras.remove(mComboNlpReadyMarker);
+ }
+
+ return location;
}
private void handleLocationChanged(Location location, boolean passive) {
@@ -2350,6 +2479,10 @@ public class LocationManagerService extends ILocationManager.Stub {
synchronized (mLock) {
if (isAllowedByCurrentUserSettingsLocked(provider)) {
if (!passive) {
+ location = screenLocationLocked(location, provider);
+ if (location == null) {
+ return;
+ }
// notify passive provider of the new location
mPassiveProvider.updateLocation(myLocation);
}
@@ -2655,6 +2788,10 @@ public class LocationManagerService extends ILocationManager.Stub {
mGeofenceManager.dump(pw);
+ if (mGeoFencer != null) {
+ mGeoFencer.dump(pw, "");
+ }
+
if (mEnabledProviders.size() > 0) {
pw.println(" Enabled Providers:");
for (String i : mEnabledProviders) {
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java
index 6cb2875..10b0bdd 100644
--- a/services/core/java/com/android/server/LockSettingsService.java
+++ b/services/core/java/com/android/server/LockSettingsService.java
@@ -58,6 +58,11 @@ import com.android.server.LockSettingsStorage.CredentialHash;
import java.util.Arrays;
import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import cyanogenmod.providers.CMSettings;
+
/**
* Keeps the lock pattern/password data and related settings for each user.
* Used by LockPatternUtils. Needs to be a service because Settings app also needs
@@ -70,6 +75,8 @@ public class LockSettingsService extends ILockSettings.Stub {
private static final String TAG = "LockSettingsService";
+ private static final String DEFAULT_PASSWORD = "default_password";
+
private final Context mContext;
private final LockSettingsStorage mStorage;
@@ -78,6 +85,7 @@ public class LockSettingsService extends ILockSettings.Stub {
private LockPatternUtils mLockPatternUtils;
private boolean mFirstCallToVold;
private IGateKeeperService mGateKeeperService;
+ private static String mSavePassword = DEFAULT_PASSWORD;
private interface CredentialUtil {
void setCredential(String credential, String savedCredential, int userId)
@@ -255,6 +263,17 @@ public class LockSettingsService extends ILockSettings.Stub {
setString("migrated_lockscreen_disabled", "true", 0);
Slog.i(TAG, "Migrated lockscreen disabled flag");
}
+
+ if (getString("migrated_pattern_size", null, 0) == null) {
+ final String val = getString(Secure.LOCK_PATTERN_SIZE, null,
+ UserHandle.USER_CURRENT);
+ if (val != null) {
+ setString(Secure.LOCK_PATTERN_SIZE, val, 0);
+ }
+
+ setString("migrated_pattern_size", "true", 0);
+ Slog.i(TAG, "Migrated primary user pattern size");
+ }
} catch (RemoteException re) {
Slog.e(TAG, "Unable to migrate old data", re);
}
@@ -350,6 +369,10 @@ public class LockSettingsService extends ILockSettings.Stub {
}
}
+ if (LockPatternUtils.LEGACY_LOCK_PATTERN_ENABLED.equals(key)) {
+ key = Settings.Secure.LOCK_PATTERN_ENABLED;
+ }
+
return mStorage.readKeyValue(key, defaultValue, userId);
}
@@ -367,6 +390,25 @@ public class LockSettingsService extends ILockSettings.Stub {
return mStorage.hasPattern(userId);
}
+ public void retainPassword(String password) {
+ if (LockPatternUtils.isDeviceEncryptionEnabled()) {
+ if (password != null)
+ mSavePassword = password;
+ else
+ mSavePassword = DEFAULT_PASSWORD;
+ }
+ }
+
+ public void sanitizePassword() {
+ if (LockPatternUtils.isDeviceEncryptionEnabled()) {
+ mSavePassword = DEFAULT_PASSWORD;
+ }
+ }
+
+ public String getPassword() {
+ return mSavePassword;
+ }
+
private void setKeystorePassword(String password, int userHandle) {
final UserManager um = (UserManager) mContext.getSystemService(USER_SERVICE);
final KeyStore ks = KeyStore.getInstance();
@@ -421,6 +463,10 @@ public class LockSettingsService extends ILockSettings.Stub {
}
+ public byte getLockPatternSize(int userId) {
+ return mStorage.getLockPatternSize(userId);
+ }
+
@Override
public void setLockPattern(String pattern, String savedCredential, int userId)
throws RemoteException {
@@ -543,8 +589,10 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public byte[] toHash(String pattern, int userId) {
+ final byte lockPatternSize = getLockPatternSize(userId);
return LockPatternUtils.patternToHash(
- LockPatternUtils.stringToPattern(pattern));
+ LockPatternUtils.stringToPattern(pattern, lockPatternSize),
+ lockPatternSize);
}
@Override
@@ -558,6 +606,8 @@ public class LockSettingsService extends ILockSettings.Stub {
&& shouldReEnrollBaseZero) {
setLockPattern(pattern, patternToVerify, userId);
}
+ if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK)
+ retainPassword(pattern);
return response;
@@ -566,7 +616,10 @@ public class LockSettingsService extends ILockSettings.Stub {
@Override
public VerifyCredentialResponse checkPassword(String password, int userId)
throws RemoteException {
- return doVerifyPassword(password, false, 0, userId);
+ VerifyCredentialResponse response = doVerifyPassword(password, false, 0, userId);
+ if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK)
+ retainPassword(password);
+ return response;
}
@Override
@@ -781,7 +834,11 @@ public class LockSettingsService extends ILockSettings.Stub {
Secure.LOCK_PATTERN_ENABLED,
Secure.LOCK_BIOMETRIC_WEAK_FLAGS,
Secure.LOCK_PATTERN_VISIBLE,
- Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED
+ Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED,
+ CMSettings.Secure.LOCK_PASS_TO_SECURITY_VIEW,
+ Secure.LOCK_PATTERN_SIZE,
+ Secure.LOCK_DOTS_VISIBLE,
+ Secure.LOCK_SHOW_ERROR_PATH,
};
// Reading these settings needs the contacts permission
diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java
index de48e71..7ac2e42 100644
--- a/services/core/java/com/android/server/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/LockSettingsStorage.java
@@ -16,6 +16,8 @@
package com.android.server;
+import android.os.RemoteException;
+import android.provider.Settings;
import com.android.internal.annotations.VisibleForTesting;
import android.content.ContentValues;
@@ -29,6 +31,7 @@ import android.os.UserManager;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
+import com.android.internal.widget.LockPatternUtils;
import java.io.File;
import java.io.IOException;
@@ -59,6 +62,7 @@ class LockSettingsStorage {
private static final String LOCK_PATTERN_FILE = "gatekeeper.pattern.key";
private static final String BASE_ZERO_LOCK_PATTERN_FILE = "gatekeeper.gesture.key";
private static final String LEGACY_LOCK_PATTERN_FILE = "gesture.key";
+ private static final String CM_LEGACY_LOCK_PATTERN_FILE = "cm_gesture.key";
private static final String LOCK_PASSWORD_FILE = "gatekeeper.password.key";
private static final String LEGACY_LOCK_PASSWORD_FILE = "password.key";
@@ -238,6 +242,11 @@ class LockSettingsStorage {
return new CredentialHash(stored, CredentialHash.VERSION_LEGACY);
}
+ stored = readFile(getCmLegacyLockPatternFilename(userId));
+ if (stored != null && stored.length > 0) {
+ return new CredentialHash(stored, CredentialHash.VERSION_LEGACY);
+ }
+
return null;
}
@@ -250,7 +259,8 @@ class LockSettingsStorage {
public boolean hasPattern(int userId) {
return hasFile(getLockPatternFilename(userId)) ||
hasFile(getBaseZeroLockPatternFilename(userId)) ||
- hasFile(getLegacyLockPatternFilename(userId));
+ hasFile(getLegacyLockPatternFilename(userId)) ||
+ hasFile(getCmLegacyLockPatternFilename(userId));
}
private boolean hasFile(String name) {
@@ -328,6 +338,7 @@ class LockSettingsStorage {
mStoredCredentialType = hash == null
? CredentialHash.TYPE_NONE
: CredentialHash.TYPE_PATTERN;
+
writeFile(getLockPatternFilename(userId), hash);
clearPasswordHash(userId);
}
@@ -348,9 +359,22 @@ class LockSettingsStorage {
writeFile(getLockPasswordFilename(userId), null);
}
+ public byte getLockPatternSize(int userId) {
+ long size = Long.valueOf(readKeyValue(Settings.Secure.LOCK_PATTERN_SIZE, "-1", userId));
+ if (size > 0 && size < 128) {
+ return (byte) size;
+ }
+ return LockPatternUtils.PATTERN_SIZE_DEFAULT;
+ }
+
+ public boolean isDefaultSize(int userId) {
+ return getLockPatternSize(userId) == LockPatternUtils.PATTERN_SIZE_DEFAULT;
+ }
+
@VisibleForTesting
String getLockPatternFilename(int userId) {
- return getLockCredentialFilePathForUser(userId, LOCK_PATTERN_FILE);
+ String baseFileName = LOCK_PATTERN_FILE;
+ return getLockCredentialFilePathForUser(userId, baseFileName);
}
@VisibleForTesting
@@ -363,6 +387,10 @@ class LockSettingsStorage {
return getLockCredentialFilePathForUser(userId, LEGACY_LOCK_PATTERN_FILE);
}
+ String getCmLegacyLockPatternFilename(int userId) {
+ return getLockCredentialFilePathForUser(userId, CM_LEGACY_LOCK_PATTERN_FILE);
+ }
+
@VisibleForTesting
String getLegacyLockPasswordFilename(int userId) {
return getLockCredentialFilePathForUser(userId, LEGACY_LOCK_PASSWORD_FILE);
diff --git a/services/core/java/com/android/server/LockSettingsStrongAuth.java b/services/core/java/com/android/server/LockSettingsStrongAuth.java
index c023f4a..5add4c0 100644
--- a/services/core/java/com/android/server/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/LockSettingsStrongAuth.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.os.Looper;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
@@ -47,6 +48,12 @@ public class LockSettingsStrongAuth {
private final ArrayList<IStrongAuthTracker> mStrongAuthTrackers = new ArrayList<>();
private final SparseIntArray mStrongAuthForUser = new SparseIntArray();
+ private final Handler mHandler;
+
+ public LockSettingsStrongAuth() {
+ mHandler = new Handler(Looper.getMainLooper(), mHandlerCallback);
+ }
+
private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) {
for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) {
@@ -145,9 +152,9 @@ public class LockSettingsStrongAuth {
requireStrongAuth(STRONG_AUTH_NOT_REQUIRED, userId);
}
- private final Handler mHandler = new Handler() {
+ private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
- public void handleMessage(Message msg) {
+ public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_TRACKER:
handleAddStrongAuthTracker((IStrongAuthTracker) msg.obj);
@@ -162,6 +169,7 @@ public class LockSettingsStrongAuth {
handleRemoveUser(msg.arg1);
break;
}
+ return true;
}
};
}
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index 1653db9..3fa02ba 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -34,6 +34,9 @@ import java.io.IOException;
public class MasterClearReceiver extends BroadcastReceiver {
private static final String TAG = "MasterClear";
+ /* {@hide} */
+ public static final String EXTRA_WIPE_MEDIA = "wipe_media";
+
@Override
public void onReceive(final Context context, final Intent intent) {
if (intent.getAction().equals(Intent.ACTION_REMOTE_INTENT)) {
@@ -54,7 +57,8 @@ public class MasterClearReceiver extends BroadcastReceiver {
@Override
public void run() {
try {
- RecoverySystem.rebootWipeUserData(context, shutdown, reason);
+ boolean wipeMedia = intent.getBooleanExtra(EXTRA_WIPE_MEDIA, true);
+ RecoverySystem.rebootWipeUserData(context, shutdown, reason, wipeMedia);
Log.wtf(TAG, "Still running after master clear?!");
} catch (IOException e) {
Slog.e(TAG, "Can't perform master clear/factory reset", e);
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 5e67414..d539201 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -1093,8 +1093,10 @@ class MountService extends IMountService.Stub
final long destroy = Long.parseLong(cooked[6]);
final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
- dropBox.addText(TAG_STORAGE_BENCHMARK, scrubPath(path)
- + " " + ident + " " + create + " " + run + " " + destroy);
+ if (dropBox != null) {
+ dropBox.addText(TAG_STORAGE_BENCHMARK, scrubPath(path)
+ + " " + ident + " " + create + " " + run + " " + destroy);
+ }
final VolumeRecord rec = findRecordForPath(path);
if (rec != null) {
@@ -1111,8 +1113,10 @@ class MountService extends IMountService.Stub
final long time = Long.parseLong(cooked[3]);
final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class);
- dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path)
- + " " + bytes + " " + time);
+ if (dropBox != null) {
+ dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path)
+ + " " + bytes + " " + time);
+ }
final VolumeRecord rec = findRecordForPath(path);
if (rec != null) {
@@ -1738,6 +1742,8 @@ class MountService extends IMountService.Stub
Preconditions.checkNotNull(fsUuid);
synchronized (mLock) {
final VolumeRecord rec = mRecords.get(fsUuid);
+ if (rec == null)
+ return;
rec.nickname = nickname;
mCallbacks.notifyVolumeRecordChanged(rec);
writeSettingsLocked();
@@ -1752,6 +1758,8 @@ class MountService extends IMountService.Stub
Preconditions.checkNotNull(fsUuid);
synchronized (mLock) {
final VolumeRecord rec = mRecords.get(fsUuid);
+ if (rec == null)
+ return;
rec.userFlags = (rec.userFlags & ~mask) | (flags & mask);
mCallbacks.notifyVolumeRecordChanged(rec);
writeSettingsLocked();
@@ -2384,6 +2392,12 @@ class MountService extends IMountService.Stub
// to let the UI to clear itself
mHandler.postDelayed(new Runnable() {
public void run() {
+ // unmount the internal emulated volume first
+ try {
+ mConnector.execute("volume", "unmount", "emulated");
+ } catch (NativeDaemonConnectorException e) {
+ Slog.e(TAG, "unable to shut down internal volume", e);
+ }
try {
mCryptConnector.execute("cryptfs", "restart");
} catch (NativeDaemonConnectorException e) {
@@ -2400,7 +2414,7 @@ class MountService extends IMountService.Stub
}
}
- public int encryptStorage(int type, String password) {
+ private int encryptStorageExtended(int type, String password, boolean wipe) {
if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) {
throw new IllegalArgumentException("password cannot be empty");
}
@@ -2430,6 +2444,22 @@ class MountService extends IMountService.Stub
return 0;
}
+ /** Encrypt Storage given a password.
+ * @param type The password type.
+ * @param password The password to be used in encryption.
+ */
+ public int encryptStorage(int type, String password) {
+ return encryptStorageExtended(type, password, false);
+ }
+
+ /** Encrypt Storage given a password after wiping it.
+ * @param type The password type.
+ * @param password The password to be used in encryption.
+ */
+ public int encryptWipeStorage(int type, String password) {
+ return encryptStorageExtended(type, password, true);
+ }
+
/** Set the password for encrypting the master key.
* @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager.
* @param password The password to set.
@@ -2444,9 +2474,13 @@ class MountService extends IMountService.Stub
Slog.i(TAG, "changing encryption password...");
}
+ LockSettingsService lockSettings = new LockSettingsService(mContext);
+ String currentPassword = lockSettings.getPassword();
+
try {
NativeDaemonEvent event = mCryptConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type],
- new SensitiveArg(password));
+ new SensitiveArg(currentPassword), new SensitiveArg(password));
+ lockSettings.sanitizePassword();
return Integer.parseInt(event.getMessage());
} catch (NativeDaemonConnectorException e) {
// Encryption failed
diff --git a/services/core/java/com/android/server/NetPluginDelegate.java b/services/core/java/com/android/server/NetPluginDelegate.java
new file mode 100644
index 0000000..18365f1
--- /dev/null
+++ b/services/core/java/com/android/server/NetPluginDelegate.java
@@ -0,0 +1,113 @@
+/*
+ *Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ *Redistribution and use in source and binary forms, with or without
+ *modification, are permitted provided that the following conditions are
+ *met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ *THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ *WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ *MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ *ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ *BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ *BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ *WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ *OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ *IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.android.server;
+
+import dalvik.system.PathClassLoader;
+
+import android.util.Slog;
+import android.net.Network;
+import android.net.NetworkStats;
+import android.util.Log;
+
+public class NetPluginDelegate {
+
+ private static final String TAG = "ConnectivityExtension";
+ private static final boolean LOGV = false;
+
+ private static Class tetherExtensionClass = null;
+ private static Object tetherExtensionObj = null;
+
+ private static boolean extensionFailed;
+
+ public static void getTetherStats(NetworkStats uidStats, NetworkStats devStats,
+ NetworkStats xtStats) {
+ if (!loadTetherExtJar()) {
+ return;
+ }
+ try {
+ tetherExtensionClass.getMethod("getTetherStats", NetworkStats.class,
+ NetworkStats.class, NetworkStats.class).invoke(tetherExtensionObj, uidStats,
+ devStats, xtStats);
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.w(TAG, "error in invoke method");
+ }
+ if (LOGV) Slog.v(TAG, "getTetherStats() X");
+ }
+
+ public static void setQuota(String iface, long quota) {
+ if (!loadTetherExtJar()) {
+ return;
+ }
+ try {
+ tetherExtensionClass.getMethod("setQuota", String.class, long.class).invoke(
+ tetherExtensionObj, iface, quota);
+ } catch (Exception ex) {
+ Log.w(TAG, "Error calling setQuota Method on extension jar");
+ }
+ if (LOGV) Slog.v(TAG, "setQuota(" + iface + ", " + quota + ") X");
+ }
+
+ public static void setUpstream(Network net) {
+ if (LOGV) Slog.v(TAG, "setUpstream(" + net + ") E");
+ loadTetherExtJar();
+ try {
+ tetherExtensionClass.getMethod("setUpstream", Network.class).invoke(
+ tetherExtensionObj, net);
+ } catch (Exception ex) {
+ Log.w(TAG, "Error calling setUpstream Method on extension jar");
+ }
+ if (LOGV) Slog.v(TAG, "setUpstream(" + net + ") E");
+ }
+
+
+ private static boolean loadTetherExtJar() {
+ final String realProvider = "com.qualcomm.qti.tetherstatsextension.TetherStatsReporting";
+ final String realProviderPath = "/system/framework/ConnectivityExt.jar";
+ if (!extensionFailed && tetherExtensionClass == null && tetherExtensionObj == null) {
+ if (LOGV) Slog.v(TAG, "loading ConnectivityExt jar");
+ try {
+
+ PathClassLoader classLoader = new PathClassLoader(realProviderPath,
+ ClassLoader.getSystemClassLoader());
+
+ tetherExtensionClass = classLoader.loadClass(realProvider);
+ tetherExtensionObj = tetherExtensionClass.newInstance();
+ if (LOGV)
+ Slog.v(TAG, "ConnectivityExt jar loaded");
+ extensionFailed = false;
+ } catch (Exception e) {
+ Log.w(TAG, "Connectivity extension is not available");
+ extensionFailed = true;
+ }
+ }
+ return !extensionFailed;
+ }
+}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index ba9279c..c063d9f 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -52,6 +52,7 @@ import android.net.INetworkManagementEventObserver;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.LinkAddress;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkPolicyManager;
import android.net.NetworkStats;
@@ -77,6 +78,7 @@ import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -110,6 +112,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.Objects;
import java.util.StringTokenizer;
import java.util.concurrent.CountDownLatch;
@@ -170,6 +173,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
public static final int InterfaceDnsServerInfo = 615;
public static final int RouteChange = 616;
public static final int StrictCleartext = 617;
+ public static final int InterfaceMessage = 618;
}
static final int DAEMON_MSG_MOBILE_CONN_REAL_TIME_INFO = 1;
@@ -187,7 +191,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
private final Handler mFgHandler;
private final Handler mDaemonHandler;
private final PhoneStateListener mPhoneStateListener;
-
+ private String mWifiInterfaceName, mDataInterfaceName;
private IBatteryStats mBatteryStats;
private final Thread mThread;
@@ -230,6 +234,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub
/** Set of states for the child firewall chains. True if the chain is active. */
@GuardedBy("mQuotaLock")
final SparseBooleanArray mFirewallChainStates = new SparseBooleanArray();
+ @GuardedBy("mQuotaLock")
+ final Map<Integer, Boolean> mWlanBlacklist = new HashMap<Integer, Boolean>();
+ @GuardedBy("mQuotaLock")
+ final Map<Integer, Boolean> mDataBlacklist = new HashMap<Integer, Boolean>();
private Object mIdleTimerLock = new Object();
/** Set of interfaces with active idle timers. */
@@ -257,6 +265,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners =
new RemoteCallbackList<INetworkActivityListener>();
private boolean mNetworkActive;
+ private HashMap<Integer, Boolean> mPendingRestrictOnData = new HashMap<Integer, Boolean>();
/**
* Constructs a new NetworkManagementService instance
@@ -281,6 +290,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
FgThread.get().getLooper());
mThread = new Thread(mConnector, NETD_TAG);
+ mWifiInterfaceName = SystemProperties.get("wifi.interface");
mDaemonHandler = new Handler(FgThread.get().getLooper());
mPhoneStateListener = new PhoneStateListener(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID,
@@ -291,6 +301,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
if (DBG) Slog.d(TAG, "onDataConnectionRealTimeInfoChanged: " + dcRtInfo);
notifyInterfaceClassActivity(ConnectivityManager.TYPE_MOBILE,
dcRtInfo.getDcPowerState(), dcRtInfo.getTime(), true);
+ processPendingDataRestrictRequests();
}
};
TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
@@ -520,6 +531,21 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
/**
+ * Notify our observers of a change in the data activity state of the interface
+ */
+ private void notifyInterfaceMessage(String message) {
+ final int length = mObservers.beginBroadcast();
+ for (int i = 0; i < length; i++) {
+ try {
+ mObservers.getBroadcastItem(i).interfaceMessageRecevied(message);
+ } catch (RemoteException e) {
+ } catch (RuntimeException e) {
+ }
+ }
+ mObservers.finishBroadcast();
+ }
+
+ /**
* Prepare native daemon once connected, enabling modules and pushing any
* existing in-memory rules.
*/
@@ -789,6 +815,22 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
throw new IllegalStateException(errorMessage);
// break;
+ case NetdResponseCode.InterfaceMessage:
+ /*
+ * An message arrived in network interface.
+ * Format: "NNN IfaceMessage <3>AP-STA-CONNECTED 00:08:22:64:9d:84
+ */
+ if (cooked.length < 3 || !cooked[1].equals("IfaceMessage")) {
+ throw new IllegalStateException(errorMessage);
+ }
+ Slog.d(TAG, "onEvent: "+ raw);
+ if(cooked[4] != null) {
+ notifyInterfaceMessage(cooked[3] + " " + cooked[4]);
+ } else {
+ notifyInterfaceMessage(cooked[3]);
+ }
+ return true;
+ // break;
case NetdResponseCode.InterfaceClassActivity:
/*
* An network interface class state changed (active/idle)
@@ -806,7 +848,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
timestampNanos = SystemClock.elapsedRealtimeNanos();
}
boolean isActive = cooked[2].equals("active");
- notifyInterfaceClassActivity(Integer.parseInt(cooked[3]),
+ notifyInterfaceClassActivity(cooked[3] == null ? 0 : Integer.parseInt(cooked[3]),
isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH
: DataConnectionRealTimeInfo.DC_POWER_STATE_LOW, timestampNanos, false);
return true;
@@ -1789,6 +1831,85 @@ public class NetworkManagementService extends INetworkManagementService.Stub
}
}
+ private void processPendingDataRestrictRequests() {
+ initDataInterface();
+ if (TextUtils.isEmpty(mDataInterfaceName) || mPendingRestrictOnData.isEmpty()) {
+ return;
+ }
+ for (Integer key : mPendingRestrictOnData.keySet()) {
+ restrictAppOnData(key, mPendingRestrictOnData.get(key));
+ }
+ mPendingRestrictOnData.clear();
+ }
+
+ @Override
+ public void restrictAppOnData(int uid, boolean restrict) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+ // silently discard when control disabled
+ // TODO: eventually migrate to be always enabled
+ if (!mBandwidthControlEnabled) return;
+
+ initDataInterface();
+ if (TextUtils.isEmpty(mDataInterfaceName)) {
+ // We don't have an interface name since data is not active
+ // yet, so queue up the request for when it comes up alive
+ mPendingRestrictOnData.put(uid, restrict);
+ return;
+ }
+
+ synchronized (mQuotaLock) {
+ if (!mDataBlacklist.containsKey(uid) && !restrict) {
+ return;
+ }
+ Boolean wasRestricted = mDataBlacklist.get(uid);
+ if (Objects.equals(wasRestricted, restrict)) {
+ return;
+ }
+ mDataBlacklist.put(uid, restrict);
+ }
+
+ try {
+ if (restrict) {
+ mConnector.execute("bandwidth", "addrestrictappsondata", mDataInterfaceName, uid);
+ } else {
+ mConnector.execute("bandwidth", "removerestrictappsondata", mDataInterfaceName, uid);
+ }
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
+ @Override
+ public void restrictAppOnWlan(int uid, boolean restrict) {
+ mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+
+ // silently discard when control disabled
+ // TODO: eventually migrate to be always enabled
+ if (!mBandwidthControlEnabled) return;
+
+ synchronized (mQuotaLock) {
+ if (!mWlanBlacklist.containsKey(uid) && !restrict) {
+ return;
+ }
+ Boolean wasRestricted = mWlanBlacklist.get(uid);
+ if (Objects.equals(wasRestricted, restrict) || TextUtils.isEmpty(mWifiInterfaceName)) {
+ return;
+ }
+ mWlanBlacklist.put(uid, restrict);
+ }
+
+
+ try {
+ if (restrict) {
+ mConnector.execute("bandwidth", "addrestrictappsonwlan", mWifiInterfaceName, uid);
+ } else {
+ mConnector.execute("bandwidth", "removerestrictappsonwlan", mWifiInterfaceName, uid);
+ }
+ } catch (NativeDaemonConnectorException e) {
+ throw e.rethrowAsParcelableException();
+ }
+ }
+
@Override
public void setUidCleartextNetworkPolicy(int uid, int policy) {
if (Binder.getCallingUid() != uid) {
@@ -2541,4 +2662,16 @@ public class NetworkManagementService extends INetworkManagementService.Stub
public void removeInterfaceFromLocalNetwork(String iface) {
modifyInterfaceInNetwork("remove", "local", iface);
}
+
+ private void initDataInterface() {
+ if (!TextUtils.isEmpty(mDataInterfaceName)) {
+ return;
+ }
+ ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ LinkProperties linkProperties = cm.getLinkProperties(ConnectivityManager.TYPE_MOBILE);
+ if (linkProperties != null) {
+ mDataInterfaceName = linkProperties.getInterfaceName();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/PermissionDialog.java b/services/core/java/com/android/server/PermissionDialog.java
new file mode 100644
index 0000000..fd676b5
--- /dev/null
+++ b/services/core/java/com/android/server/PermissionDialog.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2006 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.AppOpsManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.os.Handler;
+import android.os.Message;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+import android.widget.TextView;
+
+public class PermissionDialog extends BasePermissionDialog {
+ private final static String TAG = "PermissionDialog";
+
+ private final AppOpsService mService;
+ private final String mPackageName;
+ private final int mCode;
+ private View mView;
+ private CheckBox mChoice;
+ private int mUid;
+ final CharSequence[] mOpLabels;
+ private Context mContext;
+
+ // Event 'what' codes
+ static final int ACTION_ALLOWED = 0x2;
+ static final int ACTION_IGNORED = 0x4;
+ static final int ACTION_IGNORED_TIMEOUT = 0x8;
+
+ // 15s timeout, then we automatically dismiss the permission
+ // dialog. Otherwise, it may cause watchdog timeout sometimes.
+ static final long DISMISS_TIMEOUT = 1000 * 15 * 1;
+
+ public PermissionDialog(Context context, AppOpsService service,
+ int code, int uid, String packageName) {
+ super(context);
+
+ mContext = context;
+ Resources res = context.getResources();
+
+ mService = service;
+ mCode = code;
+ mPackageName = packageName;
+ mUid = uid;
+ mOpLabels = res.getTextArray(
+ com.android.internal.R.array.app_ops_labels);
+
+ setCancelable(false);
+
+ setButton(DialogInterface.BUTTON_POSITIVE,
+ res.getString(com.android.internal.R.string.allow), mHandler.obtainMessage(ACTION_ALLOWED));
+
+ setButton(DialogInterface.BUTTON_NEGATIVE,
+ res.getString(com.android.internal.R.string.deny), mHandler.obtainMessage(ACTION_IGNORED));
+
+ setTitle(res.getString(com.android.internal.R.string.privacy_guard_dialog_title));
+ WindowManager.LayoutParams attrs = getWindow().getAttributes();
+ attrs.setTitle("Permission info: " + getAppName(mPackageName));
+ attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
+ | WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+ getWindow().setAttributes(attrs);
+
+ mView = getLayoutInflater().inflate(
+ com.android.internal.R.layout.permission_confirmation_dialog,
+ null);
+ TextView tv = (TextView) mView.findViewById(
+ com.android.internal.R.id.permission_text);
+ mChoice = (CheckBox) mView.findViewById(
+ com.android.internal.R.id.permission_remember_choice_checkbox);
+ String name = getAppName(mPackageName);
+ if(name == null)
+ name = mPackageName;
+ tv.setText(mContext.getString(com.android.internal.R.string.privacy_guard_dialog_summary,
+ name, mOpLabels[mCode]));
+ setView(mView);
+
+ // After the timeout, pretend the user clicked the quit button
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(ACTION_IGNORED_TIMEOUT), DISMISS_TIMEOUT);
+ }
+
+ public void ignore() {
+ mHandler.sendMessage(mHandler.obtainMessage(ACTION_IGNORED_TIMEOUT));
+ }
+
+ private String getAppName(String packageName) {
+ ApplicationInfo appInfo = null;
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ appInfo = pm.getApplicationInfo(packageName,
+ PackageManager.GET_DISABLED_COMPONENTS
+ | PackageManager.GET_UNINSTALLED_PACKAGES);
+ } catch (final NameNotFoundException e) {
+ return null;
+ }
+ if(appInfo != null) {
+ return (String)pm.getApplicationLabel(appInfo);
+ }
+ return null;
+ }
+
+ private final Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ int mode;
+ boolean remember = mChoice.isChecked();
+ switch(msg.what) {
+ case ACTION_ALLOWED:
+ mode = AppOpsManager.MODE_ALLOWED;
+ break;
+ case ACTION_IGNORED:
+ mode = AppOpsManager.MODE_IGNORED;
+ break;
+ default:
+ mode = AppOpsManager.MODE_IGNORED;
+ remember = false;
+ }
+ mService.notifyOperation(mCode, mUid, mPackageName, mode,
+ remember);
+ dismiss();
+ }
+ };
+}
diff --git a/services/core/java/com/android/server/PermissionDialogReqQueue.java b/services/core/java/com/android/server/PermissionDialogReqQueue.java
new file mode 100644
index 0000000..5b602e3
--- /dev/null
+++ b/services/core/java/com/android/server/PermissionDialogReqQueue.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2006 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 java.util.ArrayList;
+import java.util.List;
+
+public class PermissionDialogReqQueue {
+ public PermissionDialog getDialog() {
+ return mDialog;
+ }
+
+ public void setDialog(PermissionDialog mDialog) {
+ this.mDialog = mDialog;
+ }
+
+ public final static class PermissionDialogReq {
+ public void set(int res) {
+ synchronized (this) {
+ mHasResult = true;
+ mResult = res;
+ notifyAll();
+ }
+ }
+
+ public int get() {
+ synchronized (this) {
+ while (!mHasResult) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ return mResult;
+ }
+
+ boolean mHasResult = false;
+ int mResult;
+ }
+
+ private PermissionDialog mDialog;
+ private List<PermissionDialogReq> resultList;
+
+ public PermissionDialogReqQueue() {
+ mDialog = null;
+ resultList = new ArrayList<PermissionDialogReq>();
+ }
+
+ public void register(PermissionDialogReq res) {
+ synchronized (this) {
+ resultList.add(res);
+ }
+ }
+
+ public void notifyAll(int mode) {
+ synchronized (this) {
+ while (resultList.size() != 0) {
+ PermissionDialogReq res = resultList.get(0);
+ res.set(mode);
+ resultList.remove(0);
+ }
+ }
+ }
+
+ public void ignore() {
+ if (mDialog != null) {
+ mDialog.ignore();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java
index 7ef3e12..fe69d98 100644
--- a/services/core/java/com/android/server/SystemConfig.java
+++ b/services/core/java/com/android/server/SystemConfig.java
@@ -16,9 +16,16 @@
package com.android.server;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
+
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.pm.FeatureInfo;
+import android.content.pm.Signature;
import android.os.*;
import android.os.Process;
import android.util.ArrayMap;
@@ -98,7 +105,10 @@ public class SystemConfig {
// These are the package names of apps which should be in the 'always'
// URL-handling state upon factory reset.
- final ArraySet<String> mLinkedApps = new ArraySet<>();
+ final ArraySet<AppLink> mLinkedApps = new ArraySet<>();
+
+ final ArrayMap<Signature, ArraySet<String>> mSignatureAllowances
+ = new ArrayMap<Signature, ArraySet<String>>();
// These are the permitted backup transport service components
final ArraySet<ComponentName> mBackupTransportWhitelist = new ArraySet<>();
@@ -144,10 +154,14 @@ public class SystemConfig {
return mFixedImeApps;
}
- public ArraySet<String> getLinkedApps() {
+ public ArraySet<AppLink> getLinkedApps() {
return mLinkedApps;
}
+ public ArrayMap<Signature, ArraySet<String>> getSignatureAllowances() {
+ return mSignatureAllowances;
+ }
+
public ArraySet<ComponentName> getBackupTransportWhitelist() {
return mBackupTransportWhitelist;
}
@@ -298,6 +312,43 @@ public class SystemConfig {
perms.add(perm);
XmlUtils.skipCurrentTag(parser);
+ } else if ("allow-permission".equals(name)) {
+ String perm = parser.getAttributeValue(null, "name");
+ if (perm == null) {
+ Slog.w(TAG,
+ "<allow-permission> without name at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ String signature = parser.getAttributeValue(null, "signature");
+ if (signature == null) {
+ Slog.w(TAG,
+ "<allow-permission> without signature at "
+ + parser.getPositionDescription());
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ Signature sig = null;
+ try {
+ sig = new Signature(signature);
+ } catch (IllegalArgumentException e) {
+ // sig will be null so we will log it below
+ }
+ if (sig != null) {
+ ArraySet<String> perms = mSignatureAllowances.get(sig);
+ if (perms == null) {
+ perms = new ArraySet<String>();
+ mSignatureAllowances.put(sig, perms);
+ }
+ perms.add(perm);
+ } else {
+ Slog.w(TAG,
+ "<allow-permission> with bad signature at "
+ + parser.getPositionDescription());
+ }
+ XmlUtils.skipCurrentTag(parser);
+
} else if ("library".equals(name) && !onlyFeatures) {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
@@ -381,11 +432,12 @@ public class SystemConfig {
} else if ("app-link".equals(name)) {
String pkgname = parser.getAttributeValue(null, "package");
+ String state = parser.getAttributeValue(null, "state");
if (pkgname == null) {
Slog.w(TAG, "<app-link> without package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
- mLinkedApps.add(pkgname);
+ mLinkedApps.add(makeLink(pkgname, state));
}
XmlUtils.skipCurrentTag(parser);
} else if ("backup-transport-whitelisted-service".equals(name)) {
@@ -426,6 +478,23 @@ public class SystemConfig {
}
}
+ private AppLink makeLink(String pkgname, String state) {
+ AppLink al = new AppLink();
+ al.pkgname = pkgname;
+ if (state == null || "always".equals(state)) { // default
+ al.state = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
+ } else if ("always-ask".equals(state)) {
+ al.state = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS_ASK;
+ } else if ("ask".equals("state")) {
+ al.state = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK;
+ } else if ("never".equals("state")) {
+ al.state = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
+ } else {
+ al.state = INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
+ }
+ return al;
+ }
+
void readPermission(XmlPullParser parser, String name)
throws IOException, XmlPullParserException {
if (mPermissions.containsKey(name)) {
@@ -460,4 +529,20 @@ public class SystemConfig {
XmlUtils.skipCurrentTag(parser);
}
}
+
+ /** Simple value class to hold an app-link entry.
+ * It is public because PackageManagerService needs to see it */
+ public static class AppLink {
+ public String pkgname;
+ public int state;
+
+ @Override
+ public int hashCode() { return pkgname.hashCode(); }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof AppLink)) { return false; }
+ return pkgname.equals(((AppLink)other).pkgname);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 19a4851..c7f4d0f 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -159,7 +159,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
private String[] mDataConnectionApn;
- private ArrayList<String> mConnectedApns;
+ private ArrayList<String>[] mConnectedApns;
private LinkProperties[] mDataConnectionLinkProperties;
@@ -292,11 +292,11 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
mContext = context;
mBatteryStats = BatteryStatsService.getService();
- mConnectedApns = new ArrayList<String>();
int numPhones = TelephonyManager.getDefault().getPhoneCount();
if (DBG) log("TelephonyRegistor: ctor numPhones=" + numPhones);
mNumPhones = numPhones;
+ mConnectedApns = new ArrayList[numPhones];
mCallState = new int[numPhones];
mDataActivity = new int[numPhones];
mDataConnectionState = new int[numPhones];
@@ -314,6 +314,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
mDataConnectionNetworkCapabilities = new NetworkCapabilities[numPhones];
mCellInfo = new ArrayList<List<CellInfo>>();
for (int i = 0; i < numPhones; i++) {
+ mConnectedApns[i] = new ArrayList<String>();
mCallState[i] = TelephonyManager.CALL_STATE_IDLE;
mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
mDataConnectionState[i] = TelephonyManager.DATA_UNKNOWN;
@@ -336,7 +337,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
location.fillInNotifierBundle(mCellLocation[i]);
}
}
- mConnectedApns = new ArrayList<String>();
mAppOps = mContext.getSystemService(AppOpsManager.class);
}
@@ -1019,7 +1019,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
if (validatePhoneId(phoneId)) {
mDataActivity[phoneId] = state;
for (Record r : mRecords) {
- if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_DATA_ACTIVITY)) {
+ if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_DATA_ACTIVITY)
+ && idMatch(r.subId, subId, phoneId)) {
try {
r.callback.onDataActivity(state);
} catch (RemoteException ex) {
@@ -1057,18 +1058,22 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
synchronized (mRecords) {
int phoneId = SubscriptionManager.getPhoneId(subId);
if (validatePhoneId(phoneId)) {
+ if (VDBG) {
+ log(" mConnectedApns[" + phoneId + "]=" + mConnectedApns[phoneId].toString());
+ }
boolean modified = false;
if (state == TelephonyManager.DATA_CONNECTED) {
- if (!mConnectedApns.contains(apnType)) {
- mConnectedApns.add(apnType);
+ if (!mConnectedApns[phoneId].contains(apnType)
+ && !apnType.equals(PhoneConstants.APN_TYPE_IMS)) {
+ mConnectedApns[phoneId].add(apnType);
if (mDataConnectionState[phoneId] != state) {
mDataConnectionState[phoneId] = state;
modified = true;
}
}
} else {
- if (mConnectedApns.remove(apnType)) {
- if (mConnectedApns.isEmpty()) {
+ if (mConnectedApns[phoneId].remove(apnType)) {
+ if (mConnectedApns[phoneId].isEmpty()) {
mDataConnectionState[phoneId] = state;
modified = true;
} else {
@@ -1245,7 +1250,41 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState, backgroundCallState,
DisconnectCause.NOT_VALID,
- PreciseDisconnectCause.NOT_VALID);
+ PreciseDisconnectCause.NOT_VALID, SubscriptionManager.INVALID_SUBSCRIPTION_ID);
+ }
+
+ public void notifyPreciseCallStateForSubscriber(int subId, int ringingCallState,
+ int foregroundCallState, int backgroundCallState) {
+ if (!checkNotifyPermission("notifyPreciseCallStateForSubscriber()")) {
+ return;
+ }
+ synchronized (mRecords) {
+ int phoneId = SubscriptionManager.getPhoneId(subId);
+ if (validatePhoneId(phoneId)) {
+ mRingingCallState = ringingCallState;
+ mForegroundCallState = foregroundCallState;
+ mBackgroundCallState = backgroundCallState;
+ mPreciseCallState = new PreciseCallState(ringingCallState, foregroundCallState,
+ backgroundCallState,
+ DisconnectCause.NOT_VALID,
+ PreciseDisconnectCause.NOT_VALID);
+ for (Record r : mRecords) {
+ if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_PRECISE_CALL_STATE)
+ && ((r.subId == subId) ||
+ (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID))) {
+ try {
+ r.callback.onPreciseCallStateChanged(mPreciseCallState);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState, backgroundCallState,
+ DisconnectCause.NOT_VALID,
+ PreciseDisconnectCause.NOT_VALID, subId);
}
public void notifyDisconnectCause(int disconnectCause, int preciseDisconnectCause) {
@@ -1267,7 +1306,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
handleRemoveListLocked();
}
broadcastPreciseCallStateChanged(mRingingCallState, mForegroundCallState,
- mBackgroundCallState, disconnectCause, preciseDisconnectCause);
+ mBackgroundCallState, disconnectCause, preciseDisconnectCause,
+ SubscriptionManager.INVALID_SUBSCRIPTION_ID);
}
public void notifyPreciseDataConnectionFailed(String reason, String apnType,
@@ -1507,13 +1547,16 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
private void broadcastPreciseCallStateChanged(int ringingCallState, int foregroundCallState,
- int backgroundCallState, int disconnectCause, int preciseDisconnectCause) {
+ int backgroundCallState, int disconnectCause, int preciseDisconnectCause, int subId) {
Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_CALL_STATE_CHANGED);
intent.putExtra(TelephonyManager.EXTRA_RINGING_CALL_STATE, ringingCallState);
intent.putExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, foregroundCallState);
intent.putExtra(TelephonyManager.EXTRA_BACKGROUND_CALL_STATE, backgroundCallState);
intent.putExtra(TelephonyManager.EXTRA_DISCONNECT_CAUSE, disconnectCause);
intent.putExtra(TelephonyManager.EXTRA_PRECISE_DISCONNECT_CAUSE, preciseDisconnectCause);
+ if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+ }
mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
android.Manifest.permission.READ_PRECISE_PHONE_STATE);
}
diff --git a/services/core/java/com/android/server/ThermalObserver.java b/services/core/java/com/android/server/ThermalObserver.java
new file mode 100644
index 0000000..aee28fb
--- /dev/null
+++ b/services/core/java/com/android/server/ThermalObserver.java
@@ -0,0 +1,146 @@
+/*
+ * 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.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.UEventObserver;
+import android.os.UserHandle;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * ThermalObserver for monitoring temperature changes.
+ */
+public class ThermalObserver extends SystemService {
+ private static final String TAG = "ThermalObserver";
+
+ private static final String CALLSTATE_UEVENT_MATCH =
+ "DEVPATH=/devices/virtual/switch/thermalstate";
+
+ private static final int MSG_THERMAL_STATE_CHANGED = 0;
+
+ private static final int SWITCH_STATE_NORMAL = 0;
+ private static final int SWITCH_STATE_WARNING = 1;
+ private static final int SWITCH_STATE_EXCEEDED = 2;
+
+ private final PowerManager mPowerManager;
+ private final PowerManager.WakeLock mWakeLock;
+
+ private final Object mLock = new Object();
+ private Integer mLastState;
+
+ private final UEventObserver mThermalWarningObserver = new UEventObserver() {
+ @Override
+ public void onUEvent(UEventObserver.UEvent event) {
+ updateLocked(Integer.parseInt(event.get("SWITCH_STATE")));
+ }
+ };
+
+ private final Handler mHandler = new Handler(true /*async*/) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_THERMAL_STATE_CHANGED:
+ handleThermalStateChange(msg.arg1);
+ mWakeLock.release();
+ break;
+ }
+ }
+ };
+
+ public ThermalObserver(Context context) {
+ super(context);
+ mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+ mThermalWarningObserver.startObserving(CALLSTATE_UEVENT_MATCH);
+ }
+
+ private void updateLocked(int state) {
+ Message message = new Message();
+ message.what = MSG_THERMAL_STATE_CHANGED;
+ message.arg1 = state;
+
+ mWakeLock.acquire();
+ mHandler.sendMessage(message);
+ }
+
+ private void handleThermalStateChange(int state) {
+ synchronized (mLock) {
+ mLastState = state;
+ Intent intent = new Intent(Intent.ACTION_THERMAL_EVENT);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+
+ final int thermalState;
+
+ switch (state) {
+ case SWITCH_STATE_WARNING:
+ thermalState = Intent.EXTRA_THERMAL_STATE_WARNING;
+ break;
+ case SWITCH_STATE_EXCEEDED:
+ thermalState = Intent.EXTRA_THERMAL_STATE_EXCEEDED;
+ break;
+ case SWITCH_STATE_NORMAL:
+ default:
+ thermalState = Intent.EXTRA_THERMAL_STATE_NORMAL;
+ break;
+ }
+
+ intent.putExtra(Intent.EXTRA_THERMAL_STATE, thermalState);
+
+ getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(TAG, new BinderService());
+ }
+
+ private final class BinderService extends Binder {
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump thermal observer service from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ if (args == null || args.length == 0 || "-a".equals(args[0])) {
+ pw.println("Current Thermal Observer Service state:");
+ pw.println(" last state change: "
+ + (mLastState != null ? mLastState : "none"));
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index c228422..f637c4f 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -24,6 +24,7 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.hardware.input.InputManager;
+import android.net.Uri;
import android.os.BatteryStats;
import android.os.Handler;
import android.os.IVibratorService;
@@ -55,6 +56,9 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;
+import cyanogenmod.hardware.CMHardwareManager;
+import cyanogenmod.providers.CMSettings;
+
public class VibratorService extends IVibratorService.Stub
implements InputManager.InputDeviceListener {
private static final String TAG = "VibratorService";
@@ -86,6 +90,10 @@ public class VibratorService extends IVibratorService.Stub
private int mCurVibUid = -1;
private boolean mLowPowerMode;
private SettingsObserver mSettingObserver;
+ private CMHardwareManager mHardware;
+ private int mMinVibratorIntensity;
+ private int mMaxVibratorIntensity;
+ private int mVibratorIntensity;
native static boolean vibratorExists();
native static void vibratorOn(long milliseconds);
@@ -241,9 +249,20 @@ public class VibratorService extends IVibratorService.Stub
@Override
public void onReceive(Context context, Intent intent) {
updateInputDeviceVibrators();
+ updateVibratorIntensity();
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
+ mHardware = CMHardwareManager.getInstance(mContext);
+ if (mHardware.isSupported(CMHardwareManager.FEATURE_VIBRATOR)) {
+ mContext.getContentResolver().registerContentObserver(
+ CMSettings.Secure.getUriFor(CMSettings.Secure.VIBRATOR_INTENSITY),
+ true, mSettingObserver, UserHandle.USER_ALL);
+ mMinVibratorIntensity = mHardware.getVibratorMinIntensity();
+ mMaxVibratorIntensity = mHardware.getVibratorMaxIntensity();
+ updateVibratorIntensity();
+ }
+
updateInputDeviceVibrators();
}
@@ -253,11 +272,22 @@ public class VibratorService extends IVibratorService.Stub
}
@Override
- public void onChange(boolean SelfChange) {
- updateInputDeviceVibrators();
+ public void onChange(boolean selfChange, Uri uri) {
+ if (uri.equals(CMSettings.Secure.getUriFor(CMSettings.Secure.VIBRATOR_INTENSITY))) {
+ updateVibratorIntensity();
+ } else {
+ updateInputDeviceVibrators();
+ }
}
}
+ private void updateVibratorIntensity() {
+ mVibratorIntensity = CMSettings.Secure.getIntForUser(mContext.getContentResolver(),
+ CMSettings.Secure.VIBRATOR_INTENSITY, mHardware.getVibratorDefaultIntensity(),
+ UserHandle.USER_CURRENT);
+ mHardware.setVibratorIntensity(mVibratorIntensity);
+ }
+
@Override // Binder call
public boolean hasVibrator() {
return doVibratorExists();
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 772a15c..22529a3 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -42,6 +42,8 @@ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Date;
+import java.text.SimpleDateFormat;
/** This class calls its monitor every minute. Killing this process if they don't return **/
public class Watchdog extends Thread {
@@ -80,6 +82,7 @@ public class Watchdog extends Thread {
int mPhonePid;
IActivityController mController;
boolean mAllowRestart = true;
+ SimpleDateFormat mTraceDateFormat = new SimpleDateFormat("dd_MMM_HH_mm_ss.SSS");
/**
* Used for checking status of handle threads and scheduling monitor callbacks.
@@ -428,9 +431,22 @@ public class Watchdog extends Thread {
dumpKernelStackTraces();
}
- // Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the kernel log
- doSysRq('w');
- doSysRq('l');
+ String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
+ String traceFileNameAmendment = "_SystemServer_WDT" + mTraceDateFormat.format(new Date());
+
+ if (tracesPath != null && tracesPath.length() != 0) {
+ File traceRenameFile = new File(tracesPath);
+ String newTracesPath;
+ int lpos = tracesPath.lastIndexOf (".");
+ if (-1 != lpos)
+ newTracesPath = tracesPath.substring (0, lpos) + traceFileNameAmendment + tracesPath.substring (lpos);
+ else
+ newTracesPath = tracesPath + traceFileNameAmendment;
+ traceRenameFile.renameTo(new File(newTracesPath));
+ tracesPath = newTracesPath;
+ }
+
+ final File newFd = new File(tracesPath);
// Try to add the error to the dropbox, but assuming that the ActivityManager
// itself may be deadlocked. (which has happened, causing this statement to
@@ -439,7 +455,7 @@ public class Watchdog extends Thread {
public void run() {
mActivity.addErrorToDropBox(
"watchdog", null, "system_server", null, null,
- subject, null, stack, null);
+ subject, null, newFd, null);
}
};
dropboxThread.start();
@@ -447,6 +463,24 @@ public class Watchdog extends Thread {
dropboxThread.join(2000); // wait up to 2 seconds for it to return.
} catch (InterruptedException ignored) {}
+ // Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the kernel log
+ Slog.e(TAG, "Triggering SysRq for system_server watchdog");
+ doSysRq('w');
+ doSysRq('l');
+
+ // At times, when user space watchdog traces don't give an indication on
+ // which component held a lock, because of which other threads are blocked,
+ // (thereby causing Watchdog), crash the device to analyze RAM dumps
+ boolean crashOnWatchdog = SystemProperties
+ .getBoolean("persist.sys.crashOnWatchdog", false);
+ if (crashOnWatchdog) {
+ // wait until the above blocked threads be dumped into kernel log
+ SystemClock.sleep(3000);
+
+ // now try to crash the target
+ doSysRq('c');
+ }
+
IActivityController controller;
synchronized (this) {
controller = mController;
diff --git a/services/core/java/com/android/server/WiredAccessoryManager.java b/services/core/java/com/android/server/WiredAccessoryManager.java
index e0e6070..3b52fce 100644
--- a/services/core/java/com/android/server/WiredAccessoryManager.java
+++ b/services/core/java/com/android/server/WiredAccessoryManager.java
@@ -22,6 +22,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
+import android.os.SystemProperties;
import android.os.UEventObserver;
import android.util.Slog;
import android.media.AudioManager;
@@ -66,6 +67,8 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
private static final String NAME_H2W = "h2w";
private static final String NAME_USB_AUDIO = "usb_audio";
+ private static final String NAME_EMU_AUDIO = "semu_audio";
+ private static final String NAME_SAMSUNG_USB_AUDIO = "dock";
private static final String NAME_HDMI_AUDIO = "hdmi_audio";
private static final String NAME_HDMI = "hdmi";
@@ -330,7 +333,8 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
FileReader file = new FileReader(uei.getSwitchStatePath());
int len = file.read(buffer, 0, 1024);
file.close();
- curState = Integer.valueOf((new String(buffer, 0, len)).trim());
+ curState = validateSwitchState(
+ Integer.valueOf((new String(buffer, 0, len)).trim()));
if (curState > 0) {
updateStateLocked(uei.getDevPath(), uei.getDevName(), curState);
@@ -345,14 +349,23 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
}
// At any given time accessories could be inserted
- // one on the board, one on the dock and one on HDMI:
- // observe three UEVENTs
+ // one on the board, one on the dock, one on the
+ // samsung dock and one on HDMI:
+ // observe all UEVENTs that have valid switch supported
+ // by the Kernel
for (int i = 0; i < mUEventInfo.size(); ++i) {
UEventInfo uei = mUEventInfo.get(i);
startObserving("DEVPATH="+uei.getDevPath());
}
}
+ private int validateSwitchState(int state) {
+ // Some drivers, namely HTC headset ones, add additional bits to
+ // the switch state. As we only are able to deal with the states
+ // 0, 1 and 2, mask out all the other bits
+ return state & 0x3;
+ }
+
private List<UEventInfo> makeObservedUEventList() {
List<UEventInfo> retVal = new ArrayList<UEventInfo>();
UEventInfo uei;
@@ -375,6 +388,19 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
Slog.w(TAG, "This kernel does not have usb audio support");
}
+ // Monitor Motorola EMU audio jack
+ uei = new UEventInfo(NAME_EMU_AUDIO, BIT_USB_HEADSET_ANLG, 0, 0);
+ if (uei.checkSwitchExists()) {
+ retVal.add(uei);
+ }
+
+ // Monitor Samsung USB audio
+ uei = new UEventInfo(NAME_SAMSUNG_USB_AUDIO, BIT_USB_HEADSET_DGTL,
+ BIT_USB_HEADSET_ANLG, 0);
+ if (uei.checkSwitchExists()) {
+ retVal.add(uei);
+ }
+
// Monitor HDMI
//
// If the kernel has support for the "hdmi_audio" switch, use that. It will be
@@ -405,7 +431,14 @@ final class WiredAccessoryManager implements WiredAccessoryCallbacks {
try {
String devPath = event.get("DEVPATH");
String name = event.get("SWITCH_NAME");
- int state = Integer.parseInt(event.get("SWITCH_STATE"));
+ if (SystemProperties.getBoolean("tcmd.whisper", false) &&
+ (name.equals("CAR") || name.equals("DESK"))) {
+ // Motorola dock - ignore this event and don't change
+ // the audio routing just because we're docked.
+ // Let only the dock emu audio jack sensing do that.
+ return;
+ }
+ int state = validateSwitchState(Integer.parseInt(event.get("SWITCH_STATE")));
synchronized (mLock) {
updateStateLocked(devPath, name, state);
}
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 3c6de07..1e0cf0a 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -528,6 +528,7 @@ public class AccountManagerService
@Override
public String getPassword(Account account) {
+ android.util.SeempLog.record(14);
int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "getPassword: " + account
@@ -627,6 +628,7 @@ public class AccountManagerService
@Override
public String getUserData(Account account, String key) {
+ android.util.SeempLog.record(15);
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
String msg = String.format("getUserData( account: %s, key: %s, callerUid: %s, pid: %s",
@@ -1199,6 +1201,7 @@ public class AccountManagerService
@Override
public void removeAccount(IAccountManagerResponse response, Account account,
boolean expectActivityLaunch) {
+ android.util.SeempLog.record(17);
removeAccountAsUser(
response,
account,
@@ -1587,6 +1590,7 @@ public class AccountManagerService
@Override
public void setPassword(Account account, String password) {
+ android.util.SeempLog.record(18);
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setAuthToken: " + account
@@ -1651,6 +1655,7 @@ public class AccountManagerService
@Override
public void clearPassword(Account account) {
+ android.util.SeempLog.record(19);
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "clearPassword: " + account
@@ -1677,6 +1682,7 @@ public class AccountManagerService
@Override
public void setUserData(Account account, String key, String value) {
+ android.util.SeempLog.record(20);
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "setUserData: " + account
@@ -2116,6 +2122,7 @@ public class AccountManagerService
public void addAccount(final IAccountManagerResponse response, final String accountType,
final String authTokenType, final String[] requiredFeatures,
final boolean expectActivityLaunch, final Bundle optionsIn) {
+ android.util.SeempLog.record(16);
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "addAccount: accountType " + accountType
+ ", response " + response
@@ -2369,6 +2376,7 @@ public class AccountManagerService
@Override
public void editProperties(IAccountManagerResponse response, final String accountType,
final boolean expectActivityLaunch) {
+ android.util.SeempLog.record(21);
final int callingUid = Binder.getCallingUid();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "editProperties: accountType " + accountType
diff --git a/services/core/java/com/android/server/accounts/TokenCache.java b/services/core/java/com/android/server/accounts/TokenCache.java
index be91f98..81c6b0e 100644
--- a/services/core/java/com/android/server/accounts/TokenCache.java
+++ b/services/core/java/com/android/server/accounts/TokenCache.java
@@ -125,7 +125,7 @@ import java.util.Objects;
* This is recursive, but it won't spiral out of control because LruCache is
* thread safe and the Evictor can only be removed once.
*/
- Evictor evictor = mTokenEvictors.remove(oldVal.token);
+ Evictor evictor = mTokenEvictors.remove(new Pair<>(k.account.type, oldVal.token));
if (evictor != null) {
evictor.evict();
}
@@ -133,13 +133,15 @@ import java.util.Objects;
}
public void putToken(Key k, Value v) {
- // Prepare for removal by token string.
- Evictor tokenEvictor = mTokenEvictors.get(v.token);
+ // Prepare for removal by pair of account type and token string.
+ Pair<String, String> pair = new Pair<>(k.account.type, v.token);
+
+ Evictor tokenEvictor = mTokenEvictors.get(pair);
if (tokenEvictor == null) {
tokenEvictor = new Evictor();
}
tokenEvictor.add(k);
- mTokenEvictors.put(new Pair<>(k.account.type, v.token), tokenEvictor);
+ mTokenEvictors.put(pair, tokenEvictor);
// Prepare for removal by associated account.
Evictor accountEvictor = mAccountEvictors.get(k.account);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 970f1b5..db3d474 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -121,6 +121,10 @@ public final class ActiveServices {
// at the same time.
final int mMaxStartingBackground;
+ // Flag to reschedule the services during app launch. Disable by default.
+ private static final boolean SERVICE_RESCHEDULE
+ = SystemProperties.getBoolean("ro.am.reschedule_service", false);
+
final SparseArray<ServiceMap> mServiceMap = new SparseArray<>();
/**
@@ -1229,6 +1233,14 @@ public final class ActiveServices {
r.pendingStarts.add(0, si);
long dur = SystemClock.uptimeMillis() - si.deliveredTime;
dur *= 2;
+ if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) {
+ Slog.w(TAG,"Can add more delay !!!"
+ +" si.deliveredTime "+si.deliveredTime
+ +" dur "+dur
+ +" si.deliveryCount "+si.deliveryCount
+ +" si.doneExecutingCount "+si.doneExecutingCount
+ +" allowCancel "+allowCancel);
+ }
if (minDuration < dur) minDuration = dur;
if (resetTime < dur) resetTime = dur;
} else {
@@ -1241,6 +1253,13 @@ public final class ActiveServices {
}
r.totalRestartCount++;
+ if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) {
+ Slog.w(TAG,"r.name "+r.name+" N "+N+" minDuration "+minDuration
+ +" resetTime "+resetTime+" now "+now
+ +" r.restartDelay "+r.restartDelay
+ +" r.restartTime+resetTime "+(r.restartTime+resetTime)
+ +" allowCancel "+allowCancel);
+ }
if (r.restartDelay == 0) {
r.restartCount++;
r.restartDelay = minDuration;
@@ -1262,6 +1281,14 @@ public final class ActiveServices {
}
r.nextRestartTime = now + r.restartDelay;
+ if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) {
+ Slog.w(TAG,"r.name "+r.name+" N "+N+" minDuration "+minDuration
+ +" resetTime "+resetTime+" now "+now
+ +" r.restartDelay "+r.restartDelay
+ +" r.restartTime+resetTime "+(r.restartTime+resetTime)
+ +" r.nextRestartTime "+r.nextRestartTime
+ +" allowCancel "+allowCancel);
+ }
// Make sure that we don't end up restarting a bunch of services
// all at the same time.
@@ -1304,6 +1331,15 @@ public final class ActiveServices {
r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
Slog.w(TAG, "Scheduling restart of crashed service "
+ r.shortName + " in " + r.restartDelay + "ms");
+
+ if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) {
+ for (int i=mRestartingServices.size()-1; i>=0; i--) {
+ ServiceRecord r2 = mRestartingServices.get(i);
+ Slog.w(TAG,"Restarting list - i "+i+" r2.nextRestartTime "
+ +r2.nextRestartTime+" r2.name "+r2.name);
+ }
+ }
+
EventLog.writeEvent(EventLogTags.AM_SCHEDULE_SERVICE_RESTART,
r.userId, r.shortName, r.restartDelay);
@@ -1324,7 +1360,31 @@ public final class ActiveServices {
return;
}
try {
- bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true);
+ if(SERVICE_RESCHEDULE == true) {
+ boolean shouldDelay = false;
+ ActivityRecord top_rc = null;
+ ActivityStack stack = mAm.getFocusedStack();
+ if(stack != null) {
+ top_rc = stack.topRunningActivityLocked(null);
+ }
+ if(top_rc != null) {
+ if(!top_rc.nowVisible && !r.shortName.contains(top_rc.packageName)) {
+ shouldDelay = true;
+ }
+ }
+ if(!shouldDelay) {
+ bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true);
+ } else {
+ if (DEBUG_DELAYED_SERVICE) {
+ Slog.v(TAG, "Reschedule service restart due to app launch"
+ +" r.shortName "+r.shortName+" r.app = "+r.app);
+ }
+ r.resetRestartCounter();
+ scheduleServiceRestartLocked(r, true);
+ }
+ } else {
+ bringUpServiceLocked(r, r.intent.getIntent().getFlags(), r.createdFromFg, true);
+ }
} catch (TransactionTooLargeException e) {
// Ignore, it's been logged and nothing upstack cares.
}
@@ -1378,7 +1438,7 @@ public final class ActiveServices {
return null;
}
- if (!whileRestarting && r.restartDelay > 0) {
+ if (!whileRestarting && r.restartDelay > 0 && mRestartingServices.contains(r)) {
// If waiting for a restart, then do nothing.
return null;
}
@@ -1547,6 +1607,11 @@ public final class ActiveServices {
if (newService) {
app.services.remove(r);
r.app = null;
+ if (SERVICE_RESCHEDULE && DEBUG_DELAYED_SERVICE) {
+ Slog.w(TAG, " Failed to create Service !!!! ."
+ +"This will introduce huge delay... "
+ +r.shortName + " in " + r.restartDelay + "ms");
+ }
}
// Retry.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9ff8415..3ae82a8 100644..100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1,6 +1,8 @@
/*
* Copyright (C) 2006-2008 The Android Open Source Project
- *
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
+ * Copyright (c) 2010-2015, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
* 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
@@ -42,6 +44,7 @@ import android.app.ApplicationThreadNative;
import android.app.BroadcastOptions;
import android.app.IActivityContainer;
import android.app.IActivityContainerCallback;
+import android.app.IActivityManager;
import android.app.IAppTask;
import android.app.ITaskStackListener;
import android.app.ProfilerInfo;
@@ -52,6 +55,7 @@ import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
import android.content.pm.PermissionInfo;
import android.content.res.Resources;
+import android.content.res.ThemeConfig;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
@@ -72,6 +76,7 @@ import android.util.DebugUtils;
import android.util.SparseIntArray;
import android.view.Display;
+import android.view.InflateException;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.AssistUtils;
@@ -178,6 +183,7 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
+import android.content.res.ThemeConfig;
import android.net.Proxy;
import android.net.ProxyInfo;
import android.net.Uri;
@@ -213,6 +219,7 @@ import android.os.UpdateLock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.telecom.TelecomManager;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.AtomicFile;
@@ -260,6 +267,13 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
+import cyanogenmod.power.PerformanceManagerInternal;
+
+import java.util.Date;
+import java.text.SimpleDateFormat;
+
+import org.cyanogenmod.internal.util.ThemeUtils;
+
public final class ActivityManagerService extends ActivityManagerNative
implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
@@ -292,6 +306,11 @@ public final class ActivityManagerService extends ActivityManagerNative
private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
private static final String TAG_VISIBLE_BEHIND = TAG + POSTFIX_VISIBLE_BEHIND;
+ private static final String ACTION_POWER_OFF_ALARM =
+ "org.codeaurora.alarm.action.POWER_OFF_ALARM";
+
+ private static final String POWER_OFF_ALARM = "powerOffAlarm";
+
/** Control over CPU and battery monitoring */
// write battery stats every 30 minutes.
static final long BATTERY_STATS_TIME = 30 * 60 * 1000;
@@ -320,6 +339,11 @@ public final class ActivityManagerService extends ActivityManagerNative
// before we decide it must be hung.
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT = 10*1000;
+ // How long we will retain processes hosting content providers in the "last activity"
+ // state before allowing them to drop down to the regular cached LRU list. This is
+ // to avoid thrashing of provider processes under low memory situations.
+ static final int CONTENT_PROVIDER_RETAIN_TIME = 20*1000;
+
// How long we wait for a launched process to attach to the activity manager
// before we decide it's never going to come up for real, when the process was
// started with a wrapper for instrumentation (such as Valgrind) because it
@@ -393,6 +417,8 @@ public final class ActivityManagerService extends ActivityManagerNative
// How many bytes to write into the dropbox log before truncating
static final int DROPBOX_MAX_SIZE = 256 * 1024;
+ static final String PROP_REFRESH_THEME = "sys.refresh_theme";
+
// Access modes for handleIncomingUser.
static final int ALLOW_NON_FULL = 0;
static final int ALLOW_NON_FULL_IN_PROFILE = 1;
@@ -406,6 +432,8 @@ public final class ActivityManagerService extends ActivityManagerNative
// Necessary ApplicationInfo flags to mark an app as persistent
private static final int PERSISTENT_MASK =
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT;
+ private boolean mHomeKilled = false;
+ private String mHomeProcessName = null;
// Delay to disable app launch boost
@@ -496,6 +524,8 @@ public final class ActivityManagerService extends ActivityManagerNative
*/
String mDeviceOwnerName;
+ SimpleDateFormat mTraceDateFormat = new SimpleDateFormat("dd_MMM_HH_mm_ss.SSS");
+
public class PendingAssistExtras extends Binder implements Runnable {
public final ActivityRecord activity;
public final Bundle extras;
@@ -1023,6 +1053,7 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean mLaunchWarningShown = false;
Context mContext;
+ Context mUiContext;
int mFactoryTest;
@@ -1068,6 +1099,8 @@ public final class ActivityManagerService extends ActivityManagerNative
*/
private IVoiceInteractionSession mRunningVoice;
+ PerformanceManagerInternal mPerf;
+
/**
* For some direct access we need to power manager.
*/
@@ -1235,6 +1268,7 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean foregroundActivities;
}
+ private Map<Integer, Object[]> mForegroundActivitiesList = new HashMap<Integer, Object[]>();
final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
@@ -1376,6 +1410,10 @@ public final class ActivityManagerService extends ActivityManagerNative
static final int SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG = 57;
static final int APP_BOOST_DEACTIVATE_MSG = 58;
static final int CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG = 59;
+ static final int POST_PRIVACY_NOTIFICATION_MSG = 60;
+ static final int CANCEL_PRIVACY_NOTIFICATION_MSG = 61;
+ static final int POST_COMPONENT_PROTECTED_MSG = 62;
+ static final int CANCEL_PROTECTED_APP_NOTIFICATION = 63;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1385,6 +1423,16 @@ public final class ActivityManagerService extends ActivityManagerNative
CompatModeDialog mCompatModeDialog;
long mLastMemUsageReportTime = 0;
+ // Min aging threshold in milliseconds to consider a B-service
+ int mMinBServiceAgingTime =
+ SystemProperties.getInt("ro.sys.fw.bservice_age", 5000);
+ // Threshold for B-services when in memory pressure
+ int mBServiceAppThreshold =
+ SystemProperties.getInt("ro.sys.fw.bservice_limit", 5);
+ // Enable B-service aging propagation on memory pressure.
+ boolean mEnableBServicePropagation =
+ SystemProperties.getBoolean("ro.sys.fw.bservice_enable", false);
+
/**
* Flag whether the current user is a "monkey", i.e. whether
* the UI is driven by a UI automation tool.
@@ -1438,7 +1486,7 @@ public final class ActivityManagerService extends ActivityManagerNative
return;
}
if (mShowDialogs && !mSleeping && !mShuttingDown) {
- Dialog d = new AppErrorDialog(mContext,
+ Dialog d = new AppErrorDialog(getUiContext(),
ActivityManagerService.this, res, proc);
d.show();
proc.crashDialog = d;
@@ -1473,7 +1521,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (mShowDialogs) {
Dialog d = new AppNotRespondingDialog(ActivityManagerService.this,
- mContext, proc, (ActivityRecord)data.get("activity"),
+ getUiContext(), proc, (ActivityRecord)data.get("activity"),
msg.arg1 != 0);
d.show();
proc.anrDialog = d;
@@ -1499,7 +1547,7 @@ public final class ActivityManagerService extends ActivityManagerNative
}
AppErrorResult res = (AppErrorResult) data.get("result");
if (mShowDialogs && !mSleeping && !mShuttingDown) {
- Dialog d = new StrictModeViolationDialog(mContext,
+ Dialog d = new StrictModeViolationDialog(getUiContext(),
ActivityManagerService.this, res, proc);
d.show();
proc.crashDialog = d;
@@ -1513,7 +1561,7 @@ public final class ActivityManagerService extends ActivityManagerNative
} break;
case SHOW_FACTORY_ERROR_MSG: {
Dialog d = new FactoryErrorDialog(
- mContext, msg.getData().getCharSequence("msg"));
+ getUiContext(), msg.getData().getCharSequence("msg"));
d.show();
ensureBootCompleted();
} break;
@@ -1524,7 +1572,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (!app.waitedForDebugger) {
Dialog d = new AppWaitingForDebuggerDialog(
ActivityManagerService.this,
- mContext, app);
+ getUiContext(), app);
app.waitDialog = d;
app.waitedForDebugger = true;
d.show();
@@ -2071,6 +2119,155 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
} break;
+ case POST_PRIVACY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+
+ ActivityRecord root = (ActivityRecord)msg.obj;
+ ProcessRecord process = root.app;
+ if (process == null) {
+ return;
+ }
+
+ try {
+ Context context = mContext.createPackageContext(process.info.packageName, 0);
+ String text = mContext.getString(R.string.privacy_guard_notification_detail,
+ context.getApplicationInfo().loadLabel(context.getPackageManager()));
+ String title = mContext.getString(R.string.privacy_guard_notification);
+
+ Intent infoIntent = new Intent(Settings.ACTION_APP_OPS_DETAILS_SETTINGS,
+ Uri.fromParts("package", root.packageName, null));
+
+ Notification notification = new Notification();
+ notification.icon = com.android.internal.R.drawable.stat_notify_privacy_guard;
+ notification.when = 0;
+ notification.flags = Notification.FLAG_ONGOING_EVENT;
+ notification.priority = Notification.PRIORITY_LOW;
+ notification.defaults = 0;
+ notification.sound = null;
+ notification.vibrate = null;
+ notification.setLatestEventInfo(mContext,
+ title, text,
+ PendingIntent.getActivityAsUser(mContext, 0, infoIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT, null,
+ new UserHandle(root.userId)));
+
+ try {
+ int[] outId = new int[1];
+ inm.enqueueNotificationWithTag("android", "android", null,
+ R.string.privacy_guard_notification,
+ notification, outId, root.userId);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error showing notification for privacy guard", e);
+ } catch (RemoteException e) {
+ }
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Unable to create context for privacy guard notification", e);
+ }
+ } break;
+ case CANCEL_PRIVACY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+ try {
+ inm.cancelNotificationWithTag("android", null,
+ R.string.privacy_guard_notification, msg.arg1);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error canceling notification for service", e);
+ } catch (RemoteException e) {
+ }
+ } break;
+ case POST_COMPONENT_PROTECTED_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+
+ Intent targetIntent = (Intent) msg.obj;
+ if (targetIntent == null) {
+ return;
+ }
+
+ int targetUserId = targetIntent.getIntExtra(
+ "com.android.settings.PROTECTED_APPS_USER_ID", mCurrentUserId);
+ // Resolve for labels and whatnot
+ ActivityInfo root = resolveActivityInfo(targetIntent, targetIntent.getFlags(),
+ targetUserId);
+
+ if (root == null) {
+ Slog.w(ActivityManagerService.TAG,
+ "No activity info found for given intent " + targetIntent.toString());
+ return;
+ }
+
+ try {
+ Intent protectedAppIntent = new Intent();
+ protectedAppIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ protectedAppIntent.setComponent(
+ new ComponentName("com.android.settings",
+ "com.android.settings.applications.ProtectedAppsActivity"));
+ protectedAppIntent.putExtra(
+ "com.android.settings.PROTECTED_APP_TARGET_INTENT",
+ targetIntent);
+ Context context = mContext.createPackageContext("com.android.settings", 0);
+ String title = mContext.getString(
+ com.android.internal.R.string
+ .notify_package_component_protected_title);
+ String text = mContext.getString(
+ com.android.internal.R.string
+ .notify_package_component_protected_text,
+ root.applicationInfo.loadLabel(mContext.getPackageManager()));
+ Notification notification = new Notification.Builder(context)
+ .setSmallIcon(com.android.internal.R.drawable.stat_notify_protected)
+ .setWhen(0)
+ .setTicker(title)
+ .setColor(mContext.getColor(
+ com.android.internal.R.color
+ .system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentText(text)
+ .setDefaults(Notification.DEFAULT_VIBRATE)
+ .setPriority(Notification.PRIORITY_MAX)
+ .setStyle(new Notification.BigTextStyle().bigText(text))
+ .setContentIntent(PendingIntent.getActivityAsUser(mContext, 0,
+ protectedAppIntent, PendingIntent.FLAG_CANCEL_CURRENT, null,
+ new UserHandle(mCurrentUserId)))
+ .build();
+ try {
+ int[] outId = new int[1];
+ inm.cancelNotificationWithTag("android", null,
+ R.string.notify_package_component_protected_title, msg.arg1);
+ inm.enqueueNotificationWithTag("android", "android", null,
+ R.string.notify_package_component_protected_title,
+ notification, outId, mCurrentUserId);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error showing notification for protected app component", e);
+ } catch (RemoteException e) {
+ }
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Unable to create context for protected app notification", e);
+ }
+ } break;
+ case CANCEL_PROTECTED_APP_NOTIFICATION: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+ try {
+ inm.cancelNotificationWithTag("android", null,
+ R.string.notify_package_component_protected_title, msg.arg1);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error canceling notification for service", e);
+ } catch (RemoteException e) {
+ }
+ } break;
}
}
};
@@ -2619,6 +2816,15 @@ public final class ActivityManagerService extends ActivityManagerNative
-1, Process.SYSTEM_UID, UserHandle.USER_ALL);
}
+ private Context getUiContext() {
+ synchronized (this) {
+ if (mUiContext == null && mBooted) {
+ mUiContext = ThemeUtils.createUiContext(mContext);
+ }
+ return mUiContext != null ? mUiContext : mContext;
+ }
+ }
+
/**
* Initialize the application bind args. These are passed to each
* process when the bindApplication() IPC is sent to the process. They're
@@ -3244,6 +3450,17 @@ public final class ActivityManagerService extends ActivityManagerNative
null /* entryPoint */, null /* entryPointArgs */);
}
+ void launchBoost(int pid, String packageName) {
+ if (mPerf == null) {
+ mPerf = LocalServices.getService(PerformanceManagerInternal.class);
+ if (mPerf == null) {
+ Slog.e(TAG, "PerformanceManager not ready!");
+ return;
+ }
+ }
+ mPerf.launchBoost(pid, packageName);
+ }
+
private final void startProcessLocked(ProcessRecord app, String hostingType,
String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
long startTime = SystemClock.elapsedRealtime();
@@ -3353,6 +3570,13 @@ public final class ActivityManagerService extends ActivityManagerNative
debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
}
+ //Check if zygote should refresh its fonts
+ boolean refreshTheme = false;
+ if (SystemProperties.getBoolean(PROP_REFRESH_THEME, false)) {
+ SystemProperties.set(PROP_REFRESH_THEME, "false");
+ refreshTheme = true;
+ }
+
String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
if (requiredAbi == null) {
requiredAbi = Build.SUPPORTED_ABIS[0];
@@ -3377,7 +3601,7 @@ public final class ActivityManagerService extends ActivityManagerNative
Process.ProcessStartResult startResult = Process.start(entryPoint,
app.processName, uid, uid, gids, debugFlags, mountExternal,
app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
- app.info.dataDir, entryPointArgs);
+ app.info.dataDir, refreshTheme, entryPointArgs);
checkTime(startTime, "startProcess: returned from zygote!");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -3399,6 +3623,9 @@ public final class ActivityManagerService extends ActivityManagerNative
checkTime(startTime, "startProcess: building log message");
StringBuilder buf = mStringBuilder;
buf.setLength(0);
+ if (hostingType.equals("activity")) {
+ launchBoost(startResult.pid, app.processName);
+ }
buf.append("Start proc ");
buf.append(startResult.pid);
buf.append(':');
@@ -3514,6 +3741,15 @@ public final class ActivityManagerService extends ActivityManagerNative
return true;
}
+ /**
+ * If system is power off alarm boot mode, we need to start alarm UI.
+ */
+ void startAlarmActivityLocked() {
+ Intent intent = new Intent(ACTION_POWER_OFF_ALARM);
+ intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
+
private ActivityInfo resolveActivityInfo(Intent intent, int flags, int userId) {
ActivityInfo ai = null;
ComponentName comp = intent.getComponent();
@@ -3897,7 +4133,8 @@ public final class ActivityManagerService extends ActivityManagerNative
if (sourceRecord == null) {
throw new SecurityException("Called with bad activity token: " + resultTo);
}
- if (!sourceRecord.info.packageName.equals("android")) {
+ if (!sourceRecord.info.packageName.equals("android") &&
+ !sourceRecord.info.packageName.equals("org.cyanogenmod.resolver")) {
throw new SecurityException(
"Must be called from an activity that is declared in the android package");
}
@@ -4103,8 +4340,8 @@ public final class ActivityManagerService extends ActivityManagerNative
if (debug) {
Slog.v(TAG, "Next matching activity: found current " + r.packageName
+ "/" + r.info.name);
- Slog.v(TAG, "Next matching activity: next is " + aInfo.packageName
- + "/" + aInfo.name);
+ Slog.v(TAG, "Next matching activity: next is " + ((aInfo == null)
+ ? "null" : aInfo.packageName + "/" + aInfo.name));
}
break;
}
@@ -4188,6 +4425,9 @@ public final class ActivityManagerService extends ActivityManagerNative
callingUid = task.mCallingUid;
callingPackage = task.mCallingPackage;
intent = task.intent;
+ if (task.origActivity != null) {
+ intent.setComponent(task.origActivity);
+ }
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
userId = task.userId;
}
@@ -4724,7 +4964,12 @@ public final class ActivityManagerService extends ActivityManagerNative
app.thread.asBinder() == thread.asBinder()) {
boolean doLowMem = app.instrumentationClass == null;
boolean doOomAdj = doLowMem;
+ boolean homeRestart = false;
if (!app.killedByAm) {
+ if (mHomeProcessName != null && app.processName.equals(mHomeProcessName)) {
+ mHomeKilled = true;
+ homeRestart = true;
+ }
Slog.i(TAG, "Process " + app.processName + " (pid " + pid
+ ") has died");
mAllowLowerMemLevel = true;
@@ -4745,6 +4990,13 @@ public final class ActivityManagerService extends ActivityManagerNative
if (doLowMem) {
doLowMemReportIfNeededLocked(app);
}
+ if (mHomeKilled && homeRestart) {
+ Intent intent = getHomeIntent();
+ ActivityInfo aInfo = mStackSupervisor.resolveActivity(intent, null, 0, null, 0);
+ startProcessLocked(aInfo.processName, aInfo.applicationInfo, true, 0,
+ "activity", null, false, false, true);
+ homeRestart = false;
+ }
} else if (app.pid != pid) {
// A new process has already been started.
Slog.i(TAG, "Process " + app.processName + " (pid " + pid
@@ -4981,6 +5233,9 @@ public final class ActivityManagerService extends ActivityManagerNative
} else if (app.crashing) {
Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
return;
+ } else if (app.killedByAm) {
+ Slog.i(TAG, "App already killed by AM skipping ANR: " + app + " " + annotation);
+ return;
}
// In case we come through here for the same app before completing
@@ -5095,6 +5350,20 @@ public final class ActivityManagerService extends ActivityManagerNative
annotation != null ? "ANR " + annotation : "ANR",
info.toString());
+ //Set the trace file name to app name + current date format to avoid overrinding trace file
+ String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
+ if (tracesPath != null && tracesPath.length() != 0) {
+ File traceRenameFile = new File(tracesPath);
+ String newTracesPath;
+ int lpos = tracesPath.lastIndexOf (".");
+ if (-1 != lpos)
+ newTracesPath = tracesPath.substring (0, lpos) + "_" + app.processName + "_" + mTraceDateFormat.format(new Date()) + tracesPath.substring (lpos);
+ else
+ newTracesPath = tracesPath + "_" + app.processName;
+
+ traceRenameFile.renameTo(new File(newTracesPath));
+ }
+
// Bring up the infamous App Not Responding dialog
Message msg = Message.obtain();
HashMap<String, Object> map = new HashMap<String, Object>();
@@ -5117,7 +5386,7 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void run() {
synchronized (ActivityManagerService.this) {
- final Dialog d = new LaunchWarningWindow(mContext, cur, next);
+ final Dialog d = new LaunchWarningWindow(getUiContext(), cur, next);
d.show();
mUiHandler.postDelayed(new Runnable() {
@Override
@@ -6300,12 +6569,14 @@ public final class ActivityManagerService extends ActivityManagerNative
}
@Override
- public void showBootMessage(final CharSequence msg, final boolean always) {
+ public void updateBootProgress(final int stage, final ApplicationInfo optimizedApp,
+ final int currentAppPos, final int totalAppCount, final boolean always) {
if (Binder.getCallingUid() != Process.myUid()) {
// These days only the core system can call this, so apps can't get in
// the way of what we show about running them.
}
- mWindowManager.showBootMessage(msg, always);
+ mWindowManager.updateBootProgress(stage, optimizedApp,
+ currentAppPos, totalAppCount, always);
}
@Override
@@ -6328,14 +6599,16 @@ public final class ActivityManagerService extends ActivityManagerNative
@Override
public void keyguardGoingAway(boolean disableWindowAnimations,
- boolean keyguardGoingToNotificationShade) {
+ boolean keyguardGoingToNotificationShade,
+ boolean keyguardShowingMedia) {
enforceNotIsolatedCaller("keyguardGoingAway");
final long token = Binder.clearCallingIdentity();
try {
synchronized (this) {
if (DEBUG_LOCKSCREEN) logLockScreen("");
mWindowManager.keyguardGoingAway(disableWindowAnimations,
- keyguardGoingToNotificationShade);
+ keyguardGoingToNotificationShade,
+ keyguardShowingMedia);
if (mLockScreenShown == LOCK_SCREEN_SHOWN) {
mLockScreenShown = LOCK_SCREEN_HIDDEN;
updateSleepIfNeededLocked();
@@ -6401,6 +6674,13 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}, dumpheapFilter);
+ ThemeUtils.registerThemeChangeReceiver(mContext, new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mUiContext = null;
+ }
+ });
+
// Let system services know.
mSystemServiceManager.startBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -6452,7 +6732,7 @@ public final class ActivityManagerService extends ActivityManagerNative
},
0, null, null,
new String[] {android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
- AppOpsManager.OP_NONE, null, true, false,
+ AppOpsManager.OP_BOOT_COMPLETED, null, true, false,
MY_PID, Process.SYSTEM_UID, userId);
}
}
@@ -6592,6 +6872,18 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ public String getCallingPackageForBroadcast(boolean foreground) {
+ BroadcastQueue queue = foreground ? mFgBroadcastQueue : mBgBroadcastQueue;
+ BroadcastRecord r = queue.getProcessingBroadcast();
+ if (r != null) {
+ return r.callerPackage;
+ } else {
+ Log.e(TAG, "Broadcast sender is only retrievable in the onReceive");
+ }
+ return null;
+ }
+
+
private ActivityRecord getCallingRecordLocked(IBinder token) {
ActivityRecord r = ActivityRecord.isInStackLocked(token);
if (r == null) {
@@ -8349,6 +8641,14 @@ public final class ActivityManagerService extends ActivityManagerNative
return list;
}
+ @Override
+ public boolean isPackageInForeground(String packageName) {
+ synchronized (this) {
+ ActivityRecord activity = mStackSupervisor.topRunningActivityLocked();
+ return activity != null && activity.packageName.equals(packageName);
+ }
+ }
+
/**
* Creates a new RecentTaskInfo from a TaskRecord.
*/
@@ -8783,6 +9083,27 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ private void cleanupProtectedComponentTasksLocked() {
+ for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
+ TaskRecord tr = mRecentTasks.get(i);
+
+ for (int j = tr.mActivities.size() - 1; j >= 0; j--) {
+ ActivityRecord r = tr.mActivities.get(j);
+ ComponentName cn = r.realActivity;
+
+ try {
+ boolean isProtected = AppGlobals.getPackageManager()
+ .isComponentProtected(null, -1, cn, getCurrentUserIdLocked());
+ if (isProtected) {
+ removeTaskByIdLocked(tr.taskId, false);
+ }
+ } catch (RemoteException re) {
+
+ }
+ }
+ }
+ }
+
/**
* Removes the task with the specified task id.
*
@@ -9189,6 +9510,10 @@ public final class ActivityManagerService extends ActivityManagerNative
mStackSupervisor.setLockTaskModeLocked(null, ActivityManager.LOCK_TASK_MODE_NONE,
"stopLockTask", true);
}
+ TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+ if (tm != null) {
+ tm.showInCallScreen(false);
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -9458,6 +9783,14 @@ public final class ActivityManagerService extends ActivityManagerNative
if (conn.stableCount == 0 && conn.unstableCount == 0) {
cpr.connections.remove(conn);
conn.client.conProviders.remove(conn);
+ if (conn.client.setProcState < ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ // The client is more important than last activity -- note the time this
+ // is happening, so we keep the old provider process around a bit as last
+ // activity to avoid thrashing it.
+ if (cpr.proc != null) {
+ cpr.proc.lastProviderTime = SystemClock.uptimeMillis();
+ }
+ }
stopAssociationLocked(conn.client.uid, conn.client.processName, cpr.uid, cpr.name);
return true;
}
@@ -9546,6 +9879,7 @@ public final class ActivityManagerService extends ActivityManagerNative
checkTime(startTime, "getContentProviderImpl: incProviderCountLocked");
+ boolean importantCaller = false;
// In this case the provider instance already exists, so we can
// return it right away.
conn = incProviderCountLocked(r, cpr, token, stable);
@@ -9555,6 +9889,7 @@ public final class ActivityManagerService extends ActivityManagerNative
// make sure to count it as being accessed and thus
// back up on the LRU list. This is good because
// content providers are often expensive to start.
+ importantCaller = true;
checkTime(startTime, "getContentProviderImpl: before updateLruProcess");
updateLruProcessLocked(cpr.proc, false, null);
checkTime(startTime, "getContentProviderImpl: after updateLruProcess");
@@ -9567,36 +9902,37 @@ public final class ActivityManagerService extends ActivityManagerNative
"com.android.providers.calendar/.CalendarProvider2")) {
Slog.v(TAG, "****************** KILLING "
+ cpr.name.flattenToShortString());
- Process.killProcess(cpr.proc.pid);
+ cpr.proc.kill("test killing calendar provider", true);
}
}
- checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
- boolean success = updateOomAdjLocked(cpr.proc);
- maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
- checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
- if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "Adjust success: " + success);
- // NOTE: there is still a race here where a signal could be
- // pending on the process even though we managed to update its
- // adj level. Not sure what to do about this, but at least
- // the race is now smaller.
+
+ boolean success = !cpr.proc.killedByAm;
+ if (success) {
+ checkTime(startTime, "getContentProviderImpl: before updateOomAdj");
+ success = updateOomAdjLocked(cpr.proc);
+ maybeUpdateProviderUsageStatsLocked(r, cpr.info.packageName, name);
+ checkTime(startTime, "getContentProviderImpl: after updateOomAdj");
+ if (DEBUG_PROVIDER) Slog.i(TAG_PROVIDER, "Adjust success: " + success);
+ }
+
+ // There is still a race here where a signal could be pending on
+ // the process even though we managed to update its adj level.
+ // We check this case for perceptible app but exclude persistent
+ // because it should not be killed normally.
+ if (success && importantCaller && r != null && !r.persistent
+ && r.pid != cpr.proc.pid && !cpr.proc.persistent) {
+ success = ProcessList.isAlive(cpr.proc.pid, true);
+ }
+
if (!success) {
- // Uh oh... it looks like the provider's process
- // has been killed on us. We need to wait for a new
- // process to be started, and make sure its death
- // doesn't kill our process.
+ // It looks like the provider's process has been killed.
+ // We need to wait for a new process to be started,
+ // and make sure its death doesn't kill caller process.
Slog.i(TAG, "Existing provider " + cpr.name.flattenToShortString()
- + " is crashing; detaching " + r);
- boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
- checkTime(startTime, "getContentProviderImpl: before appDied");
- appDiedLocked(cpr.proc);
- checkTime(startTime, "getContentProviderImpl: after appDied");
- if (!lastRef) {
- // This wasn't the last ref our process had on
- // the provider... we have now been killed, bail.
- return null;
- }
- providerRunning = false;
- conn = null;
+ + " is gone; waiting it to restart for " + r);
+ cpr.provider = null;
+ cpr.launchingApp = cpr.proc;
+ mLaunchingProviders.add(cpr);
}
}
@@ -10262,7 +10598,13 @@ public final class ActivityManagerService extends ActivityManagerNative
if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
app.persistent = true;
- app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
+
+ // The Adj score defines an order of processes to be killed.
+ // If a process is shared by multiple apps, maxAdj must be set by the highest
+ // prioritized app to avoid being killed.
+ if (app.maxAdj >= ProcessList.PERSISTENT_PROC_ADJ) {
+ app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
+ }
}
if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
mPersistentStartingProcesses.add(app);
@@ -10700,6 +11042,7 @@ public final class ActivityManagerService extends ActivityManagerNative
public void requestBugReport() {
enforceCallingPermission(android.Manifest.permission.DUMP, "requestBugReport");
+ mContext.sendBroadcast(new Intent("android.intent.action.BUGREPORT_STARTED"));
SystemProperties.set("ctl.start", "bugreport");
}
@@ -10777,9 +11120,15 @@ public final class ActivityManagerService extends ActivityManagerNative
return true;
}
}
+ final int anrPid = proc.pid;
mHandler.post(new Runnable() {
@Override
public void run() {
+ if (anrPid != proc.pid) {
+ Slog.i(TAG, "Ignoring stale ANR (occurred in " + anrPid +
+ ", but current pid is " + proc.pid + ")");
+ return;
+ }
appNotResponding(proc, activity, parent, aboveSystem, annotation);
}
});
@@ -10999,6 +11348,18 @@ public final class ActivityManagerService extends ActivityManagerNative
enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
"registerProcessObserver()");
synchronized (this) {
+ for (Integer key : mForegroundActivitiesList.keySet()) {
+ Object[] o = mForegroundActivitiesList.get(key);
+ if (o.length == 3) {
+ try {
+ observer.onForegroundActivitiesChanged((int) o[0],
+ (int) o[1], (boolean) o[2]);
+ } catch (RemoteException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
mProcessObservers.register(observer);
}
}
@@ -11637,8 +11998,8 @@ public final class ActivityManagerService extends ActivityManagerNative
intent.setComponent(comp);
doneReceivers.add(comp);
lastRi = curRi;
- CharSequence label = ai.loadLabel(mContext.getPackageManager());
- showBootMessage(mContext.getString(R.string.android_preparing_apk, label), false);
+ updateBootProgress(IActivityManager.BOOT_STAGE_PREPARING_APPS,
+ ai.applicationInfo, 0, 0, false);
}
Slog.i(TAG, "Pre-boot of " + intent.getComponent().toShortString()
+ " for user " + users[curUser]);
@@ -11746,6 +12107,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mRecentTasks.clear();
mRecentTasks.addAll(mTaskPersister.restoreTasksLocked());
+ cleanupProtectedComponentTasksLocked();
mRecentTasks.cleanupLocked(UserHandle.USER_ALL);
mTaskPersister.startPersisting();
@@ -11760,9 +12122,8 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (ActivityManagerService.this) {
mDidUpdate = true;
}
- showBootMessage(mContext.getText(
- R.string.android_upgrading_complete),
- false);
+ updateBootProgress(IActivityManager.BOOT_STAGE_COMPLETE,
+ null, 0, 0, false);
writeLastDonePreBootReceivers(doneReceivers);
systemReady(goingCallback);
}
@@ -11887,6 +12248,12 @@ public final class ActivityManagerService extends ActivityManagerNative
mBooting = true;
startHomeActivityLocked(mCurrentUserId, "systemReady");
+ // start the power off alarm by boot mode
+ boolean isAlarmBoot = SystemProperties.getBoolean("ro.alarm_boot", false);
+ if (isAlarmBoot) {
+ startAlarmActivityLocked();
+ }
+
try {
if (AppGlobals.getPackageManager().hasSystemUidErrors()) {
Slog.e(TAG, "UIDs on the system are inconsistent, you need to wipe your"
@@ -12002,6 +12369,28 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ private void sendAppFailureBroadcast(String pkgName) {
+ Intent intent = new Intent(cyanogenmod.content.Intent.ACTION_APP_FAILURE,
+ (pkgName != null)? Uri.fromParts("package", pkgName, null) : null);
+ mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ }
+
+ /**
+ * A possible theme crash is one that throws one of the following exceptions
+ * {@link android.content.res.Resources.NotFoundException}
+ * {@link android.view.InflateException}
+ *
+ * @param exceptionClassName
+ * @return True if exceptionClassName is one of the above exceptions
+ */
+ private boolean isPossibleThemeCrash(String exceptionClassName) {
+ if (Resources.NotFoundException.class.getName().equals(exceptionClassName) ||
+ InflateException.class.getName().equals(exceptionClassName)) {
+ return true;
+ }
+ return false;
+ }
+
private boolean handleAppCrashLocked(ProcessRecord app, String reason,
String shortMsg, String longMsg, String stackTrace) {
long now = SystemClock.uptimeMillis();
@@ -12012,6 +12401,9 @@ public final class ActivityManagerService extends ActivityManagerNative
} else {
crashTime = null;
}
+
+ if (isPossibleThemeCrash(shortMsg)) sendAppFailureBroadcast(app.info.packageName);
+
if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) {
// This process loses!
Slog.w(TAG, "Process " + app.info.processName
@@ -12221,6 +12613,7 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized (sb) {
bufferWasEmpty = sb.length() == 0;
appendDropBoxProcessHeaders(process, processName, sb);
+ sb.append("CM Version: ").append(cyanogenmod.os.Build.CYANOGENMOD_VERSION).append("\n");
sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
sb.append("System-App: ").append(isSystemApp).append("\n");
sb.append("Uptime-Millis: ").append(info.violationUptimeMillis).append("\n");
@@ -12490,6 +12883,7 @@ public final class ActivityManagerService extends ActivityManagerNative
if (subject != null) {
sb.append("Subject: ").append(subject).append("\n");
}
+ sb.append("CM Version: ").append(cyanogenmod.os.Build.CYANOGENMOD_VERSION).append("\n");
sb.append("Build: ").append(Build.FINGERPRINT).append("\n");
if (Debug.isDebuggerConnected()) {
sb.append("Debugger: Connected\n");
@@ -12811,6 +13205,9 @@ public final class ActivityManagerService extends ActivityManagerNative
|| (!allUids && app.uid != callingUid)) {
continue;
}
+ if (app.processName.equals("system")) {
+ continue;
+ }
if ((app.thread != null) && (!app.crashing && !app.notResponding)) {
// Generate process state info for running application
ActivityManager.RunningAppProcessInfo currApp =
@@ -15085,6 +15482,10 @@ public final class ActivityManagerService extends ActivityManagerNative
pw.print(" Lost RAM: "); pw.print(memInfo.getTotalSizeKb()
- totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- memInfo.getKernelUsedSizeKb()); pw.println(" kB");
+ } else {
+ pw.print("lostram,"); pw.println(memInfo.getTotalSizeKb()
+ - totalPss - memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
+ - memInfo.getKernelUsedSizeKb());
}
if (!brief) {
if (memInfo.getZramTotalSizeKb() != 0) {
@@ -15665,6 +16066,7 @@ public final class ActivityManagerService extends ActivityManagerNative
mProcessesOnHold.remove(app);
if (app == mHomeProcess) {
+ mHomeProcessName = mHomeProcess.processName;
mHomeProcess = null;
}
if (app == mPreviousProcess) {
@@ -16051,11 +16453,22 @@ public final class ActivityManagerService extends ActivityManagerNative
// Cause the target app to be launched if necessary and its backup agent
// instantiated. The backup agent will invoke backupAgentCreated() on the
// activity manager to announce its creation.
- public boolean bindBackupAgent(ApplicationInfo app, int backupMode) {
- if (DEBUG_BACKUP) Slog.v(TAG_BACKUP,
- "bindBackupAgent: app=" + app + " mode=" + backupMode);
+ public boolean bindBackupAgent(String packageName, int backupMode, int userId) {
+ if (DEBUG_BACKUP) Slog.v(TAG, "bindBackupAgent: app=" + packageName + " mode=" + backupMode);
enforceCallingPermission("android.permission.CONFIRM_FULL_BACKUP", "bindBackupAgent");
+ IPackageManager pm = AppGlobals.getPackageManager();
+ ApplicationInfo app = null;
+ try {
+ app = pm.getApplicationInfo(packageName, 0, userId);
+ } catch (RemoteException e) {
+ // can't happen; package manager is process-local
+ }
+ if (app == null) {
+ Slog.w(TAG, "Unable to bind backup agent for " + packageName);
+ return false;
+ }
+
synchronized(this) {
// !!! TODO: currently no check here that we're already bound
BatteryStatsImpl.Uid.Pkg.Serv ss = null;
@@ -16580,7 +16993,8 @@ public final class ActivityManagerService extends ActivityManagerNative
} else if (callerApp == null || !callerApp.persistent) {
try {
if (AppGlobals.getPackageManager().isProtectedBroadcast(
- intent.getAction())) {
+ intent.getAction()) && !AppGlobals.getPackageManager()
+ .isProtectedBroadcastAllowed(intent.getAction(), callingUid)) {
String msg = "Permission Denial: not allowed to send broadcast "
+ intent.getAction() + " from pid="
+ callingPid + ", uid=" + callingUid;
@@ -16751,6 +17165,14 @@ public final class ActivityManagerService extends ActivityManagerNative
ProxyInfo proxy = intent.getParcelableExtra(Proxy.EXTRA_PROXY_INFO);
mHandler.sendMessage(mHandler.obtainMessage(UPDATE_HTTP_PROXY_MSG, proxy));
break;
+ case cyanogenmod.content.Intent.ACTION_PROTECTED_CHANGED:
+ final boolean state =
+ intent.getBooleanExtra(
+ cyanogenmod.content.Intent.EXTRA_PROTECTED_STATE, false);
+ if (state == PackageManager.COMPONENT_PROTECTED_STATUS) {
+ cleanupProtectedComponentTasksLocked();
+ }
+ break;
}
}
@@ -17320,6 +17742,9 @@ public final class ActivityManagerService extends ActivityManagerNative
synchronized(this) {
ci = new Configuration(mConfiguration);
ci.userSetLocale = false;
+ if (ci.themeConfig == null) {
+ ci.themeConfig = ThemeConfig.getBootTheme(mContext.getContentResolver());
+ }
}
return ci;
}
@@ -17409,6 +17834,16 @@ public final class ActivityManagerService extends ActivityManagerNative
values.locale));
}
+ if (values.themeConfig != null) {
+ saveThemeResourceLocked(values.themeConfig,
+ !values.themeConfig.equals(mConfiguration.themeConfig));
+ }
+
+ if ((changes & ActivityInfo.CONFIG_THEME_FONT) != 0) {
+ // Notify zygote that themes need a refresh
+ SystemProperties.set(PROP_REFRESH_THEME, "1");
+ }
+
mConfigurationSeq++;
if (mConfigurationSeq <= 0) {
mConfigurationSeq = 1;
@@ -17464,6 +17899,10 @@ public final class ActivityManagerService extends ActivityManagerNative
null, AppOpsManager.OP_NONE, null, false, false,
MY_PID, Process.SYSTEM_UID, UserHandle.USER_ALL);
if ((changes&ActivityInfo.CONFIG_LOCALE) != 0) {
+ // if locale changed, time format may have changed
+ final int is24Hour = android.text.format.DateFormat.is24HourFormat(mContext) ? 1 : 0;
+ mHandler.sendMessage(mHandler.obtainMessage(UPDATE_TIME, is24Hour, 0));
+ // now send general broadcast
intent = new Intent(Intent.ACTION_LOCALE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
if (!mProcessesReady) {
@@ -17561,6 +18000,14 @@ public final class ActivityManagerService extends ActivityManagerNative
return srec.launchedFromPackage;
}
+ private void saveThemeResourceLocked(ThemeConfig t, boolean isDiff){
+ if(isDiff) {
+ Settings.Secure.putStringForUser(mContext.getContentResolver(),
+ Configuration.THEME_PKG_CONFIGURATION_PERSISTENCE_PROPERTY, t.toJson(),
+ UserHandle.USER_CURRENT);
+ }
+ }
+
// =========================================================
// LIFETIME MANAGEMENT
// =========================================================
@@ -17720,6 +18167,10 @@ public final class ActivityManagerService extends ActivityManagerNative
app.adjType = "top-activity";
foregroundActivities = true;
procState = PROCESS_STATE_TOP;
+ if(app == mHomeProcess) {
+ mHomeKilled = false;
+ mHomeProcessName = mHomeProcess.processName;
+ }
} else if (app.instrumentationClass != null) {
// Don't want to kill running instrumentation.
adj = ProcessList.FOREGROUND_APP_ADJ;
@@ -17755,6 +18206,14 @@ public final class ActivityManagerService extends ActivityManagerNative
app.cached = true;
app.empty = true;
app.adjType = "cch-empty";
+
+ if (mHomeKilled && app.processName.equals(mHomeProcessName)) {
+ adj = ProcessList.PERSISTENT_PROC_ADJ;
+ schedGroup = Process.THREAD_GROUP_DEFAULT;
+ app.cached = false;
+ app.empty = false;
+ app.adjType = "top-activity";
+ }
}
// Examine all activities if not already foreground.
@@ -18212,6 +18671,18 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
+ if (app.lastProviderTime > 0 && (app.lastProviderTime+CONTENT_PROVIDER_RETAIN_TIME) > now) {
+ if (adj > ProcessList.PREVIOUS_APP_ADJ) {
+ adj = ProcessList.PREVIOUS_APP_ADJ;
+ schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+ app.cached = false;
+ app.adjType = "provider";
+ }
+ if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+ procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+ }
+ }
+
if (mayBeTop && procState > ActivityManager.PROCESS_STATE_TOP) {
// A client of one of our services or providers is in the top state. We
// *may* want to be in the top state, but not if we are already running in
@@ -18678,6 +19149,18 @@ public final class ActivityManagerService extends ActivityManagerNative
boolean success = true;
if (app.curRawAdj != app.setRawAdj) {
+ String seempStr = "app_uid=" + app.uid
+ + ",app_pid=" + app.pid + ",oom_adj=" + app.curAdj
+ + ",setAdj=" + app.setAdj + ",hasShownUi=" + (app.hasShownUi ? 1 : 0)
+ + ",cached=" + (app.cached ? 1 : 0)
+ + ",fA=" + (app.foregroundActivities ? 1 : 0)
+ + ",fS=" + (app.foregroundServices ? 1 : 0)
+ + ",systemNoUi=" + (app.systemNoUi ? 1 : 0)
+ + ",curSchedGroup=" + app.curSchedGroup
+ + ",curProcState=" + app.curProcState + ",setProcState=" + app.setProcState
+ + ",killed=" + (app.killed ? 1 : 0) + ",killedByAm=" + (app.killedByAm ? 1 : 0)
+ + ",debugging=" + (app.debugging ? 1 : 0);
+ android.util.SeempLog.record_str(385, seempStr);
app.setRawAdj = app.curRawAdj;
}
@@ -18853,6 +19336,12 @@ public final class ActivityManagerService extends ActivityManagerNative
item.changes |= changes;
item.processState = app.repProcState;
item.foregroundActivities = app.repForegroundActivities;
+ if (item.foregroundActivities) {
+ Object[] o = new Object[]{item.pid, item.uid, item.foregroundActivities};
+ mForegroundActivitiesList.put(item.pid, o);
+ } else if (!item.foregroundActivities && mForegroundActivitiesList.get(item.pid) != null) {
+ mForegroundActivitiesList.remove(item.pid);
+ }
if (DEBUG_PROCESS_OBSERVERS) Slog.i(TAG_PROCESS_OBSERVERS,
"Item " + Integer.toHexString(System.identityHashCode(item))
+ " " + app.toShortString() + ": changes=" + item.changes
@@ -19132,8 +19621,39 @@ public final class ActivityManagerService extends ActivityManagerNative
int nextCachedAdj = curCachedAdj+1;
int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
int nextEmptyAdj = curEmptyAdj+2;
+ ProcessRecord selectedAppRecord = null;
+ long serviceLastActivity = 0;
+ int numBServices = 0;
for (int i=N-1; i>=0; i--) {
ProcessRecord app = mLruProcesses.get(i);
+ if (mEnableBServicePropagation && app.serviceb
+ && (app.curAdj == ProcessList.SERVICE_B_ADJ)) {
+ numBServices++;
+ for (int s = app.services.size() - 1; s >= 0; s--) {
+ ServiceRecord sr = app.services.valueAt(s);
+ if (DEBUG_OOM_ADJ) Slog.d(TAG,"app.processName = " + app.processName
+ + " serviceb = " + app.serviceb + " s = " + s + " sr.lastActivity = "
+ + sr.lastActivity + " packageName = " + sr.packageName
+ + " processName = " + sr.processName);
+ if (SystemClock.uptimeMillis() - sr.lastActivity
+ < mMinBServiceAgingTime) {
+ if (DEBUG_OOM_ADJ) {
+ Slog.d(TAG,"Not aged enough!!!");
+ }
+ continue;
+ }
+ if (serviceLastActivity == 0) {
+ serviceLastActivity = sr.lastActivity;
+ selectedAppRecord = app;
+ } else if (sr.lastActivity < serviceLastActivity) {
+ serviceLastActivity = sr.lastActivity;
+ selectedAppRecord = app;
+ }
+ }
+ }
+ if (DEBUG_OOM_ADJ && selectedAppRecord != null) Slog.d(TAG,
+ "Identified app.processName = " + selectedAppRecord.processName
+ + " app.pid = " + selectedAppRecord.pid);
if (!app.killedByAm && app.thread != null) {
app.procStateChanged = false;
computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
@@ -19242,6 +19762,14 @@ public final class ActivityManagerService extends ActivityManagerNative
}
}
}
+ if ((numBServices > mBServiceAppThreshold) && (true == mAllowLowerMemLevel)
+ && (selectedAppRecord != null)) {
+ ProcessList.setOomAdj(selectedAppRecord.pid, selectedAppRecord.info.uid,
+ ProcessList.CACHED_APP_MAX_ADJ);
+ selectedAppRecord.setAdj = selectedAppRecord.curAdj;
+ if (DEBUG_OOM_ADJ) Slog.d(TAG,"app.processName = " + selectedAppRecord.processName
+ + " app.pid = " + selectedAppRecord.pid + " is moved to higher adj");
+ }
mNumServiceProcs = mNewNumServiceProcs;
@@ -20258,7 +20786,7 @@ public final class ActivityManagerService extends ActivityManagerNative
broadcastIntentLocked(null, null, intent,
null, null, 0, null, null,
new String[] {android.Manifest.permission.RECEIVE_BOOT_COMPLETED},
- AppOpsManager.OP_NONE, null, true, false, MY_PID, Process.SYSTEM_UID,
+ AppOpsManager.OP_BOOT_COMPLETED, null, true, false, MY_PID, Process.SYSTEM_UID,
userId);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 6e34876..f676db9 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -25,6 +25,7 @@ import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
import static com.android.server.am.ActivityStackSupervisor.HOME_STACK_ID;
+import android.text.TextUtils;
import android.util.ArraySet;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
@@ -40,6 +41,7 @@ import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AppGlobals;
+import android.app.AppOpsManager;
import android.app.IActivityController;
import android.app.ResultInfo;
import android.app.ActivityManager.RunningTaskInfo;
@@ -67,6 +69,11 @@ import android.util.EventLog;
import android.util.Slog;
import android.view.Display;
+import com.android.server.LocalServices;
+
+import cyanogenmod.power.PerformanceManagerInternal;
+import cyanogenmod.providers.CMSettings;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
@@ -264,6 +271,10 @@ final class ActivityStack {
}
}
+ private final PerformanceManagerInternal mPerf;
+ private static final String PROTECTED_APPS_TARGET_VALIDATION_COMPONENT =
+ "com.android.settings/com.android.settings.applications.ProtectedAppsActivity";
+
final Handler mHandler;
final class ActivityStackHandler extends Handler {
@@ -362,6 +373,7 @@ final class ActivityStack {
mCurrentUser = mService.mCurrentUserId;
mRecentTasks = recentTasks;
mOverrideConfig = Configuration.EMPTY;
+ mPerf = LocalServices.getService(PerformanceManagerInternal.class);
}
boolean okToShowLocked(ActivityRecord r) {
@@ -941,10 +953,13 @@ final class ActivityStack {
r.userId, System.identityHashCode(r), r.shortComponentName,
mPausingActivity != null
? mPausingActivity.shortComponentName : "(none)");
- if (r.finishing && r.state == ActivityState.PAUSING) {
- if (DEBUG_PAUSE) Slog.v(TAG,
- "Executing finish of failed to pause activity: " + r);
- finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false);
+ if (r.state == ActivityState.PAUSING) {
+ r.state = ActivityState.PAUSED;
+ if (r.finishing) {
+ if (DEBUG_PAUSE) Slog.v(TAG,
+ "Executing finish of failed to pause activity: " + r);
+ finishCurrentActivityLocked(r, FINISH_AFTER_VISIBLE, false);
+ }
}
}
}
@@ -1130,6 +1145,9 @@ final class ActivityStack {
// When resuming an activity, require it to call requestVisibleBehind() again.
mActivityContainer.mActivityDisplay.setVisibleBehindActivity(null);
}
+
+ updatePrivacyGuardNotificationLocked(next);
+ updateProtectedAppNotificationLocked(next);
}
private void setVisible(ActivityRecord r, boolean visible) {
@@ -1682,6 +1700,11 @@ final class ActivityStack {
if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Resuming " + next);
+ // Some activities may want to alter the system power management
+ if (mStackSupervisor.mService.mPerf != null) {
+ mStackSupervisor.mService.mPerf.activityResumed(next.intent);
+ }
+
// If we are currently pausing an activity, then don't do anything
// until that is done.
if (!mStackSupervisor.allPausedActivitiesComplete()) {
@@ -1814,6 +1837,11 @@ final class ActivityStack {
mWindowManager.prepareAppTransition(prev.task == next.task
? AppTransition.TRANSIT_ACTIVITY_CLOSE
: AppTransition.TRANSIT_TASK_CLOSE, false);
+ if (prev.task != next.task) {
+ if (mStackSupervisor.mService.mPerf != null) {
+ mStackSupervisor.mService.mPerf.cpuBoost(2000 * 1000);
+ }
+ }
}
mWindowManager.setAppWillBeHidden(prev.appToken);
mWindowManager.setAppVisibility(prev.appToken, false);
@@ -1829,6 +1857,11 @@ final class ActivityStack {
: next.mLaunchTaskBehind
? AppTransition.TRANSIT_TASK_OPEN_BEHIND
: AppTransition.TRANSIT_TASK_OPEN, false);
+ if (prev.task != next.task) {
+ if (mStackSupervisor.mService.mPerf != null) {
+ mStackSupervisor.mService.mPerf.cpuBoost(2000 * 1000);
+ }
+ }
}
}
if (false) {
@@ -2071,6 +2104,42 @@ final class ActivityStack {
updateTaskMovement(task, true);
}
+ private final void updateProtectedAppNotificationLocked(ActivityRecord next) {
+ ComponentName componentName = ComponentName.unflattenFromString(next.shortComponentName);
+ if (TextUtils.equals(componentName.flattenToString(),
+ PROTECTED_APPS_TARGET_VALIDATION_COMPONENT)) {
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.CANCEL_PROTECTED_APP_NOTIFICATION, next);
+ msg.sendToTarget();
+ }
+ }
+
+ private final void updatePrivacyGuardNotificationLocked(ActivityRecord next) {
+
+ String privacyGuardPackageName = mStackSupervisor.mPrivacyGuardPackageName;
+ if (privacyGuardPackageName != null && privacyGuardPackageName.equals(next.packageName)) {
+ return;
+ }
+
+ boolean privacy = mService.mAppOpsService.getPrivacyGuardSettingForPackage(
+ next.app.uid, next.packageName);
+ boolean privacyNotification = (CMSettings.Secure.getInt(
+ mService.mContext.getContentResolver(),
+ CMSettings.Secure.PRIVACY_GUARD_NOTIFICATION, 1) == 1);
+
+ if (privacyGuardPackageName != null && !privacy) {
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.CANCEL_PRIVACY_NOTIFICATION_MSG, next.userId);
+ msg.sendToTarget();
+ mStackSupervisor.mPrivacyGuardPackageName = null;
+ } else if (privacy && privacyNotification) {
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.POST_PRIVACY_NOTIFICATION_MSG, next);
+ msg.sendToTarget();
+ mStackSupervisor.mPrivacyGuardPackageName = next.packageName;
+ }
+ }
+
final void startActivityLocked(ActivityRecord r, boolean newTask,
boolean doResume, boolean keepCurTransition, Bundle options) {
TaskRecord rTask = r.task;
@@ -2620,7 +2689,23 @@ final class ActivityStack {
final String myReason = reason + " adjustFocus";
if (next != r) {
final TaskRecord task = r.task;
- if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) {
+ boolean adjust = false;
+ if ((next == null || next.task != task) && r.frontOfTask) {
+ if (task.isOverHomeStack() && task == topTask()) {
+ adjust = true;
+ } else {
+ for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+ final TaskRecord tr = mTaskHistory.get(taskNdx);
+ if (tr.getTopActivity() != null) {
+ break;
+ } else if (tr.isOverHomeStack()) {
+ adjust = true;
+ break;
+ }
+ }
+ }
+ }
+ if (adjust) {
// For non-fullscreen stack, we want to move the focus to the next visible
// stack to prevent the home screen from moving to the top and obscuring
// other visible stacks.
@@ -4145,7 +4230,8 @@ final class ActivityStack {
if (focusedStack && topTask) {
// Give the latest time to ensure foreground task can be sorted
// at the first, because lastActiveTime of creating task is 0.
- ci.lastActiveTime = System.currentTimeMillis();
+ // Only do this if the clock didn't run backwards, though.
+ ci.lastActiveTime = Math.max(ci.lastActiveTime, System.currentTimeMillis());
topTask = false;
}
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 17a86ca..bde32e7 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -101,12 +101,12 @@ import android.util.ArraySet;
import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
-
import android.util.SparseIntArray;
import android.view.Display;
import android.view.DisplayInfo;
import android.view.InputEvent;
import android.view.Surface;
+
import com.android.internal.app.HeavyWeightSwitcherActivity;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.ReferrerIntent;
@@ -118,7 +118,6 @@ import com.android.server.LocalServices;
import com.android.server.am.ActivityStack.ActivityState;
import com.android.server.wm.WindowManagerService;
-
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
@@ -273,6 +272,13 @@ public final class ActivityStackSupervisor implements DisplayListener {
* setWindowManager is called. **/
private boolean mLeanbackOnlyDevice;
+ PowerManager mPm;
+
+ /**
+ * Is the privacy guard currently enabled? Shared between ActivityStacks
+ */
+ String mPrivacyGuardPackageName = null;
+
/**
* We don't want to allow the device to go to sleep while in the process
* of launching an activity. This is primarily to allow alarm intent
@@ -349,9 +355,9 @@ public final class ActivityStackSupervisor implements DisplayListener {
* initialized. So we initialize our wakelocks afterwards.
*/
void initPowerManagement() {
- PowerManager pm = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
- mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
- mLaunchingActivity = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*launch*");
+ mPm = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
+ mGoingToSleep = mPm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
+ mLaunchingActivity = mPm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*launch*");
mLaunchingActivity.setReferenceCounted(false);
}
@@ -511,7 +517,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
mHomeStack.moveHomeStackTaskToTop(homeStackTaskType);
ActivityRecord r = getHomeActivity();
- if (r != null) {
+ // Only resume home activity if isn't finishing.
+ if (r != null && !r.finishing) {
mService.setFocusedActivityLocked(r, reason);
return resumeTopActivitiesLocked(mHomeStack, prev, null);
}
@@ -949,6 +956,40 @@ public final class ActivityStackSupervisor implements DisplayListener {
// Cannot start a child activity if the parent is not resumed.
return ActivityManager.START_CANCELED;
}
+
+ if (intent.getAction() != null && !isProvisioned()) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_SEARCH:
+ case Intent.ACTION_WEB_SEARCH:
+ case Intent.ACTION_PROCESS_TEXT:
+ case Intent.ACTION_ASSIST:
+ case Intent.ACTION_VOICE_ASSIST:
+ Slog.w(TAG, "not starting assist intent while not provisioned");
+ return ActivityManager.START_NOT_CURRENT_USER_ACTIVITY;
+ }
+ }
+ try {
+ //TODO: This needs to be a flushed out API in the future.
+ boolean isProtected = intent.getComponent() != null
+ && AppGlobals.getPackageManager()
+ .isComponentProtected(callingPackage, callingUid,
+ intent.getComponent(), userId) &&
+ (intent.getFlags()&Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0;
+
+ if (isProtected) {
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.POST_COMPONENT_PROTECTED_MSG);
+ //Store start flags, userid
+ intent.setFlags(startFlags);
+ intent.putExtra("com.android.settings.PROTECTED_APPS_USER_ID", userId);
+ msg.obj = intent;
+ mService.mHandler.sendMessage(msg);
+ return ActivityManager.START_NOT_CURRENT_USER_ACTIVITY;
+ }
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+
final int realCallingPid = Binder.getCallingPid();
final int realCallingUid = Binder.getCallingUid();
int callingPid;
@@ -1481,6 +1522,11 @@ public final class ActivityStackSupervisor implements DisplayListener {
if (err == ActivityManager.START_SUCCESS && intent.getComponent() == null) {
// We couldn't find a class that can handle the given Intent.
// That's the end of that!
+ final Uri data = intent.getData();
+ final String strData = data != null ? data.toSafeString() : null;
+ EventLog.writeEvent(EventLogTags.AM_INTENT_NOT_RESOLVED, callingPackage,
+ intent.getAction(), intent.getType(), strData, intent.getFlags());
+
err = ActivityManager.START_INTENT_NOT_RESOLVED;
}
@@ -1840,6 +1886,29 @@ public final class ActivityStackSupervisor implements DisplayListener {
inTask = null;
}
+ try {
+ //TODO: This needs to be a flushed out API in the future.
+ boolean isProtected = intent.getComponent() != null
+ && AppGlobals.getPackageManager()
+ .isComponentProtected(sourceRecord == null ? "android" :
+ sourceRecord.launchedFromPackage, r.launchedFromUid,
+ intent.getComponent(), r.userId) &&
+ (intent.getFlags()&Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0;
+
+ if (isProtected && r.state == INITIALIZING) {
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.POST_COMPONENT_PROTECTED_MSG);
+ //Store start flags, userid
+ intent.setFlags(startFlags);
+ intent.putExtra("com.android.settings.PROTECTED_APPS_USER_ID", r.userId);
+ msg.obj = intent;
+ mService.mHandler.sendMessage(msg);
+ return ActivityManager.START_NOT_CURRENT_USER_ACTIVITY;
+ }
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+
final boolean launchSingleTop = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP;
final boolean launchSingleInstance = r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE;
final boolean launchSingleTask = r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK;
@@ -2674,8 +2743,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
boolean didSomething = false;
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
- final int numStacks = stacks.size();
- for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
+ for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = stacks.get(stackNdx);
if (stack.finishDisabledPackageActivitiesLocked(
packageName, filterByClasses, doit, evenPersistent, userId)) {
@@ -2774,6 +2842,12 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
void findTaskToMoveToFrontLocked(TaskRecord task, int flags, Bundle options, String reason) {
+ ActivityRecord top = task.stack.topRunningActivityLocked(null);
+ /* App is launching from recent apps and it's a new process */
+ if(top != null && top.state == ActivityState.DESTROYED) {
+ mService.launchBoost(-1, top.packageName);
+ }
+
if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
mUserLeaving = true;
}
@@ -3055,10 +3129,15 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
final ActivityRecord ar = stack.findTaskLocked(r);
if (ar != null) {
+ if (ar.state == ActivityState.DESTROYED) {
+ mService.launchBoost(-1, r.packageName);
+ }
return ar;
}
}
}
+ mService.launchBoost(-1, r.packageName);
+
if (DEBUG_TASKS) Slog.d(TAG_TASKS, "No task found");
return null;
}
@@ -3868,7 +3947,9 @@ public final class ActivityStackSupervisor implements DisplayListener {
}
void showLockTaskToast() {
- mLockTaskNotify.showToast(mLockTaskModeState);
+ if (mLockTaskNotify != null) {
+ mLockTaskNotify.showToast(mLockTaskModeState);
+ }
}
void showLockTaskEscapeMessageLocked(TaskRecord task) {
@@ -4607,4 +4688,9 @@ public final class ActivityStackSupervisor implements DisplayListener {
return onLeanbackOnly;
}
+
+ private boolean isProvisioned() {
+ return Settings.Global.getInt(mService.mContext.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) == 1;
+ }
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 62768c3..5fd3510 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2006-2007 The Android Open Source Project
+ * Copyright (C) 2016 The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -50,6 +51,8 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BatteryStatsHelper;
import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.DockBatteryStatsImpl;
+
import com.android.internal.os.PowerProfile;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -75,6 +78,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
static IBatteryStats sService;
final BatteryStatsImpl mStats;
+ // The dock stats only collect statistics about battery (no wakelocks, no counters, ...),
+ // just the dock battery history
+ final DockBatteryStatsImpl mDockStats;
final BatteryStatsHandler mHandler;
Context mContext;
PowerManagerInternal mPowerManagerInternal;
@@ -167,6 +173,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
// BatteryStatsImpl expects the ActivityManagerService handler, so pass that one through.
mStats = new BatteryStatsImpl(systemDir, handler, mHandler);
+ mDockStats = new DockBatteryStatsImpl(systemDir, handler, mHandler);
}
public void publish(Context context) {
@@ -196,6 +203,9 @@ public final class BatteryStatsService extends IBatteryStats.Stub
synchronized (mStats) {
mStats.shutdownLocked();
}
+ synchronized (mDockStats) {
+ mDockStats.shutdownLocked();
+ }
}
public static IBatteryStats getService() {
@@ -231,6 +241,16 @@ public final class BatteryStatsService extends IBatteryStats.Stub
mHandler.sendEmptyMessage(BatteryStatsHandler.MSG_WRITE_TO_DISK);
}
+ /**
+ * @return the current dock statistics object, which may be modified
+ * to reflect events that affect battery usage. You must lock the
+ * stats object before doing anything with it.
+ * @hide
+ */
+ public BatteryStatsImpl getActiveDockStatistics() {
+ return mDockStats;
+ }
+
// These are for direct use by the activity manager...
/**
@@ -327,6 +347,48 @@ public final class BatteryStatsService extends IBatteryStats.Stub
}
}
+ /** @hide */
+ public byte[] getDockStatistics() {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.BATTERY_STATS, null);
+ //Slog.i("foo", "SENDING DOCK BATTERY INFO:");
+ //mDockStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
+ Parcel out = Parcel.obtain();
+ mDockStats.writeToParcel(out, 0);
+ byte[] data = out.marshall();
+ out.recycle();
+ return data;
+ }
+
+ /** @hide */
+ public ParcelFileDescriptor getDockStatisticsStream() {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.BATTERY_STATS, null);
+ //Slog.i("foo", "SENDING DOCK BATTERY INFO:");
+ //mDockStats.dumpLocked(new LogPrinter(Log.INFO, "foo", Log.LOG_ID_SYSTEM));
+ Parcel out = Parcel.obtain();
+ mDockStats.writeToParcel(out, 0);
+ byte[] data = out.marshall();
+ out.recycle();
+ try {
+ return ParcelFileDescriptor.fromData(data, "dock-battery-stats");
+ } catch (IOException e) {
+ Slog.w(TAG, "Unable to create shared memory", e);
+ return null;
+ }
+ }
+
+ public void resetStatistics() {
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.RESET_BATTERY_STATS, null);
+ synchronized (mStats) {
+ mStats.resetAllStatsCmdLocked();
+ }
+ synchronized (mDockStats) {
+ mDockStats.resetAllStatsCmdLocked();
+ }
+ }
+
public long computeBatteryTimeRemaining() {
synchronized (mStats) {
long time = mStats.computeBatteryTimeRemaining(SystemClock.elapsedRealtime());
@@ -901,6 +963,31 @@ public final class BatteryStatsService extends IBatteryStats.Stub
return mStats.getAwakeTimePlugged();
}
+ /** @hide */
+ public boolean isOnDockBattery() {
+ return mDockStats.isOnBattery();
+ }
+
+ /** @hide */
+ public void setDockBatteryState(int status, int health, int plugType, int level,
+ int temp, int volt) {
+ enforceCallingPermission();
+ mDockStats.setBatteryStateLocked(status, health, plugType, level, temp, volt);
+ }
+
+ /** @hide */
+ public long getAwakeTimeDockBattery() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BATTERY_STATS, null);
+ return mDockStats.getAwakeTimeBattery();
+ }
+
+ public long getAwakeTimeDockPlugged() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.BATTERY_STATS, null);
+ return mDockStats.getAwakeTimePlugged();
+ }
+
public void enforceCallingPermission() {
if (Binder.getCallingPid() == Process.myPid()) {
return;
@@ -1065,6 +1152,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
} else if ("--reset".equals(arg)) {
synchronized (mStats) {
mStats.resetAllStatsCmdLocked();
+ mDockStats.resetAllStatsCmdLocked();
pw.println("Battery stats reset.");
noOutput = true;
}
@@ -1073,6 +1161,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
updateExternalStats("dump", UPDATE_ALL);
synchronized (mStats) {
mStats.writeSyncLocked();
+ mDockStats.writeSyncLocked();
pw.println("Battery stats written.");
noOutput = true;
}
@@ -1176,19 +1265,44 @@ public final class BatteryStatsService extends IBatteryStats.Stub
+ mStats.mCheckinFile.getBaseFile(), e);
}
}
+ if (mDockStats.mCheckinFile.exists()) {
+ try {
+ byte[] raw = mDockStats.mCheckinFile.readFully();
+ if (raw != null) {
+ Parcel in = Parcel.obtain();
+ in.unmarshall(raw, 0, raw.length);
+ in.setDataPosition(0);
+ DockBatteryStatsImpl checkinStats = new DockBatteryStatsImpl(
+ null, mStats.mHandler, null);
+ checkinStats.readSummaryFromParcel(in);
+ in.recycle();
+ checkinStats.dumpCheckinLocked(mContext, pw, apps, flags,
+ historyStart);
+ mDockStats.mCheckinFile.delete();
+ return;
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "Failure reading dock checkin file "
+ + mDockStats.mCheckinFile.getBaseFile(), e);
+ }
+ }
}
}
synchronized (mStats) {
mStats.dumpCheckinLocked(mContext, pw, apps, flags, historyStart);
+ mDockStats.dumpCheckinLocked(mContext, pw, apps, flags, historyStart);
if (writeData) {
mStats.writeAsyncLocked();
+ mDockStats.writeAsyncLocked();
}
}
} else {
synchronized (mStats) {
mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
+ mDockStats.dumpLocked(mContext, pw, flags, reqUid, historyStart);
if (writeData) {
mStats.writeAsyncLocked();
+ mDockStats.writeAsyncLocked();
}
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 089c33b..e7fec19 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -142,6 +142,11 @@ public final class BroadcastQueue {
*/
BroadcastRecord mPendingBroadcast = null;
+ /**
+ * Intent broadcast that we are currently processing
+ */
+ BroadcastRecord mCurrentBroadcast = null;
+
/**
* The receiver index that is pending, to restart the broadcast if needed.
*/
@@ -641,6 +646,10 @@ public final class BroadcastQueue {
.sendToTarget();
}
+ BroadcastRecord getProcessingBroadcast() {
+ return mCurrentBroadcast;
+ }
+
final void processNextBroadcast(boolean fromMsg) {
synchronized(mService) {
BroadcastRecord r;
@@ -661,6 +670,7 @@ public final class BroadcastQueue {
r = mParallelBroadcasts.remove(0);
r.dispatchTime = SystemClock.uptimeMillis();
r.dispatchClockTime = System.currentTimeMillis();
+ mCurrentBroadcast = r;
final int N = r.receivers.size();
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
+ mQueueName + "] " + r);
@@ -674,6 +684,7 @@ public final class BroadcastQueue {
addBroadcastToHistoryLocked(r);
if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
+ mQueueName + "] " + r);
+ mCurrentBroadcast = null;
}
// Now take care of the next serialized one...
@@ -719,6 +730,7 @@ public final class BroadcastQueue {
return;
}
r = mOrderedBroadcasts.get(0);
+ mCurrentBroadcast = r;
boolean forceReceive = false;
// Ensure that even if something goes awry with the timeout
@@ -789,6 +801,7 @@ public final class BroadcastQueue {
// ... and on to the next...
addBroadcastToHistoryLocked(r);
mOrderedBroadcasts.remove(0);
+ mCurrentBroadcast = null;
r = null;
looped = true;
continue;
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 9a645df..33fa714 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -78,6 +78,8 @@ option java_package com.android.server.am
30036 am_provider_lost_process (User|1|5),(Package Name|3),(UID|1|5),(Name|3)
# The activity manager gave up on a new process taking too long to start
30037 am_process_start_timeout (User|1|5),(PID|1|5),(UID|1|5),(Process Name|3)
+# The activity manager was unable to resolve the intent to an Activity
+30038 am_intent_not_resolved (Calling Package Name|3),(Action|3),(MIME Type|3),(URI|3),(Flags|1|5)
# Unhandled exception
30039 am_crash (User|1|5),(PID|1|5),(Process Name|3),(Flags|1|5),(Exception|3),(Message|3),(File|3),(Line|1|5)
diff --git a/services/core/java/com/android/server/am/LockTaskNotify.java b/services/core/java/com/android/server/am/LockTaskNotify.java
index afde322..a5d37f0 100644
--- a/services/core/java/com/android/server/am/LockTaskNotify.java
+++ b/services/core/java/com/android/server/am/LockTaskNotify.java
@@ -20,11 +20,14 @@ import android.app.ActivityManager;
import android.content.Context;
import android.os.Handler;
import android.os.Message;
+import android.os.RemoteException;
+import android.os.UserHandle;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
import com.android.internal.R;
+import cyanogenmod.providers.CMSettings;
/**
* Helper to manage showing/hiding a image to notify them that they are entering
@@ -45,25 +48,32 @@ public class LockTaskNotify {
mHandler = new H();
}
+ private boolean hasNavigationBar() {
+ return mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_showNavigationBar)
+ || CMSettings.Global.getIntForUser(mContext.getContentResolver(),
+ CMSettings.Global.DEV_FORCE_SHOW_NAVBAR, 0, UserHandle.USER_CURRENT) == 1;
+ }
+
public void showToast(int lockTaskModeState) {
mHandler.obtainMessage(H.SHOW_TOAST, lockTaskModeState, 0 /* Not used */).sendToTarget();
}
public void handleShowToast(int lockTaskModeState) {
- String text = null;
+ final int textResId;
if (lockTaskModeState == ActivityManager.LOCK_TASK_MODE_LOCKED) {
- text = mContext.getString(R.string.lock_to_app_toast_locked);
- } else if (lockTaskModeState == ActivityManager.LOCK_TASK_MODE_PINNED) {
- text = mContext.getString(mAccessibilityManager.isEnabled()
- ? R.string.lock_to_app_toast_accessible : R.string.lock_to_app_toast);
- }
- if (text == null) {
- return;
+ textResId = R.string.lock_to_app_toast_locked;
+ } else if (lockTaskModeState == ActivityManager.LOCK_TASK_MODE_PINNED &&
+ mAccessibilityManager.isEnabled() && hasNavigationBar()) {
+ textResId = R.string.lock_to_app_toast_accessible;
+ } else {
+ textResId = hasNavigationBar()
+ ? R.string.lock_to_app_toast : R.string.lock_to_app_toast_no_navbar;
}
if (mLastToast != null) {
mLastToast.cancel();
}
- mLastToast = makeAllUserToastAndShow(text);
+ mLastToast = makeAllUserToastAndShow(mContext.getString(textResId));
}
public void show(boolean starting) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 0e24952..4def2fb 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -32,6 +32,7 @@ import com.android.server.wm.WindowManagerService;
import android.content.res.Resources;
import android.graphics.Point;
import android.os.SystemProperties;
+import android.os.Process;
import android.net.LocalSocketAddress;
import android.net.LocalSocket;
import android.util.Slog;
@@ -133,7 +134,17 @@ final class ProcessList {
// we have no limit on the number of service, visible, foreground, or other such
// processes and the number of those processes does not count against the cached
// process limit.
- static final int MAX_CACHED_APPS = 32;
+ static final int MAX_CACHED_APPS = SystemProperties.getInt("ro.sys.fw.bg_apps_limit",32);
+ static final boolean USE_TRIM_SETTINGS =
+ SystemProperties.getBoolean("ro.sys.fw.use_trim_settings",false);
+ static final int EMPTY_APP_PERCENT = SystemProperties.getInt("ro.sys.fw.empty_app_percent",50);
+ static final int TRIM_EMPTY_PERCENT =
+ SystemProperties.getInt("ro.sys.fw.trim_empty_percent",100);
+ static final int TRIM_CACHE_PERCENT =
+ SystemProperties.getInt("ro.sys.fw.trim_cache_percent",100);
+ static final long TRIM_ENABLE_MEMORY =
+ SystemProperties.getLong("ro.sys.fw.trim_enable_memory",1073741824);
+ public static boolean allowTrim() { return Process.getTotalMemory() < TRIM_ENABLE_MEMORY ; }
// We allow empty processes to stick around for at most 30 minutes.
static final long MAX_EMPTY_TIME = 30*60*1000;
@@ -143,11 +154,25 @@ final class ProcessList {
// The number of empty apps at which we don't consider it necessary to do
// memory trimming.
- static final int TRIM_EMPTY_APPS = MAX_EMPTY_APPS/2;
+ public static int computeTrimEmptyApps() {
+ if (USE_TRIM_SETTINGS && allowTrim()) {
+ return MAX_EMPTY_APPS*TRIM_EMPTY_PERCENT/100;
+ } else {
+ return MAX_EMPTY_APPS/2;
+ }
+ }
+ static final int TRIM_EMPTY_APPS = computeTrimEmptyApps();
// The number of cached at which we don't consider it necessary to do
// memory trimming.
- static final int TRIM_CACHED_APPS = (MAX_CACHED_APPS-MAX_EMPTY_APPS)/3;
+ public static int computeTrimCachedApps() {
+ if (USE_TRIM_SETTINGS && allowTrim()) {
+ return MAX_CACHED_APPS*TRIM_CACHE_PERCENT/100;
+ } else {
+ return (MAX_CACHED_APPS-MAX_EMPTY_APPS)/3;
+ }
+ }
+ static final int TRIM_CACHED_APPS = computeTrimCachedApps();
// Threshold of number of cached+empty where we consider memory critical.
static final int TRIM_CRITICAL_THRESHOLD = 3;
@@ -172,6 +197,17 @@ final class ProcessList {
FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
};
+
+ // These are the low-end OOM level limits for 32bit 1 GB RAM
+ private final int[] mOomMinFreeLow32Bit = new int[] {
+ 12288, 18432, 24576,
+ 36864, 43008, 49152
+ };
+ // These are the high-end OOM level limits for 32bit 1 GB RAM
+ private final int[] mOomMinFreeHigh32Bit = new int[] {
+ 61440, 76800, 92160,
+ 107520, 137660, 174948
+ };
// These are the low-end OOM level limits. This is appropriate for an
// HVGA or smaller phone with less than 512MB. Values are in KB.
private final int[] mOomMinFreeLow = new int[] {
@@ -224,8 +260,8 @@ final class ProcessList {
int maxSize = 1280*800; // 1024000 230400 870400 .264
float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
if (false) {
- Slog.i("XXXXXX", "scaleMem=" + scaleMem);
- Slog.i("XXXXXX", "scaleDisp=" + scaleDisp + " dw=" + displayWidth
+ Slog.i(TAG, "scaleMem=" + scaleMem);
+ Slog.i(TAG, "scaleDisp=" + scaleDisp + " dw=" + displayWidth
+ " dh=" + displayHeight);
}
@@ -237,18 +273,27 @@ final class ProcessList {
int minfree_abs = Resources.getSystem().getInteger(
com.android.internal.R.integer.config_lowMemoryKillerMinFreeKbytesAbsolute);
if (false) {
- Slog.i("XXXXXX", "minfree_adj=" + minfree_adj + " minfree_abs=" + minfree_abs);
+ Slog.i(TAG, "minfree_adj=" + minfree_adj + " minfree_abs=" + minfree_abs);
}
+ // We've now baked in the increase to the basic oom values above, since
+ // they seem to be useful more generally for devices that are tight on
+ // memory than just for 64 bit. This should probably have some more
+ // tuning done, so not deleting it quite yet...
final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;
for (int i=0; i<mOomAdj.length; i++) {
int low = mOomMinFreeLow[i];
int high = mOomMinFreeHigh[i];
if (is64bit) {
+ Slog.i(TAG, "choosing minFree values for 64 Bit");
// Increase the high min-free levels for cached processes for 64-bit
if (i == 4) high = (high*3)/2;
else if (i == 5) high = (high*7)/4;
+ } else {
+ Slog.i(TAG, "choosing minFree values for 32 Bit");
+ low = mOomMinFreeLow32Bit[i];
+ high = mOomMinFreeHigh32Bit[i];
}
mOomMinFree[i] = (int)(low + ((high-low)*scale));
}
@@ -308,7 +353,11 @@ final class ProcessList {
}
public static int computeEmptyProcessLimit(int totalProcessLimit) {
- return totalProcessLimit/2;
+ if(USE_TRIM_SETTINGS && allowTrim()) {
+ return totalProcessLimit*EMPTY_APP_PERCENT/100;
+ } else {
+ return totalProcessLimit/2;
+ }
}
private static String buildOomTag(String prefix, String space, int val, int base) {
@@ -671,16 +720,15 @@ final class ProcessList {
}
private static void writeLmkd(ByteBuffer buf) {
-
for (int i = 0; i < 3; i++) {
if (sLmkdSocket == null) {
- if (openLmkdSocket() == false) {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException ie) {
- }
- continue;
+ if (openLmkdSocket() == false) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ie) {
}
+ continue;
+ }
}
try {
@@ -698,4 +746,55 @@ final class ProcessList {
}
}
}
+
+ private static final int[] PROCESS_STATS_FORMAT;
+ static {
+ PROCESS_STATS_FORMAT = new int[31];
+ java.util.Arrays.fill(PROCESS_STATS_FORMAT, android.os.Process.PROC_SPACE_TERM);
+ // Process name enclosed in parentheses
+ PROCESS_STATS_FORMAT[1] |= android.os.Process.PROC_PARENS;
+ // Process state (D/R/S/T/Z)
+ PROCESS_STATS_FORMAT[2] |= android.os.Process.PROC_OUT_STRING;
+ // Bit mask of pending signals
+ PROCESS_STATS_FORMAT[30] |= android.os.Process.PROC_OUT_STRING;
+ }
+
+ static boolean isAlive(int pid, boolean noisy) {
+ final String[] procStats = new String[2];
+ final String stat = "/proc/" + pid + "/stat";
+ if (android.os.Process.readProcFile(stat, PROCESS_STATS_FORMAT,
+ procStats, null, null)) {
+ if ("Z".equals(procStats[0])) {
+ if (noisy) {
+ Slog.i(TAG, pid + " is zombie state");
+ }
+ return false;
+ }
+ try {
+ int pendingSignals = Integer.parseInt(procStats[1]);
+ if ((pendingSignals & (1 << 8)) != 0) {
+ if (noisy) {
+ Slog.i(TAG, pid + " has pending signal 9");
+ }
+ return false;
+ }
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "Unknown pending signals " + procStats[1] + " of " + pid);
+ }
+ } else {
+ boolean exists = false;
+ try {
+ exists = libcore.io.Libcore.os.access(stat, android.system.OsConstants.F_OK);
+ } catch (android.system.ErrnoException e) {
+ exists = e.errno != android.system.OsConstants.ENOENT;
+ }
+ if (!exists) {
+ if (noisy) {
+ Slog.i(TAG, stat + " does not exist");
+ }
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 3e9d0b8..30f2c3e 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -136,6 +136,7 @@ final class ProcessRecord {
long curCpuTime; // How long proc has run CPU most recently
long lastRequestedGc; // When we last asked the app to do a gc
long lastLowMemory; // When we last told the app that memory is low
+ long lastProviderTime; // The last time someone else was using a provider in this process.
boolean reportLowMemory; // Set to true when waiting to report low mem
boolean empty; // Is this an empty background process?
boolean cached; // Is this a cached process?
@@ -317,6 +318,11 @@ final class ProcessRecord {
pw.print(" foregroundActivities="); pw.print(foregroundActivities);
pw.print(" (rep="); pw.print(repForegroundActivities); pw.println(")");
}
+ if (lastProviderTime > 0) {
+ pw.print(prefix); pw.print("lastProviderTime=");
+ TimeUtils.formatDuration(lastProviderTime, now, pw);
+ pw.println();
+ }
if (hasStartedServices) {
pw.print(prefix); pw.print("hasStartedServices="); pw.println(hasStartedServices);
}
@@ -432,6 +438,18 @@ final class ProcessRecord {
}
public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) {
+ String seempStr = "app_uid=" + uid
+ + ",app_pid=" + pid + ",oom_adj=" + curAdj
+ + ",setAdj=" + setAdj + ",hasShownUi=" + (hasShownUi ? 1 : 0)
+ + ",cached=" + (cached ? 1 : 0)
+ + ",fA=" + (foregroundActivities ? 1 : 0)
+ + ",fS=" + (foregroundServices ? 1 : 0)
+ + ",systemNoUi=" + (systemNoUi ? 1 : 0)
+ + ",curSchedGroup=" + curSchedGroup
+ + ",curProcState=" + curProcState + ",setProcState=" + setProcState
+ + ",killed=" + (killed ? 1 : 0) + ",killedByAm=" + (killedByAm ? 1 : 0)
+ + ",debugging=" + (debugging ? 1 : 0);
+ android.util.SeempLog.record_str(386, seempStr);
if (thread == null) {
final ProcessStats.ProcessState origBase = baseProcessTracker;
if (origBase != null) {
@@ -458,6 +476,18 @@ final class ProcessRecord {
}
public void makeInactive(ProcessStatsService tracker) {
+ String seempStr = "app_uid=" + uid
+ + ",app_pid=" + pid + ",oom_adj=" + curAdj
+ + ",setAdj=" + setAdj + ",hasShownUi=" + (hasShownUi ? 1 : 0)
+ + ",cached=" + (cached ? 1 : 0)
+ + ",fA=" + (foregroundActivities ? 1 : 0)
+ + ",fS=" + (foregroundServices ? 1 : 0)
+ + ",systemNoUi=" + (systemNoUi ? 1 : 0)
+ + ",curSchedGroup=" + curSchedGroup
+ + ",curProcState=" + curProcState + ",setProcState=" + setProcState
+ + ",killed=" + (killed ? 1 : 0) + ",killedByAm=" + (killedByAm ? 1 : 0)
+ + ",debugging=" + (debugging ? 1 : 0);
+ android.util.SeempLog.record_str(387, seempStr);
thread = null;
final ProcessStats.ProcessState origBase = baseProcessTracker;
if (origBase != null) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0f957db..fe3df61 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -35,6 +35,7 @@ import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -59,6 +60,7 @@ import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
import android.media.AudioPort;
+import android.media.AudioRecord;
import android.media.AudioRoutesInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioRoutesObserver;
@@ -125,6 +127,8 @@ import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
+import cyanogenmod.providers.CMSettings;
+
/**
* The implementation of the volume manager service.
* <p>
@@ -171,6 +175,27 @@ public class AudioService extends IAudioService.Stub {
// the platform type affects volume and silent mode behavior
private final int mPlatformType;
+ private static final ArrayList<MediaPlayerInfo> mMediaPlayers =
+ new ArrayList<MediaPlayerInfo>();
+
+ private class MediaPlayerInfo {
+ private String mPackageName;
+ private boolean mIsfocussed;
+ public MediaPlayerInfo(String packageName, boolean isfocussed) {
+ mPackageName = packageName;
+ mIsfocussed = isfocussed;
+ }
+ public boolean isFocussed() {
+ return mIsfocussed;
+ }
+ public void setFocus(boolean focus) {
+ mIsfocussed = focus;
+ }
+ public String getPackageName() {
+ return mPackageName;
+ }
+ }
+
private boolean isPlatformVoice() {
return mPlatformType == AudioSystem.PLATFORM_VOICE;
}
@@ -384,6 +409,15 @@ public class AudioService extends IAudioService.Stub {
* @see System#MUTE_STREAMS_AFFECTED */
private int mMuteAffectedStreams;
+ /** @see #handleHotwordInput **/
+ private Object mHotwordInputLock = new Object();
+
+ /** The package name of the application that is
+ * currently using the HOTWORD input.
+ */
+ // protected by mHotwordInputLock
+ private String mHotwordAudioInputPackage;
+
/**
* NOTE: setVibrateSetting(), getVibrateSetting(), shouldVibrate() are deprecated.
* mVibrateSetting is just maintained during deprecation period but vibration policy is
@@ -424,6 +458,11 @@ public class AudioService extends IAudioService.Stub {
private final ArrayMap<String, DeviceListSpec> mConnectedDevices = new ArrayMap<>();
+ private String mA2dpConnectedDevice = ""; //Used for BT a2dp connection
+ //Add connected A2dp devices in this list
+ private ArrayList<BluetoothDevice> mConnectedBTDevicesList =
+ new ArrayList<BluetoothDevice>();
+
// Forced device usage for communications
private int mForcedUseForComm;
@@ -515,7 +554,6 @@ public class AudioService extends IAudioService.Stub {
// Devices for which the volume is fixed and VolumePanel slider should be disabled
int mFixedVolumeDevices = AudioSystem.DEVICE_OUT_HDMI |
AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
- AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET |
AudioSystem.DEVICE_OUT_HDMI_ARC |
AudioSystem.DEVICE_OUT_SPDIF |
AudioSystem.DEVICE_OUT_AUX_LINE;
@@ -527,6 +565,9 @@ public class AudioService extends IAudioService.Stub {
private boolean mDockAudioMediaEnabled = true;
+ private boolean mForceAnalogDeskDock;
+ private boolean mForceAnalogCarDock;
+
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
// Used when safe volume warning message display is requested by setStreamVolume(). In this
@@ -545,11 +586,14 @@ public class AudioService extends IAudioService.Stub {
// If absolute volume is supported in AVRCP device
private boolean mAvrcpAbsVolSupported = false;
+ private boolean mLinkNotificationWithVolume;
+ private final boolean mVoiceCapable;
private static Long mLastDeviceConnectMsgTime = new Long(0);
private AudioManagerInternal.RingerModeDelegate mRingerModeDelegate;
private VolumePolicy mVolumePolicy = VolumePolicy.DEFAULT;
private long mLoweredFromNormalToVibrateTime;
+ private boolean mVolumeKeysControlRingStream;
// Intent "extra" data keys.
public static final String CONNECT_INTENT_KEY_PORT_NAME = "portName";
@@ -602,6 +646,9 @@ public class AudioService extends IAudioService.Stub {
mForcedUseForComm = AudioSystem.FORCE_NONE;
+ mVoiceCapable = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_voice_capable);
+
createAudioSystemThread();
AudioSystem.setErrorCallback(mAudioSystemCallback);
@@ -628,6 +675,15 @@ public class AudioService extends IAudioService.Stub {
mUseFixedVolume = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useFixedVolume);
+ mForceAnalogDeskDock = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_forceAnalogDeskDock);
+ mForceAnalogCarDock = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_forceAnalogCarDock);
+
+ // read this in before readPersistedSettings() because updateStreamVolumeAlias needs it
+ mLinkNotificationWithVolume = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.VOLUME_LINK_NOTIFICATION, 1) == 1;
+
// must be called before readPersistedSettings() which needs a valid mStreamVolumeAlias[]
// array initialized by updateStreamVolumeAlias()
updateStreamVolumeAlias(false /*updateVolumes*/, TAG);
@@ -658,6 +714,7 @@ public class AudioService extends IAudioService.Stub {
intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ intentFilter.addAction(Intent.ACTION_SHUTDOWN);
// TODO merge orientation and rotation
mMonitorOrientation = SystemProperties.getBoolean("ro.audio.monitorOrientation", false);
if (mMonitorOrientation) {
@@ -850,6 +907,106 @@ public class AudioService extends IAudioService.Stub {
}
}
+ /**
+ * @hide
+ */
+ public void addMediaPlayerAndUpdateRemoteController (String packageName) {
+ synchronized(mMediaPlayers) {
+ Log.v(TAG, "addMediaPlayerAndUpdateRemoteController: size of existing list: " +
+ mMediaPlayers.size());
+ boolean playerToAdd = true;
+ if (mMediaPlayers.size() > 0) {
+ final Iterator<MediaPlayerInfo> rccIterator = mMediaPlayers.iterator();
+ while (rccIterator.hasNext()) {
+ final MediaPlayerInfo player = rccIterator.next();
+ if (packageName.equals(player.getPackageName())) {
+ Log.e(TAG, "Player entry present, no need to add");
+ playerToAdd = false;
+ player.setFocus(true);
+ } else {
+ Log.e(TAG, "Player: " + player.getPackageName()+ "Lost Focus");
+ player.setFocus(false);
+ }
+ }
+ }
+ if (playerToAdd) {
+ Log.e(TAG, "Adding Player: " + packageName + " to available player list");
+ mMediaPlayers.add(new MediaPlayerInfo(packageName, true));
+ }
+ Intent intent = new Intent(AudioManager.RCC_CHANGED_ACTION);
+ intent.putExtra(AudioManager.EXTRA_CALLING_PACKAGE_NAME, packageName);
+ intent.putExtra(AudioManager.EXTRA_FOCUS_CHANGED_VALUE, true);
+ intent.putExtra(AudioManager.EXTRA_AVAILABLITY_CHANGED_VALUE, true);
+ sendBroadcastToAll(intent);
+ Log.v(TAG, "updating focussed RCC change to RCD: CallingPackageName:"
+ + packageName);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void updateRemoteControllerOnExistingMediaPlayers() {
+ synchronized(mMediaPlayers) {
+ Log.v(TAG, "updateRemoteControllerOnExistingMediaPlayers: size of Player list: " +
+ mMediaPlayers.size());
+ if (mMediaPlayers.size() > 0) {
+ Log.v(TAG, "Inform RemoteController regarding existing RCC entry");
+ final Iterator<MediaPlayerInfo> rccIterator = mMediaPlayers.iterator();
+ while (rccIterator.hasNext()) {
+ final MediaPlayerInfo player = rccIterator.next();
+ Intent intent = new Intent(AudioManager.RCC_CHANGED_ACTION);
+ intent.putExtra(AudioManager.EXTRA_CALLING_PACKAGE_NAME,
+ player.getPackageName());
+ intent.putExtra(AudioManager.EXTRA_FOCUS_CHANGED_VALUE,
+ player.isFocussed());
+ intent.putExtra(AudioManager.EXTRA_AVAILABLITY_CHANGED_VALUE, true);
+ sendBroadcastToAll(intent);
+ Log.v(TAG, "updating RCC change: CallingPackageName:" +
+ player.getPackageName());
+ }
+ } else {
+ Log.e(TAG, "No RCC entry present to update");
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void removeMediaPlayerAndUpdateRemoteController (String packageName) {
+ synchronized(mMediaPlayers) {
+ Log.v(TAG, "removeMediaPlayerAndUpdateRemoteController: size of existing list: " +
+ mMediaPlayers.size());
+ boolean playerToRemove = false;
+ int index = -1;
+ if (mMediaPlayers.size() > 0) {
+ final Iterator<MediaPlayerInfo> rccIterator = mMediaPlayers.iterator();
+ while (rccIterator.hasNext()) {
+ index++;
+ final MediaPlayerInfo player = rccIterator.next();
+ if (packageName.equals(player.getPackageName())) {
+ Log.v(TAG, "Player entry present remove and update RemoteController");
+ playerToRemove = true;
+ break;
+ } else {
+ Log.v(TAG, "Player entry for " + player.getPackageName()+ " is not present");
+ }
+ }
+ }
+ if (playerToRemove) {
+ Log.e(TAG, "Removing Player: " + packageName + " from index" + index);
+ mMediaPlayers.remove(index);
+ }
+ Intent intent = new Intent(AudioManager.RCC_CHANGED_ACTION);
+ intent.putExtra(AudioManager.EXTRA_CALLING_PACKAGE_NAME, packageName);
+ intent.putExtra(AudioManager.EXTRA_FOCUS_CHANGED_VALUE, false);
+ intent.putExtra(AudioManager.EXTRA_AVAILABLITY_CHANGED_VALUE, false);
+ sendBroadcastToAll(intent);
+ Log.v(TAG, "Updated List size: " + mMediaPlayers.size());
+ }
+ }
+
private void checkAllAliasStreamVolumes() {
synchronized (VolumeStreamState.class) {
int numStreamTypes = AudioSystem.getNumStreamTypes();
@@ -943,6 +1100,13 @@ public class AudioService extends IAudioService.Stub {
}
mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
+
+ if (mLinkNotificationWithVolume && mVoiceCapable) {
+ mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
+ } else {
+ mStreamVolumeAlias[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION;
+ }
+
if (updateVolumes) {
mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
caller);
@@ -1013,8 +1177,15 @@ public class AudioService extends IAudioService.Stub {
updateRingerModeAffectedStreams();
readDockAudioSettings(cr);
+
+ mVolumeKeysControlRingStream = CMSettings.System.getIntForUser(cr,
+ CMSettings.System.VOLUME_KEYS_CONTROL_RING_STREAM, 1,
+ UserHandle.USER_CURRENT) == 1;
}
+ mLinkNotificationWithVolume = Settings.Secure.getInt(cr,
+ Settings.Secure.VOLUME_LINK_NOTIFICATION, 1) == 1;
+
mMuteAffectedStreams = System.getIntForUser(cr,
System.MUTE_STREAMS_AFFECTED, AudioSystem.DEFAULT_MUTE_STREAMS_AFFECTED,
UserHandle.USER_CURRENT);
@@ -1451,6 +1622,46 @@ public class AudioService extends IAudioService.Stub {
sendVolumeUpdate(streamType, oldIndex, index, flags);
}
+ /**
+ * Retrieve the package name of the application that currently controls
+ * the {@link android.media.MediaRecorder.AudioSource#HOTWORD} input.
+ * @return The package name of the application that controls the input
+ * or null if no package currently controls it.
+ */
+ public String getCurrentHotwordInputPackageName() {
+ return mHotwordAudioInputPackage;
+ }
+
+ /**
+ * Handle the change of state of the HOTWORD input.
+ *
+ * When the {@link android.media.MediaRecorder.AudioSource#HOTWORD} input is
+ * in use, send a broadcast to alert the new state and store the name of the current
+ * package that controls the input in mHotwordAudioInputPackage.
+ * @param listening Whether the input is being listened to.
+ */
+ public void handleHotwordInput(boolean listening) {
+ synchronized (mHotwordInputLock) {
+ Intent broadcastIntent = new Intent(Intent.ACTION_HOTWORD_INPUT_CHANGED);
+ String[] packages = mContext.getPackageManager().getPackagesForUid(
+ Binder.getCallingUid());
+ if (packages.length > 0) {
+ if (listening) {
+ mHotwordAudioInputPackage = packages[0];
+ }
+ broadcastIntent.putExtra(Intent.EXTRA_CURRENT_PACKAGE_NAME, packages[0]);
+ }
+ broadcastIntent.putExtra(Intent.EXTRA_HOTWORD_INPUT_STATE,
+ listening ? AudioRecord.RECORDSTATE_RECORDING :
+ AudioRecord.RECORDSTATE_STOPPED);
+ // Set the currently listening package to null if listening has stopped.
+ if (!listening) {
+ mHotwordAudioInputPackage = null;
+ }
+ sendBroadcastToAll(broadcastIntent, Manifest.permission.CAPTURE_AUDIO_HOTWORD);
+ }
+ }
+
/** @see AudioManager#forceVolumeControlStream(int) */
public void forceVolumeControlStream(int streamType, IBinder cb) {
synchronized(mForceControlStreamLock) {
@@ -1503,11 +1714,15 @@ public class AudioService extends IAudioService.Stub {
}
private void sendBroadcastToAll(Intent intent) {
+ sendBroadcastToAll(intent, null);
+ }
+
+ private void sendBroadcastToAll(Intent intent, String receiverPermission) {
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
final long ident = Binder.clearCallingIdentity();
try {
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL, receiverPermission);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2938,9 +3153,18 @@ public class AudioService extends IAudioService.Stub {
synchronized (mConnectedDevices) {
synchronized (mA2dpAvrcpLock) {
mA2dp = (BluetoothA2dp) proxy;
+ if (mConnectedBTDevicesList.size() > 0) {
+ Log.d(TAG,"A2dp connection list not empty, purge it, size " +
+ mConnectedBTDevicesList.size());
+ mConnectedBTDevicesList.clear();
+ }
+ //In Dual A2dp, we can have two devices connected
deviceList = mA2dp.getConnectedDevices();
- if (deviceList.size() > 0) {
- btDevice = deviceList.get(0);
+ Log.d(TAG, "onServiceConnected: A2dp Service connected: " +
+ deviceList.size());
+ for (int i = 0; i < deviceList.size(); i++) {
+ //Add the device in Connected list
+ btDevice = deviceList.get(i);
int state = mA2dp.getConnectionState(btDevice);
int delay = checkSendBecomingNoisyIntent(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
@@ -3038,6 +3262,10 @@ public class AudioService extends IAudioService.Stub {
case BluetoothProfile.A2DP:
synchronized (mConnectedDevices) {
synchronized (mA2dpAvrcpLock) {
+ Log.d(TAG,"mConnectedBTDevicesList size " + mConnectedBTDevicesList.size());
+ if (mConnectedBTDevicesList.size() > 0) {
+ mConnectedBTDevicesList.clear();
+ }
// Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices
for (int i = 0; i < mConnectedDevices.size(); i++) {
DeviceListSpec deviceSpec = mConnectedDevices.valueAt(i);
@@ -3394,10 +3622,16 @@ public class AudioService extends IAudioService.Stub {
if (DEBUG_VOL)
Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
return AudioSystem.STREAM_MUSIC;
- } else {
+ } else {
+ if (mVolumeKeysControlRingStream) {
if (DEBUG_VOL)
Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default");
return AudioSystem.STREAM_RING;
+ } else {
+ if (DEBUG_VOL)
+ Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC b/c default");
+ return AudioSystem.STREAM_MUSIC;
+ }
}
} else if (isAfMusicActiveRecently(0)) {
if (DEBUG_VOL)
@@ -3432,9 +3666,16 @@ public class AudioService extends IAudioService.Stub {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: forcing STREAM_MUSIC");
return AudioSystem.STREAM_MUSIC;
} else {
- if (DEBUG_VOL) Log.v(TAG,
- "getActiveStreamType: using STREAM_NOTIFICATION as default");
- return AudioSystem.STREAM_NOTIFICATION;
+ if (mVolumeKeysControlRingStream) {
+ if (DEBUG_VOL)
+ Log.v(TAG,
+ "getActiveStreamType: using STREAM_NOTIFICATION as default");
+ return AudioSystem.STREAM_NOTIFICATION;
+ } else {
+ if (DEBUG_VOL)
+ Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC b/c default");
+ return AudioSystem.STREAM_MUSIC;
+ }
}
}
break;
@@ -3593,10 +3834,41 @@ public class AudioService extends IAudioService.Stub {
public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state, int profile)
{
- int delay;
+ int delay = 0;
if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK) {
throw new IllegalArgumentException("invalid profile " + profile);
}
+ /*check the state of the currnt device*/
+ if (state == BluetoothA2dp.STATE_CONNECTING) {
+ Log.d(TAG, "Device is still connecting ");
+ return delay;
+ }
+ if ((mConnectedBTDevicesList.contains(device) &&
+ (state == BluetoothA2dp.STATE_CONNECTED))) {
+ Log.d(TAG, "Device conn is updated again, ignore ");
+ return delay;
+ }
+ if (!mConnectedBTDevicesList.contains(device) &&
+ (state == BluetoothA2dp.STATE_CONNECTED)) {
+ /*add the device in the list*/
+ Log.d(TAG, "Add new connected device in the list: " + device);
+ mConnectedBTDevicesList.add(device);
+ if (mConnectedBTDevicesList.size() > 1) {
+ Log.d(TAG, "Second device connected, add new device ");
+ return delay;
+ }
+ } else if ((state == BluetoothA2dp.STATE_DISCONNECTED) ||
+ (state == BluetoothA2dp.STATE_DISCONNECTING)) {
+ Log.d(TAG, "Device is getting disconnected: " + device);
+ if (mConnectedBTDevicesList.contains(device)) {
+ Log.d(TAG, "Remove the BT device ");
+ mConnectedBTDevicesList.remove(device);
+ }
+ if (mConnectedBTDevicesList.size() > 0) {
+ Log.d(TAG, "Not all are disconnected ");
+ return delay;
+ }
+ }
synchronized (mConnectedDevices) {
if (profile == BluetoothProfile.A2DP) {
delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
@@ -4383,7 +4655,11 @@ public class AudioService extends IAudioService.Stub {
break;
case MSG_PLAY_SOUND_EFFECT:
- onPlaySoundEffect(msg.arg1, msg.arg2);
+ if (isStreamMute(AudioSystem.STREAM_SYSTEM)) {
+ Log.d(TAG, "Stream muted, skip playback");
+ } else {
+ onPlaySoundEffect(msg.arg1, msg.arg2);
+ }
break;
case MSG_BTA2DP_DOCK_TIMEOUT:
@@ -4501,6 +4777,10 @@ public class AudioService extends IAudioService.Stub {
Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
mContentResolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.DOCK_AUDIO_MEDIA_ENABLED), false, this);
+ mContentResolver.registerContentObserver(Settings.Secure.getUriFor(
+ Settings.Secure.VOLUME_LINK_NOTIFICATION), false, this);
+ mContentResolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.VOLUME_KEYS_CONTROL_RING_STREAM), false, this);
}
@Override
@@ -4519,6 +4799,17 @@ public class AudioService extends IAudioService.Stub {
setRingerModeInt(getRingerModeInternal(), false);
}
readDockAudioSettings(mContentResolver);
+
+ boolean linkNotificationWithVolume = Settings.Secure.getInt(mContentResolver,
+ Settings.Secure.VOLUME_LINK_NOTIFICATION, 1) == 1;
+ if (linkNotificationWithVolume != mLinkNotificationWithVolume) {
+ mLinkNotificationWithVolume = linkNotificationWithVolume;
+ createStreamStates();
+ updateStreamVolumeAlias(true, TAG);
+ }
+ mVolumeKeysControlRingStream = CMSettings.System.getIntForUser(mContentResolver,
+ CMSettings.System.VOLUME_KEYS_CONTROL_RING_STREAM, 1,
+ UserHandle.USER_CURRENT) == 1;
}
}
}
@@ -4631,11 +4922,12 @@ public class AudioService extends IAudioService.Stub {
// introduction of a delay for transient disconnections of docks when
// power is rapidly turned off/on, this message will be canceled if
// we reconnect the dock under a preset delay
- makeA2dpDeviceUnavailableLater(address, BTA2DP_DOCK_TIMEOUT_MILLIS);
+ makeA2dpDeviceUnavailableLater(btDevice.getAddress(), BTA2DP_DOCK_TIMEOUT_MILLIS);
// the next time isConnected is evaluated, it will be false for the dock
}
} else {
- makeA2dpDeviceUnavailableNow(address);
+ Log.d(TAG, "All devices are disconneted, update Policymanager ");
+ makeA2dpDeviceUnavailableNow(btDevice.getAddress());
}
synchronized (mCurAudioRoutes) {
if (mCurAudioRoutes.bluetoothName != null) {
@@ -4645,21 +4937,24 @@ public class AudioService extends IAudioService.Stub {
}
}
} else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
+ //This function is not implemented
+ mA2dpConnectedDevice = "BluetoothA2dp"; // Add this String
if (btDevice.isBluetoothDock()) {
// this could be a reconnection after a transient disconnection
cancelA2dpDeviceTimeout();
- mDockAddress = address;
+ mDockAddress = mA2dpConnectedDevice;
} else {
// this could be a connection of another A2DP device before the timeout of
// a dock: cancel the dock timeout, and make the dock unavailable now
if(hasScheduledA2dpDockTimeout()) {
cancelA2dpDeviceTimeout();
- makeA2dpDeviceUnavailableNow(mDockAddress);
+ makeA2dpDeviceUnavailableNow(btDevice.getAddress());
}
}
- makeA2dpDeviceAvailable(address, btDevice.getName());
+ makeA2dpDeviceAvailable(btDevice.getAddress(), btDevice.getName());
+ //Updated the Router for a2dp device
synchronized (mCurAudioRoutes) {
- String name = btDevice.getAliasName();
+ String name = mA2dpConnectedDevice;
if (!TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) {
mCurAudioRoutes.bluetoothName = name;
sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
@@ -4676,6 +4971,7 @@ public class AudioService extends IAudioService.Stub {
Log.d(TAG, "onSetA2dpSourceConnectionState btDevice="+btDevice+" state="+state);
}
if (btDevice == null) {
+ Log.d(TAG, "onSetA2dpSourceConnectionState device is null"); //gasati
return;
}
String address = btDevice.getAddress();
@@ -4759,8 +5055,14 @@ public class AudioService extends IAudioService.Stub {
// Called synchronized on mConnectedDevices
private int checkSendBecomingNoisyIntent(int device, int state) {
int delay = 0;
+ if (mConnectedBTDevicesList.size() > 1) {
+ Log.d(TAG, "checkSendBecomingNoisyIntent on state: " + state);
+ return delay;
+ }
+
if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) {
int devices = 0;
+ Log.d(TAG, "checkSendBecomingNoisyIntent update the noise");
for (int i = 0; i < mConnectedDevices.size(); i++) {
int dev = mConnectedDevices.valueAt(i).mDeviceType;
if (((dev & AudioSystem.DEVICE_BIT_IN) == 0)
@@ -4814,12 +5116,18 @@ public class AudioService extends IAudioService.Stub {
connType = AudioRoutesInfo.MAIN_HEADSET;
intent.setAction(Intent.ACTION_HEADSET_PLUG);
intent.putExtra("microphone", 1);
+ if (state == 1) {
+ startMusicPlayer();
+ }
} else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE ||
device == AudioSystem.DEVICE_OUT_LINE) {
/*do apps care about line-out vs headphones?*/
connType = AudioRoutesInfo.MAIN_HEADPHONES;
intent.setAction(Intent.ACTION_HEADSET_PLUG);
intent.putExtra("microphone", 0);
+ if (state == 1) {
+ startMusicPlayer();
+ }
} else if (device == AudioSystem.DEVICE_OUT_HDMI ||
device == AudioSystem.DEVICE_OUT_HDMI_ARC) {
connType = AudioRoutesInfo.MAIN_HDMI;
@@ -4852,6 +5160,23 @@ public class AudioService extends IAudioService.Stub {
}
}
+ private void startMusicPlayer() {
+ boolean launchPlayer = CMSettings.System.getIntForUser(mContext.getContentResolver(),
+ CMSettings.System.HEADSET_CONNECT_PLAYER, 0, UserHandle.USER_CURRENT) != 0;
+ TelecomManager tm = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+
+ if (launchPlayer && !tm.isInCall()) {
+ try {
+ Intent playerIntent = new Intent(Intent.ACTION_MAIN);
+ playerIntent.addCategory(Intent.CATEGORY_APP_MUSIC);
+ playerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(playerIntent);
+ } catch (ActivityNotFoundException | IllegalArgumentException e) {
+ Log.w(TAG, "No music player Activity could be found");
+ }
+ }
+ }
+
private void onSetWiredDeviceConnectionState(int device, int state, String address,
String deviceName, String caller) {
if (DEBUG_DEVICES) {
@@ -4983,10 +5308,12 @@ public class AudioService extends IAudioService.Stub {
int config;
switch (dockState) {
case Intent.EXTRA_DOCK_STATE_DESK:
- config = AudioSystem.FORCE_BT_DESK_DOCK;
+ config = mForceAnalogDeskDock ? AudioSystem.FORCE_ANALOG_DOCK :
+ AudioSystem.FORCE_BT_DESK_DOCK;
break;
case Intent.EXTRA_DOCK_STATE_CAR:
- config = AudioSystem.FORCE_BT_CAR_DOCK;
+ config = mForceAnalogCarDock ? AudioSystem.FORCE_ANALOG_DOCK :
+ AudioSystem.FORCE_BT_CAR_DOCK;
break;
case Intent.EXTRA_DOCK_STATE_LE_DESK:
config = AudioSystem.FORCE_ANALOG_DOCK;
@@ -5107,6 +5434,8 @@ public class AudioService extends IAudioService.Stub {
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
UserManagerService.getInstance().setSystemControlledUserRestriction(
UserManager.DISALLOW_RECORD_AUDIO, false, userId);
+ } else if (action.equals(Intent.ACTION_SHUTDOWN)) {
+ AudioSystem.setParameters("dev_shutdown=true");
}
}
} // end class AudioServiceBroadcastReceiver
@@ -5180,6 +5509,18 @@ public class AudioService extends IAudioService.Stub {
mMediaFocusControl.remoteControlDisplayWantsPlaybackPositionSync(rcd, wantsSync);
}
+ public void setRemoteControlClientPlayItem(long uid, int scope) {
+ mMediaFocusControl.setRemoteControlClientPlayItem(uid, scope);
+ }
+
+ public void getRemoteControlClientNowPlayingEntries() {
+ mMediaFocusControl.getRemoteControlClientNowPlayingEntries();
+ }
+
+ public void setRemoteControlClientBrowsedPlayer() {
+ mMediaFocusControl.setRemoteControlClientBrowsedPlayer();
+ }
+
@Override
public void setRemoteStreamVolume(int index) {
enforceVolumeController("set the remote stream volume");
diff --git a/services/core/java/com/android/server/audio/MediaFocusControl.java b/services/core/java/com/android/server/audio/MediaFocusControl.java
index f72b598..af880bd 100644
--- a/services/core/java/com/android/server/audio/MediaFocusControl.java
+++ b/services/core/java/com/android/server/audio/MediaFocusControl.java
@@ -335,6 +335,9 @@ public class MediaFocusControl implements OnFinished {
private static final int MSG_RCDISPLAY_INIT_INFO = 9;
private static final int MSG_REEVALUATE_RCD = 10;
private static final int MSG_UNREGISTER_MEDIABUTTONINTENT = 11;
+ private static final int MSG_RCC_SET_BROWSED_PLAYER = 12;
+ private static final int MSG_RCC_SET_PLAY_ITEM = 13;
+ private static final int MSG_RCC_GET_NOW_PLAYING_ENTRIES = 14;
// sendMsg() flags
/** If the msg is already queued, replace it with this one. */
@@ -382,6 +385,22 @@ public class MediaFocusControl implements OnFinished {
(IRemoteVolumeObserver)msg.obj /* rvo */);
break;
+ case MSG_RCC_SET_PLAY_ITEM:
+ Log.d(TAG, "MSG_RCC_SET_PLAY_ITEM: "+ ((Long)msg.obj).longValue());
+ onSetRemoteControlClientPlayItem(msg.arg2 /* scope */,
+ ((Long)msg.obj).longValue() /* uid */);
+ break;
+
+ case MSG_RCC_GET_NOW_PLAYING_ENTRIES:
+ Log.d(TAG, "MSG_RCC_GET_NOW_PLAYING_ENTRIES: ");
+ onGetRemoteControlClientNowPlayingEntries();
+ break;
+
+ case MSG_RCC_SET_BROWSED_PLAYER:
+ Log.d(TAG, "MSG_RCC_SET_BROWSED_PLAYER: ");
+ onSetRemoteControlClientBrowsedPlayer();
+ break;
+
case MSG_RCDISPLAY_INIT_INFO:
// msg.obj is guaranteed to be non null
onRcDisplayInitInfo((IRemoteControlDisplay)msg.obj /*newRcd*/,
@@ -2052,6 +2071,66 @@ public class MediaFocusControl implements OnFinished {
}
}
+ public void setRemoteControlClientPlayItem(long uid, int scope) {
+ sendMsg(mEventHandler, MSG_RCC_SET_PLAY_ITEM, SENDMSG_REPLACE, 0 /* arg1 */,
+ scope /* arg2*/, new Long(uid) /* obj */, 0 /* delay */);
+ }
+
+ private void onSetRemoteControlClientPlayItem(int scope, Long uid) {
+ Log.d(TAG, "onSetRemoteControlClientPlayItem: "+ uid);
+ synchronized(mCurrentRcLock) {
+ if (mCurrentRcClient != null) {
+ try {
+ mCurrentRcClient.setPlayItem(scope, uid);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Current valid remote client is dead: "+e);
+ mCurrentRcClient = null;
+ }
+ }
+ }
+ }
+
+ public void getRemoteControlClientNowPlayingEntries() {
+ sendMsg(mEventHandler, MSG_RCC_GET_NOW_PLAYING_ENTRIES, SENDMSG_REPLACE,
+ 0 /* arg1 */, 0 /* arg2 ignored*/, 0 /* obj */, 0 /* delay */);
+ }
+
+ private void onGetRemoteControlClientNowPlayingEntries() {
+ Log.d(TAG, "onGetRemoteControlClientNowPlayingEntries: ");
+ synchronized(mCurrentRcLock) {
+ if (mCurrentRcClient != null) {
+ try {
+ mCurrentRcClient.getNowPlayingEntries();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Current valid remote client is dead: "+e);
+ mCurrentRcClient = null;
+ }
+ }
+ }
+ }
+
+ public void setRemoteControlClientBrowsedPlayer() {
+ Log.d(TAG, "setRemoteControlClientBrowsedPlayer: ");
+ sendMsg(mEventHandler, MSG_RCC_SET_BROWSED_PLAYER, SENDMSG_REPLACE, 0/* arg1 */,
+ 0 /* arg2 ignored*/, 0 /* obj */, 0 /* delay */);
+ }
+
+ private void onSetRemoteControlClientBrowsedPlayer() {
+ Log.d(TAG, "onSetRemoteControlClientBrowsedPlayer: ");
+ PlayerRecord prse = mPRStack.peek();
+ if (prse.getRcc() == null) {
+ Log.d(TAG, "can not proceed with setBrowsedPlayer");
+ } else {
+ Log.d(TAG, "proceed with setBrowsedPlayer");
+ try {
+ Log.d(TAG, "Calling setBrowsedPlayer");
+ prse.getRcc().setBrowsedPlayer();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Current valid remote client is dead: "+ e);
+ }
+ }
+ }
+
// handler for MSG_RCC_NEW_VOLUME_OBS
private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
synchronized(mPRStack) {
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index a9eaeee..89569ec 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -31,6 +31,7 @@ import android.os.Handler;
import android.os.Message;
import android.os.INetworkManagementService;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.Slog;
import com.android.server.net.BaseNetworkObserver;
@@ -90,7 +91,12 @@ public class Nat464Xlat extends BaseNetworkObserver {
(nai.linkProperties != null) ? nai.linkProperties.hasIPv4Address() : false;
// Only support clat on mobile and wifi for now, because these are the only IPv6-only
// networks we can connect to.
- return connected && !hasIPv4Address && (netType == TYPE_MOBILE || netType == TYPE_WIFI);
+ boolean doXlat = SystemProperties.getBoolean("persist.net.doxlat",true);
+ if(!doXlat) {
+ Slog.i(TAG, "Android Xlat is disabled");
+ }
+ return connected && !hasIPv4Address && (((netType == TYPE_MOBILE) && doXlat )
+ || netType == TYPE_WIFI);
}
/**
@@ -125,6 +131,7 @@ public class Nat464Xlat extends BaseNetworkObserver {
}
try {
+ mNMService.unregisterObserver(this);
mNMService.registerObserver(this);
} catch(RemoteException e) {
Slog.e(TAG, "startClat: Can't register interface observer for clat on " + mNetwork);
@@ -233,16 +240,12 @@ public class Nat464Xlat extends BaseNetworkObserver {
}
@Override
- public void interfaceLinkStateChanged(String iface, boolean up) {
+ public void addressUpdated(String iface, LinkAddress clatAddress) {
// Called by the InterfaceObserver on its own thread, so can race with stop().
- if (isStarted() && up && mIface.equals(iface)) {
+ if (isStarted() && mIface.equals(iface) && clatAddress != null) {
Slog.i(TAG, "interface " + iface + " is up, mIsRunning " + mIsRunning + "->true");
if (!mIsRunning) {
- LinkAddress clatAddress = getLinkAddress(iface);
- if (clatAddress == null) {
- return;
- }
mIsRunning = true;
maybeSetIpv6NdOffload(mBaseIface, false);
LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index c1aaf07..60d6772 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -36,11 +36,17 @@ import android.net.Network;
import android.net.NetworkInfo;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiDevice;
+import android.net.wifi.WifiManager;
import android.os.Binder;
+import android.os.Handler;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
+import android.os.SystemProperties;
import android.os.UserHandle;
+import android.os.SystemProperties;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -53,6 +59,7 @@ import com.android.internal.util.IState;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.server.IoThread;
+import com.android.server.NetPluginDelegate;
import com.android.server.net.BaseNetworkObserver;
import java.io.FileDescriptor;
@@ -64,9 +71,22 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_CHANGED_ACTION;
+
/**
* @hide
@@ -137,12 +157,27 @@ public class Tethering extends BaseNetworkObserver {
private boolean mUsbTetherRequested; // true if USB tethering should be started
// when RNDIS is enabled
+ // Once STA established connection to hostapd, it will be added
+ // to mL2ConnectedDeviceMap. Then after deviceinfo update from dnsmasq,
+ // it will be added to mConnectedDeviceMap
+ private HashMap<String, WifiDevice> mL2ConnectedDeviceMap = new HashMap<String, WifiDevice>();
+ private HashMap<String, WifiDevice> mConnectedDeviceMap = new HashMap<String, WifiDevice>();
+ private static final String dhcpLocation = "/data/misc/dhcp/dnsmasq.leases";
+
+ // Device name polling interval(ms) and max times
+ private static final int DNSMASQ_POLLING_INTERVAL = 1000;
+ private static final int DNSMASQ_POLLING_MAX_TIMES = 10;
+
+ private long mWiFiApInactivityTimeout;
+ private final Handler mHandler;
+
public Tethering(Context context, INetworkManagementService nmService,
INetworkStatsService statsService, Looper looper) {
mContext = context;
mNMService = nmService;
mStatsService = statsService;
mLooper = looper;
+ mHandler = new Handler(mLooper);
mPublicSync = new Object();
@@ -158,6 +193,8 @@ public class Tethering extends BaseNetworkObserver {
filter.addAction(UsbManager.ACTION_USB_STATE);
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
+ filter.addAction(WIFI_AP_STATE_CHANGED_ACTION);
+
mContext.registerReceiver(mStateReceiver, filter);
filter = new IntentFilter();
@@ -190,11 +227,17 @@ public class Tethering extends BaseNetworkObserver {
void updateConfiguration() {
String[] tetherableUsbRegexs = mContext.getResources().getStringArray(
com.android.internal.R.array.config_tether_usb_regexs);
- String[] tetherableWifiRegexs = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_tether_wifi_regexs);
+ String[] tetherableWifiRegexs;
String[] tetherableBluetoothRegexs = mContext.getResources().getStringArray(
com.android.internal.R.array.config_tether_bluetooth_regexs);
+ if (SystemProperties.getInt("persist.fst.rate.upgrade.en", 0) == 1) {
+ tetherableWifiRegexs = new String[] {"bond0"};
+ } else {
+ tetherableWifiRegexs = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_tether_wifi_regexs);
+ }
+
int ifaceTypes[] = mContext.getResources().getIntArray(
com.android.internal.R.array.config_tether_upstream_types);
Collection<Integer> upstreamIfaceTypes = new ArrayList();
@@ -234,6 +277,18 @@ public class Tethering extends BaseNetworkObserver {
sm = new TetherInterfaceSM(iface, mLooper, usb);
mIfaces.put(iface, sm);
sm.start();
+ if (isWifi(iface)) {
+ // check if the user has specified an inactivity timeout for wifi AP and
+ // if so schedule the timeout
+ final WifiManager wm =
+ (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ final WifiConfiguration apConfig = wm.getWifiApConfiguration();
+ mWiFiApInactivityTimeout =
+ apConfig != null ? apConfig.wifiApInactivityTimeout : 0;
+ if (mWiFiApInactivityTimeout > 0 && mL2ConnectedDeviceMap.size() == 0) {
+ scheduleInactivityTimeout();
+ }
+ }
}
} else {
if (isUsb(iface)) {
@@ -243,6 +298,9 @@ public class Tethering extends BaseNetworkObserver {
} else if (sm != null) {
sm.sendMessage(TetherInterfaceSM.CMD_INTERFACE_DOWN);
mIfaces.remove(iface);
+ if (isWifi(iface)) {
+ cancelInactivityTimeout();
+ }
}
}
}
@@ -326,6 +384,194 @@ public class Tethering extends BaseNetworkObserver {
}
}
+ public List<WifiDevice> getTetherConnectedSta() {
+ Iterator it;
+ List<WifiDevice> TetherConnectedStaList = new ArrayList<WifiDevice>();
+
+ if (mContext.getResources().getBoolean(com.android.internal.R.bool.config_softap_extention)) {
+ it = mConnectedDeviceMap.keySet().iterator();
+ while(it.hasNext()) {
+ String key = (String)it.next();
+ WifiDevice device = (WifiDevice)mConnectedDeviceMap.get(key);
+ if (VDBG) {
+ Log.d(TAG, "getTetherConnectedSta: addr=" + key + " name=" + device.deviceName);
+ }
+ TetherConnectedStaList.add(device);
+ }
+ }
+
+ return TetherConnectedStaList;
+ }
+
+ private void sendTetherConnectStateChangedBroadcast() {
+ if (!getConnectivityManager().isTetheringSupported()) return;
+
+ Intent broadcast = new Intent(ConnectivityManager.TETHER_CONNECT_STATE_CHANGED);
+ broadcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING |
+ Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+
+ mContext.sendStickyBroadcastAsUser(broadcast, UserHandle.ALL);
+ }
+
+ private boolean readDeviceInfoFromDnsmasq(WifiDevice device) {
+ boolean result = false;
+ FileInputStream fstream = null;
+ String line;
+
+ try {
+ fstream = new FileInputStream(dhcpLocation);
+ DataInputStream in = new DataInputStream(fstream);
+ BufferedReader br = new BufferedReader(new InputStreamReader(in));
+
+ while ((null != (line = br.readLine())) && (line.length() != 0)) {
+ String[] fields = line.split(" ");
+
+ // 949295 00:0a:f5:6a:bf:70 192.168.43.32 android-93de88df9ec61bac *
+ if (fields.length > 3) {
+ String addr = fields[1];
+ String name = fields[3];
+
+ if (addr.equals(device.deviceAddress)) {
+ device.deviceName = name;
+ result = true;
+ break;
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Log.e(TAG, "readDeviceNameFromDnsmasq: " + ex);
+ } finally {
+ if (fstream != null) {
+ try {
+ fstream.close();
+ } catch (IOException ex) {}
+ }
+ }
+
+ return result;
+ }
+
+ private final Runnable mDisableWifiApRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (VDBG) Log.d(TAG, "Turning off hotpost due to inactivity");
+ final WifiManager wifiManager =
+ (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ wifiManager.setWifiApEnabled(null, false);
+ }
+ };
+
+ private void scheduleInactivityTimeout() {
+ if (mWiFiApInactivityTimeout > 0) {
+ if (VDBG) Log.d(TAG, "scheduleInactivityTimeout: " + mWiFiApInactivityTimeout);
+ mHandler.removeCallbacks(mDisableWifiApRunnable);
+ mHandler.postDelayed(mDisableWifiApRunnable, mWiFiApInactivityTimeout);
+ }
+ }
+
+ private void cancelInactivityTimeout() {
+ if (VDBG) Log.d(TAG, "cancelInactivityTimeout");
+ mHandler.removeCallbacks(mDisableWifiApRunnable);
+ }
+
+ /*
+ * DnsmasqThread is used to read the Device info from dnsmasq.
+ */
+ private static class DnsmasqThread extends Thread {
+ private final Tethering mTethering;
+ private int mInterval;
+ private int mMaxTimes;
+ private WifiDevice mDevice;
+
+ public DnsmasqThread(Tethering tethering, WifiDevice device,
+ int interval, int maxTimes) {
+ super("Tethering");
+ mTethering = tethering;
+ mInterval = interval;
+ mMaxTimes = maxTimes;
+ mDevice = device;
+ }
+
+ public void run() {
+ boolean result = false;
+
+ try {
+ while (mMaxTimes > 0) {
+ result = mTethering.readDeviceInfoFromDnsmasq(mDevice);
+ if (result) {
+ if (DBG) Log.d(TAG, "Successfully poll device info for " + mDevice.deviceAddress);
+ break;
+ }
+
+ mMaxTimes --;
+ Thread.sleep(mInterval);
+ }
+ } catch (Exception ex) {
+ result = false;
+ Log.e(TAG, "Pulling " + mDevice.deviceAddress + "error" + ex);
+ }
+
+ if (!result) {
+ if (DBG) Log.d(TAG, "Pulling timeout, suppose STA uses static ip " + mDevice.deviceAddress);
+ }
+
+ // When STA uses static ip, device info will be unavaiable from dnsmasq,
+ // thus no matter the result is success or failure, we will broadcast the event.
+ // But if the device is not in L2 connected state, it means the hostapd connection is
+ // disconnected before dnsmasq get device info, so in this case, don't broadcast
+ // connection event.
+ WifiDevice other = mTethering.mL2ConnectedDeviceMap.get(mDevice.deviceAddress);
+ if (other != null && other.deviceState == WifiDevice.CONNECTED) {
+ mTethering.mConnectedDeviceMap.put(mDevice.deviceAddress, mDevice);
+ mTethering.sendTetherConnectStateChangedBroadcast();
+ } else {
+ if (DBG) Log.d(TAG, "Device " + mDevice.deviceAddress + "already disconnected, ignoring");
+ }
+ }
+ }
+
+ public void interfaceMessageRecevied(String message) {
+ // if softap extension feature not enabled, do nothing
+ if (!mContext.getResources().getBoolean(com.android.internal.R.bool.config_softap_extention)) {
+ return;
+ }
+
+ if (DBG) Log.d(TAG, "interfaceMessageRecevied: message=" + message);
+
+ try {
+ WifiDevice device = new WifiDevice(message);
+
+ if (device.deviceState == WifiDevice.CONNECTED) {
+ mL2ConnectedDeviceMap.put(device.deviceAddress, device);
+
+ // When hostapd reported STA-connection event, it is possible that device
+ // info can't fetched from dnsmasq, then we start a thread to poll the
+ // device info, the thread will exit after device info avaiable.
+ // For static ip case, dnsmasq don't hold the device info, thus thread
+ // will exit after a timeout.
+ if (readDeviceInfoFromDnsmasq(device)) {
+ mConnectedDeviceMap.put(device.deviceAddress, device);
+ sendTetherConnectStateChangedBroadcast();
+ } else {
+ if (DBG) Log.d(TAG, "Starting poll device info for " + device.deviceAddress);
+ new DnsmasqThread(this, device,
+ DNSMASQ_POLLING_INTERVAL, DNSMASQ_POLLING_MAX_TIMES).start();
+ }
+ cancelInactivityTimeout();
+ } else if (device.deviceState == WifiDevice.DISCONNECTED) {
+ mL2ConnectedDeviceMap.remove(device.deviceAddress);
+ mConnectedDeviceMap.remove(device.deviceAddress);
+ sendTetherConnectStateChangedBroadcast();
+ // schedule inactivity timeout if non-zero and no more devices are connected
+ if (mWiFiApInactivityTimeout > 0 && mL2ConnectedDeviceMap.size() == 0) {
+ scheduleInactivityTimeout();
+ }
+ }
+ } catch (IllegalArgumentException ex) {
+ Log.e(TAG, "WifiDevice IllegalArgument: " + ex);
+ }
+ }
+
public int tether(String iface) {
if (DBG) Log.d(TAG, "Tethering " + iface);
TetherInterfaceSM sm = null;
@@ -453,7 +699,12 @@ public class Tethering extends BaseNetworkObserver {
if (mLastNotificationId != 0) {
if (mLastNotificationId == icon) {
- return;
+ if (!mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_softap_extention)
+ || icon != com.android.internal.R.drawable.stat_sys_tether_wifi) {
+ // if softap extension feature is on, allow to update icon else return.
+ return;
+ }
}
notificationManager.cancelAsUser(null, mLastNotificationId,
UserHandle.ALL);
@@ -526,6 +777,14 @@ public class Tethering extends BaseNetworkObserver {
}
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
updateConfiguration();
+ } else if(action.equals(WIFI_AP_STATE_CHANGED_ACTION)){
+ int wifiApState = intent.getIntExtra("wifi_state", WIFI_AP_STATE_DISABLED);
+ if (DBG) Log.d(TAG, "WIFI_AP_STATE_CHANGED: wifiApState=" + wifiApState);
+ if(wifiApState == WIFI_AP_STATE_ENABLED ||
+ wifiApState == WIFI_AP_STATE_DISABLED) {
+ mConnectedDeviceMap.clear();
+ mL2ConnectedDeviceMap.clear();
+ }
}
}
}
@@ -644,6 +903,10 @@ public class Tethering extends BaseNetworkObserver {
if (tm != null) {
secureSetting = tm.getTetherApnRequired();
}
+ // Allow override of TETHER_DUN_REQUIRED via prop
+ int prop = SystemProperties.getInt("persist.sys.dun.override", -1);
+ secureSetting = ((prop < 3) && (prop >= 0)) ? prop : secureSetting;
+
synchronized (mPublicSync) {
// 2 = not set, 0 = DUN not required, 1 = DUN required
if (secureSetting != 2) {
@@ -1333,6 +1596,8 @@ public class Tethering extends BaseNetworkObserver {
sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
}
} else {
+ Network network = getConnectivityManager().getNetworkForType(upType);
+ NetPluginDelegate.setUpstream(network);
LinkProperties linkProperties =
getConnectivityManager().getLinkProperties(upType);
if (linkProperties != null) {
@@ -1367,7 +1632,6 @@ public class Tethering extends BaseNetworkObserver {
}
}
try {
- Network network = getConnectivityManager().getNetworkForType(upType);
if (network == null) {
Log.e(TAG, "No Network for upstream type " + upType + "!");
}
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index ef086da..53c36ff 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -2435,10 +2435,9 @@ public class SyncManager {
final long shiftedLastPollTimeAbsolute =
(0 < lastPollTimeAbsolute - mSyncRandomOffsetMillis) ?
(lastPollTimeAbsolute - mSyncRandomOffsetMillis) : 0;
- long remainingMillis
- = periodInMillis - (shiftedNowAbsolute % periodInMillis);
long timeSinceLastRunMillis
= (nowAbsolute - lastPollTimeAbsolute);
+ long remainingMillis = periodInMillis - timeSinceLastRunMillis;
// Schedule this periodic sync to run early if it's close enough to its next
// runtime, and far enough from its last run time.
// If we are early, there will still be time remaining in this period.
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index e15bca6..eca6a2e 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -22,6 +22,8 @@ import com.android.server.twilight.TwilightListener;
import com.android.server.twilight.TwilightManager;
import com.android.server.twilight.TwilightState;
+import android.content.Context;
+import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -53,18 +55,16 @@ class AutomaticBrightnessController {
// auto-brightness adjustment setting.
private static final float SCREEN_AUTO_BRIGHTNESS_ADJUSTMENT_MAX_GAMMA = 3.0f;
- // Period of time in which to consider light samples in milliseconds.
- private static final int AMBIENT_LIGHT_HORIZON = 10000;
-
// Hysteresis constraints for brightening or darkening.
// The recent lux must have changed by at least this fraction relative to the
// current ambient lux before a change will be considered.
private static final float BRIGHTENING_LIGHT_HYSTERESIS = 0.10f;
private static final float DARKENING_LIGHT_HYSTERESIS = 0.20f;
- // The intercept used for the weighting calculation. This is used in order to keep all possible
- // weighting values positive.
- private static final int WEIGHTING_INTERCEPT = AMBIENT_LIGHT_HORIZON;
+ // Threshold (in lux) to select between normal and fast debounce time.
+ // If the difference between last sample and weighted value is larger than this value,
+ // fast debounce is used.
+ private static final float BRIGHTENING_FAST_THRESHOLD = 1000f;
// How long the current sensor reading is assumed to be valid beyond the current time.
// This provides a bit of prediction, as well as ensures that the weight for the last sample is
@@ -124,6 +124,7 @@ class AutomaticBrightnessController {
// when adapting to brighter or darker environments. This parameter controls how quickly
// brightness changes occur in response to an observed change in light level that exceeds the
// hysteresis threshold.
+ private final long mBrighteningLightFastDebounceConfig;
private final long mBrighteningLightDebounceConfig;
private final long mDarkeningLightDebounceConfig;
@@ -193,11 +194,23 @@ class AutomaticBrightnessController {
private int mBrightnessAdjustmentSampleOldBrightness;
private float mBrightnessAdjustmentSampleOldGamma;
- public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
+ // Period of time in which to consider light samples in milliseconds.
+ private int mAmbientLightHorizon;
+
+ // The intercept used for the weighting calculation. This is used in order to keep all possible
+ // weighting values positive.
+ private int mWeightingIntercept;
+
+ private final Context mContext;
+
+ public AutomaticBrightnessController(Context context, Callbacks callbacks, Looper looper,
SensorManager sensorManager, Spline autoBrightnessSpline, int lightSensorWarmUpTime,
int brightnessMin, int brightnessMax, float dozeScaleFactor,
int lightSensorRate, long brighteningLightDebounceConfig,
- long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig) {
+ long brighteningLightFastDebounceConfig, long darkeningLightDebounceConfig,
+ boolean resetAmbientLuxAfterWarmUpConfig,
+ int ambientLightHorizon) {
+ mContext = context;
mCallbacks = callbacks;
mTwilight = LocalServices.getService(TwilightManager.class);
mSensorManager = sensorManager;
@@ -208,11 +221,14 @@ class AutomaticBrightnessController {
mDozeScaleFactor = dozeScaleFactor;
mLightSensorRate = lightSensorRate;
mBrighteningLightDebounceConfig = brighteningLightDebounceConfig;
+ mBrighteningLightFastDebounceConfig = brighteningLightFastDebounceConfig;
mDarkeningLightDebounceConfig = darkeningLightDebounceConfig;
mResetAmbientLuxAfterWarmUpConfig = resetAmbientLuxAfterWarmUpConfig;
+ mAmbientLightHorizon = ambientLightHorizon;
+ mWeightingIntercept = ambientLightHorizon;
mHandler = new AutomaticBrightnessHandler(looper);
- mAmbientLightRingBuffer = new AmbientLightRingBuffer(mLightSensorRate);
+ mAmbientLightRingBuffer = new AmbientLightRingBuffer(mLightSensorRate, mAmbientLightHorizon);
if (!DEBUG_PRETEND_LIGHT_SENSOR_ABSENT) {
mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
@@ -256,6 +272,7 @@ class AutomaticBrightnessController {
pw.println(" mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
pw.println(" mLightSensorWarmUpTimeConfig=" + mLightSensorWarmUpTimeConfig);
pw.println(" mBrighteningLightDebounceConfig=" + mBrighteningLightDebounceConfig);
+ pw.println(" mBrighteningLightFastDebounceConfig=" + mBrighteningLightFastDebounceConfig);
pw.println(" mDarkeningLightDebounceConfig=" + mDarkeningLightDebounceConfig);
pw.println(" mResetAmbientLuxAfterWarmUpConfig=" + mResetAmbientLuxAfterWarmUpConfig);
@@ -303,13 +320,14 @@ class AutomaticBrightnessController {
private void handleLightSensorEvent(long time, float lux) {
mHandler.removeMessages(MSG_UPDATE_AMBIENT_LUX);
+ if (DEBUG) Slog.d(TAG, "handleLightSensorEvent: time=" + time + ", lux=" + lux);
applyLightSensorMeasurement(time, lux);
updateAmbientLux(time);
}
private void applyLightSensorMeasurement(long time, float lux) {
mRecentLightSamples++;
- mAmbientLightRingBuffer.prune(time - AMBIENT_LIGHT_HORIZON);
+ mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
mAmbientLightRingBuffer.push(time, lux);
// Remember this sample value.
@@ -360,17 +378,17 @@ class AutomaticBrightnessController {
return sum / totalWeight;
}
- private static float calculateWeight(long startDelta, long endDelta) {
+ private float calculateWeight(long startDelta, long endDelta) {
return weightIntegral(endDelta) - weightIntegral(startDelta);
}
- // Evaluates the integral of y = x + WEIGHTING_INTERCEPT. This is always positive for the
+ // Evaluates the integral of y = x + mWeightIntercept. This is always positive for the
// horizon we're looking at and provides a non-linear weighting for light samples.
- private static float weightIntegral(long x) {
- return x * (x * 0.5f + WEIGHTING_INTERCEPT);
+ private float weightIntegral(long x) {
+ return x * (x * 0.5f + mWeightingIntercept);
}
- private long nextAmbientLightBrighteningTransition(long time) {
+ private long nextAmbientLightBrighteningTransition(long time, float ambientLux) {
final int N = mAmbientLightRingBuffer.size();
long earliestValidTime = time;
for (int i = N - 1; i >= 0; i--) {
@@ -379,10 +397,13 @@ class AutomaticBrightnessController {
}
earliestValidTime = mAmbientLightRingBuffer.getTime(i);
}
- return earliestValidTime + mBrighteningLightDebounceConfig;
+
+ long debounceDelay = mLastObservedLux - ambientLux > BRIGHTENING_FAST_THRESHOLD
+ ? mBrighteningLightFastDebounceConfig : mBrighteningLightDebounceConfig;
+ return earliestValidTime + debounceDelay;
}
- private long nextAmbientLightDarkeningTransition(long time) {
+ private long nextAmbientLightDarkeningTransition(long time, float ambientLux) {
final int N = mAmbientLightRingBuffer.size();
long earliestValidTime = time;
for (int i = N - 1; i >= 0; i--) {
@@ -396,7 +417,7 @@ class AutomaticBrightnessController {
private void updateAmbientLux() {
long time = SystemClock.uptimeMillis();
- mAmbientLightRingBuffer.prune(time - AMBIENT_LIGHT_HORIZON);
+ mAmbientLightRingBuffer.prune(time - mAmbientLightHorizon);
updateAmbientLux(time);
}
@@ -426,13 +447,12 @@ class AutomaticBrightnessController {
updateAutoBrightness(true);
}
- long nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
- long nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
float ambientLux = calculateAmbientLux(time);
+ long nextBrightenTransition = nextAmbientLightBrighteningTransition(time, ambientLux);
+ long nextDarkenTransition = nextAmbientLightDarkeningTransition(time, ambientLux);
if (ambientLux >= mBrighteningLuxThreshold && nextBrightenTransition <= time
|| ambientLux <= mDarkeningLuxThreshold && nextDarkenTransition <= time) {
- setAmbientLux(ambientLux);
if (DEBUG) {
Slog.d(TAG, "updateAmbientLux: "
+ ((ambientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
@@ -440,9 +460,10 @@ class AutomaticBrightnessController {
+ ", mAmbientLightRingBuffer=" + mAmbientLightRingBuffer
+ ", mAmbientLux=" + mAmbientLux);
}
+ setAmbientLux(ambientLux);
updateAutoBrightness(true);
- nextBrightenTransition = nextAmbientLightBrighteningTransition(time);
- nextDarkenTransition = nextAmbientLightDarkeningTransition(time);
+ nextBrightenTransition = nextAmbientLightBrighteningTransition(time, ambientLux);
+ nextDarkenTransition = nextAmbientLightDarkeningTransition(time, ambientLux);
}
long nextTransitionTime = Math.min(nextDarkenTransition, nextBrightenTransition);
// If one of the transitions is ready to occur, but the total weighted ambient lux doesn't
@@ -653,8 +674,8 @@ class AutomaticBrightnessController {
private int mEnd;
private int mCount;
- public AmbientLightRingBuffer(long lightSensorRate) {
- mCapacity = (int) Math.ceil(AMBIENT_LIGHT_HORIZON * BUFFER_SLACK / lightSensorRate);
+ public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
+ mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
mRingLux = new float[mCapacity];
mRingTime = new long[mCapacity];
}
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 7b49530..25e59d5 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -19,6 +19,7 @@ package com.android.server.display;
import com.android.internal.app.IBatteryStats;
import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
+import com.android.server.lights.LightsManager;
import android.animation.Animator;
import android.animation.ObjectAnimator;
@@ -124,6 +125,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Battery stats.
private final IBatteryStats mBatteryStats;
+ // The lights service.
+ private final LightsManager mLights;
+
// The sensor manager.
private final SensorManager mSensorManager;
@@ -262,6 +266,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
mCallbacks = callbacks;
mBatteryStats = BatteryStatsService.getService();
+ mLights = LocalServices.getService(LightsManager.class);
mSensorManager = sensorManager;
mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
mBlanker = blanker;
@@ -307,10 +312,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
com.android.internal.R.integer.config_autoBrightnessLightSensorRate);
long brighteningLightDebounce = resources.getInteger(
com.android.internal.R.integer.config_autoBrightnessBrighteningLightDebounce);
+ long brighteningLightFastDebounce = resources.getInteger(
+ com.android.internal.R.integer.config_autoBrightnessBrighteningLightFastDebounce);
long darkeningLightDebounce = resources.getInteger(
com.android.internal.R.integer.config_autoBrightnessDarkeningLightDebounce);
boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
com.android.internal.R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
+ int ambientLightHorizon = resources.getInteger(
+ com.android.internal.R.integer.config_autoBrightnessAmbientLightHorizon);
if (mUseSoftwareAutoBrightnessConfig) {
int[] lux = resources.getIntArray(
@@ -343,12 +352,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (bottom < screenBrightnessRangeMinimum) {
screenBrightnessRangeMinimum = bottom;
}
- mAutomaticBrightnessController = new AutomaticBrightnessController(this,
+ mAutomaticBrightnessController = new AutomaticBrightnessController(mContext, this,
handler.getLooper(), sensorManager, screenAutoBrightnessSpline,
lightSensorWarmUpTimeConfig, screenBrightnessRangeMinimum,
mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
- brighteningLightDebounce, darkeningLightDebounce,
- autoBrightnessResetAmbientLuxAfterWarmUp);
+ brighteningLightDebounce, brighteningLightFastDebounce,
+ darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
+ ambientLightHorizon);
}
}
@@ -573,6 +583,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
} else {
setProximitySensorEnabled(false);
mWaitingForNegativeProximity = false;
+ mProximity = PROXIMITY_UNKNOWN;
}
if (mScreenOffBecauseOfProximity
&& mProximity != PROXIMITY_POSITIVE) {
@@ -595,6 +606,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// Use zero brightness when screen is off.
if (state == Display.STATE_OFF) {
brightness = PowerManager.BRIGHTNESS_OFF;
+ mLights.getLight(LightsManager.LIGHT_ID_BUTTONS).setBrightness(brightness);
+ mLights.getLight(LightsManager.LIGHT_ID_KEYBOARD).setBrightness(brightness);
+ }
+
+ // Disable button lights when dozing
+ if (state == Display.STATE_DOZE || state == Display.STATE_DOZE_SUSPEND) {
+ mLights.getLight(LightsManager.LIGHT_ID_BUTTONS).setBrightness(PowerManager.BRIGHTNESS_OFF);
+ mLights.getLight(LightsManager.LIGHT_ID_KEYBOARD).setBrightness(PowerManager.BRIGHTNESS_OFF);
}
// Configure auto-brightness.
@@ -1130,7 +1149,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.dump(pw);
}
-
}
private static String proximityToString(int state) {
diff --git a/services/core/java/com/android/server/display/ExtendedRemoteDisplayHelper.java b/services/core/java/com/android/server/display/ExtendedRemoteDisplayHelper.java
new file mode 100644
index 0000000..1be474b
--- /dev/null
+++ b/services/core/java/com/android/server/display/ExtendedRemoteDisplayHelper.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.android.server.display;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import android.media.RemoteDisplay;
+import android.os.Handler;
+import android.util.Slog;
+import android.content.Context;
+
+class ExtendedRemoteDisplayHelper {
+ private static final String TAG = "ExtendedRemoteDisplayHelper";
+
+ // ExtendedRemoteDisplay class
+ // ExtendedRemoteDisplay is an enhanced RemoteDisplay. It has
+ // similar interface as RemoteDisplay class
+ private static Class sExtRemoteDisplayClass;
+
+ // Method object for the API ExtendedRemoteDisplay.Listen
+ // ExtendedRemoteDisplay.Listen has the same API signature as
+ // RemoteDisplay.Listen except for an additional argument to pass the
+ // Context
+ private static Method sExtRemoteDisplayListen;
+
+ // Method Object for the API ExtendedRemoteDisplay.Dispose
+ // ExtendedRemoteDisplay.Dispose follows the same API signature as
+ // RemoteDisplay.Dispose
+ private static Method sExtRemoteDisplayDispose;
+
+ static {
+ //Check availability of ExtendedRemoteDisplay runtime
+ try {
+ sExtRemoteDisplayClass = Class.forName("com.qualcomm.wfd.ExtendedRemoteDisplay");
+ } catch (Throwable t) {
+ Slog.i(TAG, "ExtendedRemoteDisplay Not available.");
+ }
+
+ if(sExtRemoteDisplayClass != null) {
+ // If ExtendedRemoteDisplay is available find the methods
+ Slog.i(TAG, "ExtendedRemoteDisplay Is available. Find Methods");
+ try {
+ Class args[] = {
+ String.class,
+ RemoteDisplay.Listener.class,
+ Handler.class, Context.class
+ };
+ sExtRemoteDisplayListen = sExtRemoteDisplayClass.getDeclaredMethod("listen", args);
+ } catch (Throwable t) {
+ Slog.i(TAG, "ExtendedRemoteDisplay.listen Not available.");
+ }
+
+ try {
+ Class args[] = {};
+ sExtRemoteDisplayDispose = sExtRemoteDisplayClass.getDeclaredMethod("dispose", args);
+ } catch (Throwable t) {
+ Slog.i(TAG, "ExtendedRemoteDisplay.dispose Not available.");
+ }
+ }
+ }
+
+ /**
+ * Starts listening for displays to be connected on the specified interface.
+ *
+ * @param iface The interface address and port in the form "x.x.x.x:y".
+ * @param listener The listener to invoke
+ * when displays are connected or disconnected.
+ * @param handler The handler on which to invoke the listener.
+ * @param context The current service context
+ * */
+ public static Object listen(String iface, RemoteDisplay.Listener listener,
+ Handler handler, Context context)
+ {
+ Object extRemoteDisplay = null;
+ Slog.i(TAG, "ExtendedRemoteDisplay.listen");
+
+ if(sExtRemoteDisplayListen != null && sExtRemoteDisplayDispose != null){
+ try {
+ extRemoteDisplay = sExtRemoteDisplayListen.invoke(null,
+ iface, listener, handler, context);
+ } catch (InvocationTargetException e) {
+ Slog.i(TAG, "ExtendedRemoteDisplay.listen - InvocationTargetException");
+ Throwable cause = e.getCause();
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ } else if (cause instanceof Error) {
+ throw (Error) cause;
+ } else {
+ throw new RuntimeException(e);
+ }
+ } catch (IllegalAccessException e) {
+ Slog.i(TAG, "ExtendedRemoteDisplay.listen -IllegalAccessException");
+ e.printStackTrace();
+ }
+ }
+ return extRemoteDisplay;
+ }
+
+ /**
+ * Disconnects the remote display and stops listening for new connections.
+ */
+ public static void dispose(Object extRemoteDisplay) {
+ Slog.i(TAG, "ExtendedRemoteDisplay.dispose");
+ try{
+ sExtRemoteDisplayDispose.invoke(extRemoteDisplay);
+ } catch (InvocationTargetException e) {
+ Slog.i(TAG, "ExtendedRemoteDisplay.dispose - InvocationTargetException");
+ Throwable cause = e.getCause();
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ } else if (cause instanceof Error) {
+ throw (Error) cause;
+ } else {
+ throw new RuntimeException(e);
+ }
+ } catch (IllegalAccessException e) {
+ Slog.i(TAG, "ExtendedRemoteDisplay.dispose-IllegalAccessException");
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Checks if ExtendedRemoteDisplay is available
+ */
+ public static boolean isAvailable()
+ {
+ if(sExtRemoteDisplayClass != null &&
+ sExtRemoteDisplayDispose != null &&
+ sExtRemoteDisplayListen != null) {
+ Slog.i(TAG, "ExtendedRemoteDisplay isAvailable() : Available.");
+ return true;
+ }
+ Slog.i(TAG, "ExtendedRemoteDisplay isAvailable() : Not Available.");
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 088d96e..7a28f48 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -58,6 +58,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
private static final int[] BUILT_IN_DISPLAY_IDS_TO_SCAN = new int[] {
SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN,
SurfaceControl.BUILT_IN_DISPLAY_ID_HDMI,
+ SurfaceControl.BUILT_IN_DISPLAY_ID_TERTIARY,
};
private final SparseArray<LocalDisplayDevice> mDevices =
@@ -191,6 +192,9 @@ final class LocalDisplayAdapter extends DisplayAdapter {
for (int j = 0; j < colorTransforms.size(); j++) {
if (colorTransforms.get(j).getColorTransform() == info.colorTransform) {
existingMode = true;
+ if (i == activeDisplayInfo) {
+ activeColorTransform = colorTransforms.get(j);
+ }
break;
}
}
diff --git a/services/core/java/com/android/server/display/WifiDisplayController.java b/services/core/java/com/android/server/display/WifiDisplayController.java
index 8932ca0..eaeca01 100644
--- a/services/core/java/com/android/server/display/WifiDisplayController.java
+++ b/services/core/java/com/android/server/display/WifiDisplayController.java
@@ -78,6 +78,9 @@ final class WifiDisplayController implements DumpUtils.Dump {
private static final int RTSP_TIMEOUT_SECONDS = 30;
private static final int RTSP_TIMEOUT_SECONDS_CERT_MODE = 120;
+ // time given for RTSP teardown sequence to complete.
+ private static final int RTSP_TEARDOWN_TIMEOUT = 3;
+
// We repeatedly issue calls to discover peers every so often for a few reasons.
// 1. The initial request may fail and need to retried.
// 2. Discovery will self-abort after any group is initiated, which may not necessarily
@@ -137,6 +140,10 @@ final class WifiDisplayController implements DumpUtils.Dump {
// Number of connection retries remaining.
private int mConnectionRetriesLeft;
+ // The Extended remote display that is listening on the connection.
+ // Created after the Wifi P2P network is connected.
+ private Object mExtRemoteDisplay;
+
// The remote display that is listening on the connection.
// Created after the Wifi P2P network is connected.
private RemoteDisplay mRemoteDisplay;
@@ -147,6 +154,12 @@ final class WifiDisplayController implements DumpUtils.Dump {
// True if RTSP has connected.
private boolean mRemoteDisplayConnected;
+ // Waiting for displayDisconnected from ERD.
+ private boolean mRemoteDisplayTearingDown;
+
+ // Timed out waiting for RTSP teardown to complete.
+ private boolean mRemoteDisplayRtspTeardownTimedOut;
+
// The information we have most recently told WifiDisplayAdapter about.
private WifiDisplay mAdvertisedDisplay;
private Surface mAdvertisedDisplaySurface;
@@ -359,7 +372,15 @@ final class WifiDisplayController implements DumpUtils.Dump {
}
private void updateScanState() {
- if (mScanRequested && mWfdEnabled && mDesiredDevice == null) {
+
+ if (true == mRemoteDisplayTearingDown) {
+ // when rtsp teardown sequence is completed or timed out, this
+ // function will be called again.
+ Slog.i(TAG, "updateScanState no-op as rtsp teardown sequence is in progress");
+ return;
+ }
+
+ if (mScanRequested && mWfdEnabled && (mDesiredDevice == null)) {
if (!mDiscoverPeersInProgress) {
Slog.i(TAG, "Starting Wifi display scan.");
mDiscoverPeersInProgress = true;
@@ -565,20 +586,37 @@ final class WifiDisplayController implements DumpUtils.Dump {
// Step 1. Before we try to connect to a new device, tell the system we
// have disconnected from the old one.
- if (mRemoteDisplay != null && mConnectedDevice != mDesiredDevice) {
- Slog.i(TAG, "Stopped listening for RTSP connection on " + mRemoteDisplayInterface
+ if ((mRemoteDisplay != null || mExtRemoteDisplay != null) &&
+ mConnectedDevice != mDesiredDevice && false == mRemoteDisplayTearingDown) {
+ Slog.i(TAG, "Stopped listening for RTSP connection on "
+ + mRemoteDisplayInterface
+ " from Wifi display: " + mConnectedDevice.deviceName);
- mRemoteDisplay.dispose();
- mRemoteDisplay = null;
- mRemoteDisplayInterface = null;
- mRemoteDisplayConnected = false;
+ if(mRemoteDisplay != null) {
+ mRemoteDisplay.dispose();
+ } else if(mExtRemoteDisplay != null) {
+ ExtendedRemoteDisplayHelper.dispose(mExtRemoteDisplay);
+ }
+
mHandler.removeCallbacks(mRtspTimeout);
+ mRemoteDisplayTearingDown = true;
- mWifiP2pManager.setMiracastMode(WifiP2pManager.MIRACAST_DISABLED);
- unadvertiseDisplay();
+ // Use extended timeout value for certification, as some tests require user inputs
+ int rtspTimeout = mWifiDisplayCertMode ?
+ RTSP_TIMEOUT_SECONDS_CERT_MODE : RTSP_TIMEOUT_SECONDS;
+
+ Slog.i(TAG, "Starting wait for rtsp teardown sequence for " +
+ rtspTimeout + " secs");
+
+ mHandler.postDelayed(mRtspTimeout, rtspTimeout * 1000);
- // continue to next step
+ return;
+ }
+
+ if (true == mRemoteDisplayTearingDown && false == mRemoteDisplayRtspTeardownTimedOut) {
+ // Need to wait for ERD to notify that p2p connection is no longer needed.
+ Slog.i(TAG, "updateConnection - return as rtsp teardown sequence in progress");
+ return;
}
// Step 2. Before we try to connect to a new device, disconnect from the old one.
@@ -618,6 +656,11 @@ final class WifiDisplayController implements DumpUtils.Dump {
return; // wait for asynchronous callback
}
+ if (true == mRemoteDisplayTearingDown) {
+ Slog.i(TAG, "rtsp teardown sequence in progress");
+ return;
+ }
+
// Step 3. Before we try to connect to a new device, stop trying to connect
// to the old one.
if (mCancelingDevice != null) {
@@ -717,7 +760,8 @@ final class WifiDisplayController implements DumpUtils.Dump {
}
// Step 6. Listen for incoming RTSP connection.
- if (mConnectedDevice != null && mRemoteDisplay == null) {
+ if (mConnectedDevice != null && mRemoteDisplay == null &&
+ mExtRemoteDisplay== null) {
Inet4Address addr = getInterfaceAddress(mConnectedDeviceGroupInfo);
if (addr == null) {
Slog.i(TAG, "Failed to get local interface address for communicating "
@@ -736,7 +780,7 @@ final class WifiDisplayController implements DumpUtils.Dump {
Slog.i(TAG, "Listening for RTSP connection on " + iface
+ " from Wifi display: " + mConnectedDevice.deviceName);
- mRemoteDisplay = RemoteDisplay.listen(iface, new RemoteDisplay.Listener() {
+ RemoteDisplay.Listener listener = new RemoteDisplay.Listener() {
@Override
public void onDisplayConnected(Surface surface,
int width, int height, int flags, int session) {
@@ -758,10 +802,11 @@ final class WifiDisplayController implements DumpUtils.Dump {
@Override
public void onDisplayDisconnected() {
+ Slog.i(TAG, "onDisplayDisconnected called");
if (mConnectedDevice == oldDevice) {
Slog.i(TAG, "Closed RTSP connection with Wifi display: "
+ mConnectedDevice.deviceName);
- mHandler.removeCallbacks(mRtspTimeout);
+ FinishRtspTeardown();
disconnect();
}
}
@@ -775,7 +820,14 @@ final class WifiDisplayController implements DumpUtils.Dump {
handleConnectionFailure(false);
}
}
- }, mHandler, mContext.getOpPackageName());
+ };
+ if(ExtendedRemoteDisplayHelper.isAvailable()){
+ mExtRemoteDisplay = ExtendedRemoteDisplayHelper.listen(iface,
+ listener, mHandler, mContext);
+ } else {
+ mRemoteDisplay = RemoteDisplay.listen(iface, listener,
+ mHandler, mContext.getOpPackageName());
+ }
// Use extended timeout value for certification, as some tests require user inputs
int rtspTimeout = mWifiDisplayCertMode ?
@@ -887,6 +939,21 @@ final class WifiDisplayController implements DumpUtils.Dump {
}
}
+ private void FinishRtspTeardown()
+ {
+ Slog.i(TAG, "Wait for rtsp teardown sequence completed");
+ mRemoteDisplayTearingDown = false;
+
+ mExtRemoteDisplay = null; // callbacks no longer needed
+ mRemoteDisplay = null;
+ mRemoteDisplayInterface = null;
+ mRemoteDisplayConnected = false;
+ mHandler.removeCallbacks(mRtspTimeout);
+
+ mWifiP2pManager.setMiracastMode(WifiP2pManager.MIRACAST_DISABLED);
+ unadvertiseDisplay();
+ }
+
private final Runnable mDiscoverPeers = new Runnable() {
@Override
public void run() {
@@ -909,12 +976,31 @@ final class WifiDisplayController implements DumpUtils.Dump {
private final Runnable mRtspTimeout = new Runnable() {
@Override
public void run() {
+ Slog.i(TAG, "mRtspTimeout triggerred");
if (mConnectedDevice != null
- && mRemoteDisplay != null && !mRemoteDisplayConnected) {
- Slog.i(TAG, "Timed out waiting for Wifi display RTSP connection after "
- + RTSP_TIMEOUT_SECONDS + " seconds: "
- + mConnectedDevice.deviceName);
- handleConnectionFailure(true);
+ && (mRemoteDisplay != null || mExtRemoteDisplay != null)) {
+ if (true == mRemoteDisplayTearingDown) {
+ // rtsp teardown sequence timed out
+ Slog.i(TAG, "Timed out waiting for RTSP teardown sequence after "
+ + RTSP_TEARDOWN_TIMEOUT + " seconds: "
+ + mConnectedDevice.deviceName);
+ mRemoteDisplayRtspTeardownTimedOut = true;
+
+ // this should close P2P
+ disconnect();
+
+ FinishRtspTeardown();
+
+ // Ok to resume wifi-scans
+ updateConnection();
+ } else if (!mRemoteDisplayConnected) {
+ Slog.i(TAG, "Timed out waiting for Wifi display RTSP connection after "
+ + RTSP_TIMEOUT_SECONDS + " seconds: "
+ + mConnectedDevice.deviceName);
+ handleConnectionFailure(true);
+ } else {
+ Slog.i(TAG, "Timed out. no-op");
+ }
}
}
};
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 8813a61..c986e74 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -18,6 +18,7 @@ package com.android.server.dreams;
import static android.Manifest.permission.BIND_DREAM_SERVICE;
+import android.view.WindowManagerPolicy;
import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
import com.android.server.SystemService;
@@ -84,6 +85,7 @@ public final class DreamManagerService extends SystemService {
private boolean mCurrentDreamIsWaking;
private int mCurrentDreamDozeScreenState = Display.STATE_UNKNOWN;
private int mCurrentDreamDozeScreenBrightness = PowerManager.BRIGHTNESS_DEFAULT;
+ private int mLidState = WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
public DreamManagerService(Context context) {
super(context);
@@ -150,6 +152,12 @@ public final class DreamManagerService extends SystemService {
}
}
+ private boolean isDozingInternal() {
+ synchronized (mLock) {
+ return mCurrentDreamIsDozing;
+ }
+ }
+
private void requestDreamInternal() {
// Ask the power manager to nap. It will eventually call back into
// startDream() if/when it is appropriate to start dreaming.
@@ -219,7 +227,8 @@ public final class DreamManagerService extends SystemService {
}
synchronized (mLock) {
- if (mCurrentDreamToken == token && mCurrentDreamCanDoze) {
+ if (mCurrentDreamToken == token && mCurrentDreamCanDoze
+ && mLidState != WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED) {
mCurrentDreamDozeScreenState = screenState;
mCurrentDreamDozeScreenBrightness = screenBrightness;
mPowerManagerInternal.setDozeOverrideFromDreamManager(
@@ -232,6 +241,43 @@ public final class DreamManagerService extends SystemService {
}
}
+ private int getLidStateInternal() {
+ return mLidState;
+ }
+
+ private void setLidStateInternal(int state) {
+ synchronized (mLock) {
+ if (mLidState == state) {
+ return;
+ }
+ mLidState = state;
+ }
+ switch (state) {
+ case WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT:
+ // do nothing
+ break;
+ case WindowManagerPolicy.WindowManagerFuncs.LID_OPEN:
+ synchronized (mLock) {
+ mPowerManagerInternal.setDozeOverrideFromDreamManager(
+ Display.STATE_UNKNOWN, PowerManager.BRIGHTNESS_DEFAULT);
+ }
+ break;
+ case WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED:
+ // mimicing logic from stopDozingInternal(), stop any thing when the lid is closed.
+ synchronized (mLock) {
+ if (mCurrentDreamIsDozing) {
+ mCurrentDreamIsDozing = false;
+ if (mDozeWakeLock.isHeld()) {
+ mDozeWakeLock.release();
+ }
+ mPowerManagerInternal.setDozeOverrideFromDreamManager(
+ Display.STATE_OFF, PowerManager.BRIGHTNESS_OFF);
+ }
+ }
+ break;
+ }
+ }
+
private void stopDozingInternal(IBinder token) {
if (DEBUG) {
Slog.d(TAG, "Dream requested to stop dozing: " + token);
@@ -530,6 +576,18 @@ public final class DreamManagerService extends SystemService {
}
@Override // Binder call
+ public boolean isDozing() {
+ checkPermission(android.Manifest.permission.READ_DREAM_STATE);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return isDozingInternal();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override // Binder call
public void dream() {
checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
@@ -621,6 +679,30 @@ public final class DreamManagerService extends SystemService {
Binder.restoreCallingIdentity(ident);
}
}
+
+ @Override
+ public void setLidState(int lidState) {
+ checkPermission(android.Manifest.permission.WRITE_DREAM_STATE);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ setLidStateInternal(lidState);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
+ public int getLidState() {
+ checkPermission(Manifest.permission.READ_DREAM_STATE);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ return getLidStateInternal();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
}
private final class LocalService extends DreamManagerInternal {
@@ -638,6 +720,11 @@ public final class DreamManagerService extends SystemService {
public boolean isDreaming() {
return isDreamingInternal();
}
+
+ @Override
+ public boolean isDozing() {
+ return isDozingInternal();
+ }
}
private final Runnable mSystemPropertiesChanged = new Runnable() {
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 84aa2d7..e5ab37f 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -19,7 +19,6 @@ package com.android.server.fingerprint;
import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
-import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManagerNative;
import android.app.AlarmManager;
import android.app.AppOpsManager;
@@ -30,6 +29,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.hardware.fingerprint.IFingerprintServiceLockoutResetCallback;
@@ -144,12 +144,15 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
resetFailedAttempts();
}
};
+ private boolean mFingerprintManagerRestrictedToSystemAndOwner;
public FingerprintService(Context context) {
super(context);
mContext = context;
mKeyguardPackage = ComponentName.unflattenFromString(context.getResources().getString(
com.android.internal.R.string.config_keyguardComponent)).getPackageName();
+ mFingerprintManagerRestrictedToSystemAndOwner = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_fingerprintRestrictedToSystemAndOwner);
mAppOps = context.getSystemService(AppOpsManager.class);
mPowerManager = mContext.getSystemService(PowerManager.class);
mAlarmManager = mContext.getSystemService(AlarmManager.class);
@@ -586,6 +589,21 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe
Slog.w(TAG, "Rejecting " + opPackageName + " ; not in foreground");
return false;
}
+ if (mFingerprintManagerRestrictedToSystemAndOwner) {
+ try {
+ ApplicationInfo ai = mContext.getPackageManager()
+ .getApplicationInfo(opPackageName, PackageManager.GET_META_DATA);
+ if (ai != null && ai.isSystemApp() && Binder.getCallingUserHandle().isOwner()) {
+ return true;
+ }
+ Slog.w(TAG, "Rejecting " + opPackageName
+ + "(uid: " + uid + ") ; fingerprint restricted to system apps.");
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, opPackageName + " package not found, not allowing fingerprint access.");
+ return false;
+ }
+ return false;
+ }
return true;
}
diff --git a/services/core/java/com/android/server/gesture/GestureInputFilter.java b/services/core/java/com/android/server/gesture/GestureInputFilter.java
new file mode 100644
index 0000000..e40761f
--- /dev/null
+++ b/services/core/java/com/android/server/gesture/GestureInputFilter.java
@@ -0,0 +1,333 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod 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.gesture;
+
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.content.Context;
+import android.hardware.input.InputManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.view.Display;
+import android.view.GestureDetector;
+import android.view.GestureDetector.OnDoubleTapListener;
+import android.view.IInputFilter;
+import android.view.IInputFilterHost;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+import android.view.OrientationEventListener;
+import android.view.ViewConfiguration;
+import android.view.WindowManager;
+import java.io.PrintWriter;
+
+/**
+ * A simple input filter that listens for gesture sensor events and converts
+ * them to input events to be injected into the input stream.
+ */
+public class GestureInputFilter implements IInputFilter, GestureDetector.OnGestureListener, OnDoubleTapListener {
+
+ private static final String TAG = "GestureInputFilter";
+ private static final boolean DEBUG = false;
+
+ private IInputFilterHost mHost = null;
+
+ private GestureDetector mGestureDetector;
+ private InputManager mInputManager;
+ private OrientationEventListener mOrientationListener;
+ private final int mScreenWidth, mScreenHeight;
+ private float mGesturePadWidth, mGesturePadHeight;
+ private int mTouchSlop, mOrientation;
+ private Context mContext;
+ private PendingIntent mLongPressPendingIntent;
+ private PendingIntent mDoubleClickPendingIntent;
+
+ public GestureInputFilter(Context context) {
+ mInputManager = InputManager.getInstance();
+ mContext = context;
+ for (int id : mInputManager.getInputDeviceIds()) {
+ InputDevice inputDevice = mInputManager.getInputDevice(id);
+ if ((inputDevice.getSources() & InputDevice.SOURCE_GESTURE_SENSOR)
+ == mInputManager.getInputDevice(id).getSources()) {
+ mGesturePadWidth = inputDevice.getMotionRange(MotionEvent.AXIS_X).getMax();
+ mGesturePadHeight = inputDevice.getMotionRange(MotionEvent.AXIS_Y).getMax();
+ break;
+ }
+ }
+ ViewConfiguration vc = ViewConfiguration.get(context);
+ mTouchSlop = vc.getScaledTouchSlop();
+ WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ Display display = wm.getDefaultDisplay();
+ mScreenWidth = display.getWidth();
+ mScreenHeight = display.getHeight();
+ mGestureDetector = new GestureDetector(context, this);
+ mGestureDetector.setOnDoubleTapListener(this);
+ mOrientationListener = new OrientationEventListener(context) {
+ @Override
+ public void onOrientationChanged(int orientation) {
+ if (orientation == -1) {
+ return;
+ }
+ mOrientation = (orientation + 45) / 90 * 90;
+ }
+ };
+ }
+
+ /**
+ * Called to enqueue the input event for filtering.
+ * The event must be recycled after the input filter processed it.
+ * This method is guaranteed to be non-reentrant.
+ *
+ * @see InputFilter#filterInputEvent(InputEvent, int)
+ * @param event The input event to enqueue.
+ */
+ // called by the input dispatcher thread
+ public void filterInputEvent(InputEvent event, int policyFlags)
+ throws RemoteException {
+ if (DEBUG) Slog.d(TAG, event.toString());
+
+ try {
+ if (event.getSource() != InputDevice.SOURCE_GESTURE_SENSOR
+ || !(event instanceof MotionEvent)) {
+ try {
+ mHost.sendInputEvent(event, policyFlags);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ return;
+ }
+
+ MotionEvent motionEvent = (MotionEvent) event;
+ mGestureDetector.onTouchEvent(motionEvent);
+ } finally {
+ event.recycle();
+ }
+ }
+
+ // called by the input dispatcher thread
+ public void install(IInputFilterHost host) throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "Gesture input filter installed.");
+ }
+ mHost = host;
+ mOrientationListener.enable();
+ }
+
+ // called by the input dispatcher thread
+ public void uninstall() throws RemoteException {
+ if (DEBUG) {
+ Slog.d(TAG, "Gesture input filter uninstalled.");
+ }
+ mHost = null;
+ mOrientationListener.disable();
+ mContext = null;
+ }
+
+ // should never be called
+ public IBinder asBinder() {
+ throw new UnsupportedOperationException();
+ }
+
+ // called by a Binder thread
+ public void dump(PrintWriter pw, String prefix) {
+
+ }
+
+ private boolean generateSwipe(MotionEvent e1, MotionEvent e2) {
+ switch (mOrientation) {
+ case 90:
+ Slog.d(TAG, "Adjusting motion for 90 degrees");
+ e1.setLocation(e1.getY(), e1.getX());
+ e2.setLocation(e2.getY(), e2.getX());
+ break;
+ case 180:
+ Slog.d(TAG, "Adjusting motion for 180 degrees");
+ e1.setLocation(mGesturePadWidth - e1.getX(),
+ mGesturePadHeight - e1.getY());
+ e2.setLocation(mGesturePadWidth - e2.getX(),
+ mGesturePadHeight - e2.getY());
+ break;
+ case 270:
+ Slog.d(TAG, "Adjusting motion for 270 degrees");
+ e1.setLocation(mGesturePadHeight - e1.getY(),
+ e1.getX());
+ e2.setLocation(mGesturePadHeight - e2.getY(),
+ e2.getX());
+ break;
+ }
+
+ float deltaX = Math.abs(e1.getX() - e2.getX());
+ float deltaY = Math.abs(e1.getY() - e2.getY());
+
+ if (deltaX < mTouchSlop && deltaY < mTouchSlop) {
+ return false;
+ }
+
+ if (deltaX > deltaY) {
+ e2.setLocation(e2.getX(), e1.getY());
+ } else if (deltaY > deltaX) {
+ e2.setLocation(e1.getX(), e2.getY());
+ }
+
+ float scaleX = mScreenWidth / mGesturePadWidth;
+ float scaleY = mScreenHeight / mGesturePadHeight;
+
+ float magnitudeX = deltaX * scaleX;
+ float magnitudeY = deltaY * scaleY;
+
+ float origX = mScreenWidth / 2;
+ float origY = mScreenHeight / 2;
+ float endX = 0.0f;
+ float endY = 0.0f;
+
+ if (e2.getY() > e1.getY()) {
+ if (DEBUG) Slog.d(TAG, "Detected down motion");
+ // Ensure selection does not occur
+ endX = origX + mTouchSlop + 5;
+ endY = origY + magnitudeY;
+ } else if (e2.getY() < e1.getY()) {
+ if (DEBUG) Slog.d(TAG, "Detected up motion");
+ endX = origX + mTouchSlop + 5;
+ endY = origY - magnitudeY;
+ } else if (e2.getX() > e1.getX()) {
+ if (DEBUG) Slog.d(TAG, "Detected left motion");
+ endX = origX + magnitudeX;
+ endY = origY + mTouchSlop + 5;
+ } else if (e2.getX() < e1.getX()) {
+ if (DEBUG) Slog.d(TAG, "Detected right motion");
+ endX = origX - magnitudeX;
+ endY = origY + mTouchSlop + 5;
+ } else {
+ return false;
+ }
+
+ sendSwipe(origX, origY, endX, endY);
+ return true;
+ }
+
+ private void sendSwipe(float x1, float y1, float x2, float y2) {
+ final long duration = 100;
+ long now = SystemClock.uptimeMillis();
+ final long startTime = now;
+ final long endTime = startTime + duration;
+ sendMotionEvent(MotionEvent.ACTION_DOWN, now, x1, y1, 1.0f);
+
+ while (now < endTime) {
+ long elapsedTime = now - startTime;
+ float alpha = (float) elapsedTime / duration;
+ sendMotionEvent(MotionEvent.ACTION_MOVE, now,
+ lerp(x1, x2, alpha), lerp(y1, y2, alpha), 1.0f);
+ now = SystemClock.uptimeMillis();
+ }
+ sendMotionEvent(MotionEvent.ACTION_UP, now, x2, y2, 1.0f);
+ }
+
+ private void sendMotionEvent(int action, long when, float x, float y,
+ float pressure) {
+ final float DEFAULT_SIZE = 1.0f;
+ final int DEFAULT_META_STATE = 0;
+ final float DEFAULT_PRECISION_X = 1.0f;
+ final float DEFAULT_PRECISION_Y = 1.0f;
+ final int DEFAULT_DEVICE_ID = 0;
+ final int DEFAULT_EDGE_FLAGS = 0;
+
+ MotionEvent e = MotionEvent.obtain(when, when, action, x, y, pressure,
+ DEFAULT_SIZE, DEFAULT_META_STATE, DEFAULT_PRECISION_X,
+ DEFAULT_PRECISION_Y, DEFAULT_DEVICE_ID, DEFAULT_EDGE_FLAGS);
+ e.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ sendInputEvent(e);
+ }
+
+ private void sendInputEvent(InputEvent event) {
+ mInputManager.injectInputEvent(event,
+ InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
+ }
+
+ private static final float lerp(float a, float b, float alpha) {
+ return (b - a) * alpha + a;
+ }
+
+ @Override
+ public boolean onDown(MotionEvent e) {
+ return false;
+ }
+
+ @Override
+ public void onShowPress(MotionEvent e) {
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent e) {
+ return false;
+ }
+
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+ float distanceY) {
+ return false;
+ }
+
+ @Override
+ public void onLongPress(MotionEvent e) {
+ if (mLongPressPendingIntent != null) {
+ try {
+ mLongPressPendingIntent.send();
+ } catch (CanceledException e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ return generateSwipe(e1, e2);
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ return false;
+ }
+
+ @Override
+ public boolean onDoubleTap(MotionEvent e) {
+ if (mDoubleClickPendingIntent != null) {
+ try {
+ mDoubleClickPendingIntent.send();
+ return true;
+ } catch (CanceledException e1) {
+ e1.printStackTrace();
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onDoubleTapEvent(MotionEvent e) {
+ return false;
+ }
+
+ public void setOnLongPressPendingIntent(PendingIntent pendingIntent) {
+ mLongPressPendingIntent = pendingIntent;
+ }
+
+ public void setOnDoubleClickPendingIntent(PendingIntent pendingIntent) {
+ mDoubleClickPendingIntent = pendingIntent;
+ }
+}
diff --git a/services/core/java/com/android/server/gesture/GestureService.java b/services/core/java/com/android/server/gesture/GestureService.java
new file mode 100644
index 0000000..1a01e41
--- /dev/null
+++ b/services/core/java/com/android/server/gesture/GestureService.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project (Jens Doll)
+ *
+ * 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.gesture;
+
+import android.Manifest;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.service.gesture.IGestureService;
+import android.util.Slog;
+
+import com.android.server.input.InputManagerService;
+
+/**
+ * A system service to track gesture sensor gestures. This service is
+ * responsible for creating input events from motion events generated by
+ * gesture sensor input hardware:
+ * <li>Installing an input filter to listen for gesture sensor events</li>
+ * <li>Generating input events to be injected into the input stream</li>
+ */
+public class GestureService extends IGestureService.Stub {
+ public static final String TAG = "GestureService";
+ public static final boolean DEBUG = false;
+
+ private Context mContext;
+ private InputManagerService mInputManager;
+ private GestureInputFilter mInputFilter;
+
+ public GestureService(Context context, InputManagerService inputManager) {
+ mContext = context;
+ mInputManager = inputManager;
+ }
+
+ // called by system server
+ public void systemReady() {
+ if (DEBUG) Slog.d(TAG, "Starting Gesture Sensor service");
+ mInputFilter = new GestureInputFilter(mContext);
+ mInputManager.registerSecondaryInputFilter(mInputFilter);
+ }
+
+ public void setOnLongPressPendingIntent(PendingIntent pendingIntent) {
+ mInputFilter.setOnLongPressPendingIntent(pendingIntent);
+ }
+
+ public void setOnDoubleClickPendingIntent(PendingIntent pendingIntent) {
+ mInputFilter.setOnDoubleClickPendingIntent(pendingIntent);
+ }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 0205a20..37326cc 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -97,6 +97,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import cyanogenmod.providers.CMSettings;
import libcore.io.Streams;
import libcore.util.Objects;
@@ -161,8 +162,9 @@ public class InputManagerService extends IInputManager.Stub
// State for the currently installed input filter.
final Object mInputFilterLock = new Object();
- IInputFilter mInputFilter; // guarded by mInputFilterLock
- InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
+ ChainedInputFilterHost mInputFilterHost; // guarded by mInputFilterLock
+ ArrayList<ChainedInputFilterHost> mInputFilterChain =
+ new ArrayList<ChainedInputFilterHost>(); // guarded by mInputFilterLock
private static native long nativeInit(InputManagerService service,
Context context, MessageQueue messageQueue);
@@ -197,6 +199,8 @@ public class InputManagerService extends IInputManager.Stub
InputChannel fromChannel, InputChannel toChannel);
private static native void nativeSetPointerSpeed(long ptr, int speed);
private static native void nativeSetShowTouches(long ptr, boolean enabled);
+ private static native void nativeSetStylusIconEnabled(long ptr, boolean enabled);
+ private static native void nativeSetVolumeKeysRotation(long ptr, int mode);
private static native void nativeSetInteractive(long ptr, boolean interactive);
private static native void nativeReloadCalibration(long ptr);
private static native void nativeVibrate(long ptr, int deviceId, long[] pattern,
@@ -305,17 +309,22 @@ public class InputManagerService extends IInputManager.Stub
registerPointerSpeedSettingObserver();
registerShowTouchesSettingObserver();
+ registerStylusIconEnabledSettingObserver();
+ registerVolumeKeysRotationSettingObserver();
mContext.registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
+ updateVolumeKeysRotationFromSettings();
}
}, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
updatePointerSpeedFromSettings();
updateShowTouchesFromSettings();
+ updateStylusIconEnabledFromSettings();
+ updateVolumeKeysRotationFromSettings();
}
// TODO(BT) Pass in paramter for bluetooth system
@@ -512,34 +521,71 @@ public class InputManagerService extends IInputManager.Stub
*/
public void setInputFilter(IInputFilter filter) {
synchronized (mInputFilterLock) {
- final IInputFilter oldFilter = mInputFilter;
- if (oldFilter == filter) {
- return; // nothing to do
- }
-
- if (oldFilter != null) {
- mInputFilter = null;
+ if (mInputFilterHost != null) {
mInputFilterHost.disconnectLocked();
+ mInputFilterChain.remove(mInputFilterHost);
mInputFilterHost = null;
- try {
- oldFilter.uninstall();
- } catch (RemoteException re) {
- /* ignore */
- }
}
if (filter != null) {
- mInputFilter = filter;
- mInputFilterHost = new InputFilterHost();
- try {
- filter.install(mInputFilterHost);
- } catch (RemoteException re) {
- /* ignore */
+ ChainedInputFilterHost head = mInputFilterChain.isEmpty() ? null :
+ mInputFilterChain.get(0);
+ mInputFilterHost = new ChainedInputFilterHost(filter, head);
+ mInputFilterHost.connectLocked();
+ mInputFilterChain.add(0, mInputFilterHost);
+ }
+
+ nativeSetInputFilterEnabled(mPtr, !mInputFilterChain.isEmpty());
+ }
+ }
+
+ /**
+ * Registers a secondary input filter. These filters are always behind the "original"
+ * input filter. This ensures that all input events will be filtered by the
+ * {@code AccessibilityManagerService} first.
+ * <p>
+ * <b>Note:</b> Even though this implementation using AIDL interfaces, it is designed to only
+ * provide direct access. Therefore, any filter registering should reside in the
+ * system server DVM only!
+ *
+ * @param filter The input filter to register.
+ */
+ public void registerSecondaryInputFilter(IInputFilter filter) {
+ synchronized (mInputFilterLock) {
+ ChainedInputFilterHost host = new ChainedInputFilterHost(filter, null);
+ if (!mInputFilterChain.isEmpty()) {
+ mInputFilterChain.get(mInputFilterChain.size() - 1).mNext = host;
+ }
+ host.connectLocked();
+ mInputFilterChain.add(host);
+
+ nativeSetInputFilterEnabled(mPtr, !mInputFilterChain.isEmpty());
+ }
+ }
+
+ public void unregisterSecondaryInputFilter(IInputFilter filter) {
+ synchronized (mInputFilterLock) {
+ int index = findInputFilterIndexLocked(filter);
+ if (index >= 0) {
+ ChainedInputFilterHost host = mInputFilterChain.get(index);
+ host.disconnectLocked();
+ if (index >= 1) {
+ mInputFilterChain.get(index - 1).mNext = host.mNext;
}
+ mInputFilterChain.remove(index);
}
- nativeSetInputFilterEnabled(mPtr, filter != null);
+ nativeSetInputFilterEnabled(mPtr, !mInputFilterChain.isEmpty());
+ }
+ }
+
+ private int findInputFilterIndexLocked(IInputFilter filter) {
+ for (int i = 0; i < mInputFilterChain.size(); i++) {
+ if (mInputFilterChain.get(i).mInputFilter == filter) {
+ return i;
+ }
}
+ return -1;
}
@Override // Binder call
@@ -1357,6 +1403,58 @@ public class InputManagerService extends IInputManager.Stub
return result;
}
+ public void updateStylusIconEnabledFromSettings() {
+ int enabled = getStylusIconEnabled(0);
+ nativeSetStylusIconEnabled(mPtr, enabled != 0);
+ }
+
+ public void registerStylusIconEnabledSettingObserver() {
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.STYLUS_ICON_ENABLED), false,
+ new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateStylusIconEnabledFromSettings();
+ }
+ });
+ }
+
+ private int getStylusIconEnabled(int defaultValue) {
+ int result = defaultValue;
+ try {
+ result = Settings.System.getInt(mContext.getContentResolver(),
+ Settings.System.STYLUS_ICON_ENABLED);
+ } catch (SettingNotFoundException snfe) {
+ }
+ return result;
+ }
+
+ public void updateVolumeKeysRotationFromSettings() {
+ int mode = getVolumeKeysRotationSetting(0);
+ nativeSetVolumeKeysRotation(mPtr, mode);
+ }
+
+ public void registerVolumeKeysRotationSettingObserver() {
+ mContext.getContentResolver().registerContentObserver(
+ CMSettings.System.getUriFor(CMSettings.System.SWAP_VOLUME_KEYS_ON_ROTATION), false,
+ new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateVolumeKeysRotationFromSettings();
+ }
+ });
+ }
+
+ private int getVolumeKeysRotationSetting(int defaultValue) {
+ int result = defaultValue;
+ try {
+ result = CMSettings.System.getIntForUser(mContext.getContentResolver(),
+ CMSettings.System.SWAP_VOLUME_KEYS_ON_ROTATION, UserHandle.USER_CURRENT);
+ } catch (CMSettings.CMSettingNotFoundException snfe) {
+ }
+ return result;
+ }
+
// Binder call
@Override
public void vibrate(int deviceId, long[] pattern, int repeat, IBinder token) {
@@ -1521,15 +1619,22 @@ public class InputManagerService extends IInputManager.Stub
// Native callback.
final boolean filterInputEvent(InputEvent event, int policyFlags) {
+ ChainedInputFilterHost head = null;
synchronized (mInputFilterLock) {
- if (mInputFilter != null) {
- try {
- mInputFilter.filterInputEvent(event, policyFlags);
- } catch (RemoteException e) {
- /* ignore */
- }
- return false;
+ if (!mInputFilterChain.isEmpty()) {
+ head = mInputFilterChain.get(0);
+ }
+ }
+ // call filter input event outside of the lock.
+ // this is safe, because we know that mInputFilter never changes.
+ // we may loose a event, but this does not differ from the original implementation.
+ if (head != null) {
+ try {
+ head.mInputFilter.filterInputEvent(event, policyFlags);
+ } catch (RemoteException e) {
+ /* ignore */
}
+ return false;
}
event.recycle();
return true;
@@ -1786,6 +1891,66 @@ public class InputManagerService extends IInputManager.Stub
}
}
+ /**
+ * Hosting interface for input filters to call back into the input manager.
+ */
+ private final class ChainedInputFilterHost extends IInputFilterHost.Stub {
+ private final IInputFilter mInputFilter;
+ private ChainedInputFilterHost mNext;
+ private boolean mDisconnected;
+
+ private ChainedInputFilterHost(IInputFilter filter, ChainedInputFilterHost next) {
+ mInputFilter = filter;
+ mNext = next;
+ mDisconnected = false;
+ }
+
+ public void connectLocked() {
+ try {
+ mInputFilter.install(this);
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ }
+
+ public void disconnectLocked() {
+ try {
+ mInputFilter.uninstall();
+ } catch (RemoteException re) {
+ /* ignore */
+ }
+ // DO NOT set mInputFilter to null here! mInputFilter is used outside of the lock!
+ mDisconnected = true;
+ }
+
+ @Override
+ public void sendInputEvent(InputEvent event, int policyFlags) {
+ if (event == null) {
+ throw new IllegalArgumentException("event must not be null");
+ }
+
+ synchronized (mInputFilterLock) {
+ if (!mDisconnected) {
+ if (mNext == null) {
+ nativeInjectInputEvent(mPtr, event, Display.DEFAULT_DISPLAY, 0, 0,
+ InputManager.INJECT_INPUT_EVENT_MODE_ASYNC, 0,
+ policyFlags | WindowManagerPolicy.FLAG_FILTERED);
+ } else {
+ try {
+ // We need to pass a copy into filterInputEvent as it assumes
+ // the callee takes responsibility and recycles it - in case
+ // multiple filters are chained, calling into the second filter
+ // will cause event to be recycled twice
+ mNext.mInputFilter.filterInputEvent(event.copy(), policyFlags);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ }
+ }
+ }
+ }
+ }
+
private static final class KeyboardLayoutDescriptor {
public String packageName;
public String receiverName;
diff --git a/services/core/java/com/android/server/lights/Light.java b/services/core/java/com/android/server/lights/Light.java
index b496b4c..3eb570c 100644
--- a/services/core/java/com/android/server/lights/Light.java
+++ b/services/core/java/com/android/server/lights/Light.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2015 The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,6 +36,7 @@ public abstract class Light {
public abstract void setBrightness(int brightness, int brightnessMode);
public abstract void setColor(int color);
public abstract void setFlashing(int color, int mode, int onMS, int offMS);
+ public abstract void setModes(int brightnessLevel, boolean multipleLeds);
public abstract void pulse();
public abstract void pulse(int color, int onMS);
public abstract void turnOff();
diff --git a/services/core/java/com/android/server/lights/LightsManager.java b/services/core/java/com/android/server/lights/LightsManager.java
index 2f20509..e1e5aa3 100644
--- a/services/core/java/com/android/server/lights/LightsManager.java
+++ b/services/core/java/com/android/server/lights/LightsManager.java
@@ -25,7 +25,9 @@ public abstract class LightsManager {
public static final int LIGHT_ID_ATTENTION = 5;
public static final int LIGHT_ID_BLUETOOTH = 6;
public static final int LIGHT_ID_WIFI = 7;
- public static final int LIGHT_ID_COUNT = 8;
+ public static final int LIGHT_ID_CAPS = 8;
+ public static final int LIGHT_ID_FUNC = 9;
+ public static final int LIGHT_ID_COUNT = 10;
public abstract Light getLight(int id);
}
diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java
index ed884ef..d4762bd 100644
--- a/services/core/java/com/android/server/lights/LightsService.java
+++ b/services/core/java/com/android/server/lights/LightsService.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2015 The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -34,6 +35,9 @@ public class LightsService extends SystemService {
private LightImpl(int id) {
mId = id;
+ mBrightnessLevel = 0xFF;
+ mModesUpdate = false;
+ mMultipleLeds = false;
}
@Override
@@ -65,6 +69,20 @@ public class LightsService extends SystemService {
}
@Override
+ public void setModes(int brightnessLevel, boolean multipleLeds) {
+ synchronized (this) {
+ if (mBrightnessLevel != brightnessLevel) {
+ mBrightnessLevel = brightnessLevel;
+ mModesUpdate = true;
+ }
+ if (mMultipleLeds != multipleLeds) {
+ mMultipleLeds = multipleLeds;
+ mModesUpdate = true;
+ }
+ }
+ }
+
+ @Override
public void pulse() {
pulse(0x00ffffff, 7);
}
@@ -94,17 +112,21 @@ public class LightsService extends SystemService {
}
private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {
- if (color != mColor || mode != mMode || onMS != mOnMS || offMS != mOffMS) {
+ if (mModesUpdate || color != mColor || mode != mMode || onMS != mOnMS ||
+ offMS != mOffMS || mReset) {
if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"
+ Integer.toHexString(color));
+ mReset = false;
mColor = color;
mMode = mode;
mOnMS = onMS;
mOffMS = offMS;
+ mModesUpdate = false;
Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x"
+ Integer.toHexString(color) + ")");
try {
- setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode);
+ setLight_native(mNativePointer, mId, color, mode, onMS, offMS, brightnessMode,
+ mBrightnessLevel, mMultipleLeds ? 1 : 0);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_POWER);
}
@@ -116,7 +138,11 @@ public class LightsService extends SystemService {
private int mMode;
private int mOnMS;
private int mOffMS;
+ private int mBrightnessLevel;
private boolean mFlashing;
+ private boolean mModesUpdate;
+ private boolean mMultipleLeds;
+ private boolean mReset = true;
}
public LightsService(Context context) {
@@ -163,7 +189,8 @@ public class LightsService extends SystemService {
private static native void finalize_native(long ptr);
static native void setLight_native(long ptr, int light, int color, int mode,
- int onMS, int offMS, int brightnessMode);
+ int onMS, int offMS, int brightnessMode, int brightnessLevel,
+ int mMultipleLeds);
private long mNativePointer;
}
diff --git a/services/core/java/com/android/server/location/GeoFencerBase.java b/services/core/java/com/android/server/location/GeoFencerBase.java
new file mode 100644
index 0000000..eec07ab
--- /dev/null
+++ b/services/core/java/com/android/server/location/GeoFencerBase.java
@@ -0,0 +1,147 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only.
+ *
+ * Copyright (C) 2007 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.location;
+
+import android.os.Binder;
+import android.os.Parcelable;
+import android.util.Log;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.location.GeoFenceParams;
+import android.location.ILocationListener;
+import java.io.PrintWriter;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Collection;
+import java.util.ArrayList;
+
+/**
+ * This class defines a base class for GeoFencers
+ *
+ * @hide
+ */
+public abstract class GeoFencerBase {
+ private static final String TAG = "GeoFencerBase";
+ private HashMap<PendingIntent,GeoFenceParams> mGeoFences;
+
+ public GeoFencerBase() {
+ mGeoFences = new HashMap<PendingIntent,GeoFenceParams>();
+ }
+
+ public void add(double latitude, double longitude,
+ float radius, long expiration, PendingIntent intent,
+ String packageName) {
+ add(new GeoFenceParams(latitude, longitude, radius,
+ expiration, intent, packageName));
+ }
+
+ public void add(GeoFenceParams geoFence) {
+ synchronized(mGeoFences) {
+ mGeoFences.put(geoFence.mIntent, geoFence);
+ }
+ if (!start(geoFence)) {
+ synchronized(mGeoFences) {
+ mGeoFences.remove(geoFence.mIntent);
+ }
+ }
+ }
+
+ public void remove(PendingIntent intent) {
+ remove(intent, false);
+ }
+
+ public void remove(PendingIntent intent, boolean localOnly) {
+ GeoFenceParams geoFence = null;
+
+ synchronized(mGeoFences) {
+ geoFence = mGeoFences.remove(intent);
+ }
+
+ if (geoFence != null) {
+ if (!localOnly && !stop(intent)) {
+ synchronized(mGeoFences) {
+ mGeoFences.put(geoFence.mIntent, geoFence);
+ }
+ }
+ }
+ }
+
+ public int getNumbOfGeoFences() {
+ return mGeoFences.size();
+ }
+
+ public Collection<GeoFenceParams> getAllGeoFences() {
+ return mGeoFences.values();
+ }
+
+ public GeoFenceParams getGeoFence(PendingIntent intent) {
+ return mGeoFences.get(intent);
+ }
+
+ public boolean hasCaller(int uid) {
+ for (GeoFenceParams alert : mGeoFences.values()) {
+ if (alert.mUid == uid) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void removeCaller(int uid) {
+ ArrayList<PendingIntent> removedFences = null;
+ for (GeoFenceParams alert : mGeoFences.values()) {
+ if (alert.mUid == uid) {
+ if (removedFences == null) {
+ removedFences = new ArrayList<PendingIntent>();
+ }
+ removedFences.add(alert.mIntent);
+ }
+ }
+ if (removedFences != null) {
+ for (int i = removedFences.size()-1; i>=0; i--) {
+ mGeoFences.remove(removedFences.get(i));
+ }
+ }
+ }
+
+ public void transferService(GeoFencerBase geofencer) {
+ for (GeoFenceParams alert : geofencer.mGeoFences.values()) {
+ geofencer.stop(alert.mIntent);
+ add(alert);
+ }
+ }
+
+ public void dump(PrintWriter pw, String prefix) {
+ if (mGeoFences.size() > 0) {
+ pw.println(prefix + " GeoFences:");
+ prefix += " ";
+ for (Map.Entry<PendingIntent, GeoFenceParams> i
+ : mGeoFences.entrySet()) {
+ pw.println(prefix + i.getKey() + ":");
+ i.getValue().dump(pw, prefix);
+ }
+ }
+ }
+
+ abstract protected boolean start(GeoFenceParams geoFence);
+ abstract protected boolean stop(PendingIntent intent);
+}
diff --git a/services/core/java/com/android/server/location/GeoFencerProxy.java b/services/core/java/com/android/server/location/GeoFencerProxy.java
new file mode 100644
index 0000000..8ffbe8c
--- /dev/null
+++ b/services/core/java/com/android/server/location/GeoFencerProxy.java
@@ -0,0 +1,149 @@
+/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution, Apache license notifications and license are retained
+ * for attribution purposes only.
+ *
+ * Copyright (C) 2007 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.location;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+import android.app.PendingIntent;
+import android.location.IGeoFencer;
+import android.location.IGeoFenceListener;
+import android.location.GeoFenceParams;
+
+/**
+ * A class for proxying IGeoFenceProvider implementations.
+ *
+ * {@hide}
+ */
+public class GeoFencerProxy extends GeoFencerBase {
+
+ private static final String TAG = "GeoFencerProxy";
+ private static final boolean LOGV_ENABLED = true;
+
+ private final Context mContext;
+ private final Intent mIntent;
+ private IGeoFencer mGeoFencer;
+
+ private final ServiceConnection mServiceConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ synchronized (this) {
+ mGeoFencer = IGeoFencer.Stub.asInterface(service);
+ notifyAll();
+ }
+ Log.v(TAG, "onServiceConnected: mGeoFencer - "+mGeoFencer);
+ }
+ public void onServiceDisconnected(ComponentName className) {
+ synchronized (this) {
+ mGeoFencer = null;
+ }
+ Log.v(TAG, "onServiceDisconnected");
+ }
+ };
+
+ private final IGeoFenceListener.Stub mListener = new IGeoFenceListener.Stub() {
+ @Override
+ public void geoFenceExpired(PendingIntent intent) throws RemoteException {
+ logv("geoFenceExpired - "+intent);
+ remove(intent, true);
+ }
+ };
+
+ private static GeoFencerProxy mGeoFencerProxy;
+ public static GeoFencerProxy getGeoFencerProxy(Context context, String serviceName) {
+ if (mGeoFencerProxy == null) {
+ mGeoFencerProxy = new GeoFencerProxy(context, serviceName);
+ }
+ return mGeoFencerProxy;
+ }
+
+ private GeoFencerProxy(Context context, String serviceName) {
+ mContext = context;
+ mIntent = new Intent(IGeoFencer.class.getName());
+ mIntent.setPackage(serviceName);
+ mContext.bindService(mIntent, mServiceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
+ | Context.BIND_ALLOW_OOM_MANAGEMENT);
+ }
+
+ public void removeCaller(int uid) {
+ super.removeCaller(uid);
+ if(mGeoFencer != null) {
+ try {
+ mGeoFencer.clearGeoFenceUser(uid);
+ } catch (RemoteException re) {
+ }
+ }
+ else
+ Log.e(TAG, "removeCaller - mGeoFencer is null");
+ }
+
+ private boolean ensureGeoFencer() {
+ if (mGeoFencer == null) {
+ try {
+ synchronized(mServiceConnection) {
+ logv("waiting...");
+ mServiceConnection.wait(60000);
+ logv("woke up!!!");
+ }
+ } catch (InterruptedException ie) {
+ Log.w(TAG, "Interrupted while waiting for GeoFencer");
+ return false;
+ }
+
+ if (mGeoFencer == null) {
+ Log.w(TAG, "Timed out. No GeoFencer connection");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected boolean start(GeoFenceParams geofence) {
+ if (ensureGeoFencer()) {
+ try {
+ return mGeoFencer.setGeoFence(mListener, geofence);
+ } catch (RemoteException re) {
+ }
+ }
+ return false;
+ }
+
+ protected boolean stop(PendingIntent intent) {
+ if (ensureGeoFencer()) {
+ try {
+ mGeoFencer.clearGeoFence(mListener, intent);
+ return true;
+ } catch (RemoteException re) {
+ }
+ }
+ return false;
+ }
+
+ private void logv(String s) {
+ if (LOGV_ENABLED) Log.v(TAG, s);
+ }
+}
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java
index f671e64..833c340 100644
--- a/services/core/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/core/java/com/android/server/location/GpsLocationProvider.java
@@ -88,6 +88,7 @@ import java.io.StringReader;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Date;
+import java.util.HashSet;
import java.util.Map.Entry;
import java.util.Properties;
@@ -359,6 +360,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
private String mC2KServerHost;
private int mC2KServerPort;
private boolean mSuplEsEnabled = false;
+ private HashSet<String> mLastKnownMccMnc;
private final Context mContext;
private final NtpTrustedTime mNtpTime;
@@ -482,18 +484,14 @@ public class GpsLocationProvider implements LocationProviderInterface {
};
private void subscriptionOrSimChanged(Context context) {
- Log.d(TAG, "received SIM related action: ");
- TelephonyManager phone = (TelephonyManager)
- mContext.getSystemService(Context.TELEPHONY_SERVICE);
- String mccMnc = phone.getSimOperator();
- if (!TextUtils.isEmpty(mccMnc)) {
- Log.d(TAG, "SIM MCC/MNC is available: " + mccMnc);
- synchronized (mLock) {
+ HashSet<String> mccMnc = getKnownMccMnc(context);
+ Log.d(TAG, "received SIM change, new known MCC/MNC: " + mccMnc);
+ synchronized (mLock) {
+ if (!mccMnc.isEmpty() && !mccMnc.equals(mLastKnownMccMnc)) {
reloadGpsProperties(context, mProperties);
mNIHandler.setSuplEsEnabled(mSuplEsEnabled);
}
- } else {
- Log.d(TAG, "SIM MCC/MNC is still not available");
+ mLastKnownMccMnc = mccMnc;
}
}
@@ -585,6 +583,20 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
+ private HashSet<String> getKnownMccMnc(Context context) {
+ final TelephonyManager phone = (TelephonyManager)
+ context.getSystemService(Context.TELEPHONY_SERVICE);
+ final HashSet<String> mccMnc = new HashSet<String>();
+ final int phoneCnt = phone.getPhoneCount();
+ for (int i = 0;i < phoneCnt; ++i) {
+ String operator = phone.getNetworkOperatorForPhone(i);
+ if (!TextUtils.isEmpty(operator)) {
+ mccMnc.add(operator);
+ }
+ }
+ return mccMnc;
+ }
+
private void loadPropertiesFromResource(Context context,
Properties properties) {
String[] configValues = context.getResources().getStringArray(
@@ -650,6 +662,8 @@ public class GpsLocationProvider implements LocationProviderInterface {
// Construct internal handler
mHandler = new ProviderHandler(looper);
+ mLastKnownMccMnc = getKnownMccMnc(mContext);
+
// Load GPS configuration and register listeners in the background:
// some operations, such as opening files and registering broadcast receivers, can take a
// relative long time, so the ctor() is kept to create objects needed by this instance,
@@ -767,6 +781,10 @@ public class GpsLocationProvider implements LocationProviderInterface {
&& mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) {
if (mNetworkAvailable) {
String apnName = info.getExtraInfo();
+ // APN wasn't found in the intent, try to get it from the content provider.
+ if (apnName == null) {
+ apnName = getSelectedApn();
+ }
if (apnName == null) {
/* Assign a dummy value in the case of C2K as otherwise we will have a runtime
exception in the following call to native_agps_data_conn_open*/
@@ -1100,7 +1118,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
if (DEBUG) Log.d(TAG, "setRequest " + mProviderRequest);
- if (mProviderRequest.reportLocation && !mDisableGps) {
+ if (mProviderRequest.reportLocation && !mDisableGps && isEnabled()) {
// update client uids
updateClientUids(mWorkSource);
@@ -1930,7 +1948,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
type = AGPS_REF_LOCATION_TYPE_GSM_CELLID;
}
native_agps_set_ref_location_cellid(type, mcc, mnc,
- gsm_cell.getLac(), gsm_cell.getCid());
+ gsm_cell.getLac(), gsm_cell.getCid(), gsm_cell.getPsc());
} else {
Log.e(TAG,"Error getting cell location info.");
}
@@ -2289,7 +2307,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
// AGPS ril suport
private native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
- int lac, int cid);
+ int lac, int cid, int psc);
private native void native_agps_set_id(int type, String setid);
private native void native_update_network_state(boolean connected, int type,
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index f92f631..bc830f0 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -106,6 +106,9 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
private CharSequence mQueueTitle;
private int mRatingType;
private long mLastActiveTime;
+ private String mBrowsedPlayerURI;
+ private boolean mPlayItemStatus;
+ private long[] mNowPlayingList;
// End TransportPerformer fields
// Volume handling fields
@@ -518,6 +521,86 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
}
+ private void pushBrowsePlayerInfo() {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ Log.d(TAG, "pushBrowsePlayerInfo");
+ cb.onUpdateFolderInfoBrowsedPlayer(mBrowsedPlayerURI);
+ } catch (DeadObjectException e) {
+ Log.w(TAG, "Removing dead callback in pushBrowsePlayerInfo. ", e);
+ mControllerCallbacks.remove(i);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unexpected exception in pushBrowsePlayerInfo. ", e);
+ }
+ }
+ }
+ }
+
+ private void pushNowPlayingEntries() {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ Log.d(TAG, "pushNowPlayingEntries");
+ cb.onUpdateNowPlayingEntries(mNowPlayingList);
+ } catch (DeadObjectException e) {
+ Log.w(TAG, "Removing dead callback in pushNowPlayingEntries. ", e);
+ mControllerCallbacks.remove(i);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unexpected exception in pushNowPlayingEntries. ", e);
+ }
+ }
+ }
+ }
+
+ private void pushNowPlayingContentChange() {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ Log.d(TAG, "pushNowPlayingContentChange");
+ cb.onUpdateNowPlayingContentChange();
+ } catch (DeadObjectException e) {
+ Log.w(TAG, "Removing dead callback in pushNowPlayingContentChange. ", e);
+ mControllerCallbacks.remove(i);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unexpected exception in pushNowPlayingContentChange. ", e);
+ }
+ }
+ }
+ }
+
+ private void pushPlayItemResponse() {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ return;
+ }
+ for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+ ISessionControllerCallback cb = mControllerCallbacks.get(i);
+ try {
+ Log.d(TAG, "pushPlayItemResponse");
+ cb.onPlayItemResponse(mPlayItemStatus);
+ } catch (DeadObjectException e) {
+ Log.w(TAG, "Removing dead callback in pushPlayItemResponse. ", e);
+ mControllerCallbacks.remove(i);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unexpected exception in pushPlayItemResponse. ", e);
+ }
+ }
+ }
+ }
+
private void pushQueueUpdate() {
synchronized (mLock) {
if (mDestroyed) {
@@ -775,6 +858,33 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
@Override
+ public void updateFolderInfoBrowsedPlayer(String stringUri) {
+ Log.d(TAG, "SessionStub: updateFolderInfoBrowsedPlayer");
+ mBrowsedPlayerURI = stringUri;
+ mHandler.post(MessageHandler.MSG_FOLDER_INFO_BROWSED_PLAYER);
+ }
+
+ @Override
+ public void updateNowPlayingEntries(long[] playList) {
+ Log.d(TAG, "SessionStub: updateNowPlayingEntries");
+ mNowPlayingList = playList;
+ mHandler.post(MessageHandler.MSG_UPDATE_NOWPLAYING_ENTRIES);
+ }
+
+ @Override
+ public void updateNowPlayingContentChange() {
+ Log.d(TAG, "SessionStub: updateNowPlayingContentChange");
+ mHandler.post(MessageHandler.MSG_UPDATE_NOWPLAYING_CONTENT_CHANGE);
+ }
+
+ @Override
+ public void playItemResponse(boolean success) {
+ Log.d(TAG, "SessionStub: playItemResponse");
+ mPlayItemStatus = success;
+ mHandler.post(MessageHandler.MSG_PLAY_ITEM_RESPONSE);
+ }
+
+ @Override
public void setQueueTitle(CharSequence title) {
mQueueTitle = title;
mHandler.post(MessageHandler.MSG_UPDATE_QUEUE_TITLE);
@@ -957,6 +1067,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
public void seekTo(long pos) {
+ Slog.d(TAG, "seekTo in SessionCb");
try {
mCb.onSeekTo(pos);
} catch (RemoteException e) {
@@ -964,6 +1075,42 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
}
+ /**
+ * @hide
+ */
+ public void setRemoteControlClientBrowsedPlayer() {
+ Slog.d(TAG, "setRemoteControlClientBrowsedPlayer in SessionCb");
+ try {
+ mCb.setRemoteControlClientBrowsedPlayer();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in setRemoteControlClientBrowsedPlayer.", e);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void setRemoteControlClientPlayItem(long uid, int scope) throws RemoteException {
+ Slog.d(TAG, "setRemoteControlClientPlayItem in SessionCb");
+ try {
+ mCb.setRemoteControlClientPlayItem(uid, scope);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in setRemoteControlClientPlayItem.", e);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public void getRemoteControlClientNowPlayingEntries() throws RemoteException {
+ Slog.d(TAG, "getRemoteControlClientNowPlayingEntries in SessionCb");
+ try {
+ mCb.getRemoteControlClientNowPlayingEntries();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Remote failure in getRemoteControlClientNowPlayingEntries.", e);
+ }
+ }
+
public void rate(Rating rating) {
try {
mCb.onRate(rating);
@@ -1157,10 +1304,29 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
@Override
public void seekTo(long pos) throws RemoteException {
+ Log.d(TAG, "seekTo in ControllerStub");
mSessionCb.seekTo(pos);
}
@Override
+ public void setRemoteControlClientBrowsedPlayer() throws RemoteException {
+ Log.d(TAG, "setRemoteControlClientBrowsedPlayer in ControllerStub");
+ mSessionCb.setRemoteControlClientBrowsedPlayer();
+ }
+
+ @Override
+ public void setRemoteControlClientPlayItem(long uid, int scope) throws RemoteException {
+ Log.d(TAG, "setRemoteControlClientPlayItem in ControllerStub");
+ mSessionCb.setRemoteControlClientPlayItem(uid, scope);
+ }
+
+ @Override
+ public void getRemoteControlClientNowPlayingEntries() throws RemoteException {
+ Log.d(TAG, "getRemoteControlClientNowPlayingEntries in ControllerStub");
+ mSessionCb.getRemoteControlClientNowPlayingEntries();
+ }
+
+ @Override
public void rate(Rating rating) throws RemoteException {
mSessionCb.rate(rating);
}
@@ -1224,6 +1390,10 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
private static final int MSG_UPDATE_SESSION_STATE = 7;
private static final int MSG_UPDATE_VOLUME = 8;
private static final int MSG_DESTROYED = 9;
+ private static final int MSG_FOLDER_INFO_BROWSED_PLAYER = 10;
+ private static final int MSG_UPDATE_NOWPLAYING_ENTRIES = 11;
+ private static final int MSG_UPDATE_NOWPLAYING_CONTENT_CHANGE = 12;
+ private static final int MSG_PLAY_ITEM_RESPONSE = 13;
public MessageHandler(Looper looper) {
super(looper);
@@ -1257,6 +1427,18 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
break;
case MSG_DESTROYED:
pushSessionDestroyed();
+ case MSG_FOLDER_INFO_BROWSED_PLAYER:
+ pushBrowsePlayerInfo();
+ break;
+ case MSG_UPDATE_NOWPLAYING_ENTRIES:
+ pushNowPlayingEntries();
+ break;
+ case MSG_UPDATE_NOWPLAYING_CONTENT_CHANGE:
+ pushNowPlayingContentChange();
+ break;
+ case MSG_PLAY_ITEM_RESPONSE:
+ pushPlayItemResponse();
+ break;
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index b428322..4c847a2 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -45,6 +45,9 @@ import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_BACKGROUND_BATTERY_SAVE;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_ON_DATA;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_ON_WLAN;
+import static android.net.NetworkPolicyManager.POLICY_REJECT_ON_WLAN_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
@@ -105,6 +108,7 @@ import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
import android.net.NetworkInfo;
import android.net.NetworkPolicy;
@@ -159,6 +163,7 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.server.DeviceIdleController;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
+import com.android.server.NetPluginDelegate;
import com.google.android.collect.Lists;
import org.xmlpull.v1.XmlPullParser;
@@ -203,6 +208,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final int VERSION_LATEST = VERSION_SWITCH_UID;
@VisibleForTesting
+ public static final int TYPE_NONE = 0;
+ @VisibleForTesting
public static final int TYPE_WARNING = 0x1;
@VisibleForTesting
public static final int TYPE_LIMIT = 0x2;
@@ -247,6 +254,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final int MSG_RESTRICT_BACKGROUND_CHANGED = 6;
private static final int MSG_ADVISE_PERSIST_THRESHOLD = 7;
private static final int MSG_SCREEN_ON_CHANGED = 8;
+ private static final int MSG_PROCESS_LOW_POWER_CHANGED = 9;
private final Context mContext;
private final IActivityManager mActivityManager;
@@ -262,6 +270,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private PowerManagerInternal mPowerManagerInternal;
private IDeviceIdleController mDeviceIdleController;
+ private final ComponentName mNotificationComponent;
+ private int mNotificationSequenceNumber;
+
final Object mRulesLock = new Object();
volatile boolean mSystemReady;
@@ -363,6 +374,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"));
mAppOps = context.getSystemService(AppOpsManager.class);
+
+ final String notificationComponent = context.getString(
+ R.string.config_networkPolicyNotificationComponent);
+ mNotificationComponent = notificationComponent != null
+ ? ComponentName.unflattenFromString(notificationComponent) : null;
}
public void bindConnectivityManager(IConnectivityManager connManager) {
@@ -437,13 +453,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
mPowerManagerInternal.registerLowPowerModeObserver(
new PowerManagerInternal.LowPowerModeListener() {
@Override
- public void onLowPowerModeChanged(boolean enabled) {
- synchronized (mRulesLock) {
- if (mRestrictPower != enabled) {
- mRestrictPower = enabled;
- updateRulesForGlobalChangeLocked(true);
- }
- }
+ public void onLowPowerModeChanged(final boolean enabled) {
+ mHandler.removeMessages(MSG_PROCESS_LOW_POWER_CHANGED);
+ Message msg = Message.obtain(mHandler, MSG_PROCESS_LOW_POWER_CHANGED, enabled);
+ mHandler.sendMessage(msg);
}
});
mRestrictPower = mPowerManagerInternal.getLowPowerModeEnabled();
@@ -778,6 +791,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final ArraySet<String> beforeNotifs = new ArraySet<String>(mActiveNotifs);
mActiveNotifs.clear();
+ // increment the sequence number so custom components know
+ // this update is new
+ mNotificationSequenceNumber++;
+ boolean hasNotifications = false;
+
// TODO: when switching to kernel notifications, compute next future
// cycle boundary to recompute notifications.
@@ -794,6 +812,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final long totalBytes = getTotalBytes(policy.template, start, end);
if (policy.isOverLimit(totalBytes)) {
+ hasNotifications = true;
if (policy.lastLimitSnooze >= start) {
enqueueNotification(policy, TYPE_LIMIT_SNOOZED, totalBytes);
} else {
@@ -804,12 +823,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
} else {
notifyUnderLimitLocked(policy.template);
- if (policy.isOverWarning(totalBytes) && policy.lastWarningSnooze < start) {
+ if (policy.isOverWarning(totalBytes) && policy.lastWarningSnooze < start
+ && policy.limitBytes != LIMIT_DISABLED) {
enqueueNotification(policy, TYPE_WARNING, totalBytes);
+ hasNotifications = true;
}
}
}
+ // right now we don't care about restricted background notifications
+ // in the custom notification component, so trigger an update now
+ // if we didn't update anything this pass
+ if (!hasNotifications) {
+ sendNotificationToCustomComponent(null, TYPE_NONE, 0);
+ }
+
// ongoing notification when restricting background data
if (mRestrictBackground) {
enqueueRestrictedNotification(TAG_ALLOW_BACKGROUND);
@@ -856,6 +884,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
* {@link NetworkPolicy#limitBytes}, potentially showing dialog to user.
*/
private void notifyOverLimitLocked(NetworkTemplate template) {
+ if (mNotificationComponent != null) {
+ // It is the job of the notification component to handle UI,
+ // so we do nothing here
+ return;
+ }
+
if (!mOverLimitNotified.contains(template)) {
mContext.startActivity(buildNetworkOverLimitIntent(template));
mOverLimitNotified.add(template);
@@ -874,11 +908,55 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
return TAG + ":" + policy.template.hashCode() + ":" + type;
}
+ private boolean sendNotificationToCustomComponent(
+ NetworkPolicy policy,
+ int type,
+ long totalBytes) {
+ if (mNotificationComponent == null) {
+ return false;
+ }
+
+ Intent intent = new Intent();
+ intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ intent.setComponent(mNotificationComponent);
+
+ int notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_NONE;
+ switch (type) {
+ case TYPE_WARNING:
+ notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_USAGE_WARNING;
+ break;
+ case TYPE_LIMIT:
+ notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_USAGE_REACHED_LIMIT;
+ break;
+ case TYPE_LIMIT_SNOOZED:
+ notificationType = NetworkPolicyManager.NOTIFICATION_TYPE_USAGE_EXCEEDED_LIMIT;
+ break;
+ }
+
+ intent.setAction(NetworkPolicyManager.ACTION_SHOW_NETWORK_POLICY_NOTIFICATION);
+ intent.putExtra(NetworkPolicyManager.EXTRA_NOTIFICATION_TYPE, notificationType);
+ intent.putExtra(
+ NetworkPolicyManager.EXTRA_NOTIFICATION_SEQUENCE_NUMBER,
+ mNotificationSequenceNumber);
+
+ if (notificationType != NetworkPolicyManager.NOTIFICATION_TYPE_NONE) {
+ intent.putExtra(NetworkPolicyManager.EXTRA_NETWORK_POLICY, policy);
+ intent.putExtra(NetworkPolicyManager.EXTRA_BYTES_USED, totalBytes);
+ }
+
+ mContext.sendBroadcast(intent);
+ return true;
+ }
+
/**
* Show notification for combined {@link NetworkPolicy} and specific type,
* like {@link #TYPE_LIMIT}. Okay to call multiple times.
*/
private void enqueueNotification(NetworkPolicy policy, int type, long totalBytes) {
+ if (sendNotificationToCustomComponent(policy, type, totalBytes)) {
+ return;
+ }
+
final String tag = buildNotificationTag(policy, type);
final Notification.Builder builder = new Notification.Builder(mContext);
builder.setOnlyAlertOnce(true);
@@ -1132,7 +1210,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final ArrayList<Pair<String, NetworkIdentity>> connIdents = new ArrayList<>(states.length);
final ArraySet<String> connIfaces = new ArraySet<String>(states.length);
for (NetworkState state : states) {
- if (state.networkInfo.isConnected()) {
+ if (state.networkInfo.isConnected() && (state.networkCapabilities == null
+ || !state.networkCapabilities.hasTransport(
+ NetworkCapabilities.TRANSPORT_CELLULAR)
+ || state.networkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_INTERNET))) {
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
final String baseIface = state.linkProperties.getInterfaceName();
@@ -1738,6 +1820,19 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
}
+ @Override
+ public void snoozeWarning(NetworkTemplate template) {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ // TODO: this seems like a race condition? (along with snoozeLimit above)
+ performSnooze(template, TYPE_WARNING);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
void performSnooze(NetworkTemplate template, int type) {
maybeRefreshTrustedTime();
final long currentTime = currentTimeMillis();
@@ -2136,12 +2231,23 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
uidRules.clear();
// Fully update the app idle firewall chain.
+ final IPackageManager ipm = AppGlobals.getPackageManager();
final List<UserInfo> users = mUserManager.getUsers();
for (int ui = users.size() - 1; ui >= 0; ui--) {
UserInfo user = users.get(ui);
int[] idleUids = mUsageStats.getIdleUidsForUser(user.id);
for (int uid : idleUids) {
if (!mPowerSaveTempWhitelistAppIds.get(UserHandle.getAppId(uid), false)) {
+ // quick check: if this uid doesn't have INTERNET permission, it
+ // doesn't have network access anyway, so it is a waste to mess
+ // with it here.
+ try {
+ if (ipm.checkUidPermission(Manifest.permission.INTERNET, uid)
+ != PackageManager.PERMISSION_GRANTED) {
+ continue;
+ }
+ } catch (RemoteException e) {
+ }
uidRules.put(uid, FIREWALL_RULE_DENY);
}
}
@@ -2226,11 +2332,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private boolean isUidIdle(int uid) {
final String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
- final int userId = UserHandle.getUserId(uid);
- for (String packageName : packages) {
- if (!mUsageStats.isAppIdle(packageName, uid, userId)) {
- return false;
+ if (packages != null) {
+ final int userId = UserHandle.getUserId(uid);
+ for (String packageName : packages) {
+ if (!mUsageStats.isAppIdle(packageName, uid, userId)) {
+ return false;
+ }
}
}
return true;
@@ -2298,6 +2406,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
uidRules = RULE_REJECT_ALL;
}
+ try {
+ mNetworkManager.restrictAppOnWlan(uid, (uidPolicy & POLICY_REJECT_ON_WLAN) != 0 ||
+ (((uidPolicy & POLICY_REJECT_ON_WLAN_BACKGROUND) != 0) && !uidForeground));
+ mNetworkManager.restrictAppOnData(uid, (uidPolicy & POLICY_REJECT_ON_DATA) != 0);
+ } catch (RemoteException e) {
+ // ignored; service lives in system_server
+ }
+
final int oldRules = mUidRules.get(uid);
if (uidRules == RULE_ALLOW_ALL) {
mUidRules.delete(uid);
@@ -2429,6 +2545,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
updateScreenOn();
return true;
}
+ case MSG_PROCESS_LOW_POWER_CHANGED: {
+ boolean enabled = (Boolean) msg.obj;
+ synchronized (mRulesLock) {
+ if (mRestrictPower != enabled) {
+ mRestrictPower = enabled;
+ updateRulesForGlobalChangeLocked(true);
+ }
+ }
+ return true;
+ }
default: {
return false;
}
@@ -2439,6 +2565,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private void setInterfaceQuota(String iface, long quotaBytes) {
try {
mNetworkManager.setInterfaceQuota(iface, quotaBytes);
+ NetPluginDelegate.setQuota(iface, quotaBytes);
} catch (IllegalStateException e) {
Log.wtf(TAG, "problem setting interface quota", e);
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index 15b68c7..0176ec4 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -491,6 +491,21 @@ public class NetworkStatsCollection implements FileRotator.Reader {
}
}
+ /**
+ * Replace data usage history for each matching identity in the template with empty values
+ */
+ public void resetDataUsage(NetworkTemplate template) {
+ final ArrayList<Key> knownKeys = Lists.newArrayList();
+ knownKeys.addAll(mStats.keySet());
+
+ for (Key key : knownKeys) {
+ if (templateMatches(template, key.ident)) {
+ mStats.put(key, new NetworkStatsHistory(mBucketDuration, 10));
+ mDirty = true;
+ }
+ }
+ }
+
private void noteRecordedHistory(long startMillis, long endMillis, long totalBytes) {
if (startMillis < mStartMillis) mStartMillis = startMillis;
if (endMillis > mEndMillis) mEndMillis = endMillis;
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index 6490865..3201981 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -290,6 +290,31 @@ public class NetworkStatsRecorder {
}
/**
+ * Reset data usage for all matching identities in {@link FileRotator} history,
+ */
+ public void resetDataUsageLocked(NetworkTemplate template) {
+ try {
+ // Reset all persisted data to empty values
+ mRotator.rewriteAll(new ResetDataUsageRewriter(mBucketDuration, template));
+ } catch (IOException e) {
+ Log.wtf(TAG, "problem resetting data stats " + e);
+ recoverFromWtf();
+ } catch (OutOfMemoryError e) {
+ Log.wtf(TAG, "problem resetting data stats " + e);
+ recoverFromWtf();
+ }
+
+ // Reset any pending stats
+ mPending.resetDataUsage(template);
+ mSinceBoot.resetDataUsage(template);
+
+ final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
+ if (complete != null) {
+ complete.resetDataUsage(template);
+ }
+ }
+
+ /**
* Rewriter that will combine current {@link NetworkStatsCollection} values
* with anything read from disk, and write combined set to disk. Clears the
* original {@link NetworkStatsCollection} when finished writing.
@@ -359,6 +384,42 @@ public class NetworkStatsRecorder {
}
}
+ /**
+ * Rewriter that will remove any {@link NetworkStatsHistory} attributed to
+ * identities in input template, replacing it with empty values.
+ */
+ public static class ResetDataUsageRewriter implements FileRotator.Rewriter {
+ private final NetworkStatsCollection mTemp;
+ private NetworkTemplate mTemplate;
+
+ public ResetDataUsageRewriter(long bucketDuration, NetworkTemplate template) {
+ mTemp = new NetworkStatsCollection(bucketDuration);
+ mTemplate = template;
+ }
+
+ @Override
+ public void reset() {
+ mTemp.reset();
+ }
+
+ @Override
+ public void read(InputStream in) throws IOException {
+ mTemp.read(in);
+ mTemp.clearDirty();
+ mTemp.resetDataUsage(mTemplate);
+ }
+
+ @Override
+ public boolean shouldWrite() {
+ return mTemp.isDirty();
+ }
+
+ @Override
+ public void write(OutputStream out) throws IOException {
+ mTemp.write(new DataOutputStream(out));
+ }
+ }
+
public void importLegacyNetworkLocked(File file) throws IOException {
// legacy file still exists; start empty to avoid double importing
mRotator.deleteAll();
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 8449348..baaf055 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -81,6 +81,7 @@ import android.net.INetworkManagementEventObserver;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
import android.net.NetworkIdentity;
import android.net.NetworkInfo;
import android.net.NetworkState;
@@ -123,6 +124,7 @@ import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
+import com.android.server.NetPluginDelegate;
import com.android.server.connectivity.Tethering;
import java.io.File;
@@ -628,6 +630,26 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
return mXtStatsCached.getHistory(template, UID_ALL, SET_ALL, TAG_NONE, fields);
}
+ /**
+ * Reset entire data usage history for all the uids in the template and update global
+ * data stats
+ */
+ @Override
+ public void resetDataUsageHistoryForAllUid(NetworkTemplate template) {
+ mContext.enforceCallingOrSelfPermission(MODIFY_NETWORK_ACCOUNTING, TAG);
+
+ synchronized (mStatsLock) {
+ mWakeLock.acquire();
+ try {
+ resetDataUsageLocked(template);
+ } catch (Exception e) {
+ // ignored; service lives in system_server
+ } finally {
+ mWakeLock.release();
+ }
+ }
+ }
+
@Override
public long getNetworkTotalBytes(NetworkTemplate template, long start, long end) {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
@@ -920,7 +942,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final ArraySet<String> mobileIfaces = new ArraySet<>();
for (NetworkState state : states) {
- if (state.networkInfo.isConnected()) {
+ if (state.networkInfo.isConnected() && (state.networkCapabilities == null
+ || !state.networkCapabilities.hasTransport(
+ NetworkCapabilities.TRANSPORT_CELLULAR)
+ || state.networkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_INTERNET))) {
final boolean isMobile = isNetworkTypeMobile(state.networkInfo.getType());
final NetworkIdentity ident = NetworkIdentity.buildNetworkIdentity(mContext, state);
@@ -971,7 +997,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
final NetworkStats uidSnapshot = getNetworkStatsUidDetail();
final NetworkStats xtSnapshot = mNetworkManager.getNetworkStatsSummaryXt();
final NetworkStats devSnapshot = mNetworkManager.getNetworkStatsSummaryDev();
-
+ NetPluginDelegate.getTetherStats(uidSnapshot, xtSnapshot, devSnapshot);
VpnInfo[] vpnArray = mConnManager.getAllVpnInfo();
mDevRecorder.recordSnapshotLocked(devSnapshot, mActiveIfaces, null, currentTime);
mXtRecorder.recordSnapshotLocked(xtSnapshot, mActiveIfaces, null, currentTime);
@@ -1148,6 +1174,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
removeUidsLocked(uids);
}
+ /**
+ * Reset data usage history for all uids, uid tags, and global transfer data for the input template
+ */
+ private void resetDataUsageLocked(NetworkTemplate template) {
+ // Perform one last poll before removing
+ performPollLocked(FLAG_PERSIST_ALL);
+
+ mUidRecorder.resetDataUsageLocked(template);
+ mUidTagRecorder.resetDataUsageLocked(template);
+ mXtRecorder.resetDataUsageLocked(template);
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter rawWriter, String[] args) {
mContext.enforceCallingOrSelfPermission(DUMP, TAG);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index b922d26..9b2804a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2015 The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,6 +31,7 @@ import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.INotificationManager;
import android.app.ITransientNotification;
+import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
@@ -41,7 +43,9 @@ import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
+import android.content.ContentValues;
import android.content.Context;
+import android.database.Cursor;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
@@ -53,6 +57,7 @@ import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.graphics.drawable.Drawable;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
@@ -92,7 +97,9 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
+import android.util.LruCache;
import android.util.Slog;
+import android.util.SparseIntArray;
import android.util.Xml;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
@@ -101,6 +108,9 @@ import android.widget.Toast;
import com.android.internal.R;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.cm.SpamFilter;
+import com.android.internal.util.cm.SpamFilter.SpamContract.NotificationTable;
+import com.android.internal.util.cm.SpamFilter.SpamContract.PackageTable;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -110,6 +120,11 @@ import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import com.android.server.notification.ManagedServices.UserProfiles;
import com.android.server.statusbar.StatusBarManagerInternal;
+import cyanogenmod.media.AudioSessionInfo;
+import cyanogenmod.media.CMAudioManager;
+import cyanogenmod.providers.CMSettings;
+import cyanogenmod.util.ColorUtils;
+
import libcore.io.IoUtils;
import org.json.JSONArray;
@@ -134,10 +149,15 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
/** {@hide} */
public class NotificationManagerService extends SystemService {
@@ -170,6 +190,8 @@ public class NotificationManagerService extends SystemService {
static final int JUNK_SCORE = -1000;
static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10;
static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER;
+ private static final String IS_FILTERED_QUERY = NotificationTable.NORMALIZED_TEXT + "=? AND " +
+ PackageTable.PACKAGE_NAME + "=?";
// Notifications with scores below this will not interrupt the user, either via LED or
// sound or vibration
@@ -198,6 +220,9 @@ public class NotificationManagerService extends SystemService {
/** notification_enqueue status value for an ignored notification. */
private static final int EVENTLOG_ENQUEUE_STATUS_IGNORED = 2;
+ /** notification light maximum brightness value to use. */
+ private static final int LIGHT_BRIGHTNESS_MAXIMUM = 255;
+
private IActivityManager mAm;
AudioManager mAudioManager;
AudioManagerInternal mAudioManagerInternal;
@@ -217,10 +242,37 @@ public class NotificationManagerService extends SystemService {
private int mDefaultNotificationLedOff;
private long[] mDefaultVibrationPattern;
+ private boolean mAdjustableNotificationLedBrightness;
+ private int mNotificationLedBrightnessLevel = LIGHT_BRIGHTNESS_MAXIMUM;
+
+ private boolean mMultipleNotificationLeds;
+ private boolean mMultipleLedsEnabledSetting = false;
+
+ private boolean mAutoGenerateNotificationColor = true;
+
+ private boolean mScreenOnEnabled = false;
+ private boolean mScreenOnDefault = false;
+
private long[] mFallbackVibrationPattern;
private boolean mUseAttentionLight;
boolean mSystemReady;
+ private final SparseIntArray mSpamCache;
+ private ExecutorService mSpamExecutor = Executors.newSingleThreadExecutor();
+
+ private static final Uri FILTER_MSG_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SpamFilter.AUTHORITY)
+ .appendPath("messages")
+ .build();
+
+ private static final Uri UPDATE_MSG_URI = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(SpamFilter.AUTHORITY)
+ .appendPath(SpamFilter.MESSAGE_PATH)
+ .appendEncodedPath("inc_count")
+ .build();
+
private boolean mDisableNotificationEffects;
private int mCallState;
private String mSoundNotificationKey;
@@ -235,6 +287,13 @@ public class NotificationManagerService extends SystemService {
private boolean mScreenOn = true;
private boolean mInCall = false;
private boolean mNotificationPulseEnabled;
+ private HashMap<String, NotificationLedValues> mNotificationPulseCustomLedValues;
+ private Map<String, String> mPackageNameMappings;
+ private final Map<String, Integer> mGeneratedPackageLedColors =
+ new HashMap<String, Integer>();
+
+ // for checking lockscreen status
+ private KeyguardManager mKeyguardManager;
// used as a mutex for access to all active notifications & listeners
final ArrayList<NotificationRecord> mNotificationList =
@@ -275,6 +334,10 @@ public class NotificationManagerService extends SystemService {
private NotificationListeners mListeners;
private ConditionProviders mConditionProviders;
private NotificationUsageStats mUsageStats;
+ private boolean mDisableDuckingWhileMedia;
+ private boolean mActiveMedia;
+
+ private boolean mMultiColorNotificationLed;
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
@@ -487,6 +550,12 @@ public class NotificationManagerService extends SystemService {
}
}
+ class NotificationLedValues {
+ public int color;
+ public int onMS;
+ public int offMS;
+ }
+
private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() {
@Override
@@ -610,9 +679,19 @@ public class NotificationManagerService extends SystemService {
Binder.restoreCallingIdentity(identity);
}
- // light
- mLights.clear();
- updateLightsLocked();
+ // lights
+ // clear only if lockscreen is not active
+ // and LED is not forced on by Settings app
+ if (mLights.size() > 0) {
+ final String owner = mLights.get(mLights.size() - 1);
+ NotificationRecord ledNotification = mNotificationsByKey.get(owner);
+ if (mKeyguardManager != null && !mKeyguardManager.isKeyguardLocked()) {
+ if (!isLedNotificationForcedOn(ledNotification)) {
+ mLights.clear();
+ }
+ updateLightsLocked();
+ }
+ }
}
}
@@ -629,8 +708,9 @@ public class NotificationManagerService extends SystemService {
"Bad notification posted from package " + pkg
+ ": " + message);
} catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
- Binder.restoreCallingIdentity(ident);
}
@Override
@@ -774,12 +854,16 @@ public class NotificationManagerService extends SystemService {
}
} else if (action.equals(Intent.ACTION_USER_PRESENT)) {
// turn off LED when user passes through lock screen
- mNotificationLight.turnOff();
- mStatusBar.notificationLightOff();
+ // if lights with screen on is disabled.
+ if (!mScreenOnEnabled) {
+ mNotificationLight.turnOff();
+ mStatusBar.notificationLightOff();
+ }
} else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
final int user = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
// reload per-user settings
mSettingsObserver.update(null);
+ mSpamFilterObserver.update(null);
mUserProfiles.updateCache(context);
// Refresh managed services
mConditionProviders.onUserSwitched(user);
@@ -794,9 +878,103 @@ public class NotificationManagerService extends SystemService {
}
};
- private final class SettingsObserver extends ContentObserver {
+ class SpamFilterObserver extends ContentObserver {
+
+ private Future mTask;
+
+ public SpamFilterObserver(Handler handler) {
+ super(handler);
+ }
+
+ private void addToCache(Cursor c) {
+ int notifId = c.getInt(c.getColumnIndex(
+ NotificationTable.ID));
+ String pkgName = c.getString(c.getColumnIndex(
+ PackageTable.PACKAGE_NAME));
+ String normalizedText = c.getString(c.getColumnIndex(
+ NotificationTable.NORMALIZED_TEXT));
+ int hash = getSpamCacheHash(normalizedText, pkgName);
+ synchronized (mSpamCache) {
+ mSpamCache.put(hash, notifId);
+ }
+ }
+
+ private Runnable mFetchAllFilters = new Runnable() {
+ @Override
+ public void run() {
+ Cursor c = getContext().getContentResolver().query(FILTER_MSG_URI,
+ null, null, null, null);
+ if (c != null) {
+ synchronized (mSpamCache) {
+ mSpamCache.clear();
+ while (c.moveToNext()) {
+ addToCache(c);
+ if (Thread.interrupted()) {
+ break;
+ }
+ c.close();
+ }
+ }
+ }
+ }
+ };
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ update(uri);
+ }
+
+ void update(final Uri uri) {
+ if (mTask != null && !mTask.isDone()) {
+ mTask.cancel(true);
+ }
+ if (uri == null) {
+ mTask = mSpamExecutor.submit(mFetchAllFilters);
+ } else {
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ String id = uri.getLastPathSegment();
+ Cursor c = getContext().getContentResolver().query(
+ uri, null, null, null, null);
+
+ if (c != null) {
+ int index;
+ synchronized (mSpamCache) {
+ index = mSpamCache.indexOfValue(Integer.parseInt(id));
+ }
+ if (!c.moveToFirst()) {
+ synchronized (mSpamCache) {
+ // Filter was deleted
+ if (index >= 0) {
+ mSpamCache.removeAt(index);
+ }
+ }
+ } else if (index < 0) {
+ // Filter was added/updated
+ addToCache(c);
+ }
+ c.close();
+ }
+ }
+ };
+ mTask = mSpamExecutor.submit(r);
+ }
+ }
+
+ public void observe() {
+ ContentResolver resolver = getContext().getContentResolver();
+ resolver.registerContentObserver(SpamFilter.NOTIFICATION_URI,
+ true, this, UserHandle.USER_ALL);
+ update(null);
+ }
+ }
+
+ class SettingsObserver extends ContentObserver {
private final Uri NOTIFICATION_LIGHT_PULSE_URI
= Settings.System.getUriFor(Settings.System.NOTIFICATION_LIGHT_PULSE);
+ private final Uri ENABLED_NOTIFICATION_LISTENERS_URI
+ = Settings.Secure.getUriFor(Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
SettingsObserver(Handler handler) {
super(handler);
@@ -804,8 +982,44 @@ public class NotificationManagerService extends SystemService {
void observe() {
ContentResolver resolver = getContext().getContentResolver();
- resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI,
+ resolver.registerContentObserver(
+ NOTIFICATION_LIGHT_PULSE_URI, false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(
+ ENABLED_NOTIFICATION_LISTENERS_URI, false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR),
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON),
false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF),
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE),
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES),
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.NOTIFICATION_LIGHT_SCREEN_ON),
+ false, this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.Global.getUriFor(
+ CMSettings.Global.ZEN_DISABLE_DUCKING_DURING_MEDIA_PLAYBACK), false,
+ this, UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.NOTIFICATION_LIGHT_COLOR_AUTO), false,
+ this, UserHandle.USER_ALL);
+ if (mAdjustableNotificationLedBrightness) {
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.NOTIFICATION_LIGHT_BRIGHTNESS_LEVEL),
+ false, this, UserHandle.USER_ALL);
+ }
+ if (mMultipleNotificationLeds) {
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.NOTIFICATION_LIGHT_MULTIPLE_LEDS_ENABLE),
+ false, this, UserHandle.USER_ALL);
+ }
update(null);
}
@@ -815,18 +1029,109 @@ public class NotificationManagerService extends SystemService {
public void update(Uri uri) {
ContentResolver resolver = getContext().getContentResolver();
- if (uri == null || NOTIFICATION_LIGHT_PULSE_URI.equals(uri)) {
- boolean pulseEnabled = Settings.System.getInt(resolver,
- Settings.System.NOTIFICATION_LIGHT_PULSE, 0) != 0;
- if (mNotificationPulseEnabled != pulseEnabled) {
- mNotificationPulseEnabled = pulseEnabled;
- updateNotificationPulse();
- }
+
+ // LED enabled
+ mNotificationPulseEnabled = Settings.System.getIntForUser(resolver,
+ Settings.System.NOTIFICATION_LIGHT_PULSE, 0, UserHandle.USER_CURRENT) != 0;
+
+ // Automatically pick a color for LED if not set
+ mAutoGenerateNotificationColor = CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.NOTIFICATION_LIGHT_COLOR_AUTO,
+ 1, UserHandle.USER_CURRENT) != 0;
+
+ // LED default color
+ mDefaultNotificationColor = CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_COLOR,
+ mDefaultNotificationColor, UserHandle.USER_CURRENT);
+
+ // LED default on MS
+ mDefaultNotificationLedOn = CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_ON,
+ mDefaultNotificationLedOn, UserHandle.USER_CURRENT);
+
+ // LED default off MS
+ mDefaultNotificationLedOff = CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.NOTIFICATION_LIGHT_PULSE_DEFAULT_LED_OFF,
+ mDefaultNotificationLedOff, UserHandle.USER_CURRENT);
+
+ // LED generated notification colors
+ mGeneratedPackageLedColors.clear();
+
+ // LED custom notification colors
+ mNotificationPulseCustomLedValues.clear();
+ if (CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_ENABLE, 0,
+ UserHandle.USER_CURRENT) != 0) {
+ parseNotificationPulseCustomValuesString(CMSettings.System.getStringForUser(resolver,
+ CMSettings.System.NOTIFICATION_LIGHT_PULSE_CUSTOM_VALUES,
+ UserHandle.USER_CURRENT));
+ }
+
+ // Notification LED brightness
+ if (mAdjustableNotificationLedBrightness) {
+ mNotificationLedBrightnessLevel = CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.NOTIFICATION_LIGHT_BRIGHTNESS_LEVEL,
+ LIGHT_BRIGHTNESS_MAXIMUM, UserHandle.USER_CURRENT);
+ }
+
+ // Multiple LEDs enabled
+ if (mMultipleNotificationLeds) {
+ mMultipleLedsEnabledSetting = (CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.NOTIFICATION_LIGHT_MULTIPLE_LEDS_ENABLE,
+ mMultipleNotificationLeds ? 1 : 0, UserHandle.USER_CURRENT) != 0);
+ }
+
+ // Notification lights with screen on
+ mScreenOnEnabled = (CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.NOTIFICATION_LIGHT_SCREEN_ON,
+ mScreenOnDefault ? 1 : 0, UserHandle.USER_CURRENT) != 0);
+
+ updateNotificationPulse();
+
+ mDisableDuckingWhileMedia = CMSettings.Global.getInt(resolver,
+ CMSettings.Global.ZEN_DISABLE_DUCKING_DURING_MEDIA_PLAYBACK, 0) == 1;
+ updateDisableDucking();
+ }
+ }
+
+ private BroadcastReceiver mMediaSessionReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ updateForActiveSessions();
+ }
+ };
+
+ private void updateForActiveSessions() {
+ CMAudioManager manager = CMAudioManager.getInstance(getContext());
+ List<AudioSessionInfo> sessions = manager.listAudioSessions(AudioManager.STREAM_MUSIC);
+ mActiveMedia = false;
+ for (AudioSessionInfo sessionInfo : sessions) {
+ if (sessionInfo.getSessionId() > 0) {
+ mActiveMedia = true;
+ break;
}
}
}
+ private void updateDisableDucking() {
+ if (!mSystemReady) {
+ return;
+ }
+ try {
+ getContext().unregisterReceiver(mMediaSessionReceiver);
+ } catch (IllegalArgumentException e) {
+ // Never registered
+ }
+ if (mDisableDuckingWhileMedia) {
+ updateForActiveSessions();
+ IntentFilter intentFilter = new IntentFilter(CMAudioManager
+ .ACTION_AUDIO_SESSIONS_CHANGED);
+ getContext().registerReceiver(mMediaSessionReceiver, intentFilter);
+ }
+ }
+
private SettingsObserver mSettingsObserver;
+ private SpamFilterObserver mSpamFilterObserver;
private ZenModeHelper mZenModeHelper;
private final Runnable mBuzzBeepBlinked = new Runnable() {
@@ -851,6 +1156,7 @@ public class NotificationManagerService extends SystemService {
public NotificationManagerService(Context context) {
super(context);
+ mSpamCache = new SparseIntArray();
}
@Override
@@ -861,6 +1167,8 @@ public class NotificationManagerService extends SystemService {
mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
mAppUsageStats = LocalServices.getService(UsageStatsManagerInternal.class);
+ mKeyguardManager =
+ (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
mHandler = new WorkerHandler();
mRankingThread.start();
@@ -916,6 +1224,19 @@ public class NotificationManagerService extends SystemService {
mDefaultNotificationLedOff = resources.getInteger(
R.integer.config_defaultNotificationLedOff);
+ mMultiColorNotificationLed = resources.getBoolean(
+ R.bool.config_multiColorNotificationLed);
+
+ mNotificationPulseCustomLedValues = new HashMap<String, NotificationLedValues>();
+
+ mPackageNameMappings = new HashMap<String, String>();
+ final String[] defaultMapping = resources.getStringArray(
+ com.android.internal.R.array.notification_light_package_mapping);
+ for (String mapping : defaultMapping) {
+ String[] map = mapping.split("\\|");
+ mPackageNameMappings.put(map[0], map[1]);
+ }
+
mDefaultVibrationPattern = getLongArray(resources,
R.array.config_defaultNotificationVibePattern,
VIBRATE_PATTERN_MAXLEN,
@@ -926,6 +1247,11 @@ public class NotificationManagerService extends SystemService {
VIBRATE_PATTERN_MAXLEN,
DEFAULT_VIBRATE_PATTERN);
+ mAdjustableNotificationLedBrightness = resources.getBoolean(
+ org.cyanogenmod.platform.internal.R.bool.config_adjustableNotificationLedBrightness);
+ mMultipleNotificationLeds = resources.getBoolean(
+ org.cyanogenmod.platform.internal.R.bool.config_multipleNotificationLeds);
+
mUseAttentionLight = resources.getBoolean(R.bool.config_useAttentionLight);
// Don't start allowing notifications until the setup wizard has run once.
@@ -937,6 +1263,7 @@ public class NotificationManagerService extends SystemService {
mDisableNotificationEffects = true;
}
mZenModeHelper.initZenMode();
+ mZenModeHelper.readAllowLightsFromSettings();
mInterruptionFilter = mZenModeHelper.getZenModeListenerInterruptionFilter();
mUserProfiles.updateCache(getContext());
@@ -969,6 +1296,10 @@ public class NotificationManagerService extends SystemService {
null);
mSettingsObserver = new SettingsObserver(mHandler);
+ mSettingsObserver.observe();
+
+ mSpamFilterObserver = new SpamFilterObserver(mHandler);
+ mSpamFilterObserver.observe();
mArchive = new Archive(resources.getInteger(
R.integer.config_notificationServiceArchiveSize));
@@ -1011,10 +1342,13 @@ public class NotificationManagerService extends SystemService {
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
mZenModeHelper.onSystemReady();
+
+ updateDisableDucking();
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
// This observer will force an update when observe is called, causing us to
// bind to listener services.
mSettingsObserver.observe();
+ mSpamFilterObserver.observe();
mListeners.onBootPhaseAppsCanStart();
mConditionProviders.onBootPhaseAppsCanStart();
}
@@ -1246,6 +1580,20 @@ public class NotificationManagerService extends SystemService {
return mRankingHelper.getPackageVisibilityOverride(pkg, uid);
}
+ @Override
+ public void setShowNotificationForPackageOnKeyguard(
+ String pkg, int uid, int status) {
+ checkCallerIsSystem();
+ mRankingHelper.setShowNotificationForPackageOnKeyguard(pkg, uid, status);
+ savePolicyFile();
+ }
+
+ @Override
+ public int getShowNotificationForPackageOnKeyguard(String pkg, int uid) {
+ enforceSystemOrSystemUI("INotificationManager.getShowNotificationForPackageOnKeyguard");
+ return mRankingHelper.getShowNotificationForPackageOnKeyguard(pkg, uid);
+ }
+
/**
* System-only API for getting a list of current (i.e. not cleared) notifications.
*
@@ -1289,10 +1637,11 @@ public class NotificationManagerService extends SystemService {
Binder.getCallingUid(), incomingUserId, true, false,
"getAppActiveNotifications", pkg);
- final int N = mNotificationList.size();
- final ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>(N);
+ final ArrayList<StatusBarNotification> list
+ = new ArrayList<StatusBarNotification>(mNotificationList.size());
synchronized (mNotificationList) {
+ final int N = mNotificationList.size();
for (int i = 0; i < N; i++) {
final StatusBarNotification sbn = mNotificationList.get(i).sbn;
if (sbn.getPackageName().equals(pkg) && sbn.getUserId() == userId) {
@@ -1798,13 +2147,16 @@ public class NotificationManagerService extends SystemService {
};
private String disableNotificationEffects(NotificationRecord record) {
+ boolean smsRingtone = getContext().getResources().getBoolean(
+ com.android.internal.R.bool.config_sms_ringtone_incall);
if (mDisableNotificationEffects) {
return "booleanState";
}
if ((mListenerHints & HINT_HOST_DISABLE_EFFECTS) != 0) {
return "listenerHints";
}
- if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)) {
+ if (mCallState != TelephonyManager.CALL_STATE_IDLE && !mZenModeHelper.isCall(record)
+ && !smsRingtone) {
return "callState";
}
return null;
@@ -2168,6 +2520,11 @@ public class NotificationManagerService extends SystemService {
return;
}
+ if (isNotificationSpam(notification, pkg)) {
+ mArchive.record(r.sbn);
+ return;
+ }
+
int index = indexOfNotificationLocked(n.getKey());
if (index < 0) {
mNotificationList.add(r);
@@ -2340,7 +2697,8 @@ public class NotificationManagerService extends SystemService {
if (disableEffects != null) {
ZenLog.traceDisableEffects(record, disableEffects);
}
- if (disableEffects == null
+
+ if ((disableEffects == null)
&& (!(record.isUpdate
&& (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 ))
&& (record.getUserId() == UserHandle.USER_ALL ||
@@ -2378,7 +2736,7 @@ public class NotificationManagerService extends SystemService {
hasValidSound = (soundUri != null);
}
- if (hasValidSound) {
+ if (hasValidSound && (!mDisableDuckingWhileMedia || !mActiveMedia)) {
boolean looping =
(notification.flags & Notification.FLAG_INSISTENT) != 0;
AudioAttributes audioAttributes = audioAttributesForNotification(notification);
@@ -2456,7 +2814,9 @@ public class NotificationManagerService extends SystemService {
// light
// release the light
boolean wasShowLights = mLights.remove(record.getKey());
- if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && aboveThreshold) {
+ final boolean canInterruptWithLight = canInterrupt || isLedNotificationForcedOn(record)
+ || (!canInterrupt && mZenModeHelper.getAllowLights());
+ if ((notification.flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterruptWithLight) {
mLights.add(record.getKey());
updateLightsLocked();
if (mUseAttentionLight) {
@@ -2742,6 +3102,33 @@ public class NotificationManagerService extends SystemService {
return (x < low) ? low : ((x > high) ? high : x);
}
+ private int getSpamCacheHash(CharSequence message, String packageName) {
+ return (message + packageName).hashCode();
+ }
+
+ private boolean isNotificationSpam(Notification notification, String basePkg) {
+ CharSequence normalizedContent = SpamFilter
+ .getNormalizedNotificationContent(notification);
+ int notificationHash = getSpamCacheHash(normalizedContent, basePkg);
+ int notificationId;
+ synchronized (mSpamCache) {
+ notificationId = mSpamCache.get(notificationHash, -1);
+ }
+ if (notificationId != -1) {
+ final String notifIdString = String.valueOf(notificationId);
+ mSpamExecutor.submit(new Runnable() {
+ @Override
+ public void run() {
+ Uri updateUri = Uri.withAppendedPath(UPDATE_MSG_URI,
+ notifIdString);
+ getContext().getContentResolver().update(updateUri,
+ new ContentValues(), null, null);
+ }
+ });
+ }
+ return notificationId != -1;
+ }
+
void sendAccessibilityEvent(Notification notification, CharSequence packageName) {
AccessibilityManager manager = AccessibilityManager.getInstance(getContext());
if (!manager.isEnabled()) {
@@ -3041,6 +3428,16 @@ public class NotificationManagerService extends SystemService {
}
}
+ private boolean isLedNotificationForcedOn(NotificationRecord r) {
+ if (r != null) {
+ final Notification n = r.sbn.getNotification();
+ if (n.extras != null) {
+ return n.extras.getBoolean(Notification.EXTRA_FORCE_SHOW_LIGHTS, false);
+ }
+ }
+ return false;
+ }
+
// lock on mNotificationList
void updateLightsLocked()
{
@@ -3056,29 +3453,131 @@ public class NotificationManagerService extends SystemService {
}
// Don't flash while we are in a call or screen is on
- if (ledNotification == null || mInCall || mScreenOn) {
+ // (unless Notification has EXTRA_FORCE_SHOW_LGHTS)
+ final boolean enableLed;
+ if (ledNotification == null) {
+ enableLed = false;
+ } else if (isLedNotificationForcedOn(ledNotification)) {
+ enableLed = true;
+ } else if (!mScreenOnEnabled && (mInCall || mScreenOn)) {
+ enableLed = false;
+ } else {
+ enableLed = true;
+ }
+
+ if (!enableLed) {
mNotificationLight.turnOff();
mStatusBar.notificationLightOff();
} else {
final Notification ledno = ledNotification.sbn.getNotification();
- int ledARGB = ledno.ledARGB;
- int ledOnMS = ledno.ledOnMS;
- int ledOffMS = ledno.ledOffMS;
- if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
- ledARGB = mDefaultNotificationColor;
+ final NotificationLedValues ledValues = getLedValuesForNotification(ledNotification);
+ int ledARGB;
+ int ledOnMS;
+ int ledOffMS;
+
+ if (ledValues != null) {
+ ledARGB = ledValues.color != 0 ? ledValues.color : mDefaultNotificationColor;
+ ledOnMS = ledValues.onMS >= 0 ? ledValues.onMS : mDefaultNotificationLedOn;
+ ledOffMS = ledValues.offMS >= 0 ? ledValues.offMS : mDefaultNotificationLedOff;
+ } else if ((ledno.defaults & Notification.DEFAULT_LIGHTS) != 0) {
+ ledARGB = generateLedColorForNotification(ledNotification);
ledOnMS = mDefaultNotificationLedOn;
ledOffMS = mDefaultNotificationLedOff;
+ } else {
+ ledARGB = ledno.ledARGB;
+ ledOnMS = ledno.ledOnMS;
+ ledOffMS = ledno.ledOffMS;
}
+
+ // update the LEDs modes variables
+ mNotificationLight.setModes(mNotificationLedBrightnessLevel,
+ mMultipleLedsEnabledSetting);
+
if (mNotificationPulseEnabled) {
// pulse repeatedly
mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED,
ledOnMS, ledOffMS);
}
+
// let SystemUI make an independent decision
mStatusBar.notificationLightPulse(ledARGB, ledOnMS, ledOffMS);
}
}
+ private void parseNotificationPulseCustomValuesString(String customLedValuesString) {
+ if (TextUtils.isEmpty(customLedValuesString)) {
+ return;
+ }
+
+ for (String packageValuesString : customLedValuesString.split("\\|")) {
+ String[] packageValues = packageValuesString.split("=");
+ if (packageValues.length != 2) {
+ Log.e(TAG, "Error parsing custom led values for unknown package");
+ continue;
+ }
+ String packageName = packageValues[0];
+ String[] values = packageValues[1].split(";");
+ if (values.length != 3) {
+ Log.e(TAG, "Error parsing custom led values '"
+ + packageValues[1] + "' for " + packageName);
+ continue;
+ }
+ NotificationLedValues ledValues = new NotificationLedValues();
+ try {
+ ledValues.color = Integer.parseInt(values[0]);
+ ledValues.onMS = Integer.parseInt(values[1]);
+ ledValues.offMS = Integer.parseInt(values[2]);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Error parsing custom led values '"
+ + packageValues[1] + "' for " + packageName);
+ continue;
+ }
+ mNotificationPulseCustomLedValues.put(packageName, ledValues);
+ }
+ }
+
+ private NotificationLedValues getLedValuesForNotification(NotificationRecord ledNotification) {
+ final String packageName = ledNotification.sbn.getPackageName();
+ return mNotificationPulseCustomLedValues.get(mapPackage(packageName));
+ }
+
+ private int generateLedColorForNotification(NotificationRecord ledNotification) {
+ if (!mAutoGenerateNotificationColor) {
+ return mDefaultNotificationColor;
+ }
+ if (!mMultiColorNotificationLed) {
+ return mDefaultNotificationColor;
+ }
+ final String packageName = ledNotification.sbn.getPackageName();
+ final String mapping = mapPackage(packageName);
+ int color = mDefaultNotificationColor;
+
+ if (mGeneratedPackageLedColors.containsKey(mapping)) {
+ return mGeneratedPackageLedColors.get(mapping);
+ }
+
+ PackageManager pm = getContext().getPackageManager();
+ Drawable icon;
+ try {
+ icon = pm.getApplicationIcon(mapping);
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, e.getMessage(), e);
+ return color;
+ }
+
+ color = ColorUtils.generateAlertColorFromDrawable(icon);
+ mGeneratedPackageLedColors.put(mapping, color);
+
+ return color;
+ }
+
+ private String mapPackage(String pkg) {
+ if (!mPackageNameMappings.containsKey(pkg)) {
+ return pkg;
+ }
+ return mPackageNameMappings.get(pkg);
+ }
+
// lock on mNotificationList
int indexOfNotificationLocked(String pkg, String tag, int id, int userId)
{
diff --git a/services/core/java/com/android/server/notification/RankingConfig.java b/services/core/java/com/android/server/notification/RankingConfig.java
index 803db10..320cf75 100644
--- a/services/core/java/com/android/server/notification/RankingConfig.java
+++ b/services/core/java/com/android/server/notification/RankingConfig.java
@@ -27,4 +27,9 @@ public interface RankingConfig {
int getPackageVisibilityOverride(String packageName, int uid);
void setPackageVisibilityOverride(String packageName, int uid, int visibility);
+
+ void setShowNotificationForPackageOnKeyguard(String packageName, int uid, int status);
+
+ int getShowNotificationForPackageOnKeyguard(String packageName, int uid);
+
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index a089518..5112370 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -51,6 +51,7 @@ public class RankingHelper implements RankingConfig {
private static final String ATT_PRIORITY = "priority";
private static final String ATT_PEEKABLE = "peekable";
private static final String ATT_VISIBILITY = "visibility";
+ private static final String ATT_KEYGUARD = "keyguard";
private static final int DEFAULT_PRIORITY = Notification.PRIORITY_DEFAULT;
private static final boolean DEFAULT_PEEKABLE = true;
@@ -143,6 +144,8 @@ public class RankingHelper implements RankingConfig {
int priority = safeInt(parser, ATT_PRIORITY, DEFAULT_PRIORITY);
boolean peekable = safeBool(parser, ATT_PEEKABLE, DEFAULT_PEEKABLE);
int vis = safeInt(parser, ATT_VISIBILITY, DEFAULT_VISIBILITY);
+ int keyguard = safeInt(parser, ATT_KEYGUARD,
+ Notification.SHOW_ALL_NOTI_ON_KEYGUARD);
String name = parser.getAttributeValue(null, ATT_NAME);
if (!TextUtils.isEmpty(name)) {
@@ -172,6 +175,9 @@ public class RankingHelper implements RankingConfig {
if (vis != DEFAULT_VISIBILITY) {
r.visibility = vis;
}
+ if (keyguard != Notification.SHOW_ALL_NOTI_ON_KEYGUARD) {
+ r.keyguard = keyguard;
+ }
}
}
}
@@ -200,8 +206,9 @@ public class RankingHelper implements RankingConfig {
for (int i = N - 1; i >= 0; i--) {
final Record r = mRecords.valueAt(i);
if (r.priority == DEFAULT_PRIORITY && r.peekable == DEFAULT_PEEKABLE
- && r.visibility == DEFAULT_VISIBILITY) {
- mRecords.remove(i);
+ && r.visibility == DEFAULT_VISIBILITY
+ && r.keyguard == Notification.SHOW_ALL_NOTI_ON_KEYGUARD) {
+ mRecords.removeAt(i);
}
}
}
@@ -227,6 +234,9 @@ public class RankingHelper implements RankingConfig {
if (r.visibility != DEFAULT_VISIBILITY) {
out.attribute(null, ATT_VISIBILITY, Integer.toString(r.visibility));
}
+ if (r.keyguard != Notification.SHOW_ALL_NOTI_ON_KEYGUARD) {
+ out.attribute(null, ATT_KEYGUARD, Integer.toBinaryString(r.keyguard));
+ }
if (!forBackup) {
out.attribute(null, ATT_UID, Integer.toString(r.uid));
}
@@ -377,6 +387,23 @@ public class RankingHelper implements RankingConfig {
updateConfig();
}
+ @Override
+ public int getShowNotificationForPackageOnKeyguard(String packageName, int uid) {
+ final Record r = mRecords.get(recordKey(packageName, uid));
+ return r != null ? r.keyguard : Notification.SHOW_ALL_NOTI_ON_KEYGUARD;
+ }
+
+ @Override
+ public void setShowNotificationForPackageOnKeyguard(
+ String packageName, int uid, int keyguard) {
+ if (keyguard == getShowNotificationForPackageOnKeyguard(packageName, uid)) {
+ return;
+ }
+ getOrCreateRecord(packageName, uid).keyguard = keyguard;
+ removeDefaultRecords();
+ updateConfig();
+ }
+
public void dump(PrintWriter pw, String prefix, NotificationManagerService.DumpFilter filter) {
if (filter == null) {
final int N = mSignalExtractors.length;
@@ -459,6 +486,7 @@ public class RankingHelper implements RankingConfig {
int priority = DEFAULT_PRIORITY;
boolean peekable = DEFAULT_PEEKABLE;
int visibility = DEFAULT_VISIBILITY;
+ int keyguard = Notification.SHOW_ALL_NOTI_ON_KEYGUARD;
}
}
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 461c3a2..468ef8d 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -53,6 +53,7 @@ import android.util.SparseArray;
import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
import com.android.server.LocalServices;
+import cyanogenmod.providers.CMSettings;
import libcore.io.IoUtils;
@@ -89,6 +90,7 @@ public class ZenModeHelper {
private ZenModeConfig mConfig;
private AudioManagerInternal mAudioManager;
private boolean mEffectsSuppressed;
+ private boolean mAllowLights;
public ZenModeHelper(Context context, Looper looper, ConditionProviders conditionProviders) {
mContext = context;
@@ -378,6 +380,7 @@ public class ZenModeHelper {
ZenLog.traceSetZenMode(zen, reason);
mZenMode = zen;
updateRingerModeAffectedStreams();
+ readAllowLightsFromSettings();
setZenModeSetting(mZenMode);
if (setRingerMode) {
applyZenToRingerMode();
@@ -407,6 +410,24 @@ public class ZenModeHelper {
return zen;
}
+ public boolean getAllowLights() {
+ return mAllowLights;
+ }
+
+ public void readAllowLightsFromSettings() {
+ switch (mZenMode) {
+ case Global.ZEN_MODE_NO_INTERRUPTIONS:
+ case Global.ZEN_MODE_ALARMS:
+ mAllowLights = CMSettings.System.getInt(mContext.getContentResolver(),
+ CMSettings.System.ZEN_ALLOW_LIGHTS, 1) == 1;
+ break;
+ case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS:
+ mAllowLights = CMSettings.System.getInt(mContext.getContentResolver(),
+ CMSettings.System.ZEN_PRIORITY_ALLOW_LIGHTS, 1) == 1;
+ break;
+ }
+ }
+
private void applyRestrictions() {
final boolean zen = mZenMode != Global.ZEN_MODE_OFF;
@@ -415,13 +436,14 @@ public class ZenModeHelper {
applyRestrictions(muteNotifications, USAGE_NOTIFICATION);
// call restrictions
- final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers
- || mEffectsSuppressed;
+ final boolean muteCalls = zen && !mConfig.allowCalls && !mConfig.allowRepeatCallers;
applyRestrictions(muteCalls, USAGE_NOTIFICATION_RINGTONE);
// alarm restrictions
final boolean muteAlarms = mZenMode == Global.ZEN_MODE_NO_INTERRUPTIONS;
applyRestrictions(muteAlarms, USAGE_ALARM);
+
+ readAllowLightsFromSettings();
}
private void applyRestrictions(boolean mute, int usage) {
@@ -692,6 +714,10 @@ public class ZenModeHelper {
private final class SettingsObserver extends ContentObserver {
private final Uri ZEN_MODE = Global.getUriFor(Global.ZEN_MODE);
+ private final Uri ZEN_ALLOW_LIGHTS = CMSettings.System.getUriFor(
+ CMSettings.System.ZEN_ALLOW_LIGHTS);
+ private final Uri ZEN_PRIORITY_ALLOW_LIGHTS = CMSettings.System.getUriFor(
+ CMSettings.System.ZEN_PRIORITY_ALLOW_LIGHTS);
public SettingsObserver(Handler handler) {
super(handler);
@@ -700,6 +726,10 @@ public class ZenModeHelper {
public void observe() {
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(ZEN_MODE, false /*notifyForDescendents*/, this);
+ resolver.registerContentObserver(
+ ZEN_ALLOW_LIGHTS, false /*notifyForDescendents*/, this);
+ resolver.registerContentObserver(
+ ZEN_PRIORITY_ALLOW_LIGHTS, false /*notifyForDescendents*/, this);
update(null);
}
@@ -714,6 +744,8 @@ public class ZenModeHelper {
if (DEBUG) Log.d(TAG, "Fixing zen mode setting");
setZenModeSetting(mZenMode);
}
+ } else if (ZEN_ALLOW_LIGHTS.equals(uri) || ZEN_PRIORITY_ALLOW_LIGHTS.equals(uri)) {
+ readAllowLightsFromSettings();
}
}
}
diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java
index 52d0928..cab4691 100644
--- a/services/core/java/com/android/server/pm/BasePermission.java
+++ b/services/core/java/com/android/server/pm/BasePermission.java
@@ -53,6 +53,8 @@ final class BasePermission {
*/
private boolean perUser;
+ boolean allowViaWhitelist;
+
BasePermission(String _name, String _sourcePackage, int _type) {
name = _name;
sourcePackage = _sourcePackage;
diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
index b504605..29e9fa6 100644
--- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java
@@ -20,6 +20,7 @@ import android.Manifest;
import android.app.DownloadManager;
import android.app.admin.DevicePolicyManager;
import android.content.Intent;
+import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal.PackagesProvider;
@@ -571,6 +572,26 @@ final class DefaultPermissionGrantPolicy {
grantRuntimePermissionsLPw(musicPackage, STORAGE_PERMISSIONS, userId);
}
+ // Android Wear Home
+ if (mService.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN);
+ homeIntent.addCategory(Intent.CATEGORY_HOME_MAIN);
+
+ PackageParser.Package wearHomePackage = getDefaultSystemHandlerActivityPackageLPr(
+ homeIntent, userId);
+
+ if (wearHomePackage != null
+ && doesPackageSupportRuntimePermissions(wearHomePackage)) {
+ grantRuntimePermissionsLPw(wearHomePackage, CONTACTS_PERMISSIONS, false,
+ userId);
+ grantRuntimePermissionsLPw(wearHomePackage, PHONE_PERMISSIONS, true, userId);
+ grantRuntimePermissionsLPw(wearHomePackage, MICROPHONE_PERMISSIONS, false,
+ userId);
+ grantRuntimePermissionsLPw(wearHomePackage, LOCATION_PERMISSIONS, false,
+ userId);
+ }
+ }
+
mService.mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);
}
}
@@ -578,14 +599,16 @@ final class DefaultPermissionGrantPolicy {
private void grantDefaultPermissionsToDefaultSystemDialerAppLPr(
PackageParser.Package dialerPackage, int userId) {
if (doesPackageSupportRuntimePermissions(dialerPackage)) {
- grantRuntimePermissionsLPw(dialerPackage, PHONE_PERMISSIONS, userId);
+ boolean isPhonePermFixed =
+ mService.hasSystemFeature(PackageManager.FEATURE_WATCH);
+ grantRuntimePermissionsLPw(
+ dialerPackage, PHONE_PERMISSIONS, isPhonePermFixed, userId);
grantRuntimePermissionsLPw(dialerPackage, CONTACTS_PERMISSIONS, userId);
grantRuntimePermissionsLPw(dialerPackage, SMS_PERMISSIONS, userId);
grantRuntimePermissionsLPw(dialerPackage, MICROPHONE_PERMISSIONS, userId);
}
}
-
private void grantDefaultPermissionsToDefaultSystemSmsAppLPr(
PackageParser.Package smsPackage, int userId) {
if (doesPackageSupportRuntimePermissions(smsPackage)) {
@@ -595,7 +618,6 @@ final class DefaultPermissionGrantPolicy {
}
}
-
public void grantDefaultPermissionsToDefaultSmsAppLPr(String packageName, int userId) {
Log.i(TAG, "Granting permissions to default sms app for user:" + userId);
if (packageName == null) {
@@ -673,29 +695,23 @@ final class DefaultPermissionGrantPolicy {
private PackageParser.Package getDefaultSystemHandlerActivityPackageLPr(
Intent intent, int userId) {
- List<ResolveInfo> handlers = mService.mActivities.queryIntent(intent,
- intent.resolveType(mService.mContext.getContentResolver()),
- PackageManager.GET_DISABLED_COMPONENTS, userId);
- if (handlers == null) {
+ ResolveInfo handler = mService.resolveIntent(intent,
+ intent.resolveType(mService.mContext.getContentResolver()), 0, userId);
+ if (handler == null || handler.activityInfo == null) {
return null;
}
- final int handlerCount = handlers.size();
- for (int i = 0; i < handlerCount; i++) {
- ResolveInfo handler = handlers.get(i);
- PackageParser.Package handlerPackage = getSystemPackageLPr(
- handler.activityInfo.packageName);
- if (handlerPackage != null) {
- return handlerPackage;
- }
+ ActivityInfo activityInfo = handler.activityInfo;
+ if (activityInfo.packageName.equals(mService.mResolveActivity.packageName)
+ && activityInfo.name.equals(mService.mResolveActivity.name)) {
+ return null;
}
- return null;
+ return getSystemPackageLPr(handler.activityInfo.packageName);
}
private PackageParser.Package getDefaultSystemHandlerServicePackageLPr(
Intent intent, int userId) {
List<ResolveInfo> handlers = mService.queryIntentServices(intent,
- intent.resolveType(mService.mContext.getContentResolver()),
- PackageManager.GET_DISABLED_COMPONENTS, userId);
+ intent.resolveType(mService.mContext.getContentResolver()), 0, userId);
if (handlers == null) {
return null;
}
@@ -721,10 +737,9 @@ final class DefaultPermissionGrantPolicy {
for (String syncAdapterPackageName : syncAdapterPackageNames) {
homeIntent.setPackage(syncAdapterPackageName);
- List<ResolveInfo> homeActivities = mService.mActivities.queryIntent(homeIntent,
- homeIntent.resolveType(mService.mContext.getContentResolver()),
- PackageManager.GET_DISABLED_COMPONENTS, userId);
- if (!homeActivities.isEmpty()) {
+ ResolveInfo homeActivity = mService.resolveIntent(homeIntent,
+ homeIntent.resolveType(mService.mContext.getContentResolver()), 0, userId);
+ if (homeActivity != null) {
continue;
}
@@ -843,7 +858,7 @@ final class DefaultPermissionGrantPolicy {
return false;
}
PackageSetting sysPkg = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);
- if (sysPkg != null) {
+ if (sysPkg != null && sysPkg.pkg != null) {
if ((sysPkg.pkg.applicationInfo.flags & ApplicationInfo.FLAG_PERSISTENT) == 0) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index f292c9c..4be7e25 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -110,14 +110,53 @@ public final class Installer extends SystemService {
debuggable, outputPath, bootComplete);
}
- public int idmap(String targetApkPath, String overlayApkPath, int uid) {
+ public int idmap(String targetApkPath, String overlayApkPath, String cachePath,
+ int uid, int targetHash, int overlayHash) {
StringBuilder builder = new StringBuilder("idmap");
builder.append(' ');
builder.append(targetApkPath);
builder.append(' ');
builder.append(overlayApkPath);
builder.append(' ');
+ builder.append(cachePath);
+ builder.append(' ');
+ builder.append(uid);
+ builder.append(' ');
+ builder.append(targetHash);
+ builder.append(' ');
+ builder.append(overlayHash);
+ return mInstaller.execute(builder.toString());
+ }
+
+ public int aapt(String themeApkPath, String internalPath, String resTablePath, int uid,
+ int pkgId, int minSdkVersion, String appResourcesPath, String commonResourcesPath) {
+
+ StringBuilder builder = new StringBuilder();
+ if (TextUtils.isEmpty(commonResourcesPath)) {
+ builder.append("aapt");
+ } else {
+ builder.append("aapt_with_common");
+ }
+ builder.append(' ');
+ builder.append(themeApkPath);
+ builder.append(' ');
+ builder.append(internalPath);
+ builder.append(' ');
+ builder.append(resTablePath);
+ builder.append(' ');
builder.append(uid);
+ builder.append(' ');
+ builder.append(pkgId);
+ builder.append(' ');
+ builder.append(minSdkVersion);
+ builder.append(' ');
+ builder.append(appResourcesPath);
+
+ if (!TextUtils.isEmpty(commonResourcesPath)) {
+ builder.append(' ');
+ builder.append(commonResourcesPath);
+ }
+
return mInstaller.execute(builder.toString());
}
diff --git a/services/core/java/com/android/server/pm/MultiTaskDealer.java b/services/core/java/com/android/server/pm/MultiTaskDealer.java
new file mode 100644
index 0000000..9b8d46e
--- /dev/null
+++ b/services/core/java/com/android/server/pm/MultiTaskDealer.java
@@ -0,0 +1,141 @@
+/*
+* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions are
+* met:
+* * Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* * Redistributions in binary form must reproduce the above
+* copyright notice, this list of conditions and the following
+* disclaimer in the documentation and/or other materials provided
+* with the distribution.
+* * Neither the name of The Linux Foundation nor the names of its
+* contributors may be used to endorse or promote products derived
+* from this software without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+package com.android.server.pm;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.locks.ReentrantLock;
+
+import android.util.Log;
+
+public class MultiTaskDealer {
+
+ public static final String TAG = "MultiTaskDealer";
+ public static final String PACKAGEMANAGER_SCANER = "packagescan";
+ private static final boolean DEBUG_TASK = false;
+
+ private static HashMap<String, WeakReference<MultiTaskDealer>> map = new HashMap<String, WeakReference<MultiTaskDealer>>();
+
+ public static MultiTaskDealer getDealer(String name) {
+ WeakReference<MultiTaskDealer> ref = map.get(name);
+ MultiTaskDealer dealer = ref!=null?ref.get():null;
+ return dealer;
+ }
+
+ public static MultiTaskDealer startDealer(String name,int taskCount) {
+ MultiTaskDealer dealer = getDealer(name);
+ if(dealer==null) {
+ dealer = new MultiTaskDealer(name,taskCount);
+ WeakReference<MultiTaskDealer> ref = new WeakReference<MultiTaskDealer>(dealer);
+ map.put(name,ref);
+ }
+ return dealer;
+ }
+
+ public void startLock() {
+ mLock.lock();
+ }
+
+ public void endLock() {
+ mLock.unlock();
+ }
+
+ private ThreadPoolExecutor mExecutor;
+ private int mTaskCount = 0;
+ private boolean mNeedNotifyEnd = false;
+ private Object mObjWaitAll = new Object();
+ private ReentrantLock mLock = new ReentrantLock();
+
+ public MultiTaskDealer(String name,int taskCount) {
+ final String taskName = name;
+ ThreadFactory factory = new ThreadFactory()
+ {
+ private final AtomicInteger mCount = new AtomicInteger(1);
+
+ public Thread newThread(final Runnable r) {
+ if (DEBUG_TASK) Log.d(TAG, "create a new thread:" + taskName);
+ return new Thread(r, taskName + "-" + mCount.getAndIncrement());
+ }
+ };
+ mExecutor = new ThreadPoolExecutor(taskCount, taskCount, 5, TimeUnit.SECONDS,
+ new LinkedBlockingQueue<Runnable>(), factory){
+ protected void afterExecute(Runnable r, Throwable t) {
+ if(t!=null) {
+ t.printStackTrace();
+ }
+ MultiTaskDealer.this.TaskCompleteNotify(r);
+ if (DEBUG_TASK) Log.d(TAG, "end task");
+ super.afterExecute(r,t);
+ }
+ protected void beforeExecute(Thread t, Runnable r) {
+ if (DEBUG_TASK) Log.d(TAG, "start task");
+ super.beforeExecute(t,r);
+ }
+ };
+ }
+
+ public void addTask(Runnable task) {
+ synchronized (mObjWaitAll) {
+ mTaskCount+=1;
+ }
+ mExecutor.execute(task);
+ if (DEBUG_TASK) Log.d(TAG, "addTask");
+ }
+
+ private void TaskCompleteNotify(Runnable task) {
+ synchronized (mObjWaitAll) {
+ mTaskCount-=1;
+ if(mTaskCount<=0 && mNeedNotifyEnd) {
+ if (DEBUG_TASK) Log.d(TAG, "complete notify");
+ mObjWaitAll.notify();
+ }
+ }
+ }
+
+ public void waitAll() {
+ if (DEBUG_TASK) Log.d(TAG, "start wait all");
+ synchronized (mObjWaitAll) {
+ if(mTaskCount>0) {
+ mNeedNotifyEnd = true;
+ try {
+ mObjWaitAll.wait();
+ } catch (Exception e) {
+ }
+ mNeedNotifyEnd = false;
+ }
+ if (DEBUG_TASK) Log.d(TAG, "wait finish");
+ return;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index b692def..fa11ff8 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageParser;
+import android.os.Environment;
import android.os.PowerManager;
import android.os.UserHandle;
import android.os.WorkSource;
@@ -56,6 +57,8 @@ final class PackageDexOptimizer {
private final PowerManager.WakeLock mDexoptWakeLock;
private volatile boolean mSystemReady;
+ private Object mDeferredDexOptSync = new Object();
+
PackageDexOptimizer(PackageManagerService packageManagerService) {
this.mPackageManagerService = packageManagerService;
PowerManager powerManager = (PowerManager)packageManagerService.mContext.getSystemService(
@@ -149,7 +152,9 @@ final class PackageDexOptimizer {
// We're deciding to defer a needed dexopt. Don't bother dexopting for other
// paths and instruction sets. We'll deal with them all together when we process
// our list of deferred dexopts.
- addPackageForDeferredDexopt(pkg);
+ synchronized (mDeferredDexOptSync) {
+ addPackageForDeferredDexopt(pkg);
+ }
return DEX_OPT_DEFERRED;
}
@@ -224,6 +229,11 @@ final class PackageDexOptimizer {
File codePath = new File(pkg.codePath);
if (codePath.isDirectory()) {
File oatDir = getOatDir(codePath);
+ // skip the prebundled apps dir since it's actually read-only
+ if (oatDir.getAbsolutePath().startsWith(
+ Environment.getPrebundledDirectory().getAbsolutePath())) {
+ return null;
+ }
mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(),
dexInstructionSet);
return oatDir.getAbsolutePath();
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4a473fd..65f5bce 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -381,7 +381,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
if (stageDir != null && deltaBytes > 0) {
mPm.freeStorage(params.volumeUuid, deltaBytes);
}
- Libcore.os.posix_fallocate(targetFd, 0, lengthBytes);
+ try {
+ Libcore.os.posix_fallocate(targetFd, 0, lengthBytes);
+ } catch (ErrnoException e) {
+ if (e.errno == OsConstants.ENOTSUP) {
+ Libcore.os.ftruncate(targetFd, lengthBytes);
+ } else {
+ throw e.rethrowAsIOException();
+ }
+ }
}
if (offsetBytes > 0) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c1d091b..920a850 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1,5 +1,9 @@
/*
+ * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2006 The Android Open Source Project
+ * This code has been modified. Portions copyright (C) 2010, T-Mobile USA, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -42,6 +46,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
import static android.content.pm.PackageManager.INSTALL_FAILED_PACKAGE_CHANGED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_REGION_LOCKED_PREBUNDLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_REPLACE_COULDNT_DELETE;
import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE;
import static android.content.pm.PackageManager.INSTALL_FAILED_TEST_ONLY;
@@ -64,6 +69,7 @@ import static android.content.pm.PackageManager.MOVE_FAILED_OPERATION_PENDING;
import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE;
import static android.content.pm.PackageManager.PERMISSION_DENIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_UNINSTALLED_PREBUNDLE;
import static android.content.pm.PackageParser.isApkFile;
import static android.os.Process.PACKAGE_INFO_GID;
import static android.os.Process.SYSTEM_UID;
@@ -82,12 +88,23 @@ import static com.android.server.pm.InstructionSets.getPrimaryInstructionSet;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_FAILURE;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS;
import static com.android.server.pm.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
+import static com.android.internal.util.ArrayUtils.removeInt;
+
+import android.content.res.Configuration;
import android.Manifest;
+
+import cyanogenmod.app.CMContextConstants;
+import cyanogenmod.app.suggest.AppSuggestManager;
+
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
+import android.app.ComposedIconInfo;
+import android.app.AppOpsManager;
import android.app.IActivityManager;
+import android.app.IconPackHelper;
+import android.app.PackageInstallObserver;
import android.app.admin.IDevicePolicyManager;
import android.app.backup.IBackupManager;
import android.app.usage.UsageStats;
@@ -129,6 +146,9 @@ import android.content.pm.PackageParser.ActivityIntentInfo;
import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageStats;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageParser.Activity;
+import android.content.pm.PackageParser.Package;
import android.content.pm.PackageUserState;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
@@ -138,10 +158,13 @@ import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
+import android.content.pm.ManifestDigest;
import android.content.pm.VerificationParams;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
import android.content.res.Resources;
+import android.content.res.AssetManager;
+import android.content.res.ThemeConfig;
import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Debug;
@@ -172,6 +195,8 @@ import android.os.storage.StorageEventListener;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
+import android.provider.Settings.Global;
+import android.provider.Settings.Secure;
import android.security.KeyStore;
import android.security.SystemKeyStore;
import android.system.ErrnoException;
@@ -188,6 +213,7 @@ import android.util.ExceptionUtils;
import android.util.Log;
import android.util.LogPrinter;
import android.util.MathUtils;
+import android.util.Pair;
import android.util.PrintStreamPrinter;
import android.util.Slog;
import android.util.SparseArray;
@@ -196,6 +222,9 @@ import android.util.SparseIntArray;
import android.util.Xml;
import android.view.Display;
+import cyanogenmod.providers.CMSettings;
+import cyanogenmod.themes.IThemeService;
+
import dalvik.system.DexFile;
import dalvik.system.VMRuntime;
@@ -222,12 +251,14 @@ import com.android.server.IntentResolver;
import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
+import com.android.server.SystemConfig.AppLink;
import com.android.server.Watchdog;
import com.android.server.pm.PermissionsState.PermissionState;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
import com.android.server.storage.DeviceStorageMonitorInternal;
+import org.cyanogenmod.internal.util.ThemeUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -237,16 +268,25 @@ import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
+import java.io.BufferedWriter;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
+import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.IntBuffer;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.CertificateEncodingException;
@@ -297,7 +337,10 @@ public class PackageManagerService extends IPackageManager.Stub {
private static final boolean DEBUG_PACKAGE_SCANNING = false;
private static final boolean DEBUG_VERIFY = false;
private static final boolean DEBUG_DEXOPT = false;
+ private static final boolean DEBUG_FILTERS = false;
private static final boolean DEBUG_ABI_SELECTION = false;
+ private static final boolean DEBUG_PREBUNDLED_SCAN = false;
+ private static final boolean DEBUG_PROTECTED = false;
static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false;
@@ -339,7 +382,7 @@ public class PackageManagerService extends IPackageManager.Stub {
* minute but we sometimes do very lengthy I/O operations on this thread,
* such as installing multi-gigabyte applications, so ours needs to be longer.
*/
- private static final long WATCHDOG_TIMEOUT = 1000*60*10; // ten minutes
+ private static final long WATCHDOG_TIMEOUT = 1000*60*60; // sixty minutes
/**
* Wall-clock timeout (in milliseconds) after which we *require* that an fstrim
@@ -414,6 +457,24 @@ public class PackageManagerService extends IPackageManager.Stub {
final ServiceThread mHandlerThread;
+ //Where overlays are be found in a theme APK
+ private static final String APK_PATH_TO_OVERLAY = "assets/overlays/";
+
+ //Where the icon pack can be found in a themed apk
+ private static final String APK_PATH_TO_ICONS = "assets/icons/";
+
+ private static final String COMMON_OVERLAY = ThemeUtils.COMMON_RES_TARGET;
+
+ private static final long COMMON_RESOURCE_EXPIRATION = 3*60*1000; // 3 minutes
+
+ private static final String PROTECTED_APPS_TARGET_VALIDATION_COMPONENT =
+ "com.android.settings/com.android.settings.applications.ProtectedAppsActivity";
+
+ /**
+ * The offset in bytes to the beginning of the hashes in an idmap
+ */
+ private static final int IDMAP_HASH_START_OFFSET = 16;
+
final PackageHandler mHandler;
/**
@@ -500,10 +561,13 @@ public class PackageManagerService extends IPackageManager.Stub {
final Settings mSettings;
boolean mRestoredSettings;
+ private Resources mCustomResources;
+
// System configuration read by SystemConfig.
final int[] mGlobalGids;
final SparseArray<ArraySet<String>> mSystemPermissions;
final ArrayMap<String, FeatureInfo> mAvailableFeatures;
+ final ArrayMap<Signature, ArraySet<String>> mSignatureAllowances;
// If mac_permissions.xml was found for seinfo labeling.
boolean mFoundPolicyFile;
@@ -557,7 +621,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final ArraySet<String> mTransferedPackages = new ArraySet<String>();
// Broadcast actions that are only available to the system.
- final ArraySet<String> mProtectedBroadcasts = new ArraySet<String>();
+ final ArrayMap<String, String> mProtectedBroadcasts = new ArrayMap<>();
/** List of packages waiting for verification. */
final SparseArray<PackageVerificationState> mPendingVerification
@@ -819,6 +883,16 @@ public class PackageManagerService extends IPackageManager.Stub {
private IntentFilterVerifier mIntentFilterVerifier;
+ private IconPackHelper mIconPackHelper;
+
+ private Map<String, Long> mAvailableCommonResources = new ArrayMap<String, Long>();
+
+ private ThemeConfig mBootThemeConfig;
+
+ ArrayList<ComponentName> mDisabledComponentsList;
+
+ private AppOpsManager mAppOps;
+
// Set of pending broadcasts for aggregating enable/disable of components.
static class PendingPackageBroadcasts {
// for each user id, a map of <package name -> components within that package>
@@ -1388,18 +1462,23 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- packageName, extras, null, null, firstUsers);
+ packageName, null, extras, null, null, firstUsers);
final boolean update = res.removedInfo.removedPackage != null;
if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
+ String category = null;
+ if(res.pkg.mIsThemeApk) {
+ category = cyanogenmod.content.Intent
+ .CATEGORY_THEME_PACKAGE_INSTALLED_STATE_CHANGE;
+ }
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- packageName, extras, null, null, updateUsers);
+ packageName, null, extras, null, null, updateUsers);
if (update) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- packageName, extras, null, null, updateUsers);
+ packageName, null, extras, null, null, updateUsers);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
- null, null, packageName, null, updateUsers);
+ null, null, null, packageName, null, updateUsers);
// treat asec-hosted packages like removable media on upgrade
if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
@@ -1414,6 +1493,21 @@ public class PackageManagerService extends IPackageManager.Stub {
pkgList,uidArray, null);
}
}
+ // if this was a theme, send it off to the theme service for processing
+ if(res.pkg.mIsThemeApk || res.pkg.mIsLegacyIconPackApk) {
+ processThemeResourcesInThemeService(res.pkg.packageName);
+ } else if (mOverlays.containsKey(res.pkg.packageName)) {
+
+ // if this was an app and is themed send themes that theme it
+ // for processing
+ ArrayMap<String, PackageParser.Package> themes =
+ mOverlays.get(res.pkg.packageName);
+
+ for (PackageParser.Package themePkg : themes.values()) {
+ processThemeResourcesInThemeService(themePkg.packageName);
+ }
+
+ }
if (res.removedInfo.args != null) {
// Remove the replaced package's older resources safely now
deleteOld = true;
@@ -1432,6 +1526,17 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
}
+ if (!update && !isSystemApp(res.pkg)) {
+ boolean privacyGuard = CMSettings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ CMSettings.Secure.PRIVACY_GUARD_DEFAULT,
+ 0, UserHandle.USER_CURRENT) == 1;
+ if (privacyGuard) {
+ mAppOps.setPrivacyGuardSettingForPackage(
+ res.pkg.applicationInfo.uid,
+ res.pkg.applicationInfo.packageName, true);
+ }
+ }
// Log current value of "unknown sources" setting
EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
getUnknownSourcesSettings());
@@ -1810,7 +1915,9 @@ public class PackageManagerService extends IPackageManager.Stub {
mContext = context;
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
- mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
+ mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type")) ||
+ ("userdebug".equals(SystemProperties.get("ro.build.type")) &&
+ SystemProperties.getBoolean("persist.sys.lazy.dexopt", false));
mMetrics = new DisplayMetrics();
mSettings = new Settings(mPackages);
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
@@ -1835,6 +1942,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000;
+ mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
+
String separateProcesses = SystemProperties.get("debug.separate_processes");
if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
@@ -1861,14 +1970,17 @@ public class PackageManagerService extends IPackageManager.Stub {
getDefaultDisplayMetrics(context, mMetrics);
+ removeLegacyResourceCache();
+
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
+ mSignatureAllowances = systemConfig.getSignatureAllowances();
- synchronized (mInstallLock) {
+// synchronized (mInstallLock) {
// writer
- synchronized (mPackages) {
+// synchronized (mPackages) {
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
@@ -1910,10 +2022,13 @@ public class PackageManagerService extends IPackageManager.Stub {
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
- mSdkVersion, mOnlyCore);
+ mSdkVersion, mOnlyCore, mInstaller);
- String customResolverActivity = Resources.getSystem().getString(
- R.string.config_customResolverActivity);
+ String customResolverActivity = SystemProperties.get("ro.custom.resolver.activity");
+ if (TextUtils.isEmpty(customResolverActivity)) {
+ customResolverActivity = Resources.getSystem().getString(
+ R.string.config_customResolverActivity);
+ }
if (TextUtils.isEmpty(customResolverActivity)) {
customResolverActivity = null;
} else {
@@ -2005,6 +2120,10 @@ public class PackageManagerService extends IPackageManager.Stub {
// avoid the resulting log spew.
alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
+ // Gross hack for now: we know this file doesn't contain any
+ // code, so don't dexopt it to avoid the resulting log spew
+ alreadyDexOpted.add(frameworkDir.getPath() + "/org.cyanogenmod.platform-res.apk");
+
/**
* There are a number of commands implemented in Java, which
* we currently need to do the dexopt on so that they can be
@@ -2042,7 +2161,8 @@ public class PackageManagerService extends IPackageManager.Stub {
}
final VersionInfo ver = mSettings.getInternalVersion();
- mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);
+ mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint) ||
+ !Build.DISPLAY.equals(ver.displayversion);
// when upgrading from pre-M, promote system app permissions from install to runtime
mPromoteSystemApps =
mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;
@@ -2059,30 +2179,32 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ mBootThemeConfig = ThemeConfig.getSystemTheme();
+
// Collect vendor overlay packages.
// (Do this before scanning any apps.)
// For security and version matching reason, only consider
// overlay packages if they reside in VENDOR_OVERLAY_DIR.
File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
+ | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0, null);
// Find base frameworks (resource packages without code).
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
- scanFlags | SCAN_NO_DEX, 0);
+ scanFlags | SCAN_NO_DEX, 0, null);
// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
- | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
+ | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0, null);
// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
+ | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0, null);
// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app");
@@ -2092,12 +2214,19 @@ public class PackageManagerService extends IPackageManager.Stub {
// failed to look up canonical path, continue with original one
}
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
+ | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0, null);
// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
- | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
+ | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0, null);
+
+ // Collect all prebundled packages.
+ createAndSetCustomResources();
+ scanDirLI(Environment.getPrebundledDirectory(),
+ PackageParser.PARSE_IS_PREBUNDLED_DIR, scanFlags, 0, UserHandle.OWNER);
+ // Clean up
+ mCustomResources = null;
if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
mInstaller.moveFiles();
@@ -2172,10 +2301,10 @@ public class PackageManagerService extends IPackageManager.Stub {
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
- scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
+ scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0, null);
scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
- scanFlags | SCAN_REQUIRE_KNOWN, 0);
+ scanFlags | SCAN_REQUIRE_KNOWN, 0, null);
/**
* Remove disable package settings for any updated system
@@ -2288,6 +2417,12 @@ public class PackageManagerService extends IPackageManager.Stub {
updatePermissionsLPw(null, null, StorageManager.UUID_PRIVATE_INTERNAL, updateFlags);
ver.sdkVersion = mSdkVersion;
+ // Remove any stale app permissions (declared permission that now are undeclared
+ // by the same app, removed from its Manifest in newer versions)
+ if (!onlyCore) {
+ mSettings.removeStalePermissions();
+ }
+
// If this is the first boot or an update from pre-M, and it is a normal
// boot, then we need to initialize the default preferred apps across
// all defined users.
@@ -2299,10 +2434,42 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ // Disable components marked for disabling at build-time
+ mDisabledComponentsList = new ArrayList<ComponentName>();
+ for (String name : mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_disabledComponents)) {
+ ComponentName cn = ComponentName.unflattenFromString(name);
+ mDisabledComponentsList.add(cn);
+ Slog.v(TAG, "Disabling " + name);
+ String className = cn.getClassName();
+ PackageSetting pkgSetting = mSettings.mPackages.get(cn.getPackageName());
+ if (pkgSetting == null || pkgSetting.pkg == null
+ || !pkgSetting.pkg.hasComponentClassName(className)) {
+ Slog.w(TAG, "Unable to disable " + name);
+ continue;
+ }
+ pkgSetting.disableComponentLPw(className, UserHandle.USER_OWNER);
+ }
+
+ // Enable components marked for forced-enable at build-time
+ for (String name : mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_forceEnabledComponents)) {
+ ComponentName cn = ComponentName.unflattenFromString(name);
+ Slog.v(TAG, "Enabling " + name);
+ String className = cn.getClassName();
+ PackageSetting pkgSetting = mSettings.mPackages.get(cn.getPackageName());
+ if (pkgSetting == null || pkgSetting.pkg == null
+ || !pkgSetting.pkg.hasComponentClassName(className)) {
+ Slog.w(TAG, "Unable to enable " + name);
+ continue;
+ }
+ pkgSetting.enableComponentLPw(className, UserHandle.USER_OWNER);
+ }
+
// If this is first boot after an OTA, and a normal boot, then
// we need to clear code cache directories.
if (mIsUpgrade && !onlyCore) {
- Slog.i(TAG, "Build fingerprint changed; clearing code caches");
+ Slog.i(TAG, "Build fingerprint or displayversion changed; clearing code caches");
for (int i = 0; i < mSettings.mPackages.size(); i++) {
final PackageSetting ps = mSettings.mPackages.valueAt(i);
if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.volumeUuid)) {
@@ -2310,6 +2477,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
ver.fingerprint = Build.FINGERPRINT;
+ ver.displayversion = Build.DISPLAY;
}
checkDefaultBrowser();
@@ -2336,8 +2504,8 @@ public class PackageManagerService extends IPackageManager.Stub {
mIntentFilterVerifier = new IntentVerifierProxy(mContext,
mIntentFilterVerifierComponent);
- } // synchronized (mPackages)
- } // synchronized (mInstallLock)
+ //} // synchronized (mPackages)
+ //} // synchronized (mInstallLock)
// Now after opening every single application zip, make sure they
// are all flushed. Not really needed, but keeps things nice and
@@ -2474,10 +2642,11 @@ public class PackageManagerService extends IPackageManager.Stub {
}
SystemConfig systemConfig = SystemConfig.getInstance();
- ArraySet<String> packages = systemConfig.getLinkedApps();
+ ArraySet<AppLink> links = systemConfig.getLinkedApps();
ArraySet<String> domains = new ArraySet<String>();
- for (String packageName : packages) {
+ for (AppLink link : links) {
+ String packageName = link.pkgname;
PackageParser.Package pkg = mPackages.get(packageName);
if (pkg != null) {
if (!pkg.isSystemApp()) {
@@ -2502,11 +2671,11 @@ public class PackageManagerService extends IPackageManager.Stub {
// state w.r.t. the formal app-linkage "no verification attempted" state;
// and then 'always' in the per-user state actually used for intent resolution.
final IntentFilterVerificationInfo ivi;
- ivi = mSettings.createIntentFilterVerificationIfNeededLPw(packageName,
- new ArrayList<String>(domains));
+ ivi = mSettings.createIntentFilterVerificationIfNeededLPw(
+ packageName, new ArrayList<String>(domains));
ivi.setStatus(INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
- mSettings.updateIntentFilterVerificationStatusLPw(packageName,
- INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS, userId);
+ mSettings.updateIntentFilterVerificationStatusLPw(
+ packageName, link.state, userId);
} else {
Slog.w(TAG, "Sysconfig <app-link> package '" + packageName
+ "' does not handle web links");
@@ -3345,6 +3514,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (!compareStrings(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false;
// We'll take care of setting this one.
if (!compareStrings(pi1.packageName, pi2.packageName)) return false;
+ if (pi1.allowViaWhitelist != pi2.allowViaWhitelist) return false;
// These are not currently stored in settings.
//if (!compareStrings(pi1.group, pi2.group)) return false;
//if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false;
@@ -3469,6 +3639,16 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ private boolean isAllowedSignature(PackageParser.Package pkg, String permissionName) {
+ for (Signature pkgSig : pkg.mSignatures) {
+ ArraySet<String> perms = mSignatureAllowances.get(pkgSig);
+ if (perms != null && perms.contains(permissionName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
@Override
public void grantRuntimePermission(String packageName, String name, final int userId) {
if (!sUserManager.exists(userId)) {
@@ -3865,7 +4045,19 @@ public class PackageManagerService extends IPackageManager.Stub {
@Override
public boolean isProtectedBroadcast(String actionName) {
synchronized (mPackages) {
- return mProtectedBroadcasts.contains(actionName);
+ return mProtectedBroadcasts.containsKey(actionName);
+ }
+ }
+
+ @Override
+ public boolean isProtectedBroadcastAllowed(String actionName, int callingUid) {
+ synchronized (mPackages) {
+ if (mProtectedBroadcasts.containsKey(actionName)) {
+ final int result = checkUidPermission(mProtectedBroadcasts.get(actionName),
+ callingUid);
+ return result == PackageManager.PERMISSION_GRANTED;
+ }
+ return false;
}
}
@@ -3963,7 +4155,7 @@ public class PackageManagerService extends IPackageManager.Stub {
return PackageManager.SIGNATURE_NO_MATCH;
}
- // Since both signature sets are of size 1, we can compare without HashSets.
+ // Since both signature sets are of size 1, we can compare without ArraySets.
if (s1.length == 1) {
return s1[0].equals(s2[0]) ?
PackageManager.SIGNATURE_MATCH :
@@ -4266,6 +4458,17 @@ public class PackageManagerService extends IPackageManager.Stub {
if (ri.activityInfo.metaData == null) ri.activityInfo.metaData = new Bundle();
ri.activityInfo.metaData.putBoolean(Intent.METADATA_DOCK_HOME, true);
return ri;
+ } else if (shouldIncludeResolveActivity(intent)) {
+ if (userId != 0) {
+ ResolveInfo ri = new ResolveInfo(mResolveInfo);
+ ri.activityInfo = new ActivityInfo(ri.activityInfo);
+ ri.activityInfo.applicationInfo = new ApplicationInfo(
+ ri.activityInfo.applicationInfo);
+ ri.activityInfo.applicationInfo.uid = UserHandle.getUid(userId,
+ UserHandle.getAppId(ri.activityInfo.applicationInfo.uid));
+ return ri;
+ }
+ return mResolveInfo;
}
}
return null;
@@ -4526,6 +4729,17 @@ public class PackageManagerService extends IPackageManager.Stub {
return null;
}
+ private boolean shouldIncludeResolveActivity(Intent intent) {
+ // Don't call into AppSuggestManager before it comes up later in the SystemServer init!
+ if (!mSystemReady || mOnlyCore) {
+ return false;
+ }
+
+ AppSuggestManager suggest = AppSuggestManager.getInstance(mContext);
+ return mResolverReplaced && (suggest.getService() != null) ?
+ suggest.handles(intent) : false;
+ }
+
@Override
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
@@ -5586,11 +5800,17 @@ public class PackageManagerService extends IPackageManager.Stub {
private boolean createIdmapForPackagePairLI(PackageParser.Package pkg,
PackageParser.Package opkg) {
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG,
+ "Generating idmaps between " + pkg.packageName + ":" + opkg.packageName);
if (!opkg.mTrustedOverlay) {
Slog.w(TAG, "Skipping target and overlay pair " + pkg.baseCodePath + " and " +
opkg.baseCodePath + ": overlay not trusted");
return false;
}
+
+ // Some apps like to take on the package name of an existing app so we'll use the
+ // "real" package name, if it is non-null, when performing the idmap
+ final String pkgName = pkg.mRealPackage != null ? pkg.mRealPackage : pkg.packageName;
ArrayMap<String, PackageParser.Package> overlaySet = mOverlays.get(pkg.packageName);
if (overlaySet == null) {
Slog.e(TAG, "was about to create idmap for " + pkg.baseCodePath + " and " +
@@ -5598,30 +5818,19 @@ public class PackageManagerService extends IPackageManager.Stub {
return false;
}
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
- // TODO: generate idmap for split APKs
- if (mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid) != 0) {
- Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath + " and "
- + opkg.baseCodePath);
+ final String cachePath =
+ ThemeUtils.getTargetCacheDir(pkgName, opkg.packageName);
+ if (mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, cachePath, sharedGid,
+ pkg.manifestHashCode, opkg.manifestHashCode) != 0) {
+ Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath +
+ " and " + opkg.baseCodePath);
return false;
}
- PackageParser.Package[] overlayArray =
- overlaySet.values().toArray(new PackageParser.Package[0]);
- Comparator<PackageParser.Package> cmp = new Comparator<PackageParser.Package>() {
- public int compare(PackageParser.Package p1, PackageParser.Package p2) {
- return p1.mOverlayPriority - p2.mOverlayPriority;
- }
- };
- Arrays.sort(overlayArray, cmp);
-
- pkg.applicationInfo.resourceDirs = new String[overlayArray.length];
- int i = 0;
- for (PackageParser.Package p : overlayArray) {
- pkg.applicationInfo.resourceDirs[i++] = p.baseCodePath;
- }
return true;
}
- private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
+ private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime,
+ final UserHandle user) {
final File[] files = dir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + dir);
@@ -5633,6 +5842,23 @@ public class PackageManagerService extends IPackageManager.Stub {
+ " flags=0x" + Integer.toHexString(parseFlags));
}
+ final int prebundledUserId = user == null ? UserHandle.USER_OWNER : user.getIdentifier();
+ final boolean isPrebundled = (parseFlags & PackageParser.PARSE_IS_PREBUNDLED_DIR) != 0;
+ if (isPrebundled) {
+ synchronized (mPackages) {
+ if (DEBUG_PREBUNDLED_SCAN) Log.d(TAG, "Reading prebundled packages for user "
+ + prebundledUserId);
+ mSettings.readPrebundledPackagesLPr(prebundledUserId);
+ }
+ }
+
+ Log.d(TAG, "start scanDirLI:"+dir);
+ // use multi thread to speed up scanning
+ int iMultitaskNum = SystemProperties.getInt("persist.pm.multitask", 6);
+ Log.d(TAG, "max thread:" + iMultitaskNum);
+ final MultiTaskDealer dealer = (iMultitaskNum > 1) ? MultiTaskDealer.startDealer(
+ MultiTaskDealer.PACKAGEMANAGER_SCANER, iMultitaskNum) : null;
+
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
@@ -5640,24 +5866,75 @@ public class PackageManagerService extends IPackageManager.Stub {
// Ignore entries which are not packages
continue;
}
- try {
- scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
- scanFlags, currentTime, null);
- } catch (PackageManagerException e) {
- Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());
- // Delete invalid userdata apps
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
- e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
- logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
- if (file.isDirectory()) {
- mInstaller.rmPackageDir(file.getAbsolutePath());
- } else {
- file.delete();
+ final File ref_file = file;
+ final int ref_parseFlags = parseFlags;
+ final int ref_scanFlags = scanFlags;
+ final long ref_currentTime = currentTime;
+
+ Runnable scanTask = new Runnable() {
+ public void run() {
+
+ try {
+ scanPackageLI(ref_file, ref_parseFlags | PackageParser.PARSE_MUST_BE_APK,
+ ref_scanFlags, ref_currentTime, user);
+ if (isPrebundled) {
+ final PackageParser.Package pkg;
+ try {
+ pkg = new PackageParser().parsePackage(ref_file, ref_parseFlags);
+ } catch (PackageParserException e) {
+ throw PackageManagerException.from(e);
+ }
+ if (DEBUG_PREBUNDLED_SCAN) Log.d(TAG,
+ "Marking prebundled package " + pkg.packageName +
+ " for user " + prebundledUserId);
+ mSettings.markPrebundledPackageInstalledLPr(prebundledUserId,
+ pkg.packageName);
+ // do this for every other user
+ for (UserInfo userInfo : sUserManager.getUsers(true)) {
+ if (userInfo.id == prebundledUserId) continue;
+ if (DEBUG_PREBUNDLED_SCAN) Log.d(TAG,
+ "Marking for secondary user " + userInfo.id);
+ mSettings.markPrebundledPackageInstalledLPr(userInfo.id,
+ pkg.packageName);
+ }
+ }
+ } catch (PackageManagerException e) {
+ Slog.w(TAG, "Failed to parse " + ref_file + ": " + e.getMessage());
+
+ // Delete invalid userdata apps
+ if ((ref_parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
+ e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
+ logCriticalInfo(Log.WARN, "Deleting invalid package at " + ref_file);
+ if (ref_file.isDirectory()) {
+ mInstaller.rmPackageDir(ref_file.getAbsolutePath());
+ } else {
+ ref_file.delete();
+ }
+ }
}
}
+ };
+
+ if (dealer != null)
+ dealer.addTask(scanTask);
+ else
+ scanTask.run();
+ }
+
+ if (dealer != null) {
+ dealer.waitAll();
+ }
+
+ if (isPrebundled) {
+ synchronized (mPackages) {
+ if (DEBUG_PREBUNDLED_SCAN) Log.d(TAG, "Writing prebundled packages for user "
+ + prebundledUserId);
+ mSettings.writePrebundledPackagesLPr(prebundledUserId);
}
}
+
+ Log.d(TAG, "end scanDirLI:"+dir);
}
private static File getSettingsProblemFile() {
@@ -5671,7 +5948,7 @@ public class PackageManagerService extends IPackageManager.Stub {
logCriticalInfo(priority, msg);
}
- static void logCriticalInfo(int priority, String msg) {
+ static synchronized void logCriticalInfo(int priority, String msg) {
Slog.println(priority, TAG, msg);
EventLogTags.writePmCriticalInfo(msg);
try {
@@ -5711,6 +5988,12 @@ public class PackageManagerService extends IPackageManager.Stub {
// if the package appears to be unchanged.
pkg.mSignatures = ps.signatures.mSignatures;
pkg.mSigningKeys = signingKs;
+ // Collect manifest digest
+ try{
+ pp.collectManifestDigest(pkg);
+ } catch (PackageParserException e) {
+ throw PackageManagerException.from(e);
+ }
return;
}
@@ -5730,7 +6013,7 @@ public class PackageManagerService extends IPackageManager.Stub {
/*
* Scan a package and return the newly parsed package.
- * Returns null in case of errors and the error code is stored in mLastScanError
+ * Returns null in case of errors and the error code is stored in
*/
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
long currentTime, UserHandle user) throws PackageManagerException {
@@ -5752,6 +6035,38 @@ public class PackageManagerService extends IPackageManager.Stub {
throw PackageManagerException.from(e);
}
+ if ((parseFlags & PackageParser.PARSE_IS_PREBUNDLED_DIR) != 0) {
+ synchronized (mPackages) {
+ PackageSetting existingSettings = mSettings.peekPackageLPr(pkg.packageName);
+
+ if (!mSettings.shouldPrebundledPackageBeInstalledForUserLPr(existingSettings,
+ user.getIdentifier(), pkg.packageName)) {
+ // The prebundled app was installed at some point for the owner and isn't
+ // currently installed for the owner, dont install it for a new user
+ // OR the prebundled app was installed for the user at some point and isn't
+ // current installed for the user, so skip reinstalling it
+ throw new PackageManagerException(INSTALL_FAILED_UNINSTALLED_PREBUNDLE,
+ "skip reinstall for " + pkg.packageName);
+ } else if (!mSettings.shouldPrebundledPackageBeInstalledForRegion(
+ mContext.getResources(), pkg.packageName, mCustomResources)) {
+ // The prebundled app is not needed for the default mobile country code,
+ // skip installing it
+ throw new PackageManagerException(INSTALL_FAILED_REGION_LOCKED_PREBUNDLE,
+ "skip install for " + pkg.packageName);
+ } else if (existingSettings != null
+ && existingSettings.versionCode >= pkg.mVersionCode
+ && !existingSettings.codePathString.contains(
+ Environment.getPrebundledDirectory().getPath())) {
+ // This app is installed in a location that is not the prebundled location
+ // and has a higher (or same) version as the prebundled one. Skip
+ // installing the prebundled version.
+ Slog.d(TAG, pkg.packageName + " already installed at " +
+ existingSettings.codePathString);
+ return null; // return null so we still mark package as installed
+ }
+ }
+ }
+
PackageSetting ps = null;
PackageSetting updatedPkg;
// reader
@@ -6049,9 +6364,8 @@ public class PackageManagerService extends IPackageManager.Stub {
if (doTrim) {
if (!isFirstBoot()) {
try {
- ActivityManagerNative.getDefault().showBootMessage(
- mContext.getResources().getString(
- R.string.android_upgrading_fstrim), true);
+ ActivityManagerNative.getDefault().updateBootProgress(
+ IActivityManager.BOOT_STAGE_FSTRIM, null, 0, 0, true);
} catch (RemoteException e) {
}
}
@@ -6177,13 +6491,11 @@ public class PackageManagerService extends IPackageManager.Stub {
if (DEBUG_DEXOPT) {
Log.i(TAG, "Optimizing app " + curr + " of " + total + ": " + pkg.packageName);
}
- if (!isFirstBoot()) {
- try {
- ActivityManagerNative.getDefault().showBootMessage(
- mContext.getResources().getString(R.string.android_upgrading_apk,
- curr, total), true);
- } catch (RemoteException e) {
- }
+ try {
+ ActivityManagerNative.getDefault().updateBootProgress(
+ IActivityManager.BOOT_STAGE_PREPARING_APPS,
+ pkg.applicationInfo, curr, total, true);
+ } catch (RemoteException e) {
}
PackageParser.Package p = pkg;
synchronized (mInstallLock) {
@@ -6584,6 +6896,20 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ if (Build.TAGS.equals("test-keys") &&
+ !pkg.applicationInfo.sourceDir.startsWith(Environment.getRootDirectory().getPath()) &&
+ !pkg.applicationInfo.sourceDir.startsWith("/vendor")) {
+ Object obj = mSettings.getUserIdLPr(1000);
+ Signature[] s1 = null;
+ if (obj instanceof SharedUserSetting) {
+ s1 = ((SharedUserSetting)obj).signatures.mSignatures;
+ }
+ if ((compareSignatures(pkg.mSignatures, s1) == PackageManager.SIGNATURE_MATCH)) {
+ throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+ "Cannot install platform packages to user storage!");
+ }
+ }
+
// Initialize package source and resource directories
File destCodeFile = new File(pkg.applicationInfo.getCodePath());
File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
@@ -6598,6 +6924,16 @@ public class PackageManagerService extends IPackageManager.Stub {
pkg.mAdoptPermissions = null;
}
+ // collect manifest digest which includes getting manifest hash code for themes
+ if (pkg.manifestDigest == null || pkg.manifestHashCode == 0) {
+ PackageParser pp = new PackageParser();
+ try {
+ pp.collectManifestDigest(pkg);
+ } catch (PackageParserException e) {
+ Slog.w(TAG, "Unable to collect manifest digest", e);
+ }
+ }
+
// writer
synchronized (mPackages) {
if (pkg.mSharedUserId != null) {
@@ -6963,7 +7299,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
- derivePackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);
+ derivePackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */, parseFlags);
// Some system apps still use directory structure for native libraries
// in which case we might end up not detecting abi solely based on apk
@@ -6971,7 +7307,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
pkg.applicationInfo.primaryCpuAbi == null) {
setBundledAppAbisAndRoots(pkg, pkgSetting);
- setNativeLibraryPaths(pkg);
+ setNativeLibraryPaths(pkg, parseFlags);
}
} else {
@@ -6987,7 +7323,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// ABIs we've determined above. For non-moves, the path will be updated based on the
// ABIs we determined during compilation, but the path will depend on the final
// package path (after the rename away from the stage path).
- setNativeLibraryPaths(pkg);
+ setNativeLibraryPaths(pkg, parseFlags);
}
if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
@@ -7186,12 +7522,38 @@ public class PackageManagerService extends IPackageManager.Stub {
KeySetManagerService ksms = mSettings.mKeySetManagerService;
ksms.assertScannedPackageValid(pkg);
+ // Get the current theme config. We do this outside the lock
+ // since ActivityManager might be waiting on us already
+ // and a deadlock would result.
+ final boolean isBootScan = (scanFlags & SCAN_BOOTING) != 0;
+ ThemeConfig config = mBootThemeConfig;
+ if (!isBootScan) {
+ final IActivityManager am = ActivityManagerNative.getDefault();
+ try {
+ if (am != null) {
+ config = am.getConfiguration().themeConfig;
+ } else {
+ Log.w(TAG, "ActivityManager getDefault() " +
+ "returned null, cannot compile app's theme");
+ }
+ } catch(RemoteException e) {
+ Log.w(TAG, "Failed to get the theme config from ActivityManager");
+ }
+ }
+
// writer
synchronized (mPackages) {
// We don't expect installation to fail beyond this point
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
+
+ // Themes: handle case where app was installed after icon mapping applied
+ if (mIconPackHelper != null) {
+ int id = mIconPackHelper.getResourceIdForApp(pkg.packageName);
+ pkg.applicationInfo.themedIcon = id;
+ }
+
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
// Make sure we don't accidentally delete its data.
@@ -7330,6 +7692,13 @@ public class PackageManagerService extends IPackageManager.Stub {
PackageParser.Activity a = pkg.activities.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
+
+ // Themes: handle case where app was installed after icon mapping applied
+ if (mIconPackHelper != null) {
+ a.info.themedIcon = mIconPackHelper
+ .getResourceIdForActivityIcon(a.info);
+ }
+
mActivities.addActivity(a, "activity");
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
@@ -7415,6 +7784,7 @@ public class PackageManagerService extends IPackageManager.Stub {
bp.perm = p;
bp.uid = pkg.applicationInfo.uid;
bp.sourcePackage = p.info.packageName;
+ bp.allowViaWhitelist = p.info.allowViaWhitelist;
p.info.flags |= PermissionInfo.FLAG_INSTALLED;
} else if (!currentOwnerIsSystem) {
String msg = "New decl " + p.owner + " of permission "
@@ -7428,6 +7798,7 @@ public class PackageManagerService extends IPackageManager.Stub {
if (bp == null) {
bp = new BasePermission(p.info.name, p.info.packageName,
BasePermission.TYPE_NORMAL);
+ bp.allowViaWhitelist = p.info.allowViaWhitelist;
permissionMap.put(p.info.name, bp);
}
@@ -7441,6 +7812,7 @@ public class PackageManagerService extends IPackageManager.Stub {
bp.perm = p;
bp.uid = pkg.applicationInfo.uid;
bp.sourcePackage = p.info.packageName;
+ bp.allowViaWhitelist = p.info.allowViaWhitelist;
p.info.flags |= PermissionInfo.FLAG_INSTALLED;
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
@@ -7510,33 +7882,105 @@ public class PackageManagerService extends IPackageManager.Stub {
if (pkg.protectedBroadcasts != null) {
N = pkg.protectedBroadcasts.size();
for (i=0; i<N; i++) {
- mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
+ mProtectedBroadcasts.put(pkg.protectedBroadcasts.keyAt(i),
+ pkg.protectedBroadcasts.valueAt(i));
}
}
pkgSetting.setTimeStamp(scanFileTime);
- // Create idmap files for pairs of (packages, overlay packages).
- // Note: "android", ie framework-res.apk, is handled by native layers.
- if (pkg.mOverlayTarget != null) {
- // This is an overlay package.
- if (pkg.mOverlayTarget != null && !pkg.mOverlayTarget.equals("android")) {
- if (!mOverlays.containsKey(pkg.mOverlayTarget)) {
- mOverlays.put(pkg.mOverlayTarget,
- new ArrayMap<String, PackageParser.Package>());
+ // Generate resources & idmaps if pkg is NOT a theme
+ // We must compile resources here because during the initial boot process we may get
+ // here before a default theme has had a chance to compile its resources
+ // During app installation we only compile applied theme here (rest will be compiled
+ // in background)
+ if (pkg.mOverlayTargets.isEmpty() && mOverlays.containsKey(pkg.packageName)) {
+ ArrayMap<String, PackageParser.Package> themes = mOverlays.get(pkg.packageName);
+
+ if (config != null) {
+ for(PackageParser.Package themePkg : themes.values()) {
+ if (themePkg.packageName.equals(config.getOverlayPkgName()) ||
+ themePkg.packageName.equals(
+ config.getOverlayPkgNameForApp(pkg.packageName))) {
+ try {
+ compileResourcesAndIdmapIfNeeded(pkg, themePkg);
+ } catch (Exception e) {
+ // Do not stop a pkg installation just because of one bad theme
+ // Also we don't break here because we should try to compile other
+ // themes
+ Slog.w(TAG, "Unable to compile " + themePkg.packageName
+ + " for target " + pkg.packageName, e);
+ themePkg.mOverlayTargets.remove(pkg.packageName);
+ }
+ }
}
- ArrayMap<String, PackageParser.Package> map = mOverlays.get(pkg.mOverlayTarget);
- map.put(pkg.packageName, pkg);
- PackageParser.Package orig = mPackages.get(pkg.mOverlayTarget);
- if (orig != null && !createIdmapForPackagePairLI(orig, pkg)) {
- throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
- "scanPackageLI failed to createIdmap");
+ }
+ }
+
+ // If this is a theme we should re-compile common resources if they exist so
+ // remove this package from mAvailableCommonResources.
+ if (!isBootScan && pkg.mOverlayTargets.size() > 0) {
+ mAvailableCommonResources.remove(pkg.packageName);
+ }
+
+ // Generate Idmaps and res tables if pkg is a theme
+ Iterator<String> iterator = pkg.mOverlayTargets.iterator();
+ while(iterator.hasNext()) {
+ String target = iterator.next();
+ Exception failedException = null;
+
+ insertIntoOverlayMap(target, pkg);
+ if (isBootScan && mBootThemeConfig != null &&
+ (pkg.packageName.equals(mBootThemeConfig.getOverlayPkgName()) ||
+ pkg.packageName.equals(
+ mBootThemeConfig.getOverlayPkgNameForApp(target)))) {
+ try {
+ compileResourcesAndIdmapIfNeeded(mPackages.get(target), pkg);
+ } catch (IdmapException e) {
+ failedException = e;
+ } catch (AaptException e) {
+ failedException = e;
+ } catch (Exception e) {
+ failedException = e;
+ }
+
+ if (failedException != null) {
+ if (failedException instanceof AaptException &&
+ ((AaptException) failedException).isCommon) {
+ Slog.e(TAG, "Unable to process common resources for " + pkgName +
+ ", uninstalling theme.", failedException);
+ uninstallThemeForAllApps(pkg);
+ deletePackageLI(pkg.packageName, null, true, null, null, 0, null,
+ false);
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_THEME_AAPT_ERROR,
+ "Unable to process theme " + pkgName, failedException);
+ } else {
+ Slog.w(TAG, "Unable to process theme " + pkgName + " for " + target,
+ failedException);
+ // remove target from mOverlayTargets
+ iterator.remove();
+ }
+ }
+ }
+ }
+
+ //Icon Packs need aapt too
+ if (isBootScan && (mBootThemeConfig != null &&
+ pkg.packageName.equals(mBootThemeConfig.getIconPackPkgName()))) {
+ if (isIconCompileNeeded(pkg)) {
+ try {
+ ThemeUtils.createCacheDirIfNotExists();
+ ThemeUtils.createIconDirIfNotExists(pkg.packageName);
+ compileIconPack(pkg);
+ } catch (Exception e) {
+ uninstallThemeForAllApps(pkg);
+ deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false);
+ throw new PackageManagerException(
+ PackageManager.INSTALL_FAILED_THEME_AAPT_ERROR,
+ "Unable to process theme " + pkgName);
}
}
- } else if (mOverlays.containsKey(pkg.packageName) &&
- !pkg.packageName.equals("android")) {
- // This is a regular package, with one or more known overlay packages.
- createIdmapsForPackageLI(pkg);
}
}
@@ -7551,7 +7995,8 @@ public class PackageManagerService extends IPackageManager.Stub {
* If {@code extractLibs} is true, native libraries are extracted from the app if required.
*/
public void derivePackageAbi(PackageParser.Package pkg, File scanFile,
- String cpuAbiOverride, boolean extractLibs)
+ String cpuAbiOverride, boolean extractLibs,
+ int parseFlags)
throws PackageManagerException {
// TODO: We can probably be smarter about this stuff. For installed apps,
// we can calculate this information at install time once and for all. For
@@ -7560,7 +8005,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Give ourselves some initial paths; we'll come back for another
// pass once we've determined ABI below.
- setNativeLibraryPaths(pkg);
+ setNativeLibraryPaths(pkg, parseFlags);
// We would never need to extract libs for forward-locked and external packages,
// since the container service will do it for us. We shouldn't attempt to
@@ -7680,7 +8125,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Now that we've calculated the ABIs and determined if it's an internal app,
// we will go ahead and populate the nativeLibraryPath.
- setNativeLibraryPaths(pkg);
+ setNativeLibraryPaths(pkg, parseFlags);
}
/**
@@ -7778,27 +8223,349 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+
+ private boolean isIconCompileNeeded(Package pkg) {
+ if (!pkg.hasIconPack) return false;
+ // Read in the stored hash value and compare to the pkgs computed hash value
+ FileInputStream in = null;
+ DataInputStream dataInput = null;
+ try {
+ String hashFile = ThemeUtils.getIconHashFile(pkg.packageName);
+ in = new FileInputStream(hashFile);
+ dataInput = new DataInputStream(in);
+ int storedHashCode = dataInput.readInt();
+ int actualHashCode = pkg.manifestHashCode;
+ return storedHashCode != actualHashCode;
+ } catch(IOException e) {
+ // all is good enough for government work here,
+ // we'll just return true and the icons will be processed
+ } finally {
+ IoUtils.closeQuietly(in);
+ IoUtils.closeQuietly(dataInput);
+ }
+
+ return true;
+ }
+
+ private void compileResourcesAndIdmapIfNeeded(PackageParser.Package targetPkg,
+ PackageParser.Package themePkg) throws IdmapException, AaptException, IOException {
+ if (!shouldCreateIdmap(targetPkg, themePkg)) {
+ return;
+ }
+
+ // Always use the manifest's pkgName when compiling resources
+ // the member value of "packageName" is dependent on whether this was a clean install
+ // or an upgrade w/ If the app is an upgrade then the original package name is used.
+ // because libandroidfw uses the manifests's pkgName during idmap creation we must
+ // be consistent here and use the same name, otherwise idmap will look in the wrong place
+ // for the resource table.
+ String pkgName = targetPkg.mRealPackage != null ?
+ targetPkg.mRealPackage : targetPkg.packageName;
+ compileResourcesIfNeeded(pkgName, themePkg);
+ generateIdmap(targetPkg.packageName, themePkg);
+ }
+
+ private void compileResourcesIfNeeded(String target, PackageParser.Package pkg)
+ throws AaptException, IOException
+ {
+ ThemeUtils.createCacheDirIfNotExists();
+
+ if (hasCommonResources(pkg)
+ && shouldCompileCommonResources(pkg)) {
+ ThemeUtils.createResourcesDirIfNotExists(COMMON_OVERLAY, pkg.packageName);
+ compileResources(COMMON_OVERLAY, pkg);
+ mAvailableCommonResources.put(pkg.packageName, System.currentTimeMillis());
+ }
+
+ ThemeUtils.createResourcesDirIfNotExists(target, pkg.packageName);
+ compileResources(target, pkg);
+ }
+
+ private void compileResources(String target, PackageParser.Package pkg)
+ throws IOException, AaptException {
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Compile resource table for " + pkg.packageName);
+ //TODO: cleanup this hack. Modify aapt? Aapt uses the manifests package name
+ //when creating the resource table. We care about the resource table's name because
+ //it is used when removing the table by cookie.
+ try {
+ createTempManifest(COMMON_OVERLAY.equals(target)
+ ? ThemeUtils.getCommonPackageName(pkg.packageName) : pkg.packageName);
+ compileResourcesWithAapt(target, pkg);
+ } finally {
+ cleanupTempManifest();
+ }
+ }
+
+ private void compileIconPack(Package pkg) throws Exception {
+ if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Compile resource table for " + pkg.packageName);
+ OutputStream out = null;
+ DataOutputStream dataOut = null;
+ try {
+ createTempManifest(pkg.packageName);
+ int code = pkg.manifestHashCode;
+ String hashFile = ThemeUtils.getIconHashFile(pkg.packageName);
+ out = new FileOutputStream(hashFile);
+ dataOut = new DataOutputStream(out);
+ dataOut.writeInt(code);
+ compileIconsWithAapt(pkg);
+ } finally {
+ IoUtils.closeQuietly(out);
+ IoUtils.closeQuietly(dataOut);
+ cleanupTempManifest();
+ }
+ }
+
+ private void insertIntoOverlayMap(String target, PackageParser.Package opkg) {
+ if (!mOverlays.containsKey(target)) {
+ mOverlays.put(target,
+ new ArrayMap<String, PackageParser.Package>());
+ }
+ ArrayMap<String, PackageParser.Package> map = mOverlays.get(target);
+ map.put(opkg.packageName, opkg);
+ }
+
+ private void generateIdmap(String target, PackageParser.Package opkg) throws IdmapException {
+ PackageParser.Package targetPkg = mPackages.get(target);
+ if (targetPkg != null && !createIdmapForPackagePairLI(targetPkg, opkg)) {
+ throw new IdmapException("idmap failed for targetPkg: " + targetPkg
+ + " and opkg: " + opkg);
+ }
+ }
+
+ public class AaptException extends Exception {
+ boolean isCommon;
+
+ public AaptException(String message) {
+ this(message, false);
+ }
+
+ public AaptException(String message, boolean isCommon) {
+ super(message);
+ this.isCommon = isCommon;
+ }
+ }
+
+ public class IdmapException extends Exception {
+ public IdmapException(String message) {
+ super(message);
+ }
+ }
+
+ private boolean hasCommonResources(PackageParser.Package pkg) throws IOException {
+ boolean ret = false;
+ // check if assets/overlays/common exists in this theme
+ AssetManager assets = new AssetManager();
+ assets.addAssetPath(pkg.baseCodePath);
+ String[] common = assets.list("overlays/common");
+ if (common != null && common.length > 0) ret = true;
+
+ return ret;
+ }
+
+ private void compileResourcesWithAapt(String target, PackageParser.Package pkg)
+ throws IOException, AaptException {
+ String internalPath = APK_PATH_TO_OVERLAY + target + File.separator;
+ String resPath = ThemeUtils.getTargetCacheDir(target, pkg);
+ final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+ final boolean isCommonResources = COMMON_OVERLAY.equals(target);
+ int pkgId;
+ if ("android".equals(target)) {
+ pkgId = Resources.THEME_FRAMEWORK_PKG_ID;
+ } else if ("cyanogenmod.platform".equals(target)) {
+ pkgId = Resources.THEME_CM_PKG_ID;
+ } else if (isCommonResources) {
+ pkgId = Resources.THEME_COMMON_PKG_ID;
+ } else {
+ pkgId = Resources.THEME_APP_PKG_ID;
+ }
+
+ boolean hasCommonResources = (hasCommonResources(pkg) && !isCommonResources);
+ PackageParser.Package targetPkg = mPackages.get(target);
+ String appPath = targetPkg != null ? targetPkg.baseCodePath :
+ Environment.getRootDirectory() + "/framework/framework-res.apk";
+
+ if (mInstaller.aapt(pkg.baseCodePath, internalPath, resPath, sharedGid, pkgId,
+ pkg.applicationInfo.targetSdkVersion,
+ appPath,
+ hasCommonResources ? ThemeUtils.getTargetCacheDir(COMMON_OVERLAY, pkg)
+ + File.separator + "resources.apk" : "") != 0) {
+ throw new AaptException("Failed to run aapt", isCommonResources);
+ }
+ }
+
+ private void compileIconsWithAapt(Package pkg) throws Exception {
+ String resPath = ThemeUtils.getIconPackDir(pkg.packageName);
+ final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+
+ if (mInstaller.aapt(pkg.baseCodePath, APK_PATH_TO_ICONS, resPath, sharedGid,
+ Resources.THEME_ICON_PKG_ID,
+ pkg.applicationInfo.targetSdkVersion,
+ "", "") != 0) {
+ throw new AaptException("Failed to run aapt");
+ }
+ }
+
+ private void uninstallThemeForAllApps(PackageParser.Package opkg) {
+ for(String target : opkg.mOverlayTargets) {
+ ArrayMap<String, PackageParser.Package> map = mOverlays.get(target);
+ if (map != null) {
+ map.remove(opkg.packageName);
+
+ if (map.isEmpty()) {
+ mOverlays.remove(target);
+ }
+ }
+ }
+
+ // Now simply delete the root overlay cache directory and all its contents
+ recursiveDelete(new File(ThemeUtils.getOverlayResourceCacheDir(opkg.packageName)));
+ }
+
+ private void uninstallThemeForApp(PackageParser.Package appPkg) {
+ ArrayMap<String, PackageParser.Package> map = mOverlays.get(appPkg.packageName);
+ if (map == null) return;
+
+ for(PackageParser.Package opkg : map.values()) {
+ String cachePath = ThemeUtils.getTargetCacheDir(appPkg.packageName, opkg.packageName);
+ recursiveDelete(new File(cachePath));
+ }
+ }
+
+ private void recursiveDelete(File f) {
+ if (f.isDirectory()) {
+ for (File c : f.listFiles())
+ recursiveDelete(c);
+ }
+ f.delete();
+ }
+
+ private void createTempManifest(String pkgName) throws IOException {
+ StringBuilder manifest = new StringBuilder();
+ manifest.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
+ manifest.append("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"");
+ manifest.append(" package=\"" + pkgName+ "\">");
+ manifest.append(" </manifest>");
+
+ BufferedWriter bw = null;
+ try {
+ bw = new BufferedWriter(new FileWriter("/data/app/AndroidManifest.xml"));
+ bw.write(manifest.toString());
+ bw.flush();
+ bw.close();
+ File resFile = new File("/data/app/AndroidManifest.xml");
+ FileUtils.setPermissions(resFile,
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IROTH, -1, -1);
+ } finally {
+ IoUtils.closeQuietly(bw);
+ }
+ }
+
+ private void cleanupTempManifest() {
+ File resFile = new File("/data/app/AndroidManifest.xml");
+ resFile.delete();
+ }
+
+ /**
+ * Compares the 32 bit hash of the target and overlay to those stored
+ * in the idmap and returns true if either hash differs
+ * @param targetPkg
+ * @param overlayPkg
+ * @return
+ */
+ private boolean shouldCreateIdmap(PackageParser.Package targetPkg,
+ PackageParser.Package overlayPkg) {
+ if (targetPkg == null || targetPkg.baseCodePath == null || overlayPkg == null) return false;
+
+ int targetHash = targetPkg.manifestHashCode;
+ int overlayHash = overlayPkg.manifestHashCode;
+
+ File idmap =
+ new File(ThemeUtils.getIdmapPath(targetPkg.packageName, overlayPkg.packageName));
+ if (!idmap.exists()) {
+ return true;
+ }
+
+ int[] hashes;
+ try {
+ hashes = getIdmapHashes(idmap);
+ } catch (IOException e) {
+ return true;
+ }
+
+ if (targetHash == 0 || overlayHash == 0 ||
+ targetHash != hashes[0] || overlayHash != hashes[1]) {
+ // if the overlay changed we'll want to recreate the common resources if it has any
+ if (overlayHash != hashes[1]
+ && mAvailableCommonResources.containsKey(overlayPkg.packageName)) {
+ mAvailableCommonResources.remove(overlayPkg.packageName);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private boolean shouldCompileCommonResources(PackageParser.Package pkg) {
+ if (!mAvailableCommonResources.containsKey(pkg.packageName)) return true;
+
+ long lastUpdated = mAvailableCommonResources.get(pkg.packageName);
+ long currentTime = System.currentTimeMillis();
+ if (currentTime - lastUpdated > COMMON_RESOURCE_EXPIRATION) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Get the file modified times for the overlay and target from the idmap
+ * @param idmap
+ * @return
+ * @throws IOException
+ */
+ private int[] getIdmapHashes(File idmap) throws IOException {
+ int[] times = new int[2];
+ ByteBuffer bb = ByteBuffer.allocate(8);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ FileInputStream fis = new FileInputStream(idmap);
+ fis.skip(IDMAP_HASH_START_OFFSET);
+ fis.read(bb.array());
+ fis.close();
+ final IntBuffer ib = bb.asIntBuffer();
+ times[0] = ib.get(0);
+ times[1] = ib.get(1);
+
+ return times;
+ }
+
private void setUpCustomResolverActivity(PackageParser.Package pkg) {
synchronized (mPackages) {
- mResolverReplaced = true;
- // Set up information for custom user intent resolution activity.
- mResolveActivity.applicationInfo = pkg.applicationInfo;
- mResolveActivity.name = mCustomResolverComponentName.getClassName();
- mResolveActivity.packageName = pkg.applicationInfo.packageName;
- mResolveActivity.processName = pkg.applicationInfo.packageName;
- mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
- mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS |
- ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
- mResolveActivity.theme = 0;
- mResolveActivity.exported = true;
- mResolveActivity.enabled = true;
- mResolveInfo.activityInfo = mResolveActivity;
- mResolveInfo.priority = 0;
- mResolveInfo.preferredOrder = 0;
- mResolveInfo.match = 0;
- mResolveComponentName = mCustomResolverComponentName;
- Slog.i(TAG, "Replacing default ResolverActivity with custom activity: " +
- mResolveComponentName);
+ for (Activity a : pkg.activities) {
+ if (a.getComponentName().getClassName()
+ .equals(mCustomResolverComponentName.getClassName())) {
+ mResolverReplaced = true;
+ // Set up information for custom user intent resolution activity.
+ mResolveActivity.applicationInfo = pkg.applicationInfo;
+ mResolveActivity.name = mCustomResolverComponentName.getClassName();
+ mResolveActivity.packageName = pkg.applicationInfo.packageName;
+ mResolveActivity.processName = pkg.applicationInfo.packageName;
+ mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
+ mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS |
+ ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
+ mResolveActivity.theme = a.info.theme;
+ mResolveActivity.exported = true;
+ mResolveActivity.enabled = true;
+ mResolveInfo.activityInfo = mResolveActivity;
+ mResolveInfo.priority = 0;
+ mResolveInfo.preferredOrder = 0;
+ mResolveInfo.match = 0;
+ mResolveComponentName = mCustomResolverComponentName;
+ Slog.i(TAG, "Replacing default ResolverActivity with custom activity: " +
+ mResolveComponentName);
+ break;
+ }
+ }
+ if (mResolveActivity.theme == 0) {
+ mResolveActivity.theme = R.style.Theme_DeviceDefault_Resolver;
+ }
}
}
@@ -7838,7 +8605,7 @@ public class PackageManagerService extends IPackageManager.Stub {
* Derive and set the location of native libraries for the given package,
* which varies depending on where and how the package was installed.
*/
- private void setNativeLibraryPaths(PackageParser.Package pkg) {
+ private void setNativeLibraryPaths(PackageParser.Package pkg, int parseFlags) {
final ApplicationInfo info = pkg.applicationInfo;
final String codePath = pkg.codePath;
final File codeFile = new File(codePath);
@@ -7884,10 +8651,17 @@ public class PackageManagerService extends IPackageManager.Stub {
info.nativeLibraryRootRequiresIsa = false;
info.nativeLibraryDir = info.nativeLibraryRootDir;
} else {
- // Cluster install
- info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
+ if ((parseFlags & PackageParser.PARSE_IS_PREBUNDLED_DIR) != 0) {
+ // mAppLib32InstallDir is the directory /data/app-lib which is used to store native
+ // libs for apps from the system paritition. It isn't really specific to 32bit in
+ // any way except for the variable name, the system will use the primary/secondary
+ // ABI computed below.
+ info.nativeLibraryRootDir =
+ new File(mAppLib32InstallDir, pkg.packageName).getAbsolutePath();
+ } else {
+ info.nativeLibraryRootDir = new File(codeFile, LIB_DIR_NAME).getAbsolutePath();
+ }
info.nativeLibraryRootRequiresIsa = true;
-
info.nativeLibraryDir = new File(info.nativeLibraryRootDir,
getPrimaryInstructionSet(info)).getAbsolutePath();
@@ -8631,7 +9405,6 @@ public class PackageManagerService extends IPackageManager.Stub {
& PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0) {
if (isSystemApp(pkg)) {
// For updated system applications, a system permission
- // is granted only if it had been defined by the original application.
if (pkg.isUpdatedSystemApp()) {
final PackageSetting sysPs = mSettings
.getDisabledSystemPkgLPr(pkg.packageName);
@@ -8699,6 +9472,9 @@ public class PackageManagerService extends IPackageManager.Stub {
allowed = origPermissions.hasInstallPermission(perm);
}
}
+ if (!allowed && bp.allowViaWhitelist) {
+ allowed = isAllowedSignature(pkg, perm);
+ }
return allowed;
}
@@ -8715,8 +9491,27 @@ public class PackageManagerService extends IPackageManager.Stub {
int userId) {
if (!sUserManager.exists(userId)) return null;
mFlags = flags;
- return super.queryIntent(intent, resolvedType,
+ List<ResolveInfo> list = super.queryIntent(intent, resolvedType,
(flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
+ // Remove protected Application components if they're explicitly queried for.
+ // Implicit intent queries will be gated when the returned component is acted upon.
+ int callingUid = Binder.getCallingUid();
+ String[] pkgs = getPackagesForUid(callingUid);
+ List<String> packages = (pkgs != null) ? Arrays.asList(pkgs) : Collections.EMPTY_LIST;
+ final boolean isNotSystem = callingUid != Process.SYSTEM_UID &&
+ (getFlagsForUid(callingUid) & ApplicationInfo.FLAG_SYSTEM) == 0;
+
+ if (isNotSystem && intent.getComponent() != null) {
+ Iterator<ResolveInfo> itr = list.iterator();
+ while (itr.hasNext()) {
+ ActivityInfo activityInfo = itr.next().activityInfo;
+ if (activityInfo.applicationInfo.protect && (packages == null
+ || !packages.contains(activityInfo.packageName))) {
+ itr.remove();
+ }
+ }
+ }
+ return list;
}
public List<ResolveInfo> queryIntentForPackage(Intent intent, String resolvedType,
@@ -8744,6 +9539,255 @@ public class PackageManagerService extends IPackageManager.Stub {
return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut, userId);
}
+ /**
+ * Finds a privileged activity that matches the specified activity names.
+ */
+ private PackageParser.Activity findMatchingActivity(
+ List<PackageParser.Activity> activityList, ActivityInfo activityInfo) {
+ for (PackageParser.Activity sysActivity : activityList) {
+ if (sysActivity.info.name.equals(activityInfo.name)) {
+ return sysActivity;
+ }
+ if (sysActivity.info.name.equals(activityInfo.targetActivity)) {
+ return sysActivity;
+ }
+ if (sysActivity.info.targetActivity != null) {
+ if (sysActivity.info.targetActivity.equals(activityInfo.name)) {
+ return sysActivity;
+ }
+ if (sysActivity.info.targetActivity.equals(activityInfo.targetActivity)) {
+ return sysActivity;
+ }
+ }
+ }
+ return null;
+ }
+
+ public class IterGenerator<E> {
+ public Iterator<E> generate(ActivityIntentInfo info) {
+ return null;
+ }
+ }
+
+ public class ActionIterGenerator extends IterGenerator<String> {
+ @Override
+ public Iterator<String> generate(ActivityIntentInfo info) {
+ return info.actionsIterator();
+ }
+ }
+
+ public class CategoriesIterGenerator extends IterGenerator<String> {
+ @Override
+ public Iterator<String> generate(ActivityIntentInfo info) {
+ return info.categoriesIterator();
+ }
+ }
+
+ public class SchemesIterGenerator extends IterGenerator<String> {
+ @Override
+ public Iterator<String> generate(ActivityIntentInfo info) {
+ return info.schemesIterator();
+ }
+ }
+
+ public class AuthoritiesIterGenerator extends IterGenerator<IntentFilter.AuthorityEntry> {
+ @Override
+ public Iterator<IntentFilter.AuthorityEntry> generate(ActivityIntentInfo info) {
+ return info.authoritiesIterator();
+ }
+ }
+
+ /**
+ * <em>WARNING</em> for performance reasons, the passed in intentList WILL BE
+ * MODIFIED. Do not pass in a list that should not be changed.
+ */
+ private <T> void getIntentListSubset(List<ActivityIntentInfo> intentList,
+ IterGenerator<T> generator, Iterator<T> searchIterator) {
+ // loop through the set of actions; every one must be found in the intent filter
+ while (searchIterator.hasNext()) {
+ // we must have at least one filter in the list to consider a match
+ if (intentList.size() == 0) {
+ break;
+ }
+
+ final T searchAction = searchIterator.next();
+
+ // loop through the set of intent filters
+ final Iterator<ActivityIntentInfo> intentIter = intentList.iterator();
+ while (intentIter.hasNext()) {
+ final ActivityIntentInfo intentInfo = intentIter.next();
+ boolean selectionFound = false;
+
+ // loop through the intent filter's selection criteria; at least one
+ // of them must match the searched criteria
+ final Iterator<T> intentSelectionIter = generator.generate(intentInfo);
+ while (intentSelectionIter != null && intentSelectionIter.hasNext()) {
+ final T intentSelection = intentSelectionIter.next();
+ if (intentSelection != null && intentSelection.equals(searchAction)) {
+ selectionFound = true;
+ break;
+ }
+ }
+
+ // the selection criteria wasn't found in this filter's set; this filter
+ // is not a potential match
+ if (!selectionFound) {
+ intentIter.remove();
+ }
+ }
+ }
+ }
+
+ /**
+ * Adjusts the priority of the given intent filter according to policy.
+ * <p>
+ * <ul>
+ * <li>The priority for unbundled updates to system applications is capped to the
+ * priority defined on the system partition</li>
+ * </ul>
+ */
+ private void adjustPriority(
+ List<PackageParser.Activity> systemActivities, ActivityIntentInfo intent) {
+ // nothing to do; priority is fine as-is
+ if (intent.getPriority() <= 0) {
+ return;
+ }
+
+ final ActivityInfo activityInfo = intent.activity.info;
+ final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
+
+ final boolean systemApp = applicationInfo.isSystemApp();
+ if (!systemApp) {
+ // non-system applications can never define a priority >0
+ Slog.w(TAG, "Non-system app; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ intent.setPriority(0);
+ return;
+ }
+
+ if (systemActivities == null) {
+ // the system package is not disabled; we're parsing the system partition
+ // apps on the system image get whatever priority they request
+ return;
+ }
+
+ // system app unbundled update ... try to find the same activity
+ final PackageParser.Activity foundActivity =
+ findMatchingActivity(systemActivities, activityInfo);
+ if (foundActivity == null) {
+ // this is a new activity; it cannot obtain >0 priority
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "New activity; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+
+ // found activity, now check for filter equivalence
+
+ // a shallow copy is enough; we modify the list, not its contents
+ final List<ActivityIntentInfo> intentListCopy =
+ new ArrayList<>(foundActivity.intents);
+ final List<ActivityIntentInfo> foundFilters = findFilters(intent);
+
+ // find matching action subsets
+ final Iterator<String> actionsIterator = intent.actionsIterator();
+ if (actionsIterator != null) {
+ getIntentListSubset(
+ intentListCopy, new ActionIterGenerator(), actionsIterator);
+ if (intentListCopy.size() == 0) {
+ // no more intents to match; we're not equivalent
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Mismatched action; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+ }
+
+ // find matching category subsets
+ final Iterator<String> categoriesIterator = intent.categoriesIterator();
+ if (categoriesIterator != null) {
+ getIntentListSubset(intentListCopy, new CategoriesIterGenerator(),
+ categoriesIterator);
+ if (intentListCopy.size() == 0) {
+ // no more intents to match; we're not equivalent
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Mismatched category; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+ }
+
+ // find matching schemes subsets
+ final Iterator<String> schemesIterator = intent.schemesIterator();
+ if (schemesIterator != null) {
+ getIntentListSubset(intentListCopy, new SchemesIterGenerator(),
+ schemesIterator);
+ if (intentListCopy.size() == 0) {
+ // no more intents to match; we're not equivalent
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Mismatched scheme; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+ }
+
+ // find matching authorities subsets
+ final Iterator<IntentFilter.AuthorityEntry>
+ authoritiesIterator = intent.authoritiesIterator();
+ if (authoritiesIterator != null) {
+ getIntentListSubset(intentListCopy,
+ new AuthoritiesIterGenerator(),
+ authoritiesIterator);
+ if (intentListCopy.size() == 0) {
+ // no more intents to match; we're not equivalent
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Mismatched authority; cap priority to 0;"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(0);
+ return;
+ }
+ }
+
+ // we found matching filter(s); app gets the max priority of all intents
+ int cappedPriority = 0;
+ for (int i = intentListCopy.size() - 1; i >= 0; --i) {
+ cappedPriority = Math.max(cappedPriority, intentListCopy.get(i).getPriority());
+ }
+ if (intent.getPriority() > cappedPriority) {
+ if (DEBUG_FILTERS) {
+ Slog.i(TAG, "Found matching filter(s);"
+ + " cap priority to " + cappedPriority + ";"
+ + " package: " + applicationInfo.packageName
+ + " activity: " + intent.activity.className
+ + " origPrio: " + intent.getPriority());
+ }
+ intent.setPriority(cappedPriority);
+ return;
+ }
+ // all this for nothing; the requested priority was <= what was on the system
+ }
+
public final void addActivity(PackageParser.Activity a, String type) {
final boolean systemApp = a.info.applicationInfo.isSystemApp();
mActivities.put(a.getComponentName(), a);
@@ -8756,10 +9800,12 @@ public class PackageManagerService extends IPackageManager.Stub {
final int NI = a.intents.size();
for (int j=0; j<NI; j++) {
PackageParser.ActivityIntentInfo intent = a.intents.get(j);
- if (!systemApp && intent.getPriority() > 0 && "activity".equals(type)) {
- intent.setPriority(0);
- Log.w(TAG, "Package " + a.info.applicationInfo.packageName + " has activity "
- + a.className + " with priority > 0, forcing to 0");
+ if ("activity".equals(type)) {
+ final PackageSetting ps =
+ mSettings.getDisabledSystemPkgLPr(intent.activity.info.packageName);
+ final List<PackageParser.Activity> systemActivities =
+ ps != null && ps.pkg != null ? ps.pkg.activities : null;
+ adjustPriority(systemActivities, intent);
}
if (DEBUG_SHOW_INFO) {
Log.v(TAG, " IntentFilter:");
@@ -8913,18 +9959,6 @@ public class PackageManagerService extends IPackageManager.Stub {
out.println();
}
-// List<ResolveInfo> filterEnabled(List<ResolveInfo> resolveInfoList) {
-// final Iterator<ResolveInfo> i = resolveInfoList.iterator();
-// final List<ResolveInfo> retList = Lists.newArrayList();
-// while (i.hasNext()) {
-// final ResolveInfo resolveInfo = i.next();
-// if (isEnabledLP(resolveInfo.activityInfo)) {
-// retList.add(resolveInfo);
-// }
-// }
-// return retList;
-// }
-
// Keys are String (activity class name), values are Activity.
private final ArrayMap<ComponentName, PackageParser.Activity> mActivities
= new ArrayMap<ComponentName, PackageParser.Activity>();
@@ -9396,8 +10430,8 @@ public class PackageManagerService extends IPackageManager.Stub {
};
final void sendPackageBroadcast(final String action, final String pkg,
- final Bundle extras, final String targetPkg, final IIntentReceiver finishedReceiver,
- final int[] userIds) {
+ final String intentCategory, final Bundle extras, final String targetPkg,
+ final IIntentReceiver finishedReceiver, final int[] userIds) {
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -9434,6 +10468,9 @@ public class PackageManagerService extends IPackageManager.Stub {
+ intent.toShortString(false, true, false, false)
+ " " + intent.getExtras(), here);
}
+ if (intentCategory != null) {
+ intent.addCategory(intentCategory);
+ }
am.broadcastIntent(null, intent, null, finishedReceiver,
0, null, null, null, android.app.AppOpsManager.OP_NONE,
null, finishedReceiver != null, false, id);
@@ -9489,10 +10526,10 @@ public class PackageManagerService extends IPackageManager.Stub {
void startCleaningPackages() {
// reader
+ if (!isExternalMediaAvailable()) {
+ return;
+ }
synchronized (mPackages) {
- if (!isExternalMediaAvailable()) {
- return;
- }
if (mSettings.mPackagesToBeCleaned.isEmpty()) {
return;
}
@@ -9513,6 +10550,7 @@ public class PackageManagerService extends IPackageManager.Stub {
public void installPackage(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride) {
+ android.util.SeempLog.record(90);
installPackageAsUser(originPath, observer, installFlags, installerPackageName,
verificationParams, packageAbiOverride, UserHandle.getCallingUserId());
}
@@ -9599,8 +10637,8 @@ public class PackageManagerService extends IPackageManager.Stub {
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, pkgSetting.appId));
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- packageName, extras, null, null, new int[] {userId});
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ null, extras, null, null, new int[] {userId});
try {
IActivityManager am = ActivityManagerNative.getDefault();
final boolean isSystem =
@@ -10467,7 +11505,7 @@ public class PackageManagerService extends IPackageManager.Stub {
final IPackageInstallObserver2 observer;
int installFlags;
final String installerPackageName;
- final String volumeUuid;
+ String volumeUuid;
final VerificationParams verificationParams;
private InstallArgs mArgs;
private int mRet;
@@ -10511,6 +11549,9 @@ public class PackageManagerService extends IPackageManager.Stub {
// reader
synchronized (mPackages) {
PackageParser.Package pkg = mPackages.get(packageName);
+ if (pkgLite.isTheme) {
+ return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
+ }
if (pkg != null) {
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
// Check for downgrading.
@@ -10650,7 +11691,7 @@ public class PackageManagerService extends IPackageManager.Stub {
loc = installLocationPolicy(pkgLite);
if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
- } else if (!onSd && !onInt) {
+ } else if ((!onSd && !onInt) || pkgLite.isTheme) {
// Override install location with flags
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
// Set the flag to install on external media.
@@ -10662,6 +11703,31 @@ public class PackageManagerService extends IPackageManager.Stub {
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &= ~PackageManager.INSTALL_EXTERNAL;
}
+ if (pkgLite.isTheme) {
+ installFlags &= ~PackageManager.INSTALL_FORWARD_LOCK;
+ }
+ }
+ }
+ }
+
+ // Check whether we're replacing an existing package that's
+ // installed on adopted storage. If yes, override the new
+ // package location to match.
+ if (move == null && (installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
+ synchronized (mPackages) {
+ PackageParser.Package pkg = mPackages.get(pkgLite.packageName);
+ if (pkg != null && isExternalAdopted(pkg)) {
+ // Check whether anything will actually change
+ // so that we log only when a fixup was needed
+ if (!((installFlags & PackageManager.INSTALL_INTERNAL) != 0
+ && (installFlags & PackageManager.INSTALL_EXTERNAL) == 0
+ && Objects.equals(volumeUuid, pkg.volumeUuid))) {
+ installFlags |= PackageManager.INSTALL_INTERNAL;
+ installFlags &= ~PackageManager.INSTALL_EXTERNAL;
+ volumeUuid = pkg.volumeUuid;
+ Slog.w(TAG, "Replacing package on adopted storage, updating "
+ +"new package destination to volumeUuid "+volumeUuid);
+ }
}
}
}
@@ -10974,8 +12040,6 @@ public class PackageManagerService extends IPackageManager.Stub {
* Called after the source arguments are copied. This is used mostly for
* MoveParams when it needs to read the source file to put it in the
* destination.
- *
- * @return
*/
int doPostCopy(int uid) {
return PackageManager.INSTALL_SUCCEEDED;
@@ -12440,7 +13504,7 @@ public class PackageManagerService extends IPackageManager.Stub {
try {
derivePackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
- true /* extract libs */);
+ true /* extract libs */, parseFlags);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
@@ -12619,6 +13683,14 @@ public class PackageManagerService extends IPackageManager.Stub {
return (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0;
}
+ // Package is assumed to be on adopted storage if:
+ // FLAG_EXTERNAL_STORAGE is set
+ // volumeUuid is neither private internal (null) nor primary physical
+ private static boolean isExternalAdopted(PackageParser.Package pkg) {
+ return isExternal(pkg) && !TextUtils.isEmpty(pkg.volumeUuid)
+ && !Objects.equals(pkg.volumeUuid, StorageManager.UUID_PRIMARY_PHYSICAL);
+ }
+
private static boolean isSystemApp(PackageParser.Package pkg) {
return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
@@ -12847,11 +13919,17 @@ public class PackageManagerService extends IPackageManager.Stub {
? info.removedAppId : info.uid);
extras.putBoolean(Intent.EXTRA_REPLACING, true);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
+ String category = null;
+ if (info.isThemeApk) {
+ category = cyanogenmod.content.Intent
+ .CATEGORY_THEME_PACKAGE_INSTALLED_STATE_CHANGE;
+ }
+
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, category,
extras, null, null, null);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, category,
extras, null, null, null);
- sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,
+ sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null,
null, packageName, null, null);
}
}
@@ -12876,6 +13954,7 @@ public class PackageManagerService extends IPackageManager.Stub {
boolean isRemovedPackageSystemUpdate = false;
// Clean up resources deleted packages.
InstallArgs args = null;
+ boolean isThemeApk = false;
void sendBroadcast(boolean fullRemove, boolean replacing, boolean removedForAllUsers) {
Bundle extras = new Bundle(1);
@@ -12886,15 +13965,20 @@ public class PackageManagerService extends IPackageManager.Stub {
}
extras.putBoolean(Intent.EXTRA_REMOVED_FOR_ALL_USERS, removedForAllUsers);
if (removedPackage != null) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage,
+ String category = null;
+ if (isThemeApk) {
+ category = cyanogenmod.content.Intent
+ .CATEGORY_THEME_PACKAGE_INSTALLED_STATE_CHANGE;
+ }
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_REMOVED, removedPackage, category,
extras, null, null, removedUsers);
if (fullRemove && !replacing) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_REMOVED, removedPackage,
- extras, null, null, removedUsers);
+ category, extras, null, null, removedUsers);
}
}
if (removedAppId >= 0) {
- sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, extras, null, null,
+ sendPackageBroadcast(Intent.ACTION_UID_REMOVED, null, null, extras, null, null,
removedUsers);
}
}
@@ -13201,7 +14285,8 @@ public class PackageManagerService extends IPackageManager.Stub {
false, //hidden
null, null, null,
false, // blockUninstall
- ps.readUserState(userId).domainVerificationStatus, 0);
+ ps.readUserState(userId).domainVerificationStatus, 0,
+ null, null);
if (!isSystemApp(ps)) {
if (ps.isAnyInstalled(sUserManager.getUserIds())) {
// Other user still have this package installed, so all
@@ -13274,6 +14359,15 @@ public class PackageManagerService extends IPackageManager.Stub {
outInfo, writeSettings);
}
+ //Cleanup theme related data
+ if (ps.pkg != null) {
+ if (ps.pkg.mOverlayTargets.size() > 0) {
+ uninstallThemeForAllApps(ps.pkg);
+ } else if (mOverlays.containsKey(ps.pkg.packageName)) {
+ uninstallThemeForApp(ps.pkg);
+ }
+ }
+
return ret;
}
@@ -14453,6 +15547,12 @@ public class PackageManagerService extends IPackageManager.Stub {
public void setComponentEnabledSetting(ComponentName componentName,
int newState, int flags, int userId) {
if (!sUserManager.exists(userId)) return;
+ // Don't allow to enable components marked for disabling at build-time
+ if (mDisabledComponentsList.contains(componentName)) {
+ Slog.d(TAG, "Ignoring attempt to set enabled state of disabled component "
+ + componentName.flattenToString());
+ return;
+ }
setEnabledSetting(componentName.getPackageName(),
componentName.getClassName(), newState, flags, userId, null);
}
@@ -14467,6 +15567,7 @@ public class PackageManagerService extends IPackageManager.Stub {
throw new IllegalArgumentException("Invalid new component state: "
+ newState);
}
+
PackageSetting pkgSetting;
final int uid = Binder.getCallingUid();
final int permission = mContext.checkCallingOrSelfPermission(
@@ -14594,7 +15695,7 @@ public class PackageManagerService extends IPackageManager.Stub {
extras.putStringArray(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST, nameList);
extras.putBoolean(Intent.EXTRA_DONT_KILL_APP, killFlag);
extras.putInt(Intent.EXTRA_UID, packageUid);
- sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, null, null,
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_CHANGED, packageName, null, extras, null, null,
new int[] {UserHandle.getUserId(packageUid)});
}
@@ -14670,6 +15771,9 @@ public class PackageManagerService extends IPackageManager.Stub {
int[] grantPermissionsUserIds = EMPTY_INT_ARRAY;
synchronized (mPackages) {
+ // process applied themes so their resources are up to date and ready use
+ processAppliedThemes();
+
// Verify that all of the preferred activity components actually
// exist. It is possible for applications to be updated and at
// that point remove a previously declared activity component that
@@ -15585,7 +16689,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
: Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
- sendPackageBroadcast(action, null, extras, null, finishedReceiver, null);
+ sendPackageBroadcast(action, null, null, extras, null, finishedReceiver, null);
}
}
@@ -15799,7 +16903,8 @@ public class PackageManagerService extends IPackageManager.Stub {
Slog.w(TAG, "Failed to scan " + ps.codePath + ": " + e.getMessage());
}
- if (!Build.FINGERPRINT.equals(ver.fingerprint)) {
+ if ((!Build.FINGERPRINT.equals(ver.fingerprint)) ||
+ (!Build.DISPLAY.equals(ver.displayversion))) {
deleteCodeCacheDirsLI(ps.volumeUuid, ps.name);
}
}
@@ -16353,6 +17458,14 @@ public class PackageManagerService extends IPackageManager.Stub {
mSettings.createNewUserLILPw(this, mInstaller, userHandle);
applyFactoryDefaultBrowserLPw(userHandle);
primeDomainVerificationsLPw(userHandle);
+ // Set flag to monitor and not change apk file paths when
+ // scanning install directories.
+ final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING;
+ createAndSetCustomResources();
+ scanDirLI(Environment.getPrebundledDirectory(),
+ PackageParser.PARSE_IS_PREBUNDLED_DIR, scanFlags, 0,
+ new UserHandle(userHandle));
+ mCustomResources = null;
}
}
@@ -16408,6 +17521,133 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
+ public void setComponentProtectedSetting(ComponentName componentName, boolean newState,
+ int userId) {
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "set protected");
+
+ String packageName = componentName.getPackageName();
+ String className = componentName.getClassName();
+
+ PackageSetting pkgSetting;
+ ArrayList<String> components;
+
+ synchronized (mPackages) {
+ pkgSetting = mSettings.mPackages.get(packageName);
+
+ if (pkgSetting == null) {
+ if (className == null) {
+ throw new IllegalArgumentException(
+ "Unknown package: " + packageName);
+ }
+ throw new IllegalArgumentException(
+ "Unknown component: " + packageName
+ + "/" + className);
+ }
+
+ //Protection levels must be applied at the Component Level!
+ if (className == null) {
+ throw new IllegalArgumentException(
+ "Must specify Component Class name."
+ );
+ } else {
+ PackageParser.Package pkg = pkgSetting.pkg;
+ if (pkg == null || !pkg.hasComponentClassName(className)) {
+ if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.JELLY_BEAN) {
+ throw new IllegalArgumentException("Component class " + className
+ + " does not exist in " + packageName);
+ } else {
+ Slog.w(TAG, "Failed setComponentProtectedSetting: component class "
+ + className + " does not exist in " + packageName);
+ }
+ }
+
+ pkgSetting.protectComponentLPw(className, newState, userId);
+ mSettings.writePackageRestrictionsLPr(userId);
+
+ components = mPendingBroadcasts.get(userId, packageName);
+ final boolean newPackage = components == null;
+ if (newPackage) {
+ components = new ArrayList<String>();
+ }
+ if (!components.contains(className)) {
+ components.add(className);
+ }
+ }
+ }
+
+ long callingId = Binder.clearCallingIdentity();
+ try {
+ int packageUid = UserHandle.getUid(userId, pkgSetting.appId);
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
+ @Override
+ public boolean isComponentProtected(String callingPackage, int callingUid,
+ ComponentName componentName, int userId) {
+ if (DEBUG_PROTECTED) Log.d(TAG, "Checking if component is protected "
+ + componentName.flattenToShortString() + " from calling package " + callingPackage
+ + " and callinguid " + callingUid);
+
+ enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "set protected");
+
+ //Allow managers full access
+ List<String> protectedComponentManagers =
+ CMSettings.Secure.getDelimitedStringAsList(mContext.getContentResolver(),
+ CMSettings.Secure.PROTECTED_COMPONENT_MANAGERS, "|");
+ if (protectedComponentManagers.contains(callingPackage)) {
+ if (DEBUG_PROTECTED) Log.d(TAG, "Calling package is a protected manager, allow");
+ return false;
+ }
+
+ String packageName = componentName.getPackageName();
+ String className = componentName.getClassName();
+
+ //If this component is launched from the same package, allow it.
+ if (TextUtils.equals(packageName, callingPackage)) {
+ if (DEBUG_PROTECTED) Log.d(TAG, "Calling package is same as target, allow");
+ return false;
+ }
+
+ //If this component is launched from a validation component, allow it.
+ if (TextUtils.equals(PROTECTED_APPS_TARGET_VALIDATION_COMPONENT,
+ componentName.flattenToString()) && callingUid == Process.SYSTEM_UID) {
+ return false;
+ }
+
+ //If this component is launched from the system or a uid of a protected component, allow it.
+ boolean fromProtectedComponentUid = false;
+ for (String protectedComponentManager : protectedComponentManagers) {
+ int packageUid = getPackageUid(protectedComponentManager, userId);
+ if (packageUid != -1 && callingUid == packageUid) {
+ fromProtectedComponentUid = true;
+ }
+ }
+
+ if (TextUtils.equals(callingPackage, "android") && callingUid == Process.SYSTEM_UID
+ || callingPackage == null && fromProtectedComponentUid) {
+ if (DEBUG_PROTECTED) Log.d(TAG, "Calling package is android or manager, allow");
+ return false;
+ }
+
+ PackageSetting pkgSetting;
+ ArraySet<String> components;
+
+ synchronized (mPackages) {
+ pkgSetting = mSettings.mPackages.get(packageName);
+
+ if (pkgSetting == null || className == null) {
+ return false;
+ }
+ // Get all the protected components
+ components = pkgSetting.getProtectedComponents(userId);
+ if (DEBUG_PROTECTED) Log.d(TAG, "Got " + components.size() + " protected components");
+ return components.size() > 0;
+ }
+ }
+
+ @Override
public boolean isStorageLow() {
final long token = Binder.clearCallingIdentity();
try {
@@ -16581,6 +17821,39 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
+ @Override
+ public void updateIconMapping(String pkgName) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_CONFIGURATION,
+ "could not update icon mapping because caller "
+ + "does not have change config permission");
+
+ if (pkgName == null) {
+ clearIconMapping();
+ return;
+ }
+ mIconPackHelper = new IconPackHelper(mContext);
+ try {
+ mIconPackHelper.loadIconPack(pkgName);
+ } catch(NameNotFoundException e) {
+ Log.e(TAG, "Unable to find icon pack: " + pkgName);
+ clearIconMapping();
+ return;
+ }
+
+ for (Activity activity : mActivities.mActivities.values()) {
+ activity.info.themedIcon =
+ mIconPackHelper.getResourceIdForActivityIcon(activity.info);
+ }
+
+ synchronized (mPackages) {
+ for (Package pkg : mPackages.values()) {
+ pkg.applicationInfo.themedIcon =
+ mIconPackHelper.getResourceIdForApp(pkg.packageName);
+ }
+ }
+ }
+
private static class MoveCallbacks extends Handler {
private static final int MSG_CREATED = 1;
private static final int MSG_STATUS_CHANGED = 2;
@@ -16807,4 +18080,169 @@ public class PackageManagerService extends IPackageManager.Stub {
"Cannot call " + tag + " from UID " + callingUid);
}
}
+
+ private void clearIconMapping() {
+ mIconPackHelper = null;
+ for (Activity activity : mActivities.mActivities.values()) {
+ activity.info.themedIcon = 0;
+ }
+
+ for (Package pkg : mPackages.values()) {
+ pkg.applicationInfo.themedIcon = 0;
+ }
+ }
+
+ @Override
+ public ComposedIconInfo getComposedIconInfo() {
+ return mIconPackHelper != null ? mIconPackHelper.getComposedIconInfo() : null;
+ }
+
+ @Override
+ public int processThemeResources(String themePkgName) {
+ mContext.enforceCallingOrSelfPermission(
+ cyanogenmod.platform.Manifest.permission.ACCESS_THEME_MANAGER, null);
+ PackageParser.Package pkg;
+ synchronized (mPackages) {
+ pkg = mPackages.get(themePkgName);
+ }
+ if (pkg == null) {
+ Log.w(TAG, "Unable to get pkg for processing " + themePkgName);
+ return 0;
+ }
+
+ // Process icons
+ if (isIconCompileNeeded(pkg)) {
+ try {
+ ThemeUtils.createCacheDirIfNotExists();
+ ThemeUtils.createIconDirIfNotExists(pkg.packageName);
+ compileIconPack(pkg);
+ } catch (Exception e) {
+ uninstallThemeForAllApps(pkg);
+ deletePackageX(themePkgName, getCallingUid(), PackageManager.DELETE_ALL_USERS);
+ return PackageManager.INSTALL_FAILED_THEME_AAPT_ERROR;
+ }
+ }
+
+ // Generate Idmaps and res tables if pkg is a theme
+ Iterator<String> iterator = pkg.mOverlayTargets.iterator();
+ while (iterator.hasNext()) {
+ String target = iterator.next();
+ Exception failedException = null;
+ PackageParser.Package targetPkg;
+ synchronized (mPackages) {
+ targetPkg = mPackages.get(target);
+ }
+ try {
+ compileResourcesAndIdmapIfNeeded(targetPkg, pkg);
+ } catch (IdmapException e) {
+ failedException = e;
+ } catch (AaptException e) {
+ failedException = e;
+ } catch (Exception e) {
+ failedException = e;
+ }
+
+ if (failedException != null) {
+ if (failedException instanceof AaptException &&
+ ((AaptException) failedException).isCommon) {
+ Slog.e(TAG, "Unable to process common resources for " + pkg.packageName +
+ ", uninstalling theme.", failedException);
+ uninstallThemeForAllApps(pkg);
+ deletePackageX(pkg.packageName, getCallingUid(),
+ PackageManager.DELETE_ALL_USERS);
+ return PackageManager.INSTALL_FAILED_THEME_AAPT_ERROR;
+ } else {
+ Slog.w(TAG, "Unable to process theme " + pkg.packageName + " for " + target,
+ failedException);
+ // remove target from mOverlayTargets
+ iterator.remove();
+ }
+ }
+ }
+
+ return 0;
+ }
+
+ private void processThemeResourcesInThemeService(String pkgName) {
+ IThemeService ts = IThemeService.Stub.asInterface(ServiceManager.getService(
+ CMContextConstants.CM_THEME_SERVICE));
+ if (ts == null) {
+ Slog.e(TAG, "Theme service not available");
+ return;
+ }
+ try {
+ ts.processThemeResources(pkgName);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ }
+
+ /**
+ * Makes sure resources and idmaps for themes that are applied are up to date. This should only
+ * impact boots when something on /system has changed.
+ */
+ private void processAppliedThemes() {
+ ThemeConfig themeConfig = ThemeConfig.getBootTheme(mContext.getContentResolver());
+ if (themeConfig == null) return;
+
+ // gather up all the themes applied and then process them
+ Set<String> themesToProcess = new ArraySet<String>();
+ // process theme set for icons
+ if (themeConfig.getIconPackPkgName() != null) {
+ themesToProcess.add(themeConfig.getIconPackPkgName());
+ }
+ // process theme set for non-app specific overlays
+ if (themeConfig.getOverlayPkgName() != null) {
+ themesToProcess.add(themeConfig.getOverlayPkgName());
+ }
+ // process theme set for status bar
+ if (themeConfig.getOverlayForStatusBar() != null) {
+ themesToProcess.add(themeConfig.getOverlayForStatusBar());
+ }
+ // process theme set for navigation bar
+ if (themeConfig.getOverlayForNavBar() != null) {
+ themesToProcess.add(themeConfig.getOverlayForNavBar());
+ }
+ // process themes set for specific apps
+ Map<String, ThemeConfig.AppTheme> appThemesMap = themeConfig.getAppThemes();
+ for (String themePkgName : appThemesMap.keySet()) {
+ themesToProcess.add(themePkgName);
+ }
+
+ // now start the processing
+ for (String themePkgName : themesToProcess) {
+ processThemeResources(themePkgName);
+ }
+
+ updateIconMapping(themeConfig.getIconPackPkgName());
+ }
+
+ private void createAndSetCustomResources() {
+ Configuration tempConfiguration = new Configuration();
+ String mcc = SystemProperties.get("ro.prebundled.mcc");
+ if (!TextUtils.isEmpty(mcc)) {
+ tempConfiguration.mcc = Integer.parseInt(mcc);
+ mCustomResources = new Resources(new AssetManager(), new DisplayMetrics(),
+ tempConfiguration);
+ }
+ }
+
+ /**
+ * The new resource cache structure does not flatten the paths for idmaps, so this method
+ * checks for files that end with @idmap and assumes this indicates the older format and
+ * removes all files and directories from the resource cache so that it can be rebuilt
+ * using the new format.
+ */
+ private static void removeLegacyResourceCache() {
+ File cacheDir = new File(ThemeUtils.RESOURCE_CACHE_DIR);
+ if (cacheDir.exists()) {
+ for (File f : cacheDir.listFiles()) {
+ if (f.getName().endsWith(ThemeUtils.IDMAP_SUFFIX)) {
+ Log.i(TAG, "Removing old resource cache");
+ FileUtils.deleteContents(new File(ThemeUtils.RESOURCE_CACHE_DIR));
+ return;
+ }
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 78328f5..635f46e 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -19,6 +19,7 @@ package com.android.server.pm;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.COMPONENT_VISIBLE_STATUS;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.PackageManager;
@@ -344,7 +345,8 @@ abstract class PackageSettingBase extends SettingBase {
boolean notLaunched, boolean hidden,
String lastDisableAppCaller, ArraySet<String> enabledComponents,
ArraySet<String> disabledComponents, boolean blockUninstall, int domainVerifState,
- int linkGeneration) {
+ int linkGeneration,
+ ArraySet<String> protectedComponents, ArraySet<String> visibleComponents) {
PackageUserState state = modifyUserState(userId);
state.enabled = enabled;
state.installed = installed;
@@ -357,6 +359,8 @@ abstract class PackageSettingBase extends SettingBase {
state.blockUninstall = blockUninstall;
state.domainVerificationStatus = domainVerifState;
state.appLinkGeneration = linkGeneration;
+ state.protectedComponents = protectedComponents;
+ state.visibleComponents = visibleComponents;
}
ArraySet<String> getEnabledComponents(int userId) {
@@ -396,6 +400,17 @@ abstract class PackageSettingBase extends SettingBase {
return state;
}
+ PackageUserState modifyUserStateComponents(int userId) {
+ PackageUserState state = modifyUserState(userId);
+ if (state.protectedComponents == null) {
+ state.protectedComponents = new ArraySet<String>(1);
+ }
+ if (state.visibleComponents == null) {
+ state.visibleComponents = new ArraySet<String>(1);
+ }
+ return state;
+ }
+
void addDisabledComponent(String componentClassName, int userId) {
modifyUserStateComponents(userId, true, false).disabledComponents.add(componentClassName);
}
@@ -441,6 +456,27 @@ abstract class PackageSettingBase extends SettingBase {
}
}
+ boolean protectComponentLPw(String componentClassName, boolean protect, int userId) {
+ PackageUserState state = modifyUserStateComponents(userId);
+ boolean changed = false;
+ if (protect == COMPONENT_VISIBLE_STATUS) {
+ changed = state.protectedComponents != null
+ ? state.protectedComponents.remove(componentClassName) : false;
+ changed |= state.visibleComponents.add(componentClassName);
+ } else {
+ changed = state.visibleComponents != null
+ ? state.visibleComponents.remove(componentClassName) : false;
+ changed |= state.protectedComponents.add(componentClassName);
+ }
+
+ return changed;
+ }
+
+ ArraySet<String> getProtectedComponents(int userId) {
+ PackageUserState state = modifyUserStateComponents(userId);
+ return state.protectedComponents;
+ }
+
void removeUser(int userId) {
userState.delete(userId);
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 647c17b..38478c2 100644..100755
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
+import static android.content.pm.PackageManager.INSTALL_FAILED_UNINSTALLED_PREBUNDLE;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
@@ -33,6 +34,8 @@ import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -59,6 +62,8 @@ import android.util.SparseLongArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.R;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.JournaledFile;
@@ -70,6 +75,7 @@ import com.android.server.pm.PermissionsState.PermissionState;
import java.io.FileNotFoundException;
import java.util.Collection;
+import java.util.HashSet;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -87,6 +93,7 @@ import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.content.pm.PackageUserState;
import android.content.pm.VerifierDeviceIdentity;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -96,9 +103,13 @@ import android.util.SparseIntArray;
import android.util.Xml;
import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
@@ -185,6 +196,9 @@ final class Settings {
private static final String TAG_DEFAULT_BROWSER = "default-browser";
private static final String TAG_VERSION = "version";
+ private static final String TAG_PROTECTED_COMPONENTS = "protected-components";
+ private static final String TAG_VISIBLE_COMPONENTS = "visible-components";
+
private static final String ATTR_NAME = "name";
private static final String ATTR_USER = "user";
private static final String ATTR_CODE = "code";
@@ -203,6 +217,7 @@ final class Settings {
private static final String ATTR_DOMAIN_VERIFICATON_STATE = "domainVerificationStatus";
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_FINGERPRINT = "fingerprint";
+ private static final String ATTR_DISPLAYVERSION = "displayversion";
private static final String ATTR_APP_LINK_GENERATION = "app-link-generation";
private static final String ATTR_VOLUME_UUID = "volumeUuid";
private static final String ATTR_SDK_VERSION = "sdkVersion";
@@ -217,6 +232,7 @@ final class Settings {
private final File mPackageListFilename;
private final File mStoppedPackagesFilename;
private final File mBackupStoppedPackagesFilename;
+ private final File mPrebundledPackagesFilename;
final ArrayMap<String, PackageSetting> mPackages =
new ArrayMap<String, PackageSetting>();
@@ -259,6 +275,12 @@ final class Settings {
String fingerprint;
/**
+ * Last known value of {@link Build#DISPLAY}. Used to determine when
+ * an system update has occurred, meaning we need to clear code caches.
+ */
+ String displayversion;
+
+ /**
* Force all version information to match current system values,
* typically after resolving any required upgrade steps.
*/
@@ -266,6 +288,7 @@ final class Settings {
sdkVersion = Build.VERSION.SDK_INT;
databaseVersion = CURRENT_DATABASE_VERSION;
fingerprint = Build.FINGERPRINT;
+ displayversion = Build.DISPLAY;
}
}
@@ -284,6 +307,10 @@ final class Settings {
final SparseArray<PersistentPreferredIntentResolver> mPersistentPreferredActivities =
new SparseArray<PersistentPreferredIntentResolver>();
+ // The persistent prebundled packages for a user
+ final SparseArray<HashSet<String>> mPrebundledPackages =
+ new SparseArray<HashSet<String>>();
+
// For every user, it is used to find to which other users the intent can be forwarded.
final SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers =
new SparseArray<CrossProfileIntentResolver>();
@@ -311,7 +338,7 @@ final class Settings {
// Packages that have been uninstalled and still need their external
// storage data deleted.
final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
-
+
// Packages that have been renamed since they were first installed.
// Keys are the new names of the packages, values are the original
// names. The packages appear everwhere else under their original
@@ -362,6 +389,7 @@ final class Settings {
// Deprecated: Needed for migration
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
+ mPrebundledPackagesFilename = new File(mSystemDir, "prebundled-packages.list");
}
PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage,
@@ -666,7 +694,10 @@ final class Settings {
false, // hidden
null, null, null,
false, // blockUninstall
- INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0,
+ null,
+ null
+ );
writePackageRestrictionsLPr(user.id);
}
}
@@ -1035,6 +1066,15 @@ final class Settings {
return pir;
}
+ HashSet<String> editPrebundledPackagesLPw(int userId) {
+ HashSet<String> hashSet = mPrebundledPackages.get(userId);
+ if (hashSet == null) {
+ hashSet = new HashSet<String>();
+ mPrebundledPackages.put(userId, hashSet);
+ }
+ return hashSet;
+ }
+
PersistentPreferredIntentResolver editPersistentPreferredActivitiesLPw(int userId) {
PersistentPreferredIntentResolver ppir = mPersistentPreferredActivities.get(userId);
if (ppir == null) {
@@ -1204,6 +1244,10 @@ final class Settings {
"package-restrictions-backup.xml");
}
+ private File getUserPrebundledStateFile(int userId) {
+ return new File(Environment.getUserSystemDirectory(userId), "prebundled-packages.list");
+ }
+
void writeAllUsersPackageRestrictionsLPr() {
List<UserInfo> users = getAllUsers();
if (users == null) return;
@@ -1428,7 +1472,10 @@ final class Settings {
false, // hidden
null, null, null,
false, // blockUninstall
- INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0);
+ INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0,
+ null,
+ null
+ );
}
return;
}
@@ -1513,6 +1560,8 @@ final class Settings {
ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;
+ ArraySet<String> protectedComponents = null;
+ ArraySet<String> visibleComponents = null;
int packageDepth = parser.getDepth();
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -1527,12 +1576,17 @@ final class Settings {
enabledComponents = readComponentsLPr(parser);
} else if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
disabledComponents = readComponentsLPr(parser);
+ } else if (tagName.equals(TAG_PROTECTED_COMPONENTS)) {
+ protectedComponents = readComponentsLPr(parser);
+ } else if (tagName.equals(TAG_VISIBLE_COMPONENTS)) {
+ visibleComponents = readComponentsLPr(parser);
}
}
ps.setUserState(userId, enabled, installed, stopped, notLaunched, hidden,
enabledCaller, enabledComponents, disabledComponents, blockUninstall,
- verifState, linkGeneration);
+ verifState, linkGeneration,
+ protectedComponents, visibleComponents);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -1772,7 +1826,11 @@ final class Settings {
&& ustate.disabledComponents.size() > 0)
|| ustate.blockUninstall
|| (ustate.domainVerificationStatus !=
- PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED)) {
+ PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED)
+ || (ustate.protectedComponents != null
+ && ustate.protectedComponents.size() > 0)
+ || (ustate.visibleComponents != null
+ && ustate.visibleComponents.size() > 0)) {
serializer.startTag(null, TAG_PACKAGE);
serializer.attribute(null, ATTR_NAME, pkg.name);
if (DEBUG_MU) Log.i(TAG, " pkg=" + pkg.name + ", state=" + ustate.enabled);
@@ -1829,7 +1887,26 @@ final class Settings {
}
serializer.endTag(null, TAG_DISABLED_COMPONENTS);
}
-
+ if (ustate.protectedComponents != null
+ && ustate.protectedComponents.size() > 0) {
+ serializer.startTag(null, TAG_PROTECTED_COMPONENTS);
+ for (final String name : ustate.protectedComponents) {
+ serializer.startTag(null, TAG_ITEM);
+ serializer.attribute(null, ATTR_NAME, name);
+ serializer.endTag(null, TAG_ITEM);
+ }
+ serializer.endTag(null, TAG_PROTECTED_COMPONENTS);
+ }
+ if (ustate.visibleComponents != null
+ && ustate.visibleComponents.size() > 0) {
+ serializer.startTag(null, TAG_VISIBLE_COMPONENTS);
+ for (final String name : ustate.visibleComponents) {
+ serializer.startTag(null, TAG_ITEM);
+ serializer.attribute(null, ATTR_NAME, name);
+ serializer.endTag(null, TAG_ITEM);
+ }
+ serializer.endTag(null, TAG_VISIBLE_COMPONENTS);
+ }
serializer.endTag(null, TAG_PACKAGE);
}
}
@@ -2098,6 +2175,7 @@ final class Settings {
XmlUtils.writeIntAttribute(serializer, ATTR_SDK_VERSION, ver.sdkVersion);
XmlUtils.writeIntAttribute(serializer, ATTR_DATABASE_VERSION, ver.databaseVersion);
XmlUtils.writeStringAttribute(serializer, ATTR_FINGERPRINT, ver.fingerprint);
+ XmlUtils.writeStringAttribute(serializer, ATTR_DISPLAYVERSION, ver.displayversion);
serializer.endTag(null, TAG_VERSION);
}
@@ -2315,6 +2393,201 @@ final class Settings {
}
}
+ // Migrate from previous prebundled packages file to new one
+ void migratePrebundedPackagesIfNeededLPr(List<UserInfo> users, Installer installer) {
+ if (mPrebundledPackagesFilename.exists()) {
+ // Read old file
+ editPrebundledPackagesLPw(UserHandle.USER_OWNER);
+ readPrebundledPackagesOldLPw();
+ mPrebundledPackagesFilename.delete();
+ // Migrate to new file based on user
+ writePrebundledPackagesLPr(UserHandle.USER_OWNER);
+ } else {
+ if (users == null) {
+ readPrebundledPackagesLPr(UserHandle.USER_OWNER);
+ } else {
+ for (UserInfo user : users) {
+ editPrebundledPackagesLPw(user.id);
+ readPrebundledPackagesLPr(user.id);
+ // mark all existing users as having packages installed from OWNER
+ try {
+ markAllAsInstalledForUser(user.id, installer);
+ } catch (PackageManagerException e) {
+ Log.d(TAG, e.toString());
+ }
+ }
+ }
+ }
+ }
+
+ void writePrebundledPackagesLPr(int userId) {
+ editPrebundledPackagesLPw(userId);
+ PrintWriter writer = null;
+ try {
+ writer = new PrintWriter(
+ new BufferedWriter(new FileWriter(getUserPrebundledStateFile(userId), false)));
+
+ for (String packageName : mPrebundledPackages.get(userId)) {
+ writer.println(packageName);
+ }
+ } catch (IOException e) {
+ Slog.e(PackageManagerService.TAG, "Unable to write prebundled package list", e);
+ } finally {
+ if (writer != null) {
+ writer.close();
+ }
+ }
+ }
+
+ // This is done for an intermediate migration step on upgrade
+ void readPrebundledPackagesOldLPw() {
+ if (!mPrebundledPackagesFilename.exists()) {
+ return;
+ }
+
+ readPrebundledPackagesForUserFromFileLPr(UserHandle.USER_OWNER,
+ mPrebundledPackagesFilename);
+ }
+
+ void readPrebundledPackagesLPr(int userId) {
+ if (!getUserPrebundledStateFile(userId).exists()) {
+ return;
+ }
+ readPrebundledPackagesForUserFromFileLPr(userId, getUserPrebundledStateFile(userId));
+ }
+
+ private void readPrebundledPackagesForUserFromFileLPr(int userId, File file) {
+ BufferedReader reader = null;
+ try {
+ HashSet<String> ppkg = mPrebundledPackages.get(userId);
+ if (ppkg == null) {
+ Slog.e(PackageManagerService.TAG, "Unable to get packages for user " + userId);
+ return;
+ }
+ reader = new BufferedReader(new FileReader(file));
+ String packageName = reader.readLine();
+ while (packageName != null) {
+ if (!TextUtils.isEmpty(packageName)) {
+ ppkg.add(packageName);
+ }
+ packageName = reader.readLine();
+ }
+ } catch (IOException e) {
+ Slog.e(PackageManagerService.TAG, "Unable to read prebundled package list", e);
+ } finally {
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (IOException e) {}
+ }
+ }
+ }
+
+ private void markAllAsInstalledForUser(int userHandle, Installer installer)
+ throws PackageManagerException {
+ if (mPrebundledPackages.get(userHandle) == null) {
+ throw new PackageManagerException(INSTALL_FAILED_UNINSTALLED_PREBUNDLE,
+ "Failure migrating prebundled packages to existing user " + userHandle);
+ }
+
+ // grab all the packages from the user account, move them over
+ for (String s : mPrebundledPackages.get(UserHandle.USER_OWNER)) {
+ mPrebundledPackages.get(userHandle).add(s);
+ }
+
+ for (PackageSetting ps : mPackages.values()) {
+ if (ps.pkg == null || ps.pkg.applicationInfo == null) {
+ continue;
+ }
+ // Mark the app as installed
+ boolean setInstalled =
+ wasPrebundledPackageInstalledLPr(UserHandle.USER_OWNER, ps.name);
+ ps.setInstalled(setInstalled, userHandle);
+ // Tell the installer to create the user data for the application
+ installer.createUserData(ps.name,
+ UserHandle.getUid(userHandle, ps.appId), userHandle,
+ ps.pkg.applicationInfo.seinfo);
+ }
+ // Write the package restrications
+ writePackageRestrictionsLPr(userHandle);
+ }
+
+ void markPrebundledPackageInstalledLPr(int userId, String packageName) {
+ editPrebundledPackagesLPw(userId);
+ mPrebundledPackages.get(userId).add(packageName);
+ }
+
+ boolean wasPrebundledPackageInstalledLPr(int userId, String packageName) {
+ if (mPrebundledPackages.get(userId) == null) {
+ return false;
+ }
+ return mPrebundledPackages.get(userId).contains(packageName);
+ }
+
+ boolean shouldPrebundledPackageBeInstalledForRegion(Resources res, String packageName,
+ Resources configuredResources) {
+ // Default fallback on lack of bad package
+ if (TextUtils.isEmpty(packageName)) {
+ return false;
+ }
+
+ // Configured resources can be null if the device
+ // is not region locked. In such cases, fall back to
+ // the default resources object
+ Resources resources = configuredResources;
+ if (configuredResources == null) {
+ resources = res;
+ }
+
+ // If the package is compatible with the current region, install it
+ // Note : If a package needs to be installed only if the device is
+ // not provisioned, overlay values/config_region_locked_packages
+ // TODO change config_region_locked_packages to something that is
+ // not confusing inside a non region resource bucket
+ String[] prebundledArray
+ = resources.getStringArray(R.array.config_region_locked_packages);
+ if (ArrayUtils.contains(prebundledArray, packageName)) {
+ return true;
+ }
+
+ // If the package is not compatible with the current region, check if its locked
+ // to any other region before installing it.
+ prebundledArray = resources
+ .getStringArray(R.array.config_restrict_to_region_locked_devices);
+ return !ArrayUtils.contains(prebundledArray, packageName);
+ }
+
+ boolean shouldPrebundledPackageBeInstalledForUserLPr(PackageSetting existingSettings,
+ int userIdentifier, String packageName) {
+
+ // Check if package installed for the user
+ final boolean isInstalledForUser = (existingSettings != null
+ && existingSettings.getInstalled(userIdentifier));
+
+ // Check if package installed for the owner
+ final boolean isInstalledForOwner = (existingSettings != null
+ && existingSettings.getInstalled(UserHandle.USER_OWNER));
+
+ // Check if the user is the owner
+ final boolean isOwner = userIdentifier == UserHandle.USER_OWNER;
+
+ // If the given user is not the owner, and the prebundle was installed for the owner
+ // but is no longer installed, and isn't currently installed for the user,
+ // skip installing it.
+ if (!isOwner && wasPrebundledPackageInstalledLPr(UserHandle.USER_OWNER, packageName)
+ && !isInstalledForOwner && !isInstalledForUser) {
+ return false;
+ }
+
+ // If the given package was installed for the user and isn't currently, skip reinstalling it
+ if (wasPrebundledPackageInstalledLPr(userIdentifier, packageName) &&
+ !isInstalledForUser) {
+ return false;
+ }
+
+ return true;
+ }
+
void writeDisabledSysPackageLPr(XmlSerializer serializer, final PackageSetting pkg)
throws java.io.IOException {
serializer.startTag(null, "updated-package");
@@ -2473,6 +2746,9 @@ final class Settings {
}
}
}
+ if (bp.allowViaWhitelist) {
+ serializer.attribute(null, "allowViaWhitelist", Integer.toString(1));
+ }
serializer.endTag(null, TAG_ITEM);
}
}
@@ -2498,7 +2774,7 @@ final class Settings {
}
boolean readLPw(PackageManagerService service, List<UserInfo> users, int sdkVersion,
- boolean onlyCore) {
+ boolean onlyCore, Installer installer) {
FileInputStream str = null;
if (mBackupSettingsFilename.exists()) {
try {
@@ -2626,6 +2902,8 @@ final class Settings {
external.sdkVersion = XmlUtils.readIntAttribute(parser, "external", 0);
internal.fingerprint = external.fingerprint =
XmlUtils.readStringAttribute(parser, "fingerprint");
+ internal.displayversion = external.displayversion =
+ XmlUtils.readStringAttribute(parser, "displayversion");
} else if (tagName.equals("database-version")) {
// Upgrade from older XML schema
@@ -2657,6 +2935,7 @@ final class Settings {
ver.sdkVersion = XmlUtils.readIntAttribute(parser, ATTR_SDK_VERSION);
ver.databaseVersion = XmlUtils.readIntAttribute(parser, ATTR_SDK_VERSION);
ver.fingerprint = XmlUtils.readStringAttribute(parser, ATTR_FINGERPRINT);
+ ver.displayversion = XmlUtils.readStringAttribute(parser, ATTR_DISPLAYVERSION);
} else {
Slog.w(PackageManagerService.TAG, "Unknown element under <packages>: "
@@ -2682,7 +2961,8 @@ final class Settings {
// on update drop the files before loading them.
if (PackageManagerService.CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE) {
final VersionInfo internal = getInternalVersion();
- if (!Build.FINGERPRINT.equals(internal.fingerprint)) {
+ if ((!Build.FINGERPRINT.equals(internal.fingerprint)) ||
+ (!Build.DISPLAY.equals(internal.displayversion))) {
if (users == null) {
mRuntimePermissionsPersistence.deleteUserRuntimePermissionsFile(
UserHandle.USER_OWNER);
@@ -2752,6 +3032,8 @@ final class Settings {
}
}
+ migratePrebundedPackagesIfNeededLPr(users, installer);
+
/*
* Make sure all the updated system packages have their shared users
* associated with them.
@@ -2973,6 +3255,11 @@ final class Settings {
for (int i=0; i<ri.size(); i++) {
ActivityInfo ai = ri.get(i).activityInfo;
set[i] = new ComponentName(ai.packageName, ai.name);
+ // We have already discovered the best third party match,
+ // so we only need to finish filling set with all results.
+ if (haveNonSys != null) {
+ continue;
+ }
if ((ai.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
if (ri.get(i).match >= thirdPartyMatch) {
// Keep track of the best match we find of all third
@@ -2981,7 +3268,6 @@ final class Settings {
if (PackageManagerService.DEBUG_PREFERRED) Log.d(TAG, "Result "
+ ai.packageName + "/" + ai.name + ": non-system!");
haveNonSys = set[i];
- break;
}
} else if (cn.getPackageName().equals(ai.packageName)
&& cn.getClassName().equals(ai.name)) {
@@ -3126,6 +3412,8 @@ final class Settings {
bp.protectionLevel = readInt(parser, null, "protection",
PermissionInfo.PROTECTION_NORMAL);
bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel);
+ bp.allowViaWhitelist = readInt(parser, null,
+ "allowViaWhitelist", 0) == 1;
if (dynamic) {
PermissionInfo pi = new PermissionInfo();
pi.packageName = sourcePackage.intern();
@@ -3133,6 +3421,7 @@ final class Settings {
pi.icon = readInt(parser, null, "icon", 0);
pi.nonLocalizedLabel = parser.getAttributeValue(null, "label");
pi.protectionLevel = bp.protectionLevel;
+ pi.allowViaWhitelist = bp.allowViaWhitelist;
bp.pendingInfo = pi;
}
out.put(bp.name, bp);
@@ -3498,7 +3787,7 @@ final class Settings {
}
String tagName = parser.getName();
- // Legacy
+ // Legacy
if (tagName.equals(TAG_DISABLED_COMPONENTS)) {
readDisabledComponentsLPw(packageSetting, parser, 0);
} else if (tagName.equals(TAG_ENABLED_COMPONENTS)) {
@@ -3669,7 +3958,9 @@ final class Settings {
continue;
}
// Only system apps are initially installed.
- ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle);
+ boolean setInstalled = ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0) ||
+ wasPrebundledPackageInstalledLPr(UserHandle.USER_OWNER, ps.name);
+ ps.setInstalled(setInstalled, userHandle);
// Need to create a data directory for all apps under this user.
installer.createUserData(ps.volumeUuid, ps.name,
UserHandle.getUid(userHandle, ps.appId), userHandle,
@@ -3678,6 +3969,7 @@ final class Settings {
applyDefaultPreferredAppsLPw(service, userHandle);
writePackageRestrictionsLPr(userHandle);
writePackageListLPr(userHandle);
+ writePrebundledPackagesLPr(userHandle);
}
void removeUserLPw(int userId) {
@@ -3686,10 +3978,13 @@ final class Settings {
entry.getValue().removeUser(userId);
}
mPreferredActivities.remove(userId);
+ mPrebundledPackages.remove(userId);
File file = getUserPackagesStateFile(userId);
file.delete();
file = getUserPackagesStateBackupFile(userId);
file.delete();
+ file = getUserPrebundledStateFile(userId);
+ file.delete();
removeCrossProfileIntentFiltersLPw(userId);
mRuntimePermissionsPersistence.onUserRemoved(userId);
@@ -3770,7 +4065,7 @@ final class Settings {
private String compToString(ArraySet<String> cmp) {
return cmp != null ? Arrays.toString(cmp.toArray()) : "[]";
}
-
+
boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId) {
if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
return true;
@@ -3864,7 +4159,7 @@ final class Settings {
if (pkgSetting.getNotLaunched(userId)) {
if (pkgSetting.installerPackageName != null) {
yucky.sendPackageBroadcast(Intent.ACTION_PACKAGE_FIRST_LAUNCH,
- pkgSetting.name, null,
+ pkgSetting.name, null, null,
pkgSetting.installerPackageName, null, new int[] {userId});
}
pkgSetting.setNotLaunched(false, userId);
@@ -3874,6 +4169,57 @@ final class Settings {
return false;
}
+ void removeStalePermissions() {
+ /*
+ * Remove any permission that is not currently declared by any package
+ */
+ List<BasePermission> permissionsToRemove = new ArrayList<>();
+ for (BasePermission basePerm : mPermissions.values()) {
+ // Ignore permissions declared by the system
+ if (basePerm.sourcePackage.equals("android") ||
+ basePerm.sourcePackage.equals("cyanogenmod.platform")) {
+ continue;
+ }
+ // Ignore permissions other than NORMAL (ignore DYNAMIC and BUILTIN), like the
+ // ones based on permission-trees
+ if (basePerm.type != BasePermission.TYPE_NORMAL) {
+ continue;
+ }
+
+ if (!mPackages.containsKey(basePerm.sourcePackage)) {
+ // Package doesn't exist
+ permissionsToRemove.add(basePerm);
+ continue;
+ }
+ PackageSetting pkgSettings = mPackages.get(basePerm.sourcePackage);
+ if (pkgSettings.pkg == null || pkgSettings.pkg.permissions == null) {
+ // Package doesn't declare permissions
+ permissionsToRemove.add(basePerm);
+ continue;
+ }
+ boolean found = false;
+ for (PackageParser.Permission perm : pkgSettings.pkg.permissions) {
+ if (perm.info.name != null && basePerm.name.equals(perm.info.name)) {
+ // The original package still declares the permission
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ // The original package doesn't currently declare the permission
+ permissionsToRemove.add(basePerm);
+ }
+ }
+ // And now remove all stale permissions
+ for (BasePermission basePerm : permissionsToRemove) {
+ String msg = "Removed stale permission: " + basePerm.name + " originally " +
+ "assigned to " + basePerm.sourcePackage + "\n";
+ mReadMessages.append(msg);
+ PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+ mPermissions.remove(basePerm.name);
+ }
+ }
+
List<UserInfo> getAllUsers() {
long id = Binder.clearCallingIdentity();
try {
@@ -3955,6 +4301,7 @@ final class Settings {
pw.printPair("databaseVersion", ver.databaseVersion);
pw.println();
pw.printPair("fingerprint", ver.fingerprint);
+ pw.printPair("displayversion", ver.displayversion);
pw.println();
pw.decreaseIndent();
}
@@ -4522,6 +4869,10 @@ final class Settings {
@GuardedBy("mLock")
// The mapping keys are user ids.
+ private final SparseArray<String> mDisplayversion = new SparseArray<>();
+
+ @GuardedBy("mLock")
+ // The mapping keys are user ids.
private final SparseBooleanArray mDefaultPermissionsGranted = new SparseBooleanArray();
public RuntimePermissionPersistence(Object lock) {
@@ -4534,6 +4885,7 @@ final class Settings {
public void onDefaultRuntimePermissionsGrantedLPr(int userId) {
mFingerprints.put(userId, Build.FINGERPRINT);
+ mDisplayversion.put(userId, Build.DISPLAY);
writePermissionsForUserAsyncLPr(userId);
}
@@ -4626,6 +4978,11 @@ final class Settings {
serializer.attribute(null, ATTR_FINGERPRINT, fingerprint);
}
+ String displayversion = mDisplayversion.get(userId);
+ if (displayversion != null) {
+ serializer.attribute(null, ATTR_DISPLAYVERSION, displayversion);
+ }
+
final int packageCount = permissionsForPackage.size();
for (int i = 0; i < packageCount; i++) {
String packageName = permissionsForPackage.keyAt(i);
@@ -4650,7 +5007,8 @@ final class Settings {
serializer.endDocument();
destination.finishWrite(out);
- if (Build.FINGERPRINT.equals(fingerprint)) {
+ if ((Build.FINGERPRINT.equals(fingerprint)) ||
+ (Build.DISPLAY.equals(displayversion))) {
mDefaultPermissionsGranted.put(userId, true);
}
// Any error while writing is fatal.
@@ -4733,8 +5091,11 @@ final class Settings {
switch (parser.getName()) {
case TAG_RUNTIME_PERMISSIONS: {
String fingerprint = parser.getAttributeValue(null, ATTR_FINGERPRINT);
+ String displayversion = parser.getAttributeValue(null, ATTR_DISPLAYVERSION);
mFingerprints.put(userId, fingerprint);
- final boolean defaultsGranted = Build.FINGERPRINT.equals(fingerprint);
+ mDisplayversion.put(userId, displayversion);
+ final boolean defaultsGranted = Build.FINGERPRINT.equals(fingerprint) ||
+ Build.DISPLAY.equals(displayversion);
mDefaultPermissionsGranted.put(userId, defaultsGranted);
} break;
diff --git a/services/core/java/com/android/server/pm/UserContentObserver.java b/services/core/java/com/android/server/pm/UserContentObserver.java
new file mode 100644
index 0000000..6145c3b
--- /dev/null
+++ b/services/core/java/com/android/server/pm/UserContentObserver.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod 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.pm;
+
+import android.app.ActivityManagerNative;
+import android.app.IUserSwitchObserver;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IRemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Simple extension of ContentObserver that also listens for user switch events to call update
+ */
+public abstract class UserContentObserver extends ContentObserver {
+ private static final String TAG = "UserContentObserver";
+
+ private Runnable mUpdateRunnable;
+
+ private IUserSwitchObserver mUserSwitchObserver = new IUserSwitchObserver.Stub() {
+ @Override
+ public void onUserSwitching(int newUserId, IRemoteCallback reply) {
+ }
+ @Override
+ public void onUserSwitchComplete(int newUserId) throws RemoteException {
+ mHandler.post(mUpdateRunnable);
+ }
+ @Override
+ public void onForegroundProfileSwitch(int newProfileId) {
+ }
+ };
+
+ private Handler mHandler;
+
+ /**
+ * Content observer that tracks user switches
+ * to allow clients to re-load settings for current user
+ */
+ public UserContentObserver(Handler handler) {
+ super(handler);
+ mHandler = handler;
+ mUpdateRunnable = new Runnable() {
+ @Override
+ public void run() {
+ update();
+ }
+ };
+ }
+
+ protected void observe() {
+ try {
+ ActivityManagerNative.getDefault().registerUserSwitchObserver(mUserSwitchObserver);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to register user switch observer!", e);
+ }
+ }
+
+ protected void unobserve() {
+ try {
+ mHandler.removeCallbacks(mUpdateRunnable);
+ ActivityManagerNative.getDefault().unregisterUserSwitchObserver(mUserSwitchObserver);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to unregister user switch observer!", e);
+ }
+ }
+
+ /**
+ * Called to notify of registered uri changes and user switches.
+ * Always invoked on the handler passed in at construction
+ */
+ protected abstract void update();
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ update();
+ }
+}
diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java
index 9095f57..7a20e40 100644
--- a/services/core/java/com/android/server/policy/BarController.java
+++ b/services/core/java/com/android/server/policy/BarController.java
@@ -26,6 +26,7 @@ import android.view.View;
import android.view.WindowManager;
import android.view.WindowManagerPolicy.WindowState;
+import android.view.WindowManagerPolicyControl;
import com.android.internal.statusbar.IStatusBarService;
import java.io.PrintWriter;
@@ -119,7 +120,7 @@ public class BarController {
if (mWin != null) {
if (win != null && (win.getAttrs().privateFlags
& WindowManager.LayoutParams.PRIVATE_FLAG_INHERIT_TRANSLUCENT_DECOR) == 0) {
- int fl = PolicyControl.getWindowFlags(win, null);
+ int fl = WindowManagerPolicyControl.getWindowFlags(win, null);
if ((fl & mTranslucentWmFlag) != 0) {
vis |= mTranslucentFlag;
} else {
diff --git a/services/core/java/com/android/server/policy/BurnInProtectionHelper.java b/services/core/java/com/android/server/policy/BurnInProtectionHelper.java
index fef1e57..e6ec6a6 100644
--- a/services/core/java/com/android/server/policy/BurnInProtectionHelper.java
+++ b/services/core/java/com/android/server/policy/BurnInProtectionHelper.java
@@ -72,6 +72,9 @@ public class BurnInProtectionHelper implements DisplayManager.DisplayListener,
/* 1 means increasing, -1 means decreasing */
private int mYOffsetDirection = 1;
+ private int mAppliedBurnInXOffset = 0;
+ private int mAppliedBurnInYOffset = 0;
+
private final AlarmManager mAlarmManager;
private final PendingIntent mBurnInProtectionIntent;
private final DisplayManagerInternal mDisplayManagerInternal;
@@ -139,6 +142,8 @@ public class BurnInProtectionHelper implements DisplayManager.DisplayListener,
mFirstUpdate = false;
} else {
adjustOffsets();
+ mAppliedBurnInXOffset = mLastBurnInXOffset;
+ mAppliedBurnInYOffset = mLastBurnInYOffset;
mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(),
mLastBurnInXOffset, mLastBurnInYOffset);
}
@@ -258,6 +263,8 @@ public class BurnInProtectionHelper implements DisplayManager.DisplayListener,
@Override
public void onAnimationEnd(Animator animator) {
if (animator == mCenteringAnimator && !mBurnInProtectionActive) {
+ mAppliedBurnInXOffset = 0;
+ mAppliedBurnInYOffset = 0;
// No matter how the animation finishes, we want to zero the offsets.
mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(), 0, 0);
}
@@ -276,7 +283,7 @@ public class BurnInProtectionHelper implements DisplayManager.DisplayListener,
if (!mBurnInProtectionActive) {
final float value = (Float) valueAnimator.getAnimatedValue();
mDisplayManagerInternal.setDisplayOffsets(mDisplay.getDisplayId(),
- (int) (mLastBurnInXOffset * value), (int) (mLastBurnInYOffset * value));
+ (int) (mAppliedBurnInXOffset * value), (int) (mAppliedBurnInYOffset * value));
}
}
}
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index 3cee927..96e1c70 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2010-2015 CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,26 +22,42 @@ import com.android.internal.app.AlertController.AlertParams;
import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.R;
+import com.android.internal.util.UserIcons;
import com.android.internal.widget.LockPatternUtils;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.app.INotificationManager;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.ContentResolver;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.UserInfo;
+import android.content.ServiceConnection;
import android.database.ContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.IPowerManager;
import android.os.Message;
+import android.os.Messenger;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -49,10 +66,13 @@ import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
import android.provider.Settings;
+import android.provider.Settings.Global;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.telephony.PhoneStateListener;
import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -76,9 +96,16 @@ import android.widget.ImageView.ScaleType;
import android.widget.ListView;
import android.widget.TextView;
+import cyanogenmod.providers.CMSettings;
+
import java.util.ArrayList;
+import java.util.BitSet;
import java.util.List;
+import org.cyanogenmod.internal.util.ThemeUtils;
+
+import static com.android.internal.util.cm.PowerMenuConstants.*;
+
/**
* Helper to show the global actions dialog. Each item is an {@link Action} that
* may show depending on whether the keyguard is showing, and whether the device
@@ -90,20 +117,9 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
private static final boolean SHOW_SILENT_TOGGLE = true;
- /* Valid settings for global actions keys.
- * see config.xml config_globalActionList */
- private static final String GLOBAL_ACTION_KEY_POWER = "power";
- private static final String GLOBAL_ACTION_KEY_AIRPLANE = "airplane";
- private static final String GLOBAL_ACTION_KEY_BUGREPORT = "bugreport";
- private static final String GLOBAL_ACTION_KEY_SILENT = "silent";
- private static final String GLOBAL_ACTION_KEY_USERS = "users";
- private static final String GLOBAL_ACTION_KEY_SETTINGS = "settings";
- private static final String GLOBAL_ACTION_KEY_LOCKDOWN = "lockdown";
- private static final String GLOBAL_ACTION_KEY_VOICEASSIST = "voiceassist";
- private static final String GLOBAL_ACTION_KEY_ASSIST = "assist";
-
private final Context mContext;
private final WindowManagerFuncs mWindowManagerFuncs;
+ private Context mUiContext;
private final AudioManager mAudioManager;
private final IDreamManager mDreamManager;
@@ -123,6 +139,12 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
private boolean mHasVibrator;
private final boolean mShowSilentToggle;
+ // Power menu customizations
+ String mActions;
+
+ private BitSet mAirplaneModeBits;
+ private final List<PhoneStateListener> mPhoneStateListeners = new ArrayList<>();
+
/**
* @param context everything needs a context :(
*/
@@ -137,6 +159,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
filter.addAction(Intent.ACTION_SCREEN_OFF);
+ filter.addAction(Intent.UPDATE_POWER_MENU);
filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
context.registerReceiver(mBroadcastReceiver, filter);
@@ -145,9 +168,15 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
mHasTelephony = cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
// get notified of phone state changes
- TelephonyManager telephonyManager =
- (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- telephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+ SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
+ new SubscriptionManager.OnSubscriptionsChangedListener() {
+ @Override
+ public void onSubscriptionsChanged() {
+ super.onSubscriptionsChanged();
+ setupAirplaneModeListeners();
+ }
+ });
+ setupAirplaneModeListeners();
mContext.getContentResolver().registerContentObserver(
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
mAirplaneModeObserver);
@@ -156,6 +185,60 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useFixedVolume);
+
+ updatePowerMenuActions();
+ }
+
+ /**
+ * Since there are two ways of handling airplane mode (with telephony, we depend on the internal
+ * device telephony state), and MSIM devices do not report phone state for missing SIMs, we
+ * need to dynamically setup listeners based on subscription changes.
+ *
+ * So if there is _any_ active SIM in the device, we can depend on the phone state,
+ * otherwise fall back to {@link Settings.Global#AIRPLANE_MODE_ON}.
+ */
+ private void setupAirplaneModeListeners() {
+ TelephonyManager telephonyManager =
+ (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
+
+ for (PhoneStateListener listener : mPhoneStateListeners) {
+ telephonyManager.listen(listener, PhoneStateListener.LISTEN_NONE);
+ }
+ mPhoneStateListeners.clear();
+
+ final List<SubscriptionInfo> subInfoList = SubscriptionManager.from(mContext)
+ .getActiveSubscriptionInfoList();
+ if (subInfoList != null) {
+ mHasTelephony = true;
+ mAirplaneModeBits = new BitSet(subInfoList.size());
+ for (int i = 0; i < subInfoList.size(); i++) {
+ final int finalI = i;
+ PhoneStateListener subListener = new PhoneStateListener(subInfoList.get(finalI)
+ .getSubscriptionId()) {
+ @Override
+ public void onServiceStateChanged(ServiceState serviceState) {
+ final boolean inAirplaneMode = serviceState.getState()
+ == ServiceState.STATE_POWER_OFF;
+ mAirplaneModeBits.set(finalI, inAirplaneMode);
+
+ // we're in airplane mode if _any_ of the subscriptions say we are
+ mAirplaneState = mAirplaneModeBits.cardinality() > 0
+ ? ToggleAction.State.On : ToggleAction.State.Off;
+
+ mAirplaneModeOn.updateState(mAirplaneState);
+ if (mAdapter != null) {
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+ };
+ mPhoneStateListeners.add(subListener);
+ telephonyManager.listen(subListener, PhoneStateListener.LISTEN_SERVICE_STATE);
+ }
+ } else {
+ mHasTelephony = false;
+ }
+ // Set the initial status of airplane mode toggle
+ mAirplaneState = getUpdatedAirplaneToggleState();
}
/**
@@ -206,6 +289,14 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
}
+ private Context getUiContext() {
+ if (mUiContext == null) {
+ mUiContext = ThemeUtils.createUiContext(mContext);
+ mUiContext.setTheme(com.android.internal.R.style.Theme_Power_Dialog);
+ }
+ return mUiContext != null ? mUiContext : mContext;
+ }
+
/**
* Create the global actions dialog.
* @return A new dialog.
@@ -261,18 +352,31 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
onAirplaneModeChanged();
mItems = new ArrayList<Action>();
- String[] defaultActions = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_globalActionsList);
+
+ String[] actionsArray;
+ if (mActions == null) {
+ actionsArray = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_globalActionsList);
+ } else {
+ actionsArray = mActions.split("\\|");
+ }
+
+ // Always add the power off option
+ mItems.add(new PowerAction());
ArraySet<String> addedKeys = new ArraySet<String>();
- for (int i = 0; i < defaultActions.length; i++) {
- String actionKey = defaultActions[i];
+ for (int i = 0; i < actionsArray.length; i++) {
+ String actionKey = actionsArray[i];
if (addedKeys.contains(actionKey)) {
// If we already have added this, don't add it again.
continue;
}
if (GLOBAL_ACTION_KEY_POWER.equals(actionKey)) {
- mItems.add(new PowerAction());
+ continue;
+ } else if (GLOBAL_ACTION_KEY_REBOOT.equals(actionKey)) {
+ mItems.add(new RebootAction());
+ } else if (GLOBAL_ACTION_KEY_SCREENSHOT.equals(actionKey)) {
+ mItems.add(getScreenshotAction());
} else if (GLOBAL_ACTION_KEY_AIRPLANE.equals(actionKey)) {
mItems.add(mAirplaneModeOn);
} else if (GLOBAL_ACTION_KEY_BUGREPORT.equals(actionKey)) {
@@ -285,7 +389,9 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
mItems.add(mSilentModeAction);
}
} else if (GLOBAL_ACTION_KEY_USERS.equals(actionKey)) {
- if (SystemProperties.getBoolean("fw.power_user_switcher", false)) {
+ List<UserInfo> users = ((UserManager) mContext.getSystemService(
+ Context.USER_SERVICE)).getUsers();
+ if (users.size() > 1) {
addUsersToMenu(mItems);
}
} else if (GLOBAL_ACTION_KEY_SETTINGS.equals(actionKey)) {
@@ -305,12 +411,13 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
mAdapter = new MyAdapter();
- AlertParams params = new AlertParams(mContext);
+ AlertParams params = new AlertParams(getUiContext());
params.mAdapter = mAdapter;
params.mOnClickListener = this;
params.mForceInverseBackground = true;
- GlobalActionsDialog dialog = new GlobalActionsDialog(mContext, params);
+ GlobalActionsDialog dialog = new GlobalActionsDialog(/** system context **/ mContext,
+ /** themed context **/ getUiContext(), params);
dialog.setCanceledOnTouchOutside(false); // Handled by the custom class.
dialog.getListView().setItemsCanFocus(true);
@@ -367,6 +474,53 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
}
+ private final class RebootAction extends SinglePressAction {
+ private RebootAction() {
+ super(com.android.internal.R.drawable.ic_lock_power_reboot,
+ R.string.global_action_reboot);
+ }
+
+ @Override
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ @Override
+ public boolean showBeforeProvisioning() {
+ return true;
+ }
+
+ @Override
+ public void onPress() {
+ try {
+ IPowerManager pm = IPowerManager.Stub.asInterface(ServiceManager
+ .getService(Context.POWER_SERVICE));
+ pm.reboot(true, null, false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "PowerManager service died!", e);
+ return;
+ }
+ }
+ }
+
+ private Action getScreenshotAction() {
+ return new SinglePressAction(com.android.internal.R.drawable.ic_lock_screenshot,
+ R.string.global_action_screenshot) {
+
+ public void onPress() {
+ takeScreenshot();
+ }
+
+ public boolean showDuringKeyguard() {
+ return true;
+ }
+
+ public boolean showBeforeProvisioning() {
+ return true;
+ }
+ };
+ }
+
private Action getBugReportAction() {
return new SinglePressAction(com.android.internal.R.drawable.ic_lock_bugreport,
R.string.bugreport_title) {
@@ -423,7 +577,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
private Action getSettingsAction() {
- return new SinglePressAction(com.android.internal.R.drawable.ic_settings,
+ return new SinglePressAction(com.android.internal.R.drawable.ic_lock_settings,
R.string.global_action_settings) {
@Override
@@ -533,16 +687,24 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
if (um.isUserSwitcherEnabled()) {
List<UserInfo> users = um.getUsers();
UserInfo currentUser = getCurrentUser();
+ final int avatarSize = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.global_actions_avatar_size);
for (final UserInfo user : users) {
if (user.supportsSwitchTo()) {
boolean isCurrentUser = currentUser == null
? user.id == 0 : (currentUser.id == user.id);
- Drawable icon = user.iconPath != null ? Drawable.createFromPath(user.iconPath)
- : null;
+ Drawable avatar = null;
+ Bitmap rawAvatar = um.getUserIcon(user.id);
+ if (rawAvatar == null) {
+ rawAvatar = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
+ user.isGuest() ? UserHandle.USER_NULL : user.id, /*light=*/ false));
+ }
+ avatar = new BitmapDrawable(mContext.getResources(),
+ createCircularClip(rawAvatar, avatarSize, avatarSize));
+
SinglePressAction switchToUser = new SinglePressAction(
- com.android.internal.R.drawable.ic_menu_cc, icon,
- (user.name != null ? user.name : "Primary")
- + (isCurrentUser ? " \u2714" : "")) {
+ com.android.internal.R.drawable.ic_lock_user, avatar,
+ (user.name != null ? user.name : "Primary")) {
public void onPress() {
try {
ActivityManagerNative.getDefault().switchUser(user.id);
@@ -559,12 +721,101 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
return false;
}
};
+ if (isCurrentUser) {
+ switchToUser.setStatus(mContext.getString(
+ R.string.global_action_current_user));
+ }
items.add(switchToUser);
}
}
}
}
+ /**
+ * functions needed for taking screenhots.
+ * This leverages the built in ICS screenshot functionality
+ */
+ final Object mScreenshotLock = new Object();
+ ServiceConnection mScreenshotConnection = null;
+
+ final Runnable mScreenshotTimeout = new Runnable() {
+ @Override public void run() {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ }
+ }
+ }
+ };
+
+ private void takeScreenshot() {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != null) {
+ return;
+ }
+ ComponentName cn = new ComponentName("com.android.systemui",
+ "com.android.systemui.screenshot.TakeScreenshotService");
+ Intent intent = new Intent();
+ intent.setComponent(cn);
+ ServiceConnection conn = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection != this) {
+ return;
+ }
+ Messenger messenger = new Messenger(service);
+ Message msg = Message.obtain(null, 1);
+ final ServiceConnection myConn = this;
+ Handler h = new Handler(mHandler.getLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ synchronized (mScreenshotLock) {
+ if (mScreenshotConnection == myConn) {
+ mContext.unbindService(mScreenshotConnection);
+ mScreenshotConnection = null;
+ mHandler.removeCallbacks(mScreenshotTimeout);
+ }
+ }
+ }
+ };
+ msg.replyTo = new Messenger(h);
+ msg.arg1 = msg.arg2 = 0;
+
+ /* remove for the time being
+ if (mStatusBar != null && mStatusBar.isVisibleLw())
+ msg.arg1 = 1;
+ if (mNavigationBar != null && mNavigationBar.isVisibleLw())
+ msg.arg2 = 1;
+ */
+
+ /* wait for the dialog box to close */
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ie) {
+ // Do nothing
+ }
+
+ /* take the screenshot */
+ try {
+ messenger.send(msg);
+ } catch (RemoteException e) {
+ // Do nothing
+ }
+ }
+ }
+ @Override
+ public void onServiceDisconnected(ComponentName name) {}
+ };
+ if (mContext.bindServiceAsUser(
+ intent, conn, Context.BIND_AUTO_CREATE, UserHandle.CURRENT)) {
+ mScreenshotConnection = conn;
+ mHandler.postDelayed(mScreenshotTimeout, 10000);
+ }
+ }
+ }
+
private void prepareDialog() {
refreshSilentMode();
mAirplaneModeOn.updateState(mAirplaneState);
@@ -671,7 +922,8 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
public View getView(int position, View convertView, ViewGroup parent) {
Action action = getItem(position);
- return action.create(mContext, convertView, parent, LayoutInflater.from(mContext));
+ final Context context = getUiContext();
+ return action.create(context, convertView, parent, LayoutInflater.from(context));
}
}
@@ -726,6 +978,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
private final Drawable mIcon;
private final int mMessageResId;
private final CharSequence mMessage;
+ private CharSequence mStatusMessage;
protected SinglePressAction(int iconResId, int messageResId) {
mIconResId = iconResId;
@@ -752,8 +1005,12 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
return true;
}
- public String getStatus() {
- return null;
+ public CharSequence getStatus() {
+ return mStatusMessage;
+ }
+
+ public void setStatus(CharSequence status) {
+ mStatusMessage = status;
}
abstract public void onPress();
@@ -774,7 +1031,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
TextView messageView = (TextView) v.findViewById(R.id.message);
TextView statusView = (TextView) v.findViewById(R.id.status);
- final String status = getStatus();
+ final CharSequence status = getStatus();
if (!TextUtils.isEmpty(status)) {
statusView.setText(status);
} else {
@@ -782,7 +1039,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
if (mIcon != null) {
icon.setImageDrawable(mIcon);
- icon.setScaleType(ScaleType.CENTER_CROP);
+ icon.setScaleType(ScaleType.CENTER);
} else if (mIconResId != 0) {
icon.setImageDrawable(context.getDrawable(mIconResId));
}
@@ -952,9 +1209,15 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
}
- private static class SilentModeTriStateAction implements Action, View.OnClickListener {
+ private final class SilentModeTriStateAction implements Action, View.OnClickListener {
- private final int[] ITEM_IDS = { R.id.option1, R.id.option2, R.id.option3 };
+ private final int[] ITEM_IDS = { R.id.option1, R.id.option2, R.id.option3, R.id.option4 };
+ private final int[] ITEM_INDEX_TO_ZEN_MODE = {
+ Global.ZEN_MODE_NO_INTERRUPTIONS,
+ Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ Global.ZEN_MODE_OFF,
+ Global.ZEN_MODE_OFF
+ };
private final AudioManager mAudioManager;
private final Handler mHandler;
@@ -966,14 +1229,15 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
mContext = context;
}
- private int ringerModeToIndex(int ringerMode) {
- // They just happen to coincide
- return ringerMode;
- }
-
private int indexToRingerMode(int index) {
- // They just happen to coincide
- return index;
+ if (index == 2) {
+ if (mHasVibrator) {
+ return AudioManager.RINGER_MODE_VIBRATE;
+ } else {
+ return AudioManager.RINGER_MODE_NORMAL;
+ }
+ }
+ return AudioManager.RINGER_MODE_NORMAL;
}
@Override
@@ -985,9 +1249,28 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
LayoutInflater inflater) {
View v = inflater.inflate(R.layout.global_actions_silent_mode, parent, false);
- int selectedIndex = ringerModeToIndex(mAudioManager.getRingerMode());
- for (int i = 0; i < 3; i++) {
+ int ringerMode = mAudioManager.getRingerModeInternal();
+ int zenMode = Global.getInt(mContext.getContentResolver(), Global.ZEN_MODE,
+ Global.ZEN_MODE_OFF);
+ int selectedIndex = 0;
+ if (zenMode != Global.ZEN_MODE_OFF) {
+ if (zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
+ selectedIndex = 0;
+ } else if (zenMode == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS) {
+ selectedIndex = 1;
+ }
+ } else if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
+ selectedIndex = 2;
+ } else if (ringerMode == AudioManager.RINGER_MODE_NORMAL) {
+ selectedIndex = 3;
+ }
+
+ for (int i = 0; i < ITEM_IDS.length; i++) {
View itemView = v.findViewById(ITEM_IDS[i]);
+ if (!mHasVibrator && i == 2) {
+ itemView.setVisibility(View.GONE);
+ continue;
+ }
itemView.setSelected(selectedIndex == i);
// Set up click handler
itemView.setTag(i);
@@ -1018,7 +1301,22 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
if (!(v.getTag() instanceof Integer)) return;
int index = (Integer) v.getTag();
- mAudioManager.setRingerMode(indexToRingerMode(index));
+ int zenMode = ITEM_INDEX_TO_ZEN_MODE[index];
+ // ZenModeHelper will revert zen mode back to the previous value if we just
+ // put the value into the Settings db, so use INotificationManager instead
+ INotificationManager noMan = INotificationManager.Stub.asInterface(
+ ServiceManager.getService(Context.NOTIFICATION_SERVICE));
+ try {
+ noMan.setZenMode(zenMode, null, TAG);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to set zen mode", e);
+ }
+
+ if (index == 2 || index == 3) {
+ int ringerMode = indexToRingerMode(index);
+ mAudioManager.setRingerModeInternal(ringerMode);
+ }
+ mAdapter.notifyDataSetChanged();
mHandler.sendEmptyMessageDelayed(MESSAGE_DISMISS, DIALOG_DISMISS_DELAY);
}
}
@@ -1040,18 +1338,21 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
mIsWaitingForEcmExit = false;
changeAirplaneModeSystemSetting(true);
}
+ } else if (Intent.UPDATE_POWER_MENU.equals(action)) {
+ updatePowerMenuActions();
}
}
};
- PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- @Override
- public void onServiceStateChanged(ServiceState serviceState) {
- if (!mHasTelephony) return;
- final boolean inAirplaneMode = serviceState.getState() == ServiceState.STATE_POWER_OFF;
- mAirplaneState = inAirplaneMode ? ToggleAction.State.On : ToggleAction.State.Off;
- mAirplaneModeOn.updateState(mAirplaneState);
- mAdapter.notifyDataSetChanged();
+ protected void updatePowerMenuActions() {
+ ContentResolver resolver = mContext.getContentResolver();
+ mActions = CMSettings.Secure.getStringForUser(resolver,
+ CMSettings.Secure.POWER_MENU_ACTIONS, UserHandle.USER_CURRENT);
+ }
+
+ private BroadcastReceiver mThemeChangeReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ mUiContext = null;
}
};
@@ -1096,15 +1397,17 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
};
+ private ToggleAction.State getUpdatedAirplaneToggleState() {
+ return (Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.AIRPLANE_MODE_ON, 0) == 1) ?
+ ToggleAction.State.On : ToggleAction.State.Off;
+ }
+
private void onAirplaneModeChanged() {
// Let the service state callbacks handle the state.
if (mHasTelephony) return;
- boolean airplaneModeOn = Settings.Global.getInt(
- mContext.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON,
- 0) == 1;
- mAirplaneState = airplaneModeOn ? ToggleAction.State.On : ToggleAction.State.Off;
+ mAirplaneState = getUpdatedAirplaneToggleState();
mAirplaneModeOn.updateState(mAirplaneState);
}
@@ -1125,8 +1428,36 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
}
}
+ /**
+ * Generate a new bitmap (width x height pixels, ARGB_8888) with the input bitmap scaled
+ * to fit and clipped to an inscribed circle.
+ * @param input Bitmap to resize and clip
+ * @param width Width of output bitmap (and diameter of circle)
+ * @param height Height of output bitmap
+ * @return A shiny new bitmap for you to use
+ */
+ private static Bitmap createCircularClip(Bitmap input, int width, int height) {
+ if (input == null) return null;
+
+ final int inWidth = input.getWidth();
+ final int inHeight = input.getHeight();
+ final Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(output);
+ final Paint paint = new Paint();
+ paint.setShader(new BitmapShader(input, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
+ paint.setAntiAlias(true);
+ final RectF srcRect = new RectF(0, 0, inWidth, inHeight);
+ final RectF dstRect = new RectF(0, 0, width, height);
+ final Matrix m = new Matrix();
+ m.setRectToRect(srcRect, dstRect, Matrix.ScaleToFit.CENTER);
+ canvas.setMatrix(m);
+ canvas.drawCircle(inWidth / 2, inHeight / 2, inWidth / 2, paint);
+ return output;
+ }
+
private static final class GlobalActionsDialog extends Dialog implements DialogInterface {
private final Context mContext;
+ private Context mSystemContext = null;
private final int mWindowTouchSlop;
private final AlertController mAlert;
private final MyAdapter mAdapter;
@@ -1136,7 +1467,7 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
private boolean mIntercepted;
private boolean mCancelOnUp;
- public GlobalActionsDialog(Context context, AlertParams params) {
+ private GlobalActionsDialog(Context context, AlertParams params) {
super(context, getDialogTheme(context));
mContext = getContext();
mAlert = new AlertController(mContext, this, getWindow());
@@ -1145,6 +1476,19 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
params.apply(mAlert);
}
+ /**
+ * Utilized for a working global actions dialog for both accessibility services (which
+ * require a system context) and
+ * @param systemContext Base context (should be from system process)
+ * @param themedContext Themed context (created from system ui)
+ * @param params
+ */
+ public GlobalActionsDialog(Context systemContext, Context themedContext,
+ AlertParams params) {
+ this(themedContext, params);
+ mSystemContext = systemContext;
+ }
+
private static int getDialogTheme(Context context) {
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogTheme,
@@ -1158,8 +1502,8 @@ class GlobalActions implements DialogInterface.OnDismissListener, DialogInterfac
// of dismissing the dialog on touch outside. This is because the dialog
// is dismissed on the first down while the global gesture is a long press
// with two fingers anywhere on the screen.
- if (EnableAccessibilityController.canEnableAccessibilityViaGesture(mContext)) {
- mEnableAccessibilityController = new EnableAccessibilityController(mContext,
+ if (EnableAccessibilityController.canEnableAccessibilityViaGesture(mSystemContext)) {
+ mEnableAccessibilityController = new EnableAccessibilityController(mSystemContext,
new Runnable() {
@Override
public void run() {
diff --git a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
index 160d44c..da9312c 100644
--- a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
@@ -44,6 +44,7 @@ import android.view.animation.Interpolator;
import android.widget.Button;
import android.widget.FrameLayout;
+import android.view.WindowManagerPolicyControl;
import com.android.internal.R;
/**
@@ -116,7 +117,7 @@ public class ImmersiveModeConfirmation {
boolean userSetupComplete) {
mHandler.removeMessages(H.SHOW);
if (isImmersiveMode) {
- final boolean disabled = PolicyControl.disableImmersiveConfirmation(pkg);
+ final boolean disabled = WindowManagerPolicyControl.disableImmersiveConfirmation(pkg);
if (DEBUG) Slog.d(TAG, String.format("immersiveModeChanged() disabled=%s mConfirmed=%s",
disabled, mConfirmed));
if (!disabled && (DEBUG_SHOW_EVERY_TIME || !mConfirmed) && userSetupComplete) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0d96ca6..83eb424 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -22,7 +22,6 @@ import android.app.ActivityManagerInternal.SleepToken;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
import android.app.IUiModeManager;
-import android.app.ProgressDialog;
import android.app.SearchManager;
import android.app.StatusBarManager;
import android.app.UiModeManager;
@@ -31,10 +30,12 @@ import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.CompatibilityInfo;
@@ -47,6 +48,7 @@ import android.graphics.Rect;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
+import android.hardware.input.InputManager;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -81,6 +83,13 @@ import android.service.dreams.DreamService;
import android.service.dreams.IDreamManager;
import android.speech.RecognizerIntent;
import android.telecom.TelecomManager;
+import com.android.internal.os.DeviceKeyHandler;
+
+import com.android.internal.util.cm.ActionUtils;
+
+import cyanogenmod.hardware.CMHardwareManager;
+import cyanogenmod.providers.CMSettings;
+import dalvik.system.DexClassLoader;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.Log;
@@ -114,21 +123,29 @@ import android.view.accessibility.AccessibilityManager;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.AnimationUtils;
+import android.view.WindowManagerPolicyControl;
+import android.widget.Toast;
+
import com.android.internal.R;
+import com.android.internal.policy.IKeyguardService;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.ScreenShapeHelper;
+import com.android.internal.view.RotationPolicy;
import com.android.internal.widget.PointerLocationView;
import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
import com.android.server.policy.keyguard.KeyguardServiceDelegate.DrawnListener;
+import org.cyanogenmod.internal.BootDexoptDialog;
+
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashSet;
import java.util.List;
+import java.lang.reflect.Constructor;
import static android.view.WindowManager.LayoutParams.*;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
@@ -137,6 +154,7 @@ import static android.view.WindowManagerPolicy.WindowManagerFuncs.LID_CLOSED;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
import static android.view.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
+import static org.cyanogenmod.platform.internal.Manifest.permission.THIRD_PARTY_KEYGUARD;
/**
* WindowManagerPolicy implementation for the Android phone UI. This
@@ -201,6 +219,30 @@ public class PhoneWindowManager implements WindowManagerPolicy {
static public final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
static public final String SYSTEM_DIALOG_REASON_ASSIST = "assist";
+ // Available custom actions to perform on a key press.
+ // Must match values for KEY_HOME_LONG_PRESS_ACTION in:
+ // core/java/android/provider/Settings.java
+ private static final int KEY_ACTION_NOTHING = 0;
+ private static final int KEY_ACTION_MENU = 1;
+ private static final int KEY_ACTION_APP_SWITCH = 2;
+ private static final int KEY_ACTION_SEARCH = 3;
+ private static final int KEY_ACTION_VOICE_SEARCH = 4;
+ private static final int KEY_ACTION_IN_APP_SEARCH = 5;
+ private static final int KEY_ACTION_LAUNCH_CAMERA = 6;
+ private static final int KEY_ACTION_SLEEP = 7;
+ private static final int KEY_ACTION_LAST_APP = 8;
+
+ // Masks for checking presence of hardware keys.
+ // Must match values in core/res/res/values/config.xml
+ private static final int KEY_MASK_HOME = 0x01;
+ private static final int KEY_MASK_BACK = 0x02;
+ private static final int KEY_MASK_MENU = 0x04;
+ private static final int KEY_MASK_ASSIST = 0x08;
+ private static final int KEY_MASK_APP_SWITCH = 0x10;
+ private static final int KEY_MASK_CAMERA = 0x20;
+ private static final int KEY_MASK_VOLUME = 0x40;
+
+
/**
* These are the system UI flags that, when changing, can cause the layout
* of the screen to change.
@@ -217,10 +259,26 @@ public class PhoneWindowManager implements WindowManagerPolicy {
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
.build();
+ /**
+ * Broadcast Action: WiFi Display video is enabled or disabled
+ *
+ * <p>The intent will have the following extra values:</p>
+ * <ul>
+ * <li><em>state</em> - 0 for disabled, 1 for enabled. </li>
+ * </ul>
+ */
+
+ private static final String ACTION_WIFI_DISPLAY_VIDEO =
+ "org.codeaurora.intent.action.WIFI_DISPLAY_VIDEO";
+
+
// The panic gesture may become active only after the keyguard is dismissed and the immersive
// app shows again. If that doesn't happen for 30s we drop the gesture.
private static final long PANIC_GESTURE_EXPIRATION = 30000;
+ private static final String DEPRECATED_THIRD_PARTY_KEYGUARD_PERMISSION =
+ "android.permission.THIRD_PARTY_KEYGUARD";
+
/**
* Keyguard stuff
*/
@@ -254,6 +312,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
/** Amount of time (in milliseconds) to wait for windows drawn before powering on. */
static final int WAITING_FOR_DRAWN_TIMEOUT = 1000;
+ private DeviceKeyHandler mDeviceKeyHandler;
+
/**
* Lock protecting internal state. Must not call out into window
* manager with lock held. (This lock will be acquired in places
@@ -313,10 +373,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mCanHideNavigationBar = false;
boolean mNavigationBarCanMove = false; // can the navigation bar ever move to the side?
boolean mNavigationBarOnBottom = true; // is the navigation bar on the bottom *right now*?
+ boolean mNavigationBarLeftInLandscape = false; // Navigation bar left handed?
int[] mNavigationBarHeightForRotation = new int[4];
int[] mNavigationBarWidthForRotation = new int[4];
- boolean mBootMessageNeedsHiding;
+ WindowState mKeyguardPanel;
+
KeyguardServiceDelegate mKeyguardDelegate;
final Runnable mWindowManagerDrawCallback = new Runnable() {
@Override
@@ -330,6 +392,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public void onDrawn() {
if (DEBUG_WAKEUP) Slog.d(TAG, "mKeyguardDelegate.ShowListener.onDrawn.");
mHandler.sendEmptyMessage(MSG_KEYGUARD_DRAWN_COMPLETE);
+ hideBootMessages();
}
};
@@ -367,6 +430,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mUiMode;
int mDockMode = Intent.EXTRA_DOCK_STATE_UNDOCKED;
int mLidOpenRotation;
+ boolean mHasRemovableLid;
int mCarDockRotation;
int mDeskDockRotation;
int mUndockedHdmiRotation;
@@ -384,6 +448,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE;
int mUserRotation = Surface.ROTATION_0;
+ int mUserRotationAngles = -1;
boolean mAccelerometerDefault;
boolean mSupportAutoRotation;
@@ -409,6 +474,28 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mHasSoftInput = false;
boolean mTranslucentDecorEnabled = true;
boolean mUseTvRouting;
+ int mBackKillTimeout;
+
+ int mDeviceHardwareKeys;
+
+ // Button wake control flags
+ boolean mHomeWakeScreen;
+ boolean mBackWakeScreen;
+ boolean mMenuWakeScreen;
+ boolean mAssistWakeScreen;
+ boolean mAppSwitchWakeScreen;
+ boolean mCameraWakeScreen;
+ boolean mVolumeWakeScreen;
+
+ // Camera button control flags and actions
+ boolean mCameraSleepOnRelease;
+ boolean mIsFocusPressed;
+ boolean mCameraLaunch;
+
+ // During wakeup by volume keys, we still need to capture subsequent events
+ // until the key is released. This is required since the beep sound is produced
+ // post keypressed.
+ boolean mVolumeWakeTriggered;
int mPointerLocationMode = 0; // guarded by mLock
@@ -416,6 +503,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
WindowState mFocusedWindow;
IApplicationToken mFocusedApp;
+ // Behavior of volbtn music controls
+ boolean mVolBtnMusicControls;
+ boolean mIsLongPress;
+
PointerLocationView mPointerLocationView;
// The current size of the screen; really; extends into the overscan area of
@@ -443,6 +534,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// fullscreen window flag, these are the stable dimensions without the status bar.
int mStableFullscreenLeft, mStableFullscreenTop;
int mStableFullscreenRight, mStableFullscreenBottom;
+ // For force immersive mode
+ int mForceImmersiveLeft, mForceImmersiveTop;
+ int mForceImmersiveRight, mForceImmersiveBottom;
// During layout, the current screen borders with all outer decoration
// (status bar, input method dock) accounted for.
int mCurLeft, mCurTop, mCurRight, mCurBottom;
@@ -500,6 +594,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mForcingShowNavBar;
int mForcingShowNavBarLayer;
+ boolean mDevForceNavbar = false;
+
// States of keyguard dismiss.
private static final int DISMISS_KEYGUARD_NONE = 0; // Keyguard not being dismissed.
private static final int DISMISS_KEYGUARD_START = 1; // Keyguard needs to be dismissed.
@@ -534,6 +630,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mHomePressed;
boolean mHomeConsumed;
boolean mHomeDoubleTapPending;
+ boolean mMenuPressed;
+ boolean mAppSwitchLongPressed;
Intent mHomeIntent;
Intent mCarDockIntent;
Intent mDeskDockIntent;
@@ -542,6 +640,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
boolean mAssistKeyLongPressed;
boolean mPendingMetaAction;
+ // Tracks user-customisable behavior for certain key events
+ private int mLongPressOnHomeBehavior = -1;
+ private int mPressOnMenuBehavior = -1;
+ private int mLongPressOnMenuBehavior = -1;
+ private int mPressOnAssistBehavior = -1;
+ private int mLongPressOnAssistBehavior = -1;
+ private int mPressOnAppSwitchBehavior = -1;
+ private int mLongPressOnAppSwitchBehavior = -1;
+
// support for activating the lock screen while the screen is on
boolean mAllowLockscreenWhenOn;
int mLockScreenTimeout;
@@ -554,6 +661,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// (See Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR.)
int mIncallPowerBehavior;
+ // Behavior of HOME button during incomming call ring.
+ // (See Settings.Secure.RING_HOME_BUTTON_BEHAVIOR.)
+ int mRingHomeBehavior;
+
Display mDisplay;
private int mDisplayRotation;
@@ -568,9 +679,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
int mOverscanRight = 0;
int mOverscanBottom = 0;
- // What we do when the user long presses on home
- private int mLongPressOnHomeBehavior;
-
// What we do when the user double-taps on home
private int mDoubleTapOnHomeBehavior;
@@ -637,6 +745,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private static final int MSG_POWER_DELAYED_PRESS = 13;
private static final int MSG_POWER_LONG_PRESS = 14;
private static final int MSG_UPDATE_DREAMING_SLEEP_TOKEN = 15;
+ private static final int MSG_DISPATCH_VOLKEY_WITH_WAKE_LOCK = 16;
+ private static final int MSG_CAMERA_LONG_PRESS = 17;
+
+ boolean mWifiDisplayConnected = false;
+ int mWifiDisplayCustomRotation = -1;
+
+ private boolean mHasPermanentMenuKey;
+ private boolean mClearedBecauseOfForceShow;
+ private boolean mTopWindowIsKeyguard;
+ private CMHardwareManager mCMHardware;
+ private boolean mShowKeyguardOnLeftSwipe;
private class PolicyHandler extends Handler {
@Override
@@ -688,6 +807,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case MSG_UPDATE_DREAMING_SLEEP_TOKEN:
updateDreamingSleepToken(msg.arg1 != 0);
break;
+ case MSG_DISPATCH_VOLKEY_WITH_WAKE_LOCK: {
+ KeyEvent event = (KeyEvent) msg.obj;
+ mIsLongPress = true;
+ dispatchMediaKeyWithWakeLockToAudioService(event);
+ dispatchMediaKeyWithWakeLockToAudioService(
+ KeyEvent.changeAction(event, KeyEvent.ACTION_UP));
+ break;
+ }
+ case MSG_CAMERA_LONG_PRESS: {
+ KeyEvent event = (KeyEvent) msg.obj;
+ mIsLongPress = true;
+ break;
+ }
}
}
}
@@ -713,6 +845,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR), false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.Secure.getUriFor(
+ CMSettings.Secure.RING_HOME_BUTTON_BEHAVIOR), false, this,
+ UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.WAKE_GESTURE_ENABLED), false, this,
UserHandle.USER_ALL);
@@ -728,6 +863,30 @@ public class PhoneWindowManager implements WindowManagerPolicy {
resolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.POINTER_LOCATION), false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.KEY_HOME_LONG_PRESS_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.KEY_HOME_DOUBLE_TAP_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.KEY_MENU_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.KEY_MENU_LONG_PRESS_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.KEY_ASSIST_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.KEY_ASSIST_LONG_PRESS_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.KEY_APP_SWITCH_ACTION), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.KEY_APP_SWITCH_LONG_PRESS_ACTION), false, this,
+ UserHandle.USER_ALL);
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.DEFAULT_INPUT_METHOD), false, this,
UserHandle.USER_ALL);
@@ -737,6 +896,48 @@ public class PhoneWindowManager implements WindowManagerPolicy {
resolver.registerContentObserver(Settings.Global.getUriFor(
Settings.Global.POLICY_CONTROL), false, this,
UserHandle.USER_ALL);
+ resolver.registerContentObserver(Settings.System.getUriFor(
+ Settings.System.ACCELEROMETER_ROTATION_ANGLES), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.Global.getUriFor(
+ CMSettings.Global.DEV_FORCE_SHOW_NAVBAR), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.VOLBTN_MUSIC_CONTROLS), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.BACK_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.MENU_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.ASSIST_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.APP_SWITCH_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.CAMERA_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.CAMERA_SLEEP_ON_RELEASE), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.CAMERA_LAUNCH), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.VOLUME_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.HOME_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.VOLUME_WAKE_SCREEN), false, this,
+ UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.NAVBAR_LEFT_IN_LANDSCAPE), false, this,
+ UserHandle.USER_ALL);
updateSettings();
}
@@ -906,7 +1107,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// taken over the whole screen.
boolean panic = mImmersiveModeConfirmation.onPowerKeyDown(interactive,
SystemClock.elapsedRealtime(), isImmersiveMode(mLastSystemUiFlags));
- if (panic) {
+ if (panic && !WindowManagerPolicyControl.isImmersiveFiltersActive()) {
mHandler.post(mHiddenNavPanic);
}
@@ -1211,6 +1412,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mHandler.sendEmptyMessage(MSG_DISPATCH_SHOW_GLOBAL_ACTIONS);
}
+ Runnable mBackLongPress = new Runnable() {
+ public void run() {
+ if (!unpinActivity(false) && ActionUtils.killForegroundApp(mContext, mCurrentUserId)) {
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ Toast.makeText(mContext, R.string.app_killed_message, Toast.LENGTH_SHORT).show();
+ }
+ }
+ };
+
void showGlobalActionsInternal() {
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
if (mGlobalActions == null) {
@@ -1231,8 +1441,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
boolean isUserSetupComplete() {
- return Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+ return CMSettings.Secure.getIntForUser(mContext.getContentResolver(),
+ CMSettings.Secure.CM_SETUP_WIZARD_COMPLETED, 0, UserHandle.USER_CURRENT) != 0;
}
private void handleShortPressOnHome() {
@@ -1290,26 +1500,57 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
- private void handleLongPressOnHome(int deviceId) {
- if (mLongPressOnHomeBehavior != LONG_PRESS_HOME_NOTHING) {
- mHomeConsumed = true;
- performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ private void triggerVirtualKeypress(final int keyCode) {
+ InputManager im = InputManager.getInstance();
+ long now = SystemClock.uptimeMillis();
+ final KeyEvent downEvent = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
+ keyCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
+ KeyEvent.FLAG_FROM_SYSTEM, InputDevice.SOURCE_KEYBOARD);
+ final KeyEvent upEvent = KeyEvent.changeAction(downEvent, KeyEvent.ACTION_UP);
- if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI) {
- toggleRecentApps();
- } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_ASSIST) {
- launchAssistAction(null, deviceId);
- }
- }
+ im.injectInputEvent(downEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
+ im.injectInputEvent(upEvent, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
}
- private void handleDoubleTapOnHome() {
- if (mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
- mHomeConsumed = true;
- toggleRecentApps();
- }
+ private void launchCameraAction() {
+ sendCloseSystemWindows();
+ Intent intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+ startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
}
+ private void performKeyAction(int behavior, KeyEvent event) {
+ switch (behavior) {
+ case KEY_ACTION_NOTHING:
+ break;
+ case KEY_ACTION_MENU:
+ triggerVirtualKeypress(KeyEvent.KEYCODE_MENU);
+ break;
+ case KEY_ACTION_APP_SWITCH:
+ toggleRecentApps();
+ break;
+ case KEY_ACTION_SEARCH:
+ launchAssistAction(null, event.getDeviceId());
+ break;
+ case KEY_ACTION_VOICE_SEARCH:
+ launchAssistLongPressAction();
+ break;
+ case KEY_ACTION_IN_APP_SEARCH:
+ triggerVirtualKeypress(KeyEvent.KEYCODE_SEARCH);
+ break;
+ case KEY_ACTION_LAUNCH_CAMERA:
+ launchCameraAction();
+ break;
+ case KEY_ACTION_SLEEP:
+ mPowerManager.goToSleep(SystemClock.uptimeMillis());
+ break;
+ case KEY_ACTION_LAST_APP:
+ ActionUtils.switchToLastApp(mContext, mCurrentUserId);
+ break;
+ default:
+ break;
+ }
+ }
+
private final Runnable mHomeDoubleTapTimeoutRunnable = new Runnable() {
@Override
public void run() {
@@ -1336,6 +1577,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mDreamManagerInternal = LocalServices.getService(DreamManagerInternal.class);
mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+ mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
// Init display burn-in protection
boolean burnInProtectionEnabled = context.getResources().getBoolean(
@@ -1379,7 +1621,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mOrientationListener.setCurrentRotation(windowManager.getRotation());
} catch (RemoteException ex) { }
mSettingsObserver = new SettingsObserver(mHandler);
- mSettingsObserver.observe();
mShortcutManager = new ShortcutManager(context);
mUiMode = context.getResources().getInteger(
com.android.internal.R.integer.config_defaultUiModeType);
@@ -1460,7 +1701,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mUseTvRouting = AudioSystem.getPlatformType(mContext) == AudioSystem.PLATFORM_TELEVISION;
- readConfigurationDependentBehaviors();
+ mDeviceHardwareKeys = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_deviceHardwareKeys);
+ mHasRemovableLid = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_hasRemovableLid);
+ mBackKillTimeout = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_backKillTimeout);
+
+ updateKeyAssignments();
mAccessibilityManager = (AccessibilityManager) context.getSystemService(
Context.ACCESSIBILITY_SERVICE);
@@ -1506,9 +1754,22 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
@Override
public void onSwipeFromRight() {
- if (mNavigationBar != null && !mNavigationBarOnBottom) {
+ if (mNavigationBar != null && !mNavigationBarOnBottom &&
+ !mNavigationBarLeftInLandscape) {
+ requestTransientBars(mNavigationBar);
+ }
+ }
+ @Override
+ public void onSwipeFromLeft() {
+ if (mNavigationBar != null && !mNavigationBarOnBottom &&
+ mNavigationBarLeftInLandscape) {
requestTransientBars(mNavigationBar);
}
+ if (mShowKeyguardOnLeftSwipe && isKeyguardShowingOrOccluded()
+ && mKeyguardDelegate.isKeyguardPanelFocused()) {
+ // Show keyguard
+ mKeyguardDelegate.showKeyguard();
+ }
}
@Override
public void onFling(int duration) {
@@ -1534,6 +1795,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWindowManagerFuncs.registerPointerEventListener(mSystemGestures);
mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
+
+ /* Register for WIFI Display Intents */
+ IntentFilter wifiDisplayFilter = new IntentFilter(ACTION_WIFI_DISPLAY_VIDEO);
+ Intent wifidisplayIntent = context.registerReceiver(
+ mWifiDisplayReceiver, wifiDisplayFilter);
mLongPressVibePattern = getLongIntArray(mContext.getResources(),
com.android.internal.R.array.config_longPressVibePattern);
mVirtualKeyVibePattern = getLongIntArray(mContext.getResources(),
@@ -1567,27 +1833,125 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mWindowManagerInternal.registerAppTransitionListener(
mStatusBarController.getAppTransitionListener());
+
+ String deviceKeyHandlerLib = mContext.getResources().getString(
+ com.android.internal.R.string.config_deviceKeyHandlerLib);
+
+ String deviceKeyHandlerClass = mContext.getResources().getString(
+ com.android.internal.R.string.config_deviceKeyHandlerClass);
+
+ if (!deviceKeyHandlerLib.isEmpty() && !deviceKeyHandlerClass.isEmpty()) {
+ DexClassLoader loader = new DexClassLoader(deviceKeyHandlerLib,
+ new ContextWrapper(mContext).getCacheDir().getAbsolutePath(),
+ null,
+ ClassLoader.getSystemClassLoader());
+ try {
+ Class<?> klass = loader.loadClass(deviceKeyHandlerClass);
+ Constructor<?> constructor = klass.getConstructor(Context.class);
+ mDeviceKeyHandler = (DeviceKeyHandler) constructor.newInstance(
+ mContext);
+ if(DEBUG) Slog.d(TAG, "Device key handler loaded");
+ } catch (Exception e) {
+ Slog.w(TAG, "Could not instantiate device key handler "
+ + deviceKeyHandlerClass + " from class "
+ + deviceKeyHandlerLib, e);
+ }
+ }
+
}
- /**
- * Read values from config.xml that may be overridden depending on
- * the configuration of the device.
- * eg. Disable long press on home goes to recents on sw600dp.
- */
- private void readConfigurationDependentBehaviors() {
+ private void updateKeyAssignments() {
+ int activeHardwareKeys = mDeviceHardwareKeys;
+
+ if (mDevForceNavbar) {
+ activeHardwareKeys = 0;
+ }
+ final boolean hasMenu = (activeHardwareKeys & KEY_MASK_MENU) != 0;
+ final boolean hasHome = (activeHardwareKeys & KEY_MASK_HOME) != 0;
+ final boolean hasAssist = (activeHardwareKeys & KEY_MASK_ASSIST) != 0;
+ final boolean hasAppSwitch = (activeHardwareKeys & KEY_MASK_APP_SWITCH) != 0;
+
+ final ContentResolver resolver = mContext.getContentResolver();
+
+ // Initialize all assignments to sane defaults.
+ mPressOnMenuBehavior = KEY_ACTION_MENU;
+
+ mLongPressOnMenuBehavior = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longPressOnMenuBehavior);
+
+ if (mLongPressOnMenuBehavior == KEY_ACTION_NOTHING &&
+ (hasMenu && !hasAssist)) {
+ mLongPressOnMenuBehavior = KEY_ACTION_SEARCH;
+ }
+ mPressOnAssistBehavior = KEY_ACTION_SEARCH;
+ mLongPressOnAssistBehavior = KEY_ACTION_VOICE_SEARCH;
+ mPressOnAppSwitchBehavior = KEY_ACTION_APP_SWITCH;
+ mLongPressOnAppSwitchBehavior = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_longPressOnAppSwitchBehavior);
+
mLongPressOnHomeBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnHomeBehavior);
- if (mLongPressOnHomeBehavior < LONG_PRESS_HOME_NOTHING ||
- mLongPressOnHomeBehavior > LONG_PRESS_HOME_ASSIST) {
- mLongPressOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
+ if (mLongPressOnHomeBehavior < KEY_ACTION_NOTHING ||
+ mLongPressOnHomeBehavior > KEY_ACTION_SLEEP) {
+ mLongPressOnHomeBehavior = KEY_ACTION_NOTHING;
}
mDoubleTapOnHomeBehavior = mContext.getResources().getInteger(
com.android.internal.R.integer.config_doubleTapOnHomeBehavior);
- if (mDoubleTapOnHomeBehavior < DOUBLE_TAP_HOME_NOTHING ||
- mDoubleTapOnHomeBehavior > DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
- mDoubleTapOnHomeBehavior = LONG_PRESS_HOME_NOTHING;
+ if (mDoubleTapOnHomeBehavior < KEY_ACTION_NOTHING ||
+ mDoubleTapOnHomeBehavior > KEY_ACTION_SLEEP) {
+ mDoubleTapOnHomeBehavior = KEY_ACTION_NOTHING;
}
+
+ boolean hasPermanentMenu = false;
+
+ // Check for custom assignments and whether KEY_ACTION_MENU is assigned.
+ if (hasHome) {
+ mLongPressOnHomeBehavior = CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.KEY_HOME_LONG_PRESS_ACTION,
+ mLongPressOnHomeBehavior, UserHandle.USER_CURRENT);
+ mDoubleTapOnHomeBehavior = CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.KEY_HOME_DOUBLE_TAP_ACTION,
+ mDoubleTapOnHomeBehavior, UserHandle.USER_CURRENT);
+
+ hasPermanentMenu = mLongPressOnHomeBehavior == KEY_ACTION_MENU
+ || mDoubleTapOnHomeBehavior == KEY_ACTION_MENU;
+ }
+ if (hasMenu) {
+ mPressOnMenuBehavior = CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.KEY_MENU_ACTION,
+ mPressOnMenuBehavior, UserHandle.USER_CURRENT);
+ mLongPressOnMenuBehavior = CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.KEY_MENU_LONG_PRESS_ACTION,
+ mLongPressOnMenuBehavior, UserHandle.USER_CURRENT);
+
+ hasPermanentMenu |= mPressOnMenuBehavior == KEY_ACTION_MENU
+ || mLongPressOnMenuBehavior == KEY_ACTION_MENU;
+ }
+ if (hasAssist) {
+ mPressOnAssistBehavior = CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.KEY_ASSIST_ACTION,
+ mPressOnAssistBehavior, UserHandle.USER_CURRENT);
+ mLongPressOnAssistBehavior = CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.KEY_ASSIST_LONG_PRESS_ACTION,
+ mLongPressOnAssistBehavior, UserHandle.USER_CURRENT);
+
+ hasPermanentMenu |= mPressOnAssistBehavior == KEY_ACTION_MENU
+ || mLongPressOnAssistBehavior == KEY_ACTION_MENU;
+ }
+ if (hasAppSwitch) {
+ mPressOnAppSwitchBehavior = CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.KEY_APP_SWITCH_ACTION,
+ mPressOnAppSwitchBehavior, UserHandle.USER_CURRENT);
+ mLongPressOnAppSwitchBehavior = CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.KEY_APP_SWITCH_LONG_PRESS_ACTION,
+ mLongPressOnAppSwitchBehavior, UserHandle.USER_CURRENT);
+
+ hasPermanentMenu |= mPressOnAppSwitchBehavior == KEY_ACTION_MENU
+ || mLongPressOnAppSwitchBehavior == KEY_ACTION_MENU;
+ }
+
+ mHasPermanentMenuKey = hasPermanentMenu;
}
@Override
@@ -1696,7 +2060,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
* navigation bar and touch exploration is not enabled
*/
private boolean canHideNavigationBar() {
- return mHasNavigationBar
+ return hasNavigationBar()
&& !mAccessibilityManager.isTouchExplorationEnabled();
}
@@ -1718,6 +2082,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
public void updateSettings() {
ContentResolver resolver = mContext.getContentResolver();
boolean updateRotation = false;
+ int mDeviceHardwareWakeKeys = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_deviceHardwareWakeKeys);
synchronized (mLock) {
mEndcallBehavior = Settings.System.getIntForUser(resolver,
Settings.System.END_BUTTON_BEHAVIOR,
@@ -1727,6 +2093,37 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR,
Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_DEFAULT,
UserHandle.USER_CURRENT);
+ mRingHomeBehavior = CMSettings.Secure.getIntForUser(resolver,
+ CMSettings.Secure.RING_HOME_BUTTON_BEHAVIOR,
+ CMSettings.Secure.RING_HOME_BUTTON_BEHAVIOR_DEFAULT,
+ UserHandle.USER_CURRENT);
+ mHomeWakeScreen = (CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.HOME_WAKE_SCREEN, 1, UserHandle.USER_CURRENT) == 1) &&
+ ((mDeviceHardwareWakeKeys & KEY_MASK_HOME) != 0);
+ mBackWakeScreen = (CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.BACK_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1) &&
+ ((mDeviceHardwareWakeKeys & KEY_MASK_BACK) != 0);
+ mMenuWakeScreen = (CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.MENU_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1) &&
+ ((mDeviceHardwareWakeKeys & KEY_MASK_MENU) != 0);
+ mAssistWakeScreen = (CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.ASSIST_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1) &&
+ ((mDeviceHardwareWakeKeys & KEY_MASK_ASSIST) != 0);
+ mAppSwitchWakeScreen = (CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.APP_SWITCH_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1) &&
+ ((mDeviceHardwareWakeKeys & KEY_MASK_APP_SWITCH) != 0);
+ mCameraWakeScreen = (CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.CAMERA_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1) &&
+ ((mDeviceHardwareWakeKeys & KEY_MASK_CAMERA) != 0);
+ mCameraSleepOnRelease = (CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.CAMERA_SLEEP_ON_RELEASE, 0, UserHandle.USER_CURRENT) == 1);
+ mCameraLaunch = (CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.CAMERA_LAUNCH, 0, UserHandle.USER_CURRENT) == 1);
+ mVolumeWakeScreen = (CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.VOLUME_WAKE_SCREEN, 0, UserHandle.USER_CURRENT) == 1) &&
+ ((mDeviceHardwareWakeKeys & KEY_MASK_VOLUME) != 0);
+ mVolBtnMusicControls = (CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.VOLBTN_MUSIC_CONTROLS, 1, UserHandle.USER_CURRENT) == 1);
// Configure wake gesture.
boolean wakeGestureEnabledSetting = Settings.Secure.getIntForUser(resolver,
@@ -1737,6 +2134,20 @@ public class PhoneWindowManager implements WindowManagerPolicy {
updateWakeGestureListenerLp();
}
+ boolean devForceNavbar = CMSettings.Global.getIntForUser(resolver,
+ CMSettings.Global.DEV_FORCE_SHOW_NAVBAR, 0, UserHandle.USER_CURRENT) == 1;
+ if (devForceNavbar != mDevForceNavbar) {
+ mDevForceNavbar = devForceNavbar;
+ if (mCMHardware.isSupported(CMHardwareManager.FEATURE_KEY_DISABLE)) {
+ mCMHardware.set(CMHardwareManager.FEATURE_KEY_DISABLE, mDevForceNavbar);
+ }
+ }
+
+ mNavigationBarLeftInLandscape = CMSettings.System.getIntForUser(resolver,
+ CMSettings.System.NAVBAR_LEFT_IN_LANDSCAPE, 0, UserHandle.USER_CURRENT) == 1;
+
+ updateKeyAssignments();
+
// Configure rotation lock.
int userRotation = Settings.System.getIntForUser(resolver,
Settings.System.USER_ROTATION, Surface.ROTATION_0,
@@ -1755,6 +2166,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
updateOrientationListenerLp();
}
+ mUserRotationAngles = Settings.System.getInt(resolver,
+ Settings.System.ACCELEROMETER_ROTATION_ANGLES, -1);
+
if (mSystemReady) {
int pointerLocation = Settings.System.getIntForUser(resolver,
Settings.System.POINTER_LOCATION, 0, UserHandle.USER_CURRENT);
@@ -1779,7 +2193,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
synchronized (mWindowManagerFuncs.getWindowManagerLock()) {
- PolicyControl.reloadFromSetting(mContext);
+ WindowManagerPolicyControl.reloadFromSetting(mContext);
}
if (updateRotation) {
updateRotation(true);
@@ -1896,6 +2310,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
permission = android.Manifest.permission.SYSTEM_ALERT_WINDOW;
outAppOp[0] = AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
break;
+ case TYPE_KEYGUARD_PANEL:
+ permission = THIRD_PARTY_KEYGUARD;
+ break;
default:
permission = android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
}
@@ -1929,6 +2346,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return WindowManagerGlobal.ADD_OKAY;
}
}
+ } else if (permission == THIRD_PARTY_KEYGUARD) {
+ // check if caller has the old permission and if so allow adding window
+ if (mContext.checkCallingOrSelfPermission(
+ DEPRECATED_THIRD_PARTY_KEYGUARD_PERMISSION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return WindowManagerGlobal.ADD_OKAY;
+ }
+ // fall through to the normal check below
}
if (mContext.checkCallingOrSelfPermission(permission)
@@ -1977,6 +2402,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case TYPE_SYSTEM_DIALOG:
case TYPE_VOLUME_OVERLAY:
case TYPE_PRIVATE_PRESENTATION:
+ case TYPE_KEYGUARD_PANEL:
break;
}
@@ -2019,6 +2445,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
attrs.subtreeSystemUiVisibility |= View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
}
+
+ if ((attrs.privateFlags & (WindowManager.LayoutParams.PRIVATE_FLAG_PREVENT_SYSTEM_KEYS |
+ WindowManager.LayoutParams.PRIVATE_FLAG_PREVENT_POWER_KEY)) != 0) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.PREVENT_SYSTEM_KEYS,
+ "No permission to prevent system key");
+ }
}
void readLidState() {
@@ -2040,13 +2472,16 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
+ private boolean isBuiltInKeyboardVisible() {
+ return mHaveBuiltInKeyboard && !isHidden(mLidKeyboardAccessibility);
+ }
+
/** {@inheritDoc} */
@Override
public void adjustConfigurationLw(Configuration config, int keyboardPresence,
int navigationPresence) {
mHaveBuiltInKeyboard = (keyboardPresence & PRESENCE_INTERNAL) != 0;
- readConfigurationDependentBehaviors();
readLidState();
applyLidSwitchState();
@@ -2112,6 +2547,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// the safety window that shows behind keyguard while keyguard is starting
return 14;
case TYPE_STATUS_BAR_SUB_PANEL:
+ case TYPE_KEYGUARD_PANEL:
return 15;
case TYPE_STATUS_BAR:
return 16;
@@ -2188,7 +2624,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public int getNonDecorDisplayWidth(int fullWidth, int fullHeight, int rotation) {
- if (mHasNavigationBar) {
+ if (hasNavigationBar()) {
// For a basic navigation bar, when we are in landscape mode we place
// the navigation bar to the side.
if (mNavigationBarCanMove && fullWidth > fullHeight) {
@@ -2200,7 +2636,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public int getNonDecorDisplayHeight(int fullWidth, int fullHeight, int rotation) {
- if (mHasNavigationBar) {
+ if (hasNavigationBar()) {
// For a basic navigation bar, when we are in portrait mode we place
// the navigation bar to the bottom.
if (!mNavigationBarCanMove || fullWidth < fullHeight) {
@@ -2258,6 +2694,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return mWinShowWhenLocked;
}
+ @Override
+ public WindowState getWinKeyguardPanelLw() {
+ return mKeyguardPanel;
+ }
+
/** {@inheritDoc} */
@Override
public View addStartingWindow(IBinder appToken, String packageName, int theme,
@@ -2452,6 +2893,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
android.Manifest.permission.STATUS_BAR_SERVICE,
"PhoneWindowManager");
break;
+ case TYPE_KEYGUARD_PANEL:
+ // check deprecated perm first and if not granted enforce the new permission name
+ if (mContext.checkCallingOrSelfPermission(
+ DEPRECATED_THIRD_PARTY_KEYGUARD_PERMISSION)
+ != PackageManager.PERMISSION_GRANTED) {
+ mContext.enforceCallingOrSelfPermission(THIRD_PARTY_KEYGUARD,
+ "PhoneWindowManager");
+ }
+ if (mKeyguardPanel != null) {
+ return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
+ }
+ mKeyguardPanel = win;
+ break;
case TYPE_KEYGUARD_SCRIM:
if (mKeyguardScrim != null) {
return WindowManagerGlobal.ADD_MULTIPLE_SINGLETON;
@@ -2472,9 +2926,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else if (mKeyguardScrim == win) {
Log.v(TAG, "Removing keyguard scrim");
mKeyguardScrim = null;
- } if (mNavigationBar == win) {
+ } else if (mNavigationBar == win) {
mNavigationBar = null;
mNavigationBarController.setWindow(null);
+ } else if (mKeyguardPanel == win) {
+ mKeyguardPanel = null;
}
}
@@ -2498,7 +2954,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (win.getAttrs().windowAnimations != 0) {
return 0;
}
- // This can be on either the bottom or the right.
+ // This can be on either the bottom, left, or the right.
if (mNavigationBarOnBottom) {
if (transit == TRANSIT_EXIT
|| transit == TRANSIT_HIDE) {
@@ -2510,10 +2966,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else {
if (transit == TRANSIT_EXIT
|| transit == TRANSIT_HIDE) {
- return R.anim.dock_right_exit;
+ return mNavigationBarLeftInLandscape
+ ? R.anim.dock_left_exit : R.anim.dock_right_exit;
} else if (transit == TRANSIT_ENTER
|| transit == TRANSIT_SHOW) {
- return R.anim.dock_right_enter;
+ return mNavigationBarLeftInLandscape
+ ? R.anim.dock_left_enter : R.anim.dock_right_enter;
}
}
}
@@ -2601,9 +3059,12 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
- public Animation createForceHideWallpaperExitAnimation(boolean goingToNotificationShade) {
+ public Animation createForceHideWallpaperExitAnimation(boolean goingToNotificationShade,
+ boolean keyguardShowingMedia) {
if (goingToNotificationShade) {
return null;
+ } else if (keyguardShowingMedia) {
+ return AnimationUtils.loadAnimation(mContext, R.anim.lock_screen_wallpaper_exit_noop);
} else {
return AnimationUtils.loadAnimation(mContext, R.anim.lock_screen_wallpaper_exit);
}
@@ -2651,12 +3112,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {
final boolean keyguardOn = keyguardOn();
- final int keyCode = event.getKeyCode();
final int repeatCount = event.getRepeatCount();
final int metaState = event.getMetaState();
final int flags = event.getFlags();
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final boolean canceled = event.isCanceled();
+ final boolean longPress = (flags & KeyEvent.FLAG_LONG_PRESS) != 0;
+ final boolean virtualKey = event.getDeviceId() == KeyCharacterMap.VIRTUAL_KEYBOARD;
+ final int keyCode = event.getKeyCode();
if (DEBUG_INPUT) {
Log.d(TAG, "interceptKeyTi keyCode=" + keyCode + " down=" + down + " repeatCount="
@@ -2691,16 +3154,28 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mPendingMetaAction = false;
}
+ if (keyCode == KeyEvent.KEYCODE_BACK && !down) {
+ mHandler.removeCallbacks(mBackLongPress);
+ }
+
// First we always handle the home key here, so applications
// can never break it, although if keyguard is on, we do let
// it handle it, because that gives us the correct 5 second
// timeout.
if (keyCode == KeyEvent.KEYCODE_HOME) {
+ if (mTopFullscreenOpaqueWindowState != null &&
+ (mTopFullscreenOpaqueWindowState.getAttrs().privateFlags
+ & WindowManager.LayoutParams.PRIVATE_FLAG_PREVENT_SYSTEM_KEYS) != 0
+ && mScreenOnFully) {
+ return 0;
+ }
// If we have released the home key, and didn't do anything else
// while it was pressed, then it is time to go home!
if (!down) {
- cancelPreloadRecentApps();
+ if (mDoubleTapOnHomeBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
mHomePressed = false;
if (mHomeConsumed) {
@@ -2718,12 +3193,19 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// and his ONLY options are to answer or reject the call.)
TelecomManager telecomManager = getTelecommService();
if (telecomManager != null && telecomManager.isRinging()) {
- Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
- return -1;
+ if ((mRingHomeBehavior
+ & CMSettings.Secure.RING_HOME_BUTTON_BEHAVIOR_ANSWER) != 0) {
+ Log.i(TAG, "Answering with HOME button.");
+ telecomManager.acceptRingingCall();
+ return -1;
+ } else {
+ Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
+ return -1;
+ }
}
// Delay handling home if a double-tap is possible.
- if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {
+ if (mDoubleTapOnHomeBehavior != KEY_ACTION_NOTHING) {
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in case
mHomeDoubleTapPending = true;
mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,
@@ -2761,44 +3243,90 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mHomeDoubleTapPending) {
mHomeDoubleTapPending = false;
mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);
- handleDoubleTapOnHome();
- } else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI
- || mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {
+ performKeyAction(mDoubleTapOnHomeBehavior, event);
+ mHomeConsumed = mDoubleTapOnHomeBehavior != KEY_ACTION_SLEEP;
+ } else if (mLongPressOnHomeBehavior == KEY_ACTION_APP_SWITCH
+ || mDoubleTapOnHomeBehavior == KEY_ACTION_APP_SWITCH) {
preloadRecentApps();
}
- } else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
- if (!keyguardOn) {
- handleLongPressOnHome(event.getDeviceId());
+ } else if (longPress) {
+ if (!keyguardOn && !mHomeConsumed &&
+ mLongPressOnHomeBehavior != KEY_ACTION_NOTHING) {
+ if (mLongPressOnHomeBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ mHomePressed = true;
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ performKeyAction(mLongPressOnHomeBehavior, event);
+ mHomeConsumed = true;
}
}
return -1;
} else if (keyCode == KeyEvent.KEYCODE_MENU) {
// Hijack modified menu keys for debugging features
final int chordBug = KeyEvent.META_SHIFT_ON;
+ if (virtualKey || keyguardOn) {
+ // Let the app handle the key
+ return 0;
+ }
- if (down && repeatCount == 0) {
- if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) {
- Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
- mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT,
- null, null, null, 0, null, null);
- return -1;
- } else if (SHOW_PROCESSES_ON_ALT_MENU &&
- (metaState & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) {
- Intent service = new Intent();
- service.setClassName(mContext, "com.android.server.LoadAverageService");
- ContentResolver res = mContext.getContentResolver();
- boolean shown = Settings.Global.getInt(
- res, Settings.Global.SHOW_PROCESSES, 0) != 0;
- if (!shown) {
- mContext.startService(service);
- } else {
- mContext.stopService(service);
+ if (mTopFullscreenOpaqueWindowState != null &&
+ (mTopFullscreenOpaqueWindowState.getAttrs().privateFlags
+ & WindowManager.LayoutParams.PRIVATE_FLAG_PREVENT_SYSTEM_KEYS) != 0
+ && mScreenOnFully) {
+ return 0;
+ }
+
+ if (down) {
+ if (mPressOnMenuBehavior == KEY_ACTION_APP_SWITCH
+ || mLongPressOnMenuBehavior == KEY_ACTION_APP_SWITCH) {
+ preloadRecentApps();
+ }
+ if (repeatCount == 0) {
+ mMenuPressed = true;
+ if (mEnableShiftMenuBugReports && (metaState & chordBug) == chordBug) {
+ Intent intent = new Intent(Intent.ACTION_BUG_REPORT);
+ mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT,
+ null, null, null, 0, null, null);
+ return -1;
+ } else if (SHOW_PROCESSES_ON_ALT_MENU &&
+ (metaState & KeyEvent.META_ALT_ON) == KeyEvent.META_ALT_ON) {
+ Intent service = new Intent();
+ service.setClassName(mContext, "com.android.server.LoadAverageService");
+ ContentResolver res = mContext.getContentResolver();
+ boolean shown = Settings.Global.getInt(
+ res, Settings.Global.SHOW_PROCESSES, 0) != 0;
+ if (!shown) {
+ mContext.startService(service);
+ } else {
+ mContext.stopService(service);
+ }
+ Settings.Global.putInt(
+ res, Settings.Global.SHOW_PROCESSES, shown ? 0 : 1);
+ return -1;
+ }
+ } else if (longPress) {
+ if (!keyguardOn && mLongPressOnMenuBehavior != KEY_ACTION_NOTHING) {
+ if (mLongPressOnMenuBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ performKeyAction(mLongPressOnMenuBehavior, event);
+ mMenuPressed = false;
+ return -1;
}
- Settings.Global.putInt(
- res, Settings.Global.SHOW_PROCESSES, shown ? 0 : 1);
- return -1;
}
}
+ if (!down && mMenuPressed) {
+ if (mPressOnMenuBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ mMenuPressed = false;
+ if (!canceled) {
+ performKeyAction(mPressOnMenuBehavior, event);
+ }
+ }
+ return -1;
} else if (keyCode == KeyEvent.KEYCODE_SEARCH) {
if (down) {
if (repeatCount == 0) {
@@ -2814,11 +3342,42 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
return 0;
} else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
+ if (mTopFullscreenOpaqueWindowState != null &&
+ (mTopFullscreenOpaqueWindowState.getAttrs().privateFlags
+ & WindowManager.LayoutParams.PRIVATE_FLAG_PREVENT_SYSTEM_KEYS) != 0
+ && mScreenOnFully) {
+ return 0;
+ }
+
if (!keyguardOn) {
- if (down && repeatCount == 0) {
- preloadRecentApps();
- } else if (!down) {
- toggleRecentApps();
+ if (down) {
+ if (mPressOnAppSwitchBehavior == KEY_ACTION_APP_SWITCH
+ || mLongPressOnAppSwitchBehavior == KEY_ACTION_APP_SWITCH) {
+ preloadRecentApps();
+ }
+ if (repeatCount == 0) {
+ mAppSwitchLongPressed = false;
+ } else if (longPress) {
+ if (mLongPressOnAppSwitchBehavior != KEY_ACTION_NOTHING) {
+ if (mLongPressOnAppSwitchBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ performKeyAction(mLongPressOnAppSwitchBehavior, event);
+ mAppSwitchLongPressed = true;
+ }
+ }
+ } else {
+ if (mAppSwitchLongPressed) {
+ mAppSwitchLongPressed = false;
+ } else {
+ if (mPressOnAppSwitchBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ if (!canceled) {
+ performKeyAction(mPressOnAppSwitchBehavior, event);
+ }
+ }
}
}
return -1;
@@ -2835,20 +3394,31 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
} else if (keyCode == KeyEvent.KEYCODE_ASSIST) {
if (down) {
+ if (mPressOnAssistBehavior == KEY_ACTION_APP_SWITCH
+ || mLongPressOnAssistBehavior == KEY_ACTION_APP_SWITCH) {
+ preloadRecentApps();
+ }
if (repeatCount == 0) {
mAssistKeyLongPressed = false;
- } else if (repeatCount == 1) {
- mAssistKeyLongPressed = true;
- if (!keyguardOn) {
- launchAssistLongPressAction();
+ } else if (longPress) {
+ if (!keyguardOn && mLongPressOnAssistBehavior != KEY_ACTION_NOTHING) {
+ if (mLongPressOnAssistBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
+ performKeyAction(mLongPressOnAssistBehavior, event);
+ mAssistKeyLongPressed = true;
}
}
} else {
if (mAssistKeyLongPressed) {
mAssistKeyLongPressed = false;
} else {
- if (!keyguardOn) {
- launchAssistAction(null, event.getDeviceId());
+ if (mPressOnAssistBehavior != KEY_ACTION_APP_SWITCH) {
+ cancelPreloadRecentApps();
+ }
+ if (!canceled) {
+ performKeyAction(mPressOnAssistBehavior, event);
}
}
}
@@ -2921,6 +3491,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD, event.getDeviceId());
}
return -1;
+ } else if (keyCode == KeyEvent.KEYCODE_BACK) {
+ if (unpinActivity(true) || CMSettings.Secure.getInt(mContext.getContentResolver(),
+ CMSettings.Secure.KILL_APP_LONGPRESS_BACK, 0) == 1) {
+ if (down && repeatCount == 0) {
+ mHandler.postDelayed(mBackLongPress, mBackKillTimeout);
+ }
+ }
}
// Shortcuts are invoked through Search+key, so intercept those here
@@ -3029,6 +3606,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return -1;
}
+ // Specific device key handling
+ if (mDeviceKeyHandler != null) {
+ try {
+ // The device only should consume known keys.
+ if (mDeviceKeyHandler.handleKeyEvent(event)) {
+ return -1;
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Could not dispatch event to device key handler", e);
+ }
+ }
+
// Reserve all the META modifier combos for system behavior
if ((metaState & KeyEvent.META_META_ON) != 0) {
return -1;
@@ -3038,6 +3627,22 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return 0;
}
+ private boolean unpinActivity(boolean checkOnly) {
+ if (!hasNavigationBar()) {
+ try {
+ if (ActivityManagerNative.getDefault().isInLockTaskMode()) {
+ if (!checkOnly) {
+ ActivityManagerNative.getDefault().stopLockTaskModeOnCurrent();
+ }
+ return true;
+ }
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+ return false;
+ }
+
/** {@inheritDoc} */
@Override
public KeyEvent dispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags) {
@@ -3314,8 +3919,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
hideRecentApps(false, true);
- } else {
- // Otherwise, just launch Home
+ } else if (mScreenOnFully) {
+ // check if screen is fully on before going home
+ // to avoid hardware home button wake going home
sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);
}
@@ -3415,8 +4021,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
@Override
public void getInsetHintLw(WindowManager.LayoutParams attrs, int displayRotation,
Rect outContentInsets, Rect outStableInsets, Rect outOutsets) {
- final int fl = PolicyControl.getWindowFlags(null, attrs);
- final int sysuiVis = PolicyControl.getSystemUiVisibility(null, attrs);
+ final int fl = WindowManagerPolicyControl.getWindowFlags(null, attrs);
+ final int sysuiVis = WindowManagerPolicyControl.getSystemUiVisibility(null, attrs);
final int systemUiVisibility = (sysuiVis | attrs.subtreeSystemUiVisibility);
final boolean useOutsets = outOutsets != null && shouldUseOutsets(attrs, fl);
@@ -3535,13 +4141,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mRestrictedScreenWidth = mSystemGestures.screenWidth = mUnrestrictedScreenWidth;
mRestrictedScreenHeight = mSystemGestures.screenHeight = mUnrestrictedScreenHeight;
mDockLeft = mContentLeft = mVoiceContentLeft = mStableLeft = mStableFullscreenLeft
- = mCurLeft = mUnrestrictedScreenLeft;
+ = mCurLeft = mForceImmersiveLeft = mUnrestrictedScreenLeft;
mDockTop = mContentTop = mVoiceContentTop = mStableTop = mStableFullscreenTop
- = mCurTop = mUnrestrictedScreenTop;
+ = mCurTop = mForceImmersiveTop = mUnrestrictedScreenTop;
mDockRight = mContentRight = mVoiceContentRight = mStableRight = mStableFullscreenRight
- = mCurRight = displayWidth - overscanRight;
+ = mCurRight = mForceImmersiveRight = displayWidth - overscanRight;
mDockBottom = mContentBottom = mVoiceContentBottom = mStableBottom = mStableFullscreenBottom
- = mCurBottom = displayHeight - overscanBottom;
+ = mCurBottom = mForceImmersiveBottom = displayHeight - overscanBottom;
mDockLayer = 0x10000000;
mStatusBarLayer = -1;
@@ -3626,6 +4232,34 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// we can tell the app that it is covered by it.
mSystemBottom = mTmpNavigationFrame.top;
}
+ } else if (mNavigationBarLeftInLandscape) {
+ // Landscape screen; nav bar goes to the left.
+ int right = overscanLeft + mNavigationBarWidthForRotation[displayRotation];
+ mTmpNavigationFrame.set(0, 0, right, displayHeight);
+ mStableLeft = mStableFullscreenLeft = mTmpNavigationFrame.right;
+ mStableFullscreenLeft = mTmpNavigationFrame.right;
+ if (transientNavBarShowing) {
+ mNavigationBarController.setBarShowingLw(true);
+ } else if (navVisible) {
+ mNavigationBarController.setBarShowingLw(true);
+ mDockLeft = mTmpNavigationFrame.right;
+ mRestrictedScreenLeft = mDockLeft;
+ mRestrictedScreenWidth = mDockRight - mRestrictedScreenLeft;
+ mRestrictedOverscanScreenLeft = mRestrictedScreenLeft;
+ mRestrictedOverscanScreenWidth = mDockRight
+ - mRestrictedOverscanScreenLeft;
+ } else {
+ // We currently want to hide the navigation UI.
+ mNavigationBarController.setBarShowingLw(false);
+ }
+
+ if (navVisible && !navTranslucent && !mNavigationBar.isAnimatingLw()
+ && !mNavigationBarController.wasRecentlyTranslucent()) {
+ // If the nav bar is currently requested to be visible,
+ // and not in the process of animating on or off, then
+ // we can tell the app that it is covered by it.
+ mSystemLeft = mTmpNavigationFrame.right;
+ }
} else {
// Landscape screen; nav bar goes to the right.
int left = displayWidth - overscanRight
@@ -3817,7 +4451,25 @@ public class PhoneWindowManager implements WindowManagerPolicy {
? attached.getFrameLw() : df);
}
- private void applyStableConstraints(int sysui, int fl, Rect r) {
+ private void applyForceImmersiveMode(int pfl, Rect r) {
+ if ((pfl & PRIVATE_FLAG_STATUS_HIDE_FORCED) != 0) {
+ r.top = mForceImmersiveTop;
+ }
+ if ((pfl & PRIVATE_FLAG_NAV_HIDE_FORCED) != 0) {
+ if (mNavigationBarOnBottom) {
+ r.bottom = mForceImmersiveBottom;
+ } else {
+ r.right = mForceImmersiveRight;
+ }
+ }
+ }
+
+ private void applyStableConstraints(int sysui, int fl, Rect r, Rect d) {
+ if (mNavigationBarLeftInLandscape) {
+ d.left = r.left;
+ r.left = 0;
+ }
+
if ((sysui & View.SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0) {
// If app is requesting a stable layout, don't let the
// content insets go below the stable values.
@@ -3861,9 +4513,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
offsetInputMethodWindowLw(mLastInputMethodWindow);
}
- final int fl = PolicyControl.getWindowFlags(win, attrs);
+ final int fl = WindowManagerPolicyControl.getWindowFlags(win, attrs);
+ final int pfl = WindowManagerPolicyControl.getPrivateWindowFlags(win, attrs);
final int sim = attrs.softInputMode;
- final int sysUiFl = PolicyControl.getSystemUiVisibility(win, null);
+ final int sysUiFl = WindowManagerPolicyControl.getSystemUiVisibility(win, null);
final Rect pf = mTmpParentFrame;
final Rect df = mTmpDisplayFrame;
@@ -4044,7 +4697,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
of.bottom = mUnrestrictedScreenTop + mUnrestrictedScreenHeight;
}
- if ((fl & FLAG_FULLSCREEN) == 0) {
+ if ((fl & FLAG_FULLSCREEN) == 0
+ || (pfl & PRIVATE_FLAG_WAS_NOT_FULLSCREEN) != 0) {
if (win.isVoiceInteraction()) {
cf.left = mVoiceContentLeft;
cf.top = mVoiceContentTop;
@@ -4062,6 +4716,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
cf.right = mContentRight;
cf.bottom = mContentBottom;
}
+
+ applyForceImmersiveMode(pfl, cf);
}
} else {
// Full screen windows are always given a layout that is as if the
@@ -4073,7 +4729,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
cf.right = mRestrictedScreenLeft + mRestrictedScreenWidth;
cf.bottom = mRestrictedScreenTop + mRestrictedScreenHeight;
}
- applyStableConstraints(sysUiFl, fl, cf);
+ applyStableConstraints(sysUiFl, fl, cf, df);
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.left = mCurLeft;
vf.top = mCurTop;
@@ -4082,6 +4738,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else {
vf.set(cf);
}
+
+ applyForceImmersiveMode(pfl, vf);
}
} else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0 || (sysUiFl
& (View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@@ -4092,7 +4750,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// gets everything, period.
if (attrs.type == TYPE_STATUS_BAR_PANEL
|| attrs.type == TYPE_STATUS_BAR_SUB_PANEL
- || attrs.type == TYPE_VOLUME_OVERLAY) {
+ || attrs.type == TYPE_VOLUME_OVERLAY
+ || attrs.type == TYPE_KEYGUARD_PANEL) {
pf.left = df.left = of.left = cf.left = hasNavBar
? mDockLeft : mUnrestrictedScreenLeft;
pf.top = df.top = of.top = cf.top = mUnrestrictedScreenTop;
@@ -4186,7 +4845,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
+ mRestrictedScreenHeight;
}
- applyStableConstraints(sysUiFl, fl, cf);
+ applyStableConstraints(sysUiFl, fl, cf, df);
if (adjust != SOFT_INPUT_ADJUST_NOTHING) {
vf.left = mCurLeft;
@@ -4196,6 +4855,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else {
vf.set(cf);
}
+
+ applyForceImmersiveMode(pfl, vf);
} else if (attached != null) {
if (DEBUG_LAYOUT) Slog.v(TAG, "layoutWindowLw(" + attrs.getTitle() +
"): attached to " + attached);
@@ -4253,6 +4914,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
} else {
vf.set(cf);
}
+
+ applyForceImmersiveMode(pfl, vf);
}
}
}
@@ -4324,6 +4987,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mContentBottom > top) {
mContentBottom = top;
}
+ if (mForceImmersiveBottom > top) {
+ mForceImmersiveBottom = top;
+ }
if (mVoiceContentBottom > top) {
mVoiceContentBottom = top;
}
@@ -4381,7 +5047,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
WindowState attached) {
if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisibleOrBehindKeyguardLw="
+ win.isVisibleOrBehindKeyguardLw());
- final int fl = PolicyControl.getWindowFlags(win, attrs);
+ final int fl = WindowManagerPolicyControl.getWindowFlags(win, attrs);
if (mTopFullscreenOpaqueWindowState == null
&& win.isVisibleLw() && attrs.type == TYPE_INPUT_METHOD) {
mForcingShowNavBar = true;
@@ -4395,7 +5061,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if ((attrs.privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
mForceStatusBarTransparent = true;
}
- }
+ } else if (attrs.type == TYPE_KEYGUARD_PANEL) {
+ if (mKeyguardDelegate.isKeyguardPanelFocused()) {
+ attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ attrs.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ } else {
+ attrs.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ attrs.flags &= ~WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+ }
+ }
boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
&& attrs.type < FIRST_SYSTEM_WINDOW;
@@ -4566,7 +5240,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mLastSystemUiFlags, mLastSystemUiFlags);
}
} else if (mTopFullscreenOpaqueWindowState != null) {
- final int fl = PolicyControl.getWindowFlags(null, lp);
+ final int fl = WindowManagerPolicyControl.getWindowFlags(null, lp);
if (localLOGV) {
Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrameLw()
+ " shown frame: " + mTopFullscreenOpaqueWindowState.getShownFrameLw());
@@ -4681,22 +5355,24 @@ public class PhoneWindowManager implements WindowManagerPolicy {
*/
private boolean setKeyguardOccludedLw(boolean isOccluded) {
boolean wasOccluded = mKeyguardOccluded;
- boolean showing = mKeyguardDelegate.isShowing();
- if (wasOccluded && !isOccluded && showing) {
+ if (wasOccluded && !isOccluded) {
mKeyguardOccluded = false;
mKeyguardDelegate.setOccluded(false);
- mStatusBar.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD;
- mStatusBar.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
- return true;
- } else if (!wasOccluded && isOccluded && showing) {
+ if (mKeyguardDelegate.isShowing()) {
+ mStatusBar.getAttrs().privateFlags |= PRIVATE_FLAG_KEYGUARD;
+ mStatusBar.getAttrs().flags |= FLAG_SHOW_WALLPAPER;
+ return true;
+ }
+ } else if (!wasOccluded && isOccluded) {
mKeyguardOccluded = true;
mKeyguardDelegate.setOccluded(true);
- mStatusBar.getAttrs().privateFlags &= ~PRIVATE_FLAG_KEYGUARD;
- mStatusBar.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER;
- return true;
- } else {
- return false;
+ if (mKeyguardDelegate.isShowing()) {
+ mStatusBar.getAttrs().privateFlags &= ~PRIVATE_FLAG_KEYGUARD;
+ mStatusBar.getAttrs().flags &= ~FLAG_SHOW_WALLPAPER;
+ return true;
+ }
}
+ return false;
}
private boolean isStatusBarKeyguard() {
@@ -4813,6 +5489,20 @@ public class PhoneWindowManager implements WindowManagerPolicy {
setHdmiPlugged(!mHdmiPlugged);
}
+ /**
+ * @return Whether music is being played right now "locally" (e.g. on the device's speakers
+ * or wired headphones) or "remotely" (e.g. on a device using the Cast protocol and
+ * controlled by this device, or through remote submix).
+ */
+ private boolean isMusicActive() {
+ final AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
+ if (am == null) {
+ Log.w(TAG, "isMusicActive: couldn't get AudioManager reference");
+ return false;
+ }
+ return am.isMusicActive();
+ }
+
final Object mScreenshotLock = new Object();
ServiceConnection mScreenshotConnection = null;
@@ -4894,6 +5584,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final boolean canceled = event.isCanceled();
final int keyCode = event.getKeyCode();
+ final int scanCode = event.getScanCode();
final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
@@ -4925,8 +5616,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// If we're currently dozing with the screen on and the keyguard showing, pass the key
// to the application but preserve its wake key status to make sure we still move
// from dozing to fully interactive if we would normally go from off to fully
- // interactive.
+ // interactive, unless the user has explicitly disabled this wake key.
result = ACTION_PASS_TO_USER;
+ isWakeKey = isWakeKey && isWakeKeyEnabled(keyCode);
} else {
// When the screen is off and the key is not injected, determine whether
// to wake the device but don't pass the key to the application.
@@ -4941,7 +5633,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (isValidGlobalKey(keyCode)
&& mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) {
if (isWakeKey) {
- wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");
+ wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey,
+ "android.policy:KEY", true);
}
return result;
}
@@ -4950,6 +5643,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
&& (policyFlags & WindowManagerPolicy.FLAG_VIRTUAL) != 0
&& event.getRepeatCount() == 0;
+ // Specific device key handling
+ if (mDeviceKeyHandler != null) {
+ try {
+ // The device only should consume known keys.
+ if (mDeviceKeyHandler.handleKeyEvent(event)) {
+ return 0;
+ }
+ } catch (Exception e) {
+ Slog.w(TAG, "Could not dispatch event to device key handler", e);
+ }
+ }
+
// Handle special keys.
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_DOWN:
@@ -4959,6 +5664,17 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// On TVs volume keys never go to the foreground app
result &= ~ACTION_PASS_TO_USER;
}
+ // Eat all down & up keys when using volume wake.
+ // This disables volume control, music control, and "beep" on key up.
+ if (isWakeKey && mVolumeWakeScreen) {
+ mVolumeWakeTriggered = true;
+ break;
+ } else if (mVolumeWakeTriggered && !down) {
+ result &= ~ACTION_PASS_TO_USER;
+ mVolumeWakeTriggered = false;
+ break;
+ }
+
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
if (down) {
if (interactive && !mScreenshotChordVolumeDownKeyTriggered
@@ -5018,23 +5734,101 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
}
}
+ }
+
+ // Disable music and volume control when used as wake key
+ if ((result & ACTION_PASS_TO_USER) == 0 && !mVolumeWakeScreen) {
+ boolean mayChangeVolume = false;
+
+ if (isMusicActive()) {
+ if (mVolBtnMusicControls && (keyCode != KeyEvent.KEYCODE_VOLUME_MUTE)) {
+ // Detect long key presses.
+ if (down) {
+ mIsLongPress = false;
+ // TODO: Long press of MUTE could be mapped to KEYCODE_MEDIA_PLAY_PAUSE
+ int newKeyCode = event.getKeyCode() == KeyEvent.KEYCODE_VOLUME_UP ?
+ KeyEvent.KEYCODE_MEDIA_NEXT : KeyEvent.KEYCODE_MEDIA_PREVIOUS;
+ scheduleLongPressKeyEvent(event, newKeyCode);
+ // Consume key down events of all presses.
+ break;
+ } else {
+ mHandler.removeMessages(MSG_DISPATCH_VOLKEY_WITH_WAKE_LOCK);
+ // Consume key up events of long presses only.
+ if (mIsLongPress) {
+ break;
+ }
+ // Change volume only on key up events of short presses.
+ mayChangeVolume = true;
+ }
+ } else {
+ // Long key press detection not applicable, change volume only
+ // on key down events
+ mayChangeVolume = down;
+ }
+ }
- if ((result & ACTION_PASS_TO_USER) == 0) {
+ if (mayChangeVolume) {
if (mUseTvRouting) {
dispatchDirectAudioEvent(event);
} else {
// If we aren't passing to the user and no one else
- // handled it send it to the session manager to
- // figure out.
+ // handled it send it to the session manager to figure
+ // out.
+
+ // Rewrite the event to use key-down as sendVolumeKeyEvent will
+ // only change the volume on key down.
+ KeyEvent newEvent = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
MediaSessionLegacyHelper.getHelper(mContext)
- .sendVolumeKeyEvent(event, true);
+ .sendVolumeKeyEvent(newEvent, true);
}
- break;
}
+ break;
}
break;
}
+ case KeyEvent.KEYCODE_HOME:
+ if (down && !interactive && mHomeWakeScreen) {
+ isWakeKey = true;
+ }
+ break;
+
+ case KeyEvent.KEYCODE_FOCUS:
+ if (down && !interactive && mCameraSleepOnRelease) {
+ mIsFocusPressed = true;
+ } else if ((event.getAction() == KeyEvent.ACTION_UP)
+ && mScreenOnFully && mIsFocusPressed) {
+ // Check if screen is fully on before letting the device go to sleep
+ mPowerManager.goToSleep(SystemClock.uptimeMillis());
+ mIsFocusPressed = false;
+ }
+ break;
+
+ case KeyEvent.KEYCODE_CAMERA:
+ if (down && mIsFocusPressed) {
+ mIsFocusPressed = false;
+ }
+ if (down) {
+ mIsLongPress = false;
+ scheduleLongPressKeyEvent(event, KeyEvent.KEYCODE_CAMERA);
+ // Consume key down events of all presses.
+ break;
+ } else {
+ mHandler.removeMessages(MSG_CAMERA_LONG_PRESS);
+ // Consume key up events of long presses only.
+ if (mIsLongPress && mCameraLaunch) {
+ Intent intent;
+ if (keyguardActive) {
+ intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE);
+ } else {
+ intent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+ }
+ isWakeKey = true;
+ startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ }
+ }
+ break;
+
case KeyEvent.KEYCODE_ENDCALL: {
result &= ~ACTION_PASS_TO_USER;
if (down) {
@@ -5073,6 +5867,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
case KeyEvent.KEYCODE_POWER: {
+ if (mTopFullscreenOpaqueWindowState != null &&
+ (mTopFullscreenOpaqueWindowState.getAttrs().privateFlags
+ & (WindowManager.LayoutParams.PRIVATE_FLAG_PREVENT_SYSTEM_KEYS |
+ WindowManager.LayoutParams.PRIVATE_FLAG_PREVENT_POWER_KEY)) != 0
+ && mScreenOnFully) {
+ return result;
+ }
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
@@ -5097,6 +5898,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
break;
}
+ case KeyEvent.KEYCODE_SOFT_SLEEP: {
+ result &= ~ACTION_PASS_TO_USER;
+ isWakeKey = false;
+ if (!down) {
+ mPowerManagerInternal.setUserInactiveOverrideFromWindowManager();
+ }
+ break;
+ }
+
case KeyEvent.KEYCODE_WAKEUP: {
result &= ~ACTION_PASS_TO_USER;
isWakeKey = true;
@@ -5172,12 +5982,26 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
if (isWakeKey) {
- wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY");
+ wakeUp(event.getEventTime(), mAllowTheaterModeWakeFromKey, "android.policy:KEY",
+ event.getKeyCode() == KeyEvent.KEYCODE_WAKEUP /* check prox only on wake key*/);
}
return result;
}
+ private void scheduleLongPressKeyEvent(KeyEvent origEvent, int keyCode) {
+ KeyEvent event = new KeyEvent(origEvent.getDownTime(), origEvent.getEventTime(),
+ origEvent.getAction(), keyCode, 0);
+ Message msg;
+ if (keyCode == KeyEvent.KEYCODE_CAMERA) {
+ msg = mHandler.obtainMessage(MSG_CAMERA_LONG_PRESS, event);
+ } else {
+ msg = mHandler.obtainMessage(MSG_DISPATCH_VOLKEY_WITH_WAKE_LOCK, event);
+ }
+ msg.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msg, ViewConfiguration.getLongPressTimeout());
+ }
+
/**
* Returns true if the key can have global actions attached to it.
* We reserve all power management keys for the system since they require
@@ -5195,6 +6019,34 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
/**
+ * Check if the given keyCode represents a key that is considered a wake key
+ * and is currently enabled by the user in Settings or for another reason.
+ */
+ private boolean isWakeKeyEnabled(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ case KeyEvent.KEYCODE_VOLUME_MUTE:
+ // Volume keys are still wake keys if the device is docked.
+ return mVolumeWakeScreen || mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ case KeyEvent.KEYCODE_BACK:
+ return mBackWakeScreen;
+ case KeyEvent.KEYCODE_MENU:
+ return mMenuWakeScreen;
+ case KeyEvent.KEYCODE_ASSIST:
+ return mAssistWakeScreen;
+ case KeyEvent.KEYCODE_APP_SWITCH:
+ return mAppSwitchWakeScreen;
+ case KeyEvent.KEYCODE_CAMERA:
+ case KeyEvent.KEYCODE_FOCUS:
+ return mCameraWakeScreen;
+ case KeyEvent.KEYCODE_HOME:
+ return mHomeWakeScreen;
+ }
+ return true;
+ }
+
+ /**
* When the screen is off we ignore some keys that might otherwise typically
* be considered wake keys. We filter them out here.
*
@@ -5207,9 +6059,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_MUTE:
- return mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ return mVolumeWakeScreen || mDockMode != Intent.EXTRA_DOCK_STATE_UNDOCKED;
- // ignore media and camera keys
+ // ignore media keys
case KeyEvent.KEYCODE_MUTE:
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_PLAY:
@@ -5222,8 +6074,21 @@ public class PhoneWindowManager implements WindowManagerPolicy {
case KeyEvent.KEYCODE_MEDIA_RECORD:
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
- case KeyEvent.KEYCODE_CAMERA:
return false;
+
+ case KeyEvent.KEYCODE_BACK:
+ return mBackWakeScreen;
+ case KeyEvent.KEYCODE_MENU:
+ return mMenuWakeScreen;
+ case KeyEvent.KEYCODE_ASSIST:
+ return mAssistWakeScreen;
+ case KeyEvent.KEYCODE_APP_SWITCH:
+ return mAppSwitchWakeScreen;
+ case KeyEvent.KEYCODE_CAMERA:
+ case KeyEvent.KEYCODE_FOCUS:
+ return mCameraWakeScreen;
+ case KeyEvent.KEYCODE_HOME:
+ return mHomeWakeScreen;
}
return true;
}
@@ -5255,11 +6120,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private boolean shouldDispatchInputWhenNonInteractive() {
- if (mDisplay == null || mDisplay.getState() == Display.STATE_OFF) {
- return false;
- }
- // Send events to keyguard while the screen is on and it's showing.
- if (isKeyguardShowingAndNotOccluded()) {
+ // Send events to keyguard while the screen is on.
+ if (isKeyguardShowingAndNotOccluded() && mDisplay != null
+ && mDisplay.getState() != Display.STATE_OFF) {
return true;
}
@@ -5268,7 +6131,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
IDreamManager dreamManager = getDreamManager();
try {
- if (dreamManager != null && dreamManager.isDreaming()) {
+ if (dreamManager != null && dreamManager.isDreaming() && !dreamManager.isDozing()) {
return true;
}
} catch (RemoteException e) {
@@ -5430,6 +6293,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// current user.
mSettingsObserver.onChange(false);
+ if (mGlobalActions != null) {
+ mGlobalActions.updatePowerMenuActions();
+ }
+
// force a re-application of focused window sysui visibility.
// the window may never have been shown for this user
// e.g. the keyguard when going through the new-user setup flow
@@ -5477,6 +6344,24 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
+
+ BroadcastReceiver mWifiDisplayReceiver = new BroadcastReceiver() {
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action.equals(ACTION_WIFI_DISPLAY_VIDEO)) {
+ int state = intent.getIntExtra("state", 0);
+ if(state == 1) {
+ mWifiDisplayConnected = true;
+ } else {
+ mWifiDisplayConnected = false;
+ }
+ mWifiDisplayCustomRotation =
+ intent.getIntExtra("wfd_UIBC_rot", -1);
+ updateRotation(true);
+ }
+ }
+ };
+
// Called on the PowerManager's Notifier thread.
@Override
public void startedGoingToSleep(int why) {
@@ -5540,6 +6425,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason) {
+ return wakeUp(wakeTime, wakeInTheaterMode, reason, false);
+ }
+
+ private boolean wakeUp(long wakeTime, boolean wakeInTheaterMode, String reason,
+ boolean withProximityCheck) {
final boolean theaterModeEnabled = isTheaterModeEnabled();
if (!wakeInTheaterMode && theaterModeEnabled) {
return false;
@@ -5550,7 +6440,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
Settings.Global.THEATER_MODE_ON, 0);
}
- mPowerManager.wakeUp(wakeTime, reason);
+ if (withProximityCheck) {
+ mPowerManager.wakeUpWithProximityCheck(wakeTime, reason);
+ } else {
+ mPowerManager.wakeUp(wakeTime, reason);
+ }
return true;
}
@@ -5671,10 +6565,6 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (!mKeyguardDrawnOnce && mAwake) {
mKeyguardDrawnOnce = true;
enableScreen = true;
- if (mBootMessageNeedsHiding) {
- mBootMessageNeedsHiding = false;
- hideBootMessages();
- }
} else {
enableScreen = false;
}
@@ -5694,9 +6584,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private void handleHideBootMessage() {
synchronized (mLock) {
- if (!mKeyguardDrawnOnce) {
- mBootMessageNeedsHiding = true;
- return; // keyguard hasn't drawn the first time yet, not done booting
+ if (!mKeyguardDrawComplete) {
+ return; // keyguard hasn't completed drawing, not done booting.
}
}
@@ -5829,8 +6718,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
final int preferredRotation;
- if (mLidState == LID_OPEN && mLidOpenRotation >= 0) {
- // Ignore sensor when lid switch is open and rotation is forced.
+ if ((mLidState == LID_OPEN && mLidOpenRotation >= 0)
+ && !(mHasRemovableLid
+ && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED)) {
+ // Ignore sensor when lid switch is open and rotation is forced
+ // and a removable lid was not undocked.
preferredRotation = mLidOpenRotation;
} else if (mDockMode == Intent.EXTRA_DOCK_STATE_CAR
&& (mCarDockEnablesAccelerometer || mCarDockRotation >= 0)) {
@@ -5848,10 +6740,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// enable 180 degree rotation while docked.
preferredRotation = mDeskDockEnablesAccelerometer
? sensorRotation : mDeskDockRotation;
- } else if (mHdmiPlugged && mDemoHdmiRotationLock) {
+ } else if ((mHdmiPlugged || mWifiDisplayConnected) && mDemoHdmiRotationLock) {
// Ignore sensor when plugged into HDMI when demo HDMI rotation lock enabled.
// Note that the dock orientation overrides the HDMI orientation.
preferredRotation = mDemoHdmiRotation;
+ } else if (mWifiDisplayConnected && (mWifiDisplayCustomRotation > -1)) {
+ // Ignore sensor when WFD is active and UIBC rotation is enabled
+ preferredRotation = mWifiDisplayCustomRotation;
} else if (mHdmiPlugged && mDockMode == Intent.EXTRA_DOCK_STATE_UNDOCKED
&& mUndockedHdmiRotation >= 0) {
// Ignore sensor when plugged into HDMI and an undocked orientation has
@@ -5889,10 +6784,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mAllowAllRotations = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_allowAllRotations) ? 1 : 0;
}
- if (sensorRotation != Surface.ROTATION_180
- || mAllowAllRotations == 1
- || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
- || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
+
+ // use sensor orientation if it's forced, or if the user has allowed it
+ boolean useSensorRotation =
+ orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
+ || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER
+ || RotationPolicy.isRotationAllowed(sensorRotation, mUserRotationAngles,
+ mAllowAllRotations != 0);
+ if (useSensorRotation) {
preferredRotation = sensorRotation;
} else {
preferredRotation = lastRotation;
@@ -6060,6 +6959,11 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mKeyguardDelegate = new KeyguardServiceDelegate(mContext);
mKeyguardDelegate.onSystemReady();
+ mCMHardware = CMHardwareManager.getInstance(mContext);
+ // Ensure observe happens in systemReady() since we need
+ // CMHardwareService to be up and running
+ mSettingsObserver.observe();
+
readCameraLensCoverState();
updateUiMode();
boolean bindKeyguardNow;
@@ -6114,68 +7018,18 @@ public class PhoneWindowManager implements WindowManagerPolicy {
screenTurnedOn();
}
- ProgressDialog mBootMsgDialog = null;
+ BootDexoptDialog mBootMsgDialog = null;
/** {@inheritDoc} */
@Override
- public void showBootMessage(final CharSequence msg, final boolean always) {
+ public void updateBootProgress(final int stage, final ApplicationInfo optimizedApp,
+ final int currentAppPos, final int totalAppCount) {
mHandler.post(new Runnable() {
@Override public void run() {
if (mBootMsgDialog == null) {
- int theme;
- if (mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_WATCH)) {
- theme = com.android.internal.R.style.Theme_Micro_Dialog_Alert;
- } else if (mContext.getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_TELEVISION)) {
- theme = com.android.internal.R.style.Theme_Leanback_Dialog_Alert;
- } else {
- theme = 0;
- }
-
- mBootMsgDialog = new ProgressDialog(mContext, theme) {
- // This dialog will consume all events coming in to
- // it, to avoid it trying to do things too early in boot.
- @Override public boolean dispatchKeyEvent(KeyEvent event) {
- return true;
- }
- @Override public boolean dispatchKeyShortcutEvent(KeyEvent event) {
- return true;
- }
- @Override public boolean dispatchTouchEvent(MotionEvent ev) {
- return true;
- }
- @Override public boolean dispatchTrackballEvent(MotionEvent ev) {
- return true;
- }
- @Override public boolean dispatchGenericMotionEvent(MotionEvent ev) {
- return true;
- }
- @Override public boolean dispatchPopulateAccessibilityEvent(
- AccessibilityEvent event) {
- return true;
- }
- };
- if (mContext.getPackageManager().isUpgrade()) {
- mBootMsgDialog.setTitle(R.string.android_upgrading_title);
- } else {
- mBootMsgDialog.setTitle(R.string.android_start_title);
- }
- mBootMsgDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
- mBootMsgDialog.setIndeterminate(true);
- mBootMsgDialog.getWindow().setType(
- WindowManager.LayoutParams.TYPE_BOOT_PROGRESS);
- mBootMsgDialog.getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_DIM_BEHIND
- | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
- mBootMsgDialog.getWindow().setDimAmount(1);
- WindowManager.LayoutParams lp = mBootMsgDialog.getWindow().getAttributes();
- lp.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
- mBootMsgDialog.getWindow().setAttributes(lp);
- mBootMsgDialog.setCancelable(false);
- mBootMsgDialog.show();
- }
- mBootMsgDialog.setMessage(msg);
+ mBootMsgDialog = BootDexoptDialog.create(mContext);
+ }
+ mBootMsgDialog.setProgress(stage, optimizedApp, currentAppPos, totalAppCount);
}
});
}
@@ -6295,14 +7149,37 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
private void applyLidSwitchState() {
- if (mLidState == LID_CLOSED && mLidControlsSleep) {
- mPowerManager.goToSleep(SystemClock.uptimeMillis(),
- PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
- PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
- }
+ mPowerManager.setKeyboardVisibility(isBuiltInKeyboardVisible());
+ if (mLidControlsSleep) {
+ IDreamManager dreamManager = getDreamManager();
+ if (dreamManager != null) {
+ try {
+ dreamManager.setLidState(mLidState);
+ } catch (RemoteException e) {
+ }
+ }
- synchronized (mLock) {
- updateWakeGestureListenerLp();
+ if (mLidState == LID_CLOSED) {
+ if (mFocusedWindow != null && (mFocusedWindow.getAttrs().flags
+ & WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON) != 0) {
+ // if an application requests that the screen be turned on
+ // and there's a closed device cover, don't turn the screen off!
+ return;
+ }
+
+ TelecomManager telephonyService = getTelecommService();
+ if (!(telephonyService == null
+ || telephonyService.isRinging())) {
+ mPowerManager.goToSleep(SystemClock.uptimeMillis(),
+ PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
+ PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
+ }
+
+ }
+
+ synchronized (mLock) {
+ updateWakeGestureListenerLp();
+ }
}
}
@@ -6377,7 +7254,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
ActivityInfo ai = null;
ResolveInfo info = mContext.getPackageManager().resolveActivityAsUser(
intent,
- PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
+ PackageManager.MATCH_DEFAULT_ONLY
+ | PackageManager.GET_META_DATA
+ | PackageManager.GET_RESOLVED_FILTER,
mCurrentUserId);
if (info != null) {
ai = info.activityInfo;
@@ -6600,13 +7479,38 @@ public class PhoneWindowManager implements WindowManagerPolicy {
return 0;
}
- int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
+ int tmpVisibility = WindowManagerPolicyControl.getSystemUiVisibility(win, null)
& ~mResettingSystemUiFlags
& ~mForceClearedSystemUiFlags;
+ boolean wasCleared = mClearedBecauseOfForceShow;
if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
- tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
+ tmpVisibility &=
+ ~WindowManagerPolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
+ mClearedBecauseOfForceShow = true;
+ } else {
+ mClearedBecauseOfForceShow = false;
+ }
+
+ // The window who requested navbar force showing disappeared and next window wants
+ // to hide navbar. Instead of hiding we will make it transient. SystemUI will take care
+ // about hiding after timeout. This should not happen if next window is keyguard because
+ // transient state have more priority than translucent (why?) and cause bad UX
+ if (wasCleared && !mClearedBecauseOfForceShow
+ && (tmpVisibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) {
+ mNavigationBarController.showTransient();
+ tmpVisibility |= View.NAVIGATION_BAR_TRANSIENT;
+ mWindowManagerFuncs.addSystemUIVisibilityFlag(View.NAVIGATION_BAR_TRANSIENT);
}
- tmpVisibility = updateLightStatusBarLw(tmpVisibility);
+
+ boolean topWindowWasKeyguard = mTopWindowIsKeyguard;
+ mTopWindowIsKeyguard = (win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0;
+ if (topWindowWasKeyguard && !mTopWindowIsKeyguard
+ && (tmpVisibility & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0) {
+ mStatusBarController.showTransient();
+ tmpVisibility |= View.STATUS_BAR_TRANSIENT;
+ mWindowManagerFuncs.addSystemUIVisibilityFlag(View.STATUS_BAR_TRANSIENT);
+ }
+
final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
final int diff = visibility ^ mLastSystemUiFlags;
final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
@@ -6645,7 +7549,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// If the top fullscreen-or-dimming window is also the top fullscreen, respect
// its light flag.
vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
- vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null)
+ vis |= WindowManagerPolicyControl.getSystemUiVisibility(statusColorWin, null)
& View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
} else if (statusColorWin != null && statusColorWin.isDimming()) {
// Otherwise if it's dimming, clear the light flag.
@@ -6688,7 +7592,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
(vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
boolean hideStatusBarWM =
mTopFullscreenOpaqueWindowState != null &&
- (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
+ (WindowManagerPolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
& WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
boolean hideStatusBarSysui =
(vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
@@ -6784,6 +7688,15 @@ public class PhoneWindowManager implements WindowManagerPolicy {
// overridden by qemu.hw.mainkeys in the emulator.
@Override
public boolean hasNavigationBar() {
+ return mHasNavigationBar || mDevForceNavbar;
+ }
+
+ @Override
+ public boolean hasPermanentMenuKey() {
+ return !hasNavigationBar() && mHasPermanentMenuKey;
+ }
+
+ public boolean needsNavigationBar() {
return mHasNavigationBar;
}
@@ -6881,6 +7794,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
pw.print(prefix);
pw.print("mShortPressOnPowerBehavior="); pw.print(mShortPressOnPowerBehavior);
pw.print(" mLongPressOnPowerBehavior="); pw.println(mLongPressOnPowerBehavior);
+ pw.print(" mRingHomeBehavior="); pw.print(mRingHomeBehavior);
pw.print(prefix);
pw.print("mDoublePressOnPowerBehavior="); pw.print(mDoublePressOnPowerBehavior);
pw.print(" mTriplePressOnPowerBehavior="); pw.println(mTriplePressOnPowerBehavior);
@@ -7017,7 +7931,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mGlobalKeyManager.dump(prefix, pw);
mStatusBarController.dump(pw, prefix);
mNavigationBarController.dump(pw, prefix);
- PolicyControl.dump(prefix, pw);
+ WindowManagerPolicyControl.dump(prefix, pw);
if (mWakeGestureListener != null) {
mWakeGestureListener.dump(pw, prefix);
@@ -7032,4 +7946,9 @@ public class PhoneWindowManager implements WindowManagerPolicy {
mKeyguardDelegate.dump(prefix, pw);
}
}
+
+ @Override
+ public void setLiveLockscreenEdgeDetector(boolean enable) {
+ mShowKeyguardOnLeftSwipe = enable;
+ }
}
diff --git a/services/core/java/com/android/server/policy/PolicyControl.java b/services/core/java/com/android/server/policy/PolicyControl.java
index dbafc42..0f6fc58 100644
--- a/services/core/java/com/android/server/policy/PolicyControl.java
+++ b/services/core/java/com/android/server/policy/PolicyControl.java
@@ -125,6 +125,10 @@ public class PolicyControl {
}
}
+ public static boolean isImmersiveFiltersActive() {
+ return sImmersiveStatusFilter != null || sImmersiveNavigationFilter != null;
+ }
+
public static void dump(String prefix, PrintWriter pw) {
dump("sImmersiveStatusFilter", sImmersiveStatusFilter, prefix, pw);
dump("sImmersiveNavigationFilter", sImmersiveNavigationFilter, prefix, pw);
diff --git a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
index e4bd21d..5e694a5 100644
--- a/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
+++ b/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java
@@ -42,6 +42,7 @@ public class SystemGesturesPointerEventListener implements PointerEventListener
private static final int SWIPE_FROM_TOP = 1;
private static final int SWIPE_FROM_BOTTOM = 2;
private static final int SWIPE_FROM_RIGHT = 3;
+ private static final int SWIPE_FROM_LEFT = 4;
private final Context mContext;
private final int mSwipeStartThreshold;
@@ -121,6 +122,9 @@ public class SystemGesturesPointerEventListener implements PointerEventListener
} else if (swipe == SWIPE_FROM_RIGHT) {
if (DEBUG) Slog.d(TAG, "Firing onSwipeFromRight");
mCallbacks.onSwipeFromRight();
+ } else if (swipe == SWIPE_FROM_LEFT) {
+ if (DEBUG) Slog.d(TAG, "Firing onSwipeFromLeft");
+ mCallbacks.onSwipeFromLeft();
}
}
break;
@@ -203,6 +207,11 @@ public class SystemGesturesPointerEventListener implements PointerEventListener
&& elapsed < SWIPE_TIMEOUT_MS) {
return SWIPE_FROM_BOTTOM;
}
+ if (fromX <= mSwipeStartThreshold
+ && x > fromX + mSwipeDistanceThreshold
+ && elapsed < SWIPE_TIMEOUT_MS) {
+ return SWIPE_FROM_LEFT;
+ }
if (fromX >= screenWidth - mSwipeStartThreshold
&& x < fromX - mSwipeDistanceThreshold
&& elapsed < SWIPE_TIMEOUT_MS) {
@@ -247,6 +256,7 @@ public class SystemGesturesPointerEventListener implements PointerEventListener
void onFling(int durationMs);
void onDown();
void onUpOrCancel();
+ void onSwipeFromLeft();
void onDebug();
}
}
diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java
index 9916223..651ee22 100644
--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java
+++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java
@@ -55,6 +55,7 @@ public abstract class WindowOrientationListener {
private boolean mEnabled;
private int mRate;
private String mSensorType;
+ private boolean mUseSystemClockforRotationSensor;
private Sensor mSensor;
private OrientationJudge mOrientationJudge;
private int mCurrentRotation = -1;
@@ -90,6 +91,9 @@ public abstract class WindowOrientationListener {
mSensorType = context.getResources().getString(
com.android.internal.R.string.config_orientationSensorType);
+ mUseSystemClockforRotationSensor = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_useSystemClockforRotationSensor);
+
if (!TextUtils.isEmpty(mSensorType)) {
List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
final int N = sensors.size();
@@ -598,7 +602,8 @@ public abstract class WindowOrientationListener {
// Reset the orientation listener state if the samples are too far apart in time
// or when we see values of (0, 0, 0) which indicates that we polled the
// accelerometer too soon after turning it on and we don't have any data yet.
- final long now = event.timestamp;
+ final long now = mUseSystemClockforRotationSensor
+ ? SystemClock.elapsedRealtimeNanos() : event.timestamp;
final long then = mLastFilteredTimestampNanos;
final float timeDeltaMS = (now - then) * 0.000001f;
final boolean skipSample;
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 7ae3c79..a223d05 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -49,6 +49,10 @@ public class KeyguardServiceDelegate {
private final KeyguardState mKeyguardState = new KeyguardState();
private DrawnListener mDrawnListenerWhenConnect;
+ private static final String ACTION_STATE_CHANGE =
+ "com.android.internal.action.KEYGUARD_SERVICE_STATE_CHANGED";
+ private static final String EXTRA_ACTIVE = "active";
+
private static final class KeyguardState {
KeyguardState() {
// Assume keyguard is showing and secure until we know for sure. This is here in
@@ -79,6 +83,13 @@ public class KeyguardServiceDelegate {
void onDrawn();
}
+ private void sendStateChangeBroadcast(boolean bound) {
+ Intent i = new Intent(ACTION_STATE_CHANGE);
+ i.putExtra(EXTRA_ACTIVE, bound);
+ mScrim.getContext().sendBroadcastAsUser(i, UserHandle.ALL,
+ android.Manifest.permission.CONTROL_KEYGUARD);
+ }
+
// A delegate class to map a particular invocation with a ShowListener object.
private final class KeyguardShowDelegate extends IKeyguardDrawnCallback.Stub {
private DrawnListener mDrawnListener;
@@ -171,6 +182,7 @@ public class KeyguardServiceDelegate {
}
if (mKeyguardState.bootCompleted) {
mKeyguardService.onBootCompleted();
+ sendStateChangeBroadcast(true);
}
if (mKeyguardState.occluded) {
mKeyguardService.setOccluded(mKeyguardState.occluded);
@@ -181,6 +193,7 @@ public class KeyguardServiceDelegate {
public void onServiceDisconnected(ComponentName name) {
if (DEBUG) Log.v(TAG, "*** Keyguard disconnected (boo!)");
mKeyguardService = null;
+ sendStateChangeBroadcast(false);
}
};
@@ -364,7 +377,7 @@ public class KeyguardServiceDelegate {
public void showScrim() {
synchronized (mKeyguardState) {
- if (!mKeyguardState.deviceHasKeyguard) return;
+ if (!mKeyguardState.deviceHasKeyguard || !mScrim.isAttachedToWindow()) return;
mScrimHandler.post(new Runnable() {
@Override
public void run() {
@@ -388,6 +401,7 @@ public class KeyguardServiceDelegate {
mKeyguardService.onBootCompleted();
}
mKeyguardState.bootCompleted = true;
+ sendStateChangeBroadcast(true);
}
public void onActivityDrawn() {
@@ -417,4 +431,15 @@ public class KeyguardServiceDelegate {
mKeyguardService.dump(prefix, pw);
}
}
+
+ public void showKeyguard() {
+ mKeyguardService.showKeyguard();
+ }
+
+ public boolean isKeyguardPanelFocused() {
+ if (mKeyguardService != null) {
+ return mKeyguardService.isKeyguardPanelFocused();
+ }
+ return false;
+ }
}
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index 429b188..31c7a04 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -245,4 +245,16 @@ public class KeyguardServiceWrapper implements IKeyguardService {
public void dump(String prefix, PrintWriter pw) {
mKeyguardStateMonitor.dump(prefix, pw);
}
+
+ public void showKeyguard() {
+ try {
+ mService.showKeyguard();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Remote Exception", e);
+ }
+ }
+
+ public boolean isKeyguardPanelFocused() {
+ return mKeyguardStateMonitor.isKeyguardPanelFocused();
+ }
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
index 30cff03..09f9087 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
@@ -43,6 +43,7 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub {
private volatile boolean mIsShowing = true;
private volatile boolean mSimSecure = true;
private volatile boolean mInputRestricted = true;
+ private volatile boolean mKeyguardPanelFocused = false;
private int mCurrentUserId;
@@ -70,6 +71,10 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub {
return mInputRestricted;
}
+ public boolean isKeyguardPanelFocused() {
+ return mKeyguardPanelFocused;
+ }
+
@Override // Binder interface
public void onShowingStateChanged(boolean showing) {
mIsShowing = showing;
@@ -80,6 +85,11 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub {
mSimSecure = simSecure;
}
+ @Override // Binder interface
+ public void onKeyguardPanelFocusChanged(boolean focused) {
+ mKeyguardPanelFocused = focused;
+ }
+
public synchronized void setCurrentUser(int userId) {
mCurrentUserId = userId;
}
@@ -100,5 +110,6 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub {
pw.println(prefix + "mSimSecure=" + mSimSecure);
pw.println(prefix + "mInputRestricted=" + mInputRestricted);
pw.println(prefix + "mCurrentUserId=" + mCurrentUserId);
+ pw.println(prefix + "mKeyguardPanelFocused=" + mKeyguardPanelFocused);
}
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 7108f4a..e0bb48f 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -29,10 +29,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.hardware.input.InputManagerInternal;
-import android.media.AudioManager;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.net.Uri;
import android.os.BatteryStats;
import android.os.Handler;
import android.os.Looper;
@@ -78,8 +74,7 @@ final class Notifier {
private static final int MSG_USER_ACTIVITY = 1;
private static final int MSG_BROADCAST = 2;
- private static final int MSG_WIRELESS_CHARGING_STARTED = 3;
- private static final int MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED = 4;
+ private static final int MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED = 3;
private final Object mLock = new Object();
@@ -502,11 +497,6 @@ final class Notifier {
if (DEBUG) {
Slog.d(TAG, "onWirelessChargingStarted");
}
-
- mSuspendBlocker.acquire();
- Message msg = mHandler.obtainMessage(MSG_WIRELESS_CHARGING_STARTED);
- msg.setAsynchronous(true);
- mHandler.sendMessage(msg);
}
private void updatePendingBroadcastLocked() {
@@ -642,25 +632,6 @@ final class Notifier {
}
};
- private void playWirelessChargingStartedSound() {
- final boolean enabled = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.CHARGING_SOUNDS_ENABLED, 1) != 0;
- final String soundPath = Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.WIRELESS_CHARGING_STARTED_SOUND);
- if (enabled && soundPath != null) {
- final Uri soundUri = Uri.parse("file://" + soundPath);
- if (soundUri != null) {
- final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri);
- if (sfx != null) {
- sfx.setStreamType(AudioManager.STREAM_SYSTEM);
- sfx.play();
- }
- }
- }
-
- mSuspendBlocker.release();
- }
-
private final class NotifierHandler extends Handler {
public NotifierHandler(Looper looper) {
super(looper, null, true /*async*/);
@@ -676,10 +647,6 @@ final class Notifier {
case MSG_BROADCAST:
sendNextBroadcast();
break;
-
- case MSG_WIRELESS_CHARGING_STARTED:
- playWirelessChargingStartedSound();
- break;
case MSG_SCREEN_BRIGHTNESS_BOOST_CHANGED:
sendBrightnessBoostChangedBroadcast();
break;
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index f7a8970..a197c6e 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -17,18 +17,26 @@
package com.android.server.power;
import android.app.ActivityManager;
+import android.os.IDeviceIdleController;
+import android.os.ServiceManager;
import android.util.SparseIntArray;
+
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IBatteryStats;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.util.ArrayUtils;
import com.android.server.EventLogTags;
+import com.android.server.LocalServices;
import com.android.server.ServiceThread;
+import com.android.server.SystemConfig;
import com.android.server.SystemService;
import com.android.server.am.BatteryStatsService;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.Watchdog;
+import cyanogenmod.power.PerformanceManagerInternal;
+
import android.Manifest;
import android.app.AppOpsManager;
import android.content.BroadcastReceiver;
@@ -39,6 +47,9 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
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.hardware.SystemSensorManager;
import android.hardware.display.DisplayManagerInternal;
@@ -63,6 +74,7 @@ import android.os.UserHandle;
import android.os.WorkSource;
import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
+import android.telephony.TelephonyManager;
import android.util.EventLog;
import android.util.Slog;
import android.util.TimeUtils;
@@ -74,6 +86,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import cyanogenmod.providers.CMSettings;
import libcore.util.Objects;
import static android.os.PowerManagerInternal.POWER_HINT_INTERACTION;
@@ -99,6 +112,10 @@ public final class PowerManagerService extends SystemService
private static final int MSG_SANDMAN = 2;
// Message: Sent when the screen brightness boost expires.
private static final int MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT = 3;
+ // Message: Sent when the sandman fails to acknowledge the dream state change.
+ private static final int MSG_SANDMAN_TIMEOUT = 4;
+
+ private static final int MSG_WAKE_UP = 5;
// Dirty bit: mWakeLocks changed
private static final int DIRTY_WAKE_LOCKS = 1 << 0;
@@ -124,6 +141,8 @@ public final class PowerManagerService extends SystemService
private static final int DIRTY_DOCK_STATE = 1 << 10;
// Dirty bit: brightness boost changed
private static final int DIRTY_SCREEN_BRIGHTNESS_BOOST = 1 << 11;
+ // Dirty bit: sandman state changed
+ private static final int DIRTY_SANDMAN_STATE = 1 << 12;
// Summarizes the state of all active wakelocks.
private static final int WAKE_LOCK_CPU = 1 << 0;
@@ -156,9 +175,21 @@ public final class PowerManagerService extends SystemService
// Power features defined in hardware/libhardware/include/hardware/power.h.
private static final int POWER_FEATURE_DOUBLE_TAP_TO_WAKE = 1;
+ private static final int DEFAULT_BUTTON_ON_DURATION = 5 * 1000;
+
// Default setting for double tap to wake.
private static final int DEFAULT_DOUBLE_TAP_TO_WAKE = 0;
+ private static final int BUTTON_ON_DURATION = 5 * 1000;
+
+ private static final float PROXIMITY_NEAR_THRESHOLD = 5.0f;
+
+ // Max time (microseconds) to allow a CPU boost for
+ private static final int MAX_CPU_BOOST_TIME = 5000000;
+
+ // Max time (milliseconds) to wait for the sandman to acknowledge dream state changes
+ private static final int SANDMAN_RESPONSE_TIMEOUT = 2 * 1000;
+
private final Context mContext;
private final ServiceThread mHandlerThread;
private final PowerManagerHandler mHandler;
@@ -174,6 +205,16 @@ public final class PowerManagerService extends SystemService
private SettingsObserver mSettingsObserver;
private DreamManagerInternal mDreamManager;
private Light mAttentionLight;
+ private Light mButtonsLight;
+ private Light mKeyboardLight;
+ private Light mCapsLight;
+ private Light mFnLight;
+
+ private int mButtonTimeout;
+ private int mButtonBrightness;
+ private int mButtonBrightnessSettingDefault;
+ private int mKeyboardBrightness;
+ private int mKeyboardBrightnessSettingDefault;
private final Object mLock = new Object();
@@ -248,6 +289,13 @@ public final class PowerManagerService extends SystemService
// True if the display suspend blocker has been acquired.
private boolean mHoldingDisplaySuspendBlocker;
+ // The suspend blocker used to keep the CPU alive when dreams are requesting to be
+ // started.
+ private final SuspendBlocker mDreamSuspendBlocker;
+
+ // True if the dream suspend blocker has been acquired.
+ private boolean mHoldingDreamSuspendBlocker;
+
// True if systemReady() has been called.
private boolean mSystemReady;
@@ -351,6 +399,9 @@ public final class PowerManagerService extends SystemService
// Whether device supports double tap to wake.
private boolean mSupportsDoubleTapWakeConfig;
+ // Default value for proximity prevent accidental wakeups
+ private boolean mProximityWakeEnabledByDefaultConfig;
+
// The screen off timeout setting value in milliseconds.
private int mScreenOffTimeoutSetting;
@@ -362,9 +413,12 @@ public final class PowerManagerService extends SystemService
private int mMaximumScreenOffTimeoutFromDeviceAdmin = Integer.MAX_VALUE;
// The stay on while plugged in setting.
- // A bitfield of battery conditions under which to make the screen stay on.
+ // 0: Not enabled; 1: debugging over usb; >2: charging.
private int mStayOnWhilePluggedInSetting;
+ // True if the device should wake up when plugged or unplugged
+ private int mWakeUpWhenPluggedOrUnpluggedSetting;
+
// True if the device should stay on.
private boolean mStayOn;
@@ -393,6 +447,19 @@ public final class PowerManagerService extends SystemService
// Use -1 to disable.
private int mScreenBrightnessOverrideFromWindowManager = -1;
+ // The button brightness setting override from the window manager
+ // to allow the current foreground activity to override the button brightness.
+ // Use -1 to disable.
+ private int mButtonBrightnessOverrideFromWindowManager = -1;
+
+ // The window manager has determined the user to be inactive via other means.
+ // Set this to false to disable.
+ private boolean mUserInactiveOverrideFromWindowManager;
+
+ // The next possible user activity timeout after being explicitly told the user is inactive.
+ // Set to -1 when not told the user is inactive since the last period spent dozing or asleep.
+ private long mOverriddenTimeout = -1;
+
// The user activity timeout override from the window manager
// to allow the current foreground activity to override the user activity timeout.
// Use -1 to disable.
@@ -453,6 +520,9 @@ public final class PowerManagerService extends SystemService
private final ArrayList<PowerManagerInternal.LowPowerModeListener> mLowPowerModeListeners
= new ArrayList<PowerManagerInternal.LowPowerModeListener>();
+ //track the blocked uids.
+ private final ArrayList<Integer> mBlockedUids = new ArrayList<Integer>();
+
private native void nativeInit();
private static native void nativeAcquireSuspendBlocker(String name);
@@ -461,6 +531,20 @@ public final class PowerManagerService extends SystemService
private static native void nativeSetAutoSuspend(boolean enable);
private static native void nativeSendPowerHint(int hintId, int data);
private static native void nativeSetFeature(int featureId, int data);
+ private static native int nativeGetFeature(int featureId);
+
+ private boolean mKeyboardVisible = false;
+
+ private SensorManager mSensorManager;
+ private Sensor mProximitySensor;
+ private boolean mProximityWakeEnabled;
+ private int mProximityTimeOut;
+ private boolean mProximityWakeSupported;
+ android.os.PowerManager.WakeLock mProximityWakeLock;
+ SensorEventListener mProximityListener;
+ private boolean mForceNavbar;
+
+ private PerformanceManagerInternal mPerf;
public PowerManagerService(Context context) {
super(context);
@@ -473,6 +557,7 @@ public final class PowerManagerService extends SystemService
synchronized (mLock) {
mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
+ mDreamSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Dreams");
mDisplaySuspendBlocker.acquire();
mHoldingDisplaySuspendBlocker = true;
mHalAutoSuspendModeEnabled = false;
@@ -506,6 +591,7 @@ public final class PowerManagerService extends SystemService
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
+ mPerf = LocalServices.getService(PerformanceManagerInternal.class);
}
}
}
@@ -523,6 +609,8 @@ public final class PowerManagerService extends SystemService
mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting();
mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting();
+ mButtonBrightnessSettingDefault = pm.getDefaultButtonBrightness();
+ mKeyboardBrightnessSettingDefault = pm.getDefaultKeyboardBrightness();
SensorManager sensorManager = new SystemSensorManager(mContext, mHandler.getLooper());
@@ -540,30 +628,40 @@ public final class PowerManagerService extends SystemService
mLightsManager = getLocalService(LightsManager.class);
mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
+ mButtonsLight = mLightsManager.getLight(LightsManager.LIGHT_ID_BUTTONS);
+ mKeyboardLight = mLightsManager.getLight(LightsManager.LIGHT_ID_KEYBOARD);
+ mCapsLight = mLightsManager.getLight(LightsManager.LIGHT_ID_CAPS);
+ mFnLight = mLightsManager.getLight(LightsManager.LIGHT_ID_FUNC);
// Initialize display power management.
mDisplayManagerInternal.initPowerManagement(
mDisplayPowerCallbacks, mHandler, sensorManager);
- // Register for broadcasts from other components of the system.
- IntentFilter filter = new IntentFilter();
- filter.addAction(Intent.ACTION_BATTERY_CHANGED);
- filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler);
+ // Initialize proximity sensor
+ mSensorManager = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
+ mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
+ }
+
+ // Register for broadcasts from other components of the system.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_BATTERY_CHANGED);
+ filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
+ mContext.registerReceiver(new BatteryReceiver(), filter, null, mHandler);
- filter = new IntentFilter();
- filter.addAction(Intent.ACTION_DREAMING_STARTED);
- filter.addAction(Intent.ACTION_DREAMING_STOPPED);
- mContext.registerReceiver(new DreamReceiver(), filter, null, mHandler);
+ filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_DREAMING_STARTED);
+ filter.addAction(Intent.ACTION_DREAMING_STOPPED);
+ mContext.registerReceiver(new DreamReceiver(), filter, null, mHandler);
- filter = new IntentFilter();
- filter.addAction(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);
+ filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
+ mContext.registerReceiver(new UserSwitchedReceiver(), filter, null, mHandler);
- filter = new IntentFilter();
- filter.addAction(Intent.ACTION_DOCK_EVENT);
- mContext.registerReceiver(new DockReceiver(), filter, null, mHandler);
+ filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_DOCK_EVENT);
+ mContext.registerReceiver(new DockReceiver(), filter, null, mHandler);
+ synchronized (mLock) {
// Register for settings changes.
final ContentResolver resolver = mContext.getContentResolver();
resolver.registerContentObserver(Settings.Secure.getUriFor(
@@ -605,6 +703,25 @@ public final class PowerManagerService extends SystemService
resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.DOUBLE_TAP_TO_WAKE),
false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.Secure.getUriFor(
+ CMSettings.Secure.BUTTON_BRIGHTNESS),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.Secure.getUriFor(
+ CMSettings.Secure.KEYBOARD_BRIGHTNESS),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.Secure.getUriFor(
+ CMSettings.Secure.BUTTON_BACKLIGHT_TIMEOUT),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.System.getUriFor(
+ CMSettings.System.PROXIMITY_ON_WAKE),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.Global.getUriFor(
+ CMSettings.Global.WAKE_WHEN_PLUGGED_OR_UNPLUGGED),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+ resolver.registerContentObserver(CMSettings.Global.getUriFor(
+ CMSettings.Global.DEV_FORCE_SHOW_NAVBAR),
+ false, mSettingsObserver, UserHandle.USER_ALL);
+
// Go.
readConfigurationLocked();
updateSettingsLocked();
@@ -652,6 +769,17 @@ public final class PowerManagerService extends SystemService
com.android.internal.R.fraction.config_maximumScreenDimRatio, 1, 1);
mSupportsDoubleTapWakeConfig = resources.getBoolean(
com.android.internal.R.bool.config_supportDoubleTapWake);
+ mProximityTimeOut = resources.getInteger(
+ org.cyanogenmod.platform.internal.R.integer.config_proximityCheckTimeout);
+ mProximityWakeSupported = resources.getBoolean(
+ org.cyanogenmod.platform.internal.R.bool.config_proximityCheckOnWake);
+ mProximityWakeEnabledByDefaultConfig = resources.getBoolean(
+ org.cyanogenmod.platform.internal.R.bool.config_proximityCheckOnWakeEnabledByDefault);
+ if (mProximityWakeSupported) {
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mProximityWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "ProximityWakeLock");
+ }
}
private void updateSettingsLocked() {
@@ -676,9 +804,14 @@ public final class PowerManagerService extends SystemService
Settings.Secure.SLEEP_TIMEOUT, DEFAULT_SLEEP_TIMEOUT,
UserHandle.USER_CURRENT);
mStayOnWhilePluggedInSetting = Settings.Global.getInt(resolver,
- Settings.Global.STAY_ON_WHILE_PLUGGED_IN, BatteryManager.BATTERY_PLUGGED_AC);
+ Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
mTheaterModeEnabled = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.THEATER_MODE_ON, 0) == 1;
+ mWakeUpWhenPluggedOrUnpluggedSetting = CMSettings.Global.getInt(resolver,
+ CMSettings.Global.WAKE_WHEN_PLUGGED_OR_UNPLUGGED,
+ (mWakeUpWhenPluggedOrUnpluggedConfig ? 1 : 0));
+ mProximityWakeEnabled = CMSettings.System.getInt(resolver,
+ CMSettings.System.PROXIMITY_ON_WAKE, mProximityWakeEnabledByDefaultConfig ? 1 : 0) == 1;
if (mSupportsDoubleTapWakeConfig) {
boolean doubleTapWakeEnabled = Settings.Secure.getIntForUser(resolver,
@@ -722,6 +855,18 @@ public final class PowerManagerService extends SystemService
updateLowPowerModeLocked();
}
+ mButtonTimeout = CMSettings.Secure.getIntForUser(resolver,
+ CMSettings.Secure.BUTTON_BACKLIGHT_TIMEOUT,
+ DEFAULT_BUTTON_ON_DURATION, UserHandle.USER_CURRENT);
+
+ mButtonBrightness = CMSettings.Secure.getIntForUser(resolver,
+ CMSettings.Secure.BUTTON_BRIGHTNESS, mButtonBrightnessSettingDefault,
+ UserHandle.USER_CURRENT);
+ mKeyboardBrightness = CMSettings.Secure.getIntForUser(resolver,
+ CMSettings.Secure.KEYBOARD_BRIGHTNESS, mKeyboardBrightnessSettingDefault,
+ UserHandle.USER_CURRENT);
+ mForceNavbar = CMSettings.Global.getIntForUser(resolver,
+ CMSettings.Global.DEV_FORCE_SHOW_NAVBAR, 0, UserHandle.USER_CURRENT) == 1;
mDirty |= DIRTY_SETTINGS;
}
@@ -733,6 +878,7 @@ public final class PowerManagerService extends SystemService
// Turn setting off if powered
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.LOW_POWER_MODE, 0);
+ // update performance profile
mLowPowerModeSetting = false;
}
final boolean autoLowPowerModeEnabled = !mIsPowered && mAutoLowPowerModeConfigured
@@ -800,6 +946,15 @@ public final class PowerManagerService extends SystemService
}
mWakeLocks.add(wakeLock);
setWakeLockDisabledStateLocked(wakeLock);
+ if(mBlockedUids.contains(new Integer(uid)) && uid != Process.myUid()) {
+ //wakelock acquisition for blocked uid, disable it.
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "uid is blocked disabling wakeLock flags=0x" +
+ Integer.toHexString(flags) + " tag=" + tag + " uid=" + uid +
+ " pid =" + pid);
+ }
+ updateBlockedWakelock(wakeLock, true);
+ }
notifyAcquire = true;
}
@@ -1028,6 +1183,11 @@ public final class PowerManagerService extends SystemService
mNotifier.onUserActivity(event, uid);
+ if (mUserInactiveOverrideFromWindowManager) {
+ mUserInactiveOverrideFromWindowManager = false;
+ mOverriddenTimeout = -1;
+ }
+
if (mWakefulness == WAKEFULNESS_ASLEEP
|| mWakefulness == WAKEFULNESS_DOZING
|| (flags & PowerManager.USER_ACTIVITY_FLAG_INDIRECT) != 0) {
@@ -1243,12 +1403,28 @@ public final class PowerManagerService extends SystemService
}
}
+ /**
+ * Logs the time the device would have spent awake before user activity timeout,
+ * had the system not been told the user was inactive.
+ */
+ private void logSleepTimeoutRecapturedLocked() {
+ final long now = SystemClock.uptimeMillis();
+ final long savedWakeTimeMs = mOverriddenTimeout - now;
+ if (savedWakeTimeMs >= 0) {
+ EventLog.writeEvent(EventLogTags.POWER_SOFT_SLEEP_REQUESTED, savedWakeTimeMs);
+ mOverriddenTimeout = -1;
+ }
+ }
+
private void finishWakefulnessChangeIfNeededLocked() {
if (mWakefulnessChanging && mDisplayReady) {
if (mWakefulness == WAKEFULNESS_DOZING
&& (mWakeLockSummary & WAKE_LOCK_DOZE) == 0) {
return; // wait until dream has enabled dozing
}
+ if (mWakefulness == WAKEFULNESS_DOZING || mWakefulness == WAKEFULNESS_ASLEEP) {
+ logSleepTimeoutRecapturedLocked();
+ }
mWakefulnessChanging = false;
mNotifier.onWakefulnessChangeFinished();
}
@@ -1377,7 +1553,7 @@ public final class PowerManagerService extends SystemService
private boolean shouldWakeUpWhenPluggedOrUnpluggedLocked(
boolean wasPowered, int oldPlugType, boolean dockedOnWirelessCharger) {
// Don't wake when powered unless configured to do so.
- if (!mWakeUpWhenPluggedOrUnpluggedConfig) {
+ if (mWakeUpWhenPluggedOrUnpluggedSetting == 0) {
return false;
}
@@ -1419,7 +1595,16 @@ public final class PowerManagerService extends SystemService
final boolean wasStayOn = mStayOn;
if (mStayOnWhilePluggedInSetting != 0
&& !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
- mStayOn = mBatteryManagerInternal.isPowered(mStayOnWhilePluggedInSetting);
+ switch (mStayOnWhilePluggedInSetting) {
+ case 1: // Debugging only over usb
+ mStayOn = ((mPlugType & BatteryManager.BATTERY_PLUGGED_USB) != 0)
+ && Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.ADB_ENABLED, 0) != 0;;
+ break;
+ default: // charging
+ mStayOn = mIsPowered;
+ break;
+ }
} else {
mStayOn = false;
}
@@ -1525,6 +1710,7 @@ public final class PowerManagerService extends SystemService
final int sleepTimeout = getSleepTimeoutLocked();
final int screenOffTimeout = getScreenOffTimeoutLocked(sleepTimeout);
final int screenDimDuration = getScreenDimDurationLocked(screenOffTimeout);
+ final boolean userInactiveOverride = mUserInactiveOverrideFromWindowManager;
mUserActivitySummary = 0;
if (mLastUserActivityTime >= mLastWakeTime) {
@@ -1532,10 +1718,42 @@ public final class PowerManagerService extends SystemService
+ screenOffTimeout - screenDimDuration;
if (now < nextTimeout) {
mUserActivitySummary = USER_ACTIVITY_SCREEN_BRIGHT;
+ if (mWakefulness == WAKEFULNESS_AWAKE) {
+ int buttonBrightness, keyboardBrightness;
+ if (mButtonBrightnessOverrideFromWindowManager >= 0) {
+ buttonBrightness = mButtonBrightnessOverrideFromWindowManager;
+ keyboardBrightness = mButtonBrightnessOverrideFromWindowManager;
+ } else {
+ if (!mForceNavbar) {
+ buttonBrightness = mButtonBrightness;
+ } else {
+ buttonBrightness = 0;
+ }
+ keyboardBrightness = mKeyboardBrightness;
+ }
+
+ mKeyboardLight.setBrightness(mKeyboardVisible ?
+ keyboardBrightness : 0);
+ if (mButtonTimeout != 0
+ && now > mLastUserActivityTime + mButtonTimeout) {
+ mButtonsLight.setBrightness(0);
+ } else {
+ if (!mProximityPositive) {
+ mButtonsLight.setBrightness(buttonBrightness);
+ if (buttonBrightness != 0 && mButtonTimeout != 0) {
+ nextTimeout = now + mButtonTimeout;
+ }
+ }
+ }
+ }
} else {
nextTimeout = mLastUserActivityTime + screenOffTimeout;
if (now < nextTimeout) {
mUserActivitySummary = USER_ACTIVITY_SCREEN_DIM;
+ if (mWakefulness == WAKEFULNESS_AWAKE) {
+ mButtonsLight.setBrightness(0);
+ mKeyboardLight.setBrightness(0);
+ }
}
}
}
@@ -1550,6 +1768,7 @@ public final class PowerManagerService extends SystemService
}
}
}
+
if (mUserActivitySummary == 0) {
if (sleepTimeout >= 0) {
final long anyUserActivity = Math.max(mLastUserActivityTime,
@@ -1565,6 +1784,20 @@ public final class PowerManagerService extends SystemService
nextTimeout = -1;
}
}
+
+ if (mUserActivitySummary != USER_ACTIVITY_SCREEN_DREAM && userInactiveOverride) {
+ if ((mUserActivitySummary &
+ (USER_ACTIVITY_SCREEN_BRIGHT | USER_ACTIVITY_SCREEN_DIM)) != 0) {
+ // Device is being kept awake by recent user activity
+ if (nextTimeout >= now && mOverriddenTimeout == -1) {
+ // Save when the next timeout would have occurred
+ mOverriddenTimeout = nextTimeout;
+ }
+ }
+ mUserActivitySummary = USER_ACTIVITY_SCREEN_DREAM;
+ nextTimeout = -1;
+ }
+
if (mUserActivitySummary != 0 && nextTimeout >= 0) {
Message msg = mHandler.obtainMessage(MSG_USER_ACTIVITY_TIMEOUT);
msg.setAsynchronous(true);
@@ -1709,15 +1942,16 @@ public final class PowerManagerService extends SystemService
| DIRTY_PROXIMITY_POSITIVE
| DIRTY_BATTERY_STATE)) != 0 || displayBecameReady) {
if (mDisplayReady) {
- scheduleSandmanLocked();
+ scheduleSandmanLocked(false);
}
}
}
- private void scheduleSandmanLocked() {
+ private void scheduleSandmanLocked(boolean fromDreamService) {
if (!mSandmanScheduled) {
mSandmanScheduled = true;
Message msg = mHandler.obtainMessage(MSG_SANDMAN);
+ msg.arg1 = fromDreamService ? 1 : 0;
msg.setAsynchronous(true);
mHandler.sendMessage(msg);
}
@@ -1730,7 +1964,7 @@ public final class PowerManagerService extends SystemService
* the dream and we don't want to hold our lock while doing so. There is a risk that
* the device will wake or go to sleep in the meantime so we have to handle that case.
*/
- private void handleSandman() { // runs on handler thread
+ private void handleSandman(boolean fromDreamService) { // runs on handler thread
// Handle preconditions.
final boolean startDreaming;
final int wakefulness;
@@ -1743,6 +1977,30 @@ public final class PowerManagerService extends SystemService
} else {
startDreaming = false;
}
+ // We hold the display suspend blocker as long as mSandmanSummoned is true
+ // That guarantees this code to be run before the system enters suspend. However,
+ // once we exit this lock, we are no longer guaranteed to stay awake. Hold a wake
+ // lock that is released once the dream service has acknowledged the request
+ // to start.
+ if (mDreamManager != null) {
+ if (startDreaming) {
+ if (!mHoldingDreamSuspendBlocker) {
+ mDreamSuspendBlocker.acquire();
+ mHoldingDreamSuspendBlocker = true;
+ Message msg = mHandler.obtainMessage(MSG_SANDMAN_TIMEOUT);
+ msg.setAsynchronous(true);
+ mHandler.sendMessageDelayed(msg, SANDMAN_RESPONSE_TIMEOUT);
+ mDirty |= DIRTY_SANDMAN_STATE;
+ updatePowerStateLocked();
+ }
+ } else if (fromDreamService) {
+ mHandler.removeMessages(MSG_SANDMAN_TIMEOUT);
+ if (mHoldingDreamSuspendBlocker) {
+ mDreamSuspendBlocker.release();
+ mHoldingDreamSuspendBlocker = false;
+ }
+ }
+ }
}
// Start dreaming if needed.
@@ -1799,7 +2057,7 @@ public final class PowerManagerService extends SystemService
}
// Dream has ended or will be stopped. Update the power state.
- if (isItBedTimeYetLocked()) {
+ if (isItBedTimeYetLocked() && !mDreamsActivatedOnSleepByDefaultConfig) {
goToSleepNoUpdateLocked(SystemClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT, 0, Process.SYSTEM_UID);
updatePowerStateLocked();
@@ -2172,6 +2430,11 @@ public final class PowerManagerService extends SystemService
if (mScreenBrightnessBoostInProgress) {
return true;
}
+
+ if (mSandmanSummoned) {
+ return true;
+ }
+
// Let the system suspend if the screen is off or dozing.
return false;
}
@@ -2494,6 +2757,14 @@ public final class PowerManagerService extends SystemService
}
}
+ private void setUserInactiveOverrideFromWindowManagerInternal() {
+ synchronized (mLock) {
+ mUserInactiveOverrideFromWindowManager = true;
+ mDirty |= DIRTY_USER_ACTIVITY;
+ updatePowerStateLocked();
+ }
+ }
+
private void setUserActivityTimeoutOverrideFromWindowManagerInternal(long timeoutMillis) {
synchronized (mLock) {
if (mUserActivityTimeoutOverrideFromWindowManager != timeoutMillis) {
@@ -2683,6 +2954,8 @@ public final class PowerManagerService extends SystemService
+ mScreenBrightnessOverrideFromWindowManager);
pw.println(" mUserActivityTimeoutOverrideFromWindowManager="
+ mUserActivityTimeoutOverrideFromWindowManager);
+ pw.println(" mUserInactiveOverrideFromWindowManager="
+ + mUserInactiveOverrideFromWindowManager);
pw.println(" mTemporaryScreenBrightnessSettingOverride="
+ mTemporaryScreenBrightnessSettingOverride);
pw.println(" mTemporaryScreenAutoBrightnessAdjustmentSettingOverride="
@@ -2757,7 +3030,7 @@ public final class PowerManagerService extends SystemService
@Override
public void onReceive(Context context, Intent intent) {
synchronized (mLock) {
- scheduleSandmanLocked();
+ scheduleSandmanLocked(true);
}
}
}
@@ -2814,11 +3087,20 @@ public final class PowerManagerService extends SystemService
handleUserActivityTimeout();
break;
case MSG_SANDMAN:
- handleSandman();
+ boolean fromDreamService = msg.arg1 == 1;
+ handleSandman(fromDreamService);
break;
case MSG_SCREEN_BRIGHTNESS_BOOST_TIMEOUT:
handleScreenBrightnessBoostTimeout();
break;
+ case MSG_WAKE_UP:
+ cleanupProximity();
+ ((Runnable) msg.obj).run();
+ break;
+ case MSG_SANDMAN_TIMEOUT:
+ Slog.w(TAG, "Sandman unresponsive, releasing suspend blocker");
+ handleSandman(true);
+ break;
}
}
}
@@ -2998,6 +3280,22 @@ public final class PowerManagerService extends SystemService
}
}
+ private void cleanupProximity() {
+ synchronized (mProximityWakeLock) {
+ cleanupProximityLocked();
+ }
+ }
+
+ private void cleanupProximityLocked() {
+ if (mProximityWakeLock.isHeld()) {
+ mProximityWakeLock.release();
+ }
+ if (mProximityListener != null) {
+ mSensorManager.unregisterListener(mProximityListener);
+ mProximityListener = null;
+ }
+ }
+
private final class BinderService extends IPowerManager.Stub {
@Override // Binder call
public void acquireWakeLockWithUid(IBinder lock, int flags, String tag,
@@ -3043,6 +3341,29 @@ public final class PowerManagerService extends SystemService
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
+
+ try {
+ if (mAppOps != null &&
+ mAppOps.checkOperation(AppOpsManager.OP_WAKE_LOCK, uid, packageName)
+ != AppOpsManager.MODE_ALLOWED) {
+
+ // If this app is whitelisted as "allow-in-power-save" then always allow!
+ // Current impl only looks at system-loaded ones, if we want to also include
+ // user apps which have been manually set, we would use IDeviceIdleController
+ if (!SystemConfig.getInstance().getAllowInPowerSave().contains(packageName)) {
+ Slog.d(TAG, "acquireWakeLock: ignoring request from " + packageName);
+ // For (ignore) accounting purposes
+ mAppOps.noteOperation(AppOpsManager.OP_WAKE_LOCK, uid, packageName);
+ // silent return
+ return;
+ } else {
+ Slog.d(TAG, "wake lock requested to be ignored but " + packageName
+ + " is marked to opt-out of all power save restrictions.");
+ }
+ }
+ } catch (RemoteException ignored) {
+ }
+
final long ident = Binder.clearCallingIdentity();
try {
acquireWakeLockInternal(lock, flags, tag, packageName, ws, historyTag, uid, pid);
@@ -3153,7 +3474,46 @@ public final class PowerManagerService extends SystemService
}
@Override // Binder call
- public void wakeUp(long eventTime, String reason, String opPackageName) {
+ public void setKeyboardVisibility(boolean visible) {
+ synchronized (mLock) {
+ if (DEBUG_SPEW) {
+ Slog.d(TAG, "setKeyboardVisibility: " + visible);
+ }
+ if (mKeyboardVisible != visible) {
+ mKeyboardVisible = visible;
+ if (!visible) {
+ // If hiding keyboard, turn off leds
+ setKeyboardLight(false, 1);
+ setKeyboardLight(false, 2);
+ }
+ synchronized (mLock) {
+ mDirty |= DIRTY_USER_ACTIVITY;
+ updatePowerStateLocked();
+ }
+ }
+ }
+ }
+
+ @Override // Binder call
+ public void setKeyboardLight(boolean on, int key) {
+ if (key == 1) {
+ if (on)
+ mCapsLight.setColor(0x00ffffff);
+ else
+ mCapsLight.turnOff();
+ } else if (key == 2) {
+ if (on)
+ mFnLight.setColor(0x00ffffff);
+ else
+ mFnLight.turnOff();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ private void wakeUp(final long eventTime, final String reason, final String opPackageName,
+ boolean checkProximity) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
}
@@ -3162,15 +3522,89 @@ public final class PowerManagerService extends SystemService
android.Manifest.permission.DEVICE_POWER, null);
final int uid = Binder.getCallingUid();
- final long ident = Binder.clearCallingIdentity();
- try {
- wakeUpInternal(eventTime, reason, uid, opPackageName, uid);
- } finally {
- Binder.restoreCallingIdentity(ident);
+ Runnable r = new Runnable() {
+ @Override
+ public void run() {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ wakeUpInternal(eventTime, reason, uid, opPackageName, uid);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ };
+ if (checkProximity) {
+ runWithProximityCheck(r);
+ } else {
+ r.run();
+ }
+ }
+
+ private void runWithProximityCheck(Runnable r) {
+ if (mHandler.hasMessages(MSG_WAKE_UP)) {
+ // There is already a message queued;
+ return;
+ }
+
+ TelephonyManager tm = (TelephonyManager)mContext.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ boolean hasIncomingCall = tm.getCallState() == TelephonyManager.CALL_STATE_RINGING;
+
+ if (mProximityWakeSupported && mProximityWakeEnabled && mProximitySensor != null
+ && !hasIncomingCall) {
+ Message msg = mHandler.obtainMessage(MSG_WAKE_UP);
+ msg.obj = r;
+ mHandler.sendMessageDelayed(msg, mProximityTimeOut);
+ runPostProximityCheck(r);
+ } else {
+ r.run();
+ }
+ }
+
+ private void runPostProximityCheck(final Runnable r) {
+ if (mSensorManager == null) {
+ r.run();
+ return;
+ }
+ synchronized (mProximityWakeLock) {
+ mProximityWakeLock.acquire();
+ mProximityListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ cleanupProximityLocked();
+ if (!mHandler.hasMessages(MSG_WAKE_UP)) {
+ Slog.w(TAG, "The proximity sensor took too long, wake event already triggered!");
+ return;
+ }
+ mHandler.removeMessages(MSG_WAKE_UP);
+ float distance = event.values[0];
+ if (distance >= PROXIMITY_NEAR_THRESHOLD ||
+ distance >= mProximitySensor.getMaximumRange()) {
+ r.run();
+ } else {
+ Slog.w(TAG, "Not waking up. Proximity sensor blocked.");
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+ };
+ mSensorManager.registerListener(mProximityListener,
+ mProximitySensor, SensorManager.SENSOR_DELAY_FASTEST);
}
}
@Override // Binder call
+ public void wakeUpWithProximityCheck(long eventTime, String reason, String opPackageName) {
+ wakeUp(eventTime, reason, opPackageName, true);
+ }
+
+ @Override // Binder call
+ public void wakeUp(long eventTime, String reason, String opPackageName) {
+ wakeUp(eventTime, reason, opPackageName, false);
+ }
+
+ @Override // Binder call
public void goToSleep(long eventTime, int reason, int flags) {
if (eventTime > SystemClock.uptimeMillis()) {
throw new IllegalArgumentException("event time must not be in the future");
@@ -3432,6 +3866,11 @@ public final class PowerManagerService extends SystemService
}
@Override // Binder call
+ public void cpuBoost(int duration) {
+ mPerf.cpuBoost(duration);
+ }
+
+ @Override // Binder call
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -3448,6 +3887,53 @@ public final class PowerManagerService extends SystemService
Binder.restoreCallingIdentity(ident);
}
}
+
+ /* updates the blocked uids, so if a wake lock is acquired for it
+ * can be released.
+ */
+ public void updateBlockedUids(int uid, boolean isBlocked) {
+ boolean changed = false;
+ if (DEBUG_SPEW) Slog.v(TAG, "updateBlockedUids: uid = " + uid +
+ "isBlocked = " + isBlocked);
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ if (DEBUG_SPEW) Slog.v(TAG, "UpdateBlockedUids is not allowed");
+ return;
+ }
+ synchronized(mLock) {
+ for (int index = 0; index < mWakeLocks.size(); index++) {
+ WakeLock wl = mWakeLocks.get(index);
+ if(wl != null) {
+ // update the wakelock for the blocked uid
+ if ((wl.mOwnerUid == uid || checkWorkSourceObjectId(uid, wl)) ||
+ (wl.mTag.startsWith("*sync*") && wl.mOwnerUid == Process.SYSTEM_UID)) {
+ if(updateBlockedWakelock(wl, isBlocked)){
+ changed = true;
+ }
+ }
+ }
+ }
+ if(isBlocked) {
+ mBlockedUids.add(new Integer(uid));
+ }
+ else {
+ mBlockedUids.clear();
+ }
+ if(changed){
+ mDirty |= DIRTY_WAKE_LOCKS;
+ updatePowerStateLocked();
+ }
+ }
+ }
+ }
+
+ private void setButtonBrightnessOverrideFromWindowManagerInternal(int brightness) {
+ synchronized (mLock) {
+ if (mButtonBrightnessOverrideFromWindowManager != brightness) {
+ mButtonBrightnessOverrideFromWindowManager = brightness;
+ mDirty |= DIRTY_SETTINGS;
+ updatePowerStateLocked();
+ }
+ }
}
private final class LocalService extends PowerManagerInternal {
@@ -3462,8 +3948,15 @@ public final class PowerManagerService extends SystemService
@Override
public void setButtonBrightnessOverrideFromWindowManager(int screenBrightness) {
- // Do nothing.
- // Button lights are not currently supported in the new implementation.
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DEVICE_POWER, null);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ setButtonBrightnessOverrideFromWindowManagerInternal(screenBrightness);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
}
@Override
@@ -3487,6 +3980,11 @@ public final class PowerManagerService extends SystemService
}
@Override
+ public void setUserInactiveOverrideFromWindowManager() {
+ setUserInactiveOverrideFromWindowManagerInternal();
+ }
+
+ @Override
public void setUserActivityTimeoutOverrideFromWindowManager(long timeoutMillis) {
setUserActivityTimeoutOverrideFromWindowManagerInternal(timeoutMillis);
}
@@ -3539,5 +4037,52 @@ public final class PowerManagerService extends SystemService
public void powerHint(int hintId, int data) {
powerHintInternal(hintId, data);
}
+
+ @Override
+ public boolean setPowerSaveMode(boolean mode) {
+ return setLowPowerModeInternal(mode);
+ }
+
+ @Override
+ public int getFeature(int featureId) {
+ return nativeGetFeature(featureId);
+ }
+
+ @Override
+ public void setFeature(int featureId, int data) {
+ nativeSetFeature(featureId, data);
+ }
+ }
+
+ private boolean updateBlockedWakelock(WakeLock wakeLock, boolean update) {
+ if (wakeLock != null && ((wakeLock.mFlags & PowerManager.WAKE_LOCK_LEVEL_MASK)
+ == PowerManager.PARTIAL_WAKE_LOCK )) {
+ if(wakeLock.mDisabled != update){
+ wakeLock.mDisabled = update;
+ if (wakeLock.mDisabled) {
+ // This wake lock is no longer being respected.
+ notifyWakeLockReleasedLocked(wakeLock);
+ } else {
+ notifyWakeLockAcquiredLocked(wakeLock);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean checkWorkSourceObjectId(int uid, WakeLock wl) {
+ try {
+ for (int index = 0; index < wl.mWorkSource.size(); index++) {
+ if (uid == wl.mWorkSource.get(index)) {
+ if (DEBUG_SPEW) Slog.v(TAG, "WS uid matched");
+ return true;
+ }
+ }
+ }
+ catch (Exception e) {
+ return false;
+ }
+ return false;
}
}
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index dd8648d..36a9ca2 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2013 The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,6 +22,7 @@ import android.app.ActivityManagerNative;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.IActivityManager;
+import android.app.KeyguardManager;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.IBluetoothManager;
@@ -32,7 +34,13 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.os.FileUtils;
import android.os.Handler;
+import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -44,19 +52,32 @@ import android.os.Vibrator;
import android.os.SystemVibrator;
import android.os.storage.IMountService;
import android.os.storage.IMountShutdownObserver;
+import android.provider.Settings;
import android.system.ErrnoException;
import android.system.Os;
+import android.widget.ListView;
import com.android.internal.telephony.ITelephony;
import com.android.server.pm.PackageManagerService;
-
+import com.android.server.power.PowerManagerService;
import android.util.Log;
+import android.view.IWindowManager;
import android.view.WindowManager;
+import java.lang.reflect.Method;
+
+import cyanogenmod.providers.CMSettings;
+import dalvik.system.PathClassLoader;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.OutputStreamWriter;
+
+import org.cyanogenmod.internal.util.ThemeUtils;
public final class ShutdownThread extends Thread {
// constants
@@ -74,6 +95,8 @@ public final class ShutdownThread extends Thread {
private static final int RADIO_STOP_PERCENT = 18;
private static final int MOUNT_SERVICE_STOP_PERCENT = 20;
+ private static final String SOFT_REBOOT = "soft_reboot";
+
// length of vibration before shutting down
private static final int SHUTDOWN_VIBRATE_MS = 500;
@@ -85,7 +108,11 @@ public final class ShutdownThread extends Thread {
private static final String UNCRYPT_STATUS_FILE = "/cache/recovery/uncrypt_status";
private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file";
+ // recovery command
+ private static File RECOVERY_COMMAND_FILE = new File("/cache/recovery/command");
+
private static boolean mReboot;
+ private static boolean mRebootWipe = false;
private static boolean mRebootSafeMode;
private static boolean mRebootUpdate;
private static String mRebootReason;
@@ -111,10 +138,20 @@ public final class ShutdownThread extends Thread {
private PowerManager.WakeLock mCpuWakeLock;
private PowerManager.WakeLock mScreenWakeLock;
private Handler mHandler;
+ private static MediaPlayer mMediaPlayer;
+ private static final String OEM_BOOTANIMATION_FILE = "/oem/media/shutdownanimation.zip";
+ private static final String SYSTEM_BOOTANIMATION_FILE = "/system/media/shutdownanimation.zip";
+ private static final String SYSTEM_ENCRYPTED_BOOTANIMATION_FILE = "/system/media/shutdownanimation-encrypted.zip";
+
+ private static final String SHUTDOWN_MUSIC_FILE = "/system/media/shutdown.wav";
+ private static final String OEM_SHUTDOWN_MUSIC_FILE = "/oem/media/shutdown.wav";
+
+ private boolean isShutdownMusicPlaying = false;
private static AlertDialog sConfirmDialog;
private ProgressDialog mProgressDialog;
+ private static AudioManager mAudioManager;
private ShutdownThread() {
}
@@ -127,9 +164,20 @@ public final class ShutdownThread extends Thread {
* @param confirm true if user confirmation is needed before shutting down.
*/
public static void shutdown(final Context context, boolean confirm) {
+ final Context uiContext = getUiContext(context);
mReboot = false;
mRebootSafeMode = false;
- shutdownInner(context, confirm);
+ shutdownInner(uiContext, confirm);
+ }
+
+ private static boolean isAdvancedRebootPossible(final Context context) {
+ KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
+ boolean keyguardLocked = km.inKeyguardRestrictedInputMode() && km.isKeyguardSecure();
+ boolean advancedRebootEnabled = CMSettings.Secure.getInt(context.getContentResolver(),
+ CMSettings.Secure.ADVANCED_REBOOT, 0) == 1;
+ boolean isPrimaryUser = UserHandle.getCallingUserId() == UserHandle.USER_OWNER;
+
+ return advancedRebootEnabled && !keyguardLocked && isPrimaryUser;
}
static void shutdownInner(final Context context, boolean confirm) {
@@ -142,33 +190,90 @@ public final class ShutdownThread extends Thread {
}
}
+ boolean showRebootOption = false;
+
+ String[] actionsArray;
+ String actions = CMSettings.Secure.getStringForUser(context.getContentResolver(),
+ CMSettings.Secure.POWER_MENU_ACTIONS, UserHandle.USER_CURRENT);
+ if (actions == null) {
+ actionsArray = context.getResources().getStringArray(
+ com.android.internal.R.array.config_globalActionsList);
+ } else {
+ actionsArray = actions.split("\\|");
+ }
+
+ for (int i = 0; i < actionsArray.length; i++) {
+ if (actionsArray[i].equals("reboot")) {
+ showRebootOption = true;
+ break;
+ }
+ }
final int longPressBehavior = context.getResources().getInteger(
com.android.internal.R.integer.config_longPressOnPowerBehavior);
- final int resourceId = mRebootSafeMode
+ int resourceId = mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_confirm
: (longPressBehavior == 2
? com.android.internal.R.string.shutdown_confirm_question
: com.android.internal.R.string.shutdown_confirm);
+ if (showRebootOption && !mRebootSafeMode) {
+ resourceId = com.android.internal.R.string.reboot_confirm;
+ }
Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
if (confirm) {
final CloseDialogReceiver closer = new CloseDialogReceiver(context);
+ final boolean advancedReboot = isAdvancedRebootPossible(context);
+ final Context uiContext = getUiContext(context);
+
if (sConfirmDialog != null) {
sConfirmDialog.dismiss();
+ sConfirmDialog = null;
}
- sConfirmDialog = new AlertDialog.Builder(context)
+ AlertDialog.Builder confirmDialogBuilder = new AlertDialog.Builder(uiContext)
.setTitle(mRebootSafeMode
? com.android.internal.R.string.reboot_safemode_title
- : com.android.internal.R.string.power_off)
- .setMessage(resourceId)
- .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
+ : showRebootOption
+ ? com.android.internal.R.string.reboot_title
+ : com.android.internal.R.string.power_off);
+
+ if (!advancedReboot || mRebootSafeMode) {
+ confirmDialogBuilder.setMessage(resourceId);
+ } else {
+ confirmDialogBuilder
+ .setSingleChoiceItems(com.android.internal.R.array.shutdown_reboot_options,
+ 0, null);
+ }
+
+ confirmDialogBuilder.setPositiveButton(com.android.internal.R.string.yes,
+ new DialogInterface.OnClickListener() {
+ @Override
public void onClick(DialogInterface dialog, int which) {
+ if (!mRebootSafeMode && advancedReboot) {
+ boolean softReboot = false;
+ ListView reasonsList = ((AlertDialog)dialog).getListView();
+ int selected = reasonsList.getCheckedItemPosition();
+ if (selected != ListView.INVALID_POSITION) {
+ String actions[] = context.getResources().getStringArray(
+ com.android.internal.R.array.shutdown_reboot_actions);
+ if (selected >= 0 && selected < actions.length) {
+ mRebootReason = actions[selected];
+ if (actions[selected].equals(SOFT_REBOOT)) {
+ doSoftReboot();
+ return;
+ }
+ }
+ }
+
+ mReboot = true;
+ }
beginShutdownSequence(context);
- }
- })
- .setNegativeButton(com.android.internal.R.string.no, null)
- .create();
+ }
+ });
+
+ confirmDialogBuilder.setNegativeButton(com.android.internal.R.string.no, null);
+ sConfirmDialog = confirmDialogBuilder.create();
+
closer.dialog = sConfirmDialog;
sConfirmDialog.setOnDismissListener(closer);
sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
@@ -178,6 +283,18 @@ public final class ShutdownThread extends Thread {
}
}
+ private static void doSoftReboot() {
+ try {
+ final IActivityManager am =
+ ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
+ if (am != null) {
+ am.restart();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "failure trying to perform soft reboot", e);
+ }
+ }
+
private static class CloseDialogReceiver extends BroadcastReceiver
implements DialogInterface.OnDismissListener {
private Context mContext;
@@ -209,13 +326,35 @@ public final class ShutdownThread extends Thread {
* @param confirm true if user confirmation is needed before shutting down.
*/
public static void reboot(final Context context, String reason, boolean confirm) {
+ final Context uiContext = getUiContext(context);
mReboot = true;
mRebootSafeMode = false;
mRebootUpdate = false;
mRebootReason = reason;
- shutdownInner(context, confirm);
+ shutdownInner(uiContext, confirm);
}
+ private static String getShutdownMusicFilePath() {
+ final String[] fileName = {OEM_SHUTDOWN_MUSIC_FILE, SHUTDOWN_MUSIC_FILE};
+ File checkFile = null;
+ for(String music : fileName) {
+ checkFile = new File(music);
+ if (checkFile.exists()) {
+ return music;
+ }
+ }
+ return null;
+ }
+
+ private static void lockDevice() {
+ IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager
+ .getService(Context.WINDOW_SERVICE));
+ try {
+ wm.updateRotation(false, false);
+ } catch (RemoteException e) {
+ Log.w(TAG, "boot animation can not lock device!");
+ }
+ }
/**
* Request a reboot into safe mode. Must be called from a Looper thread in which its UI
* is shown.
@@ -262,6 +401,13 @@ public final class ShutdownThread extends Thread {
// UI: spinning circle only (no progress bar)
if (PowerManager.REBOOT_RECOVERY.equals(mRebootReason)) {
mRebootUpdate = new File(UNCRYPT_PACKAGE_FILE).exists();
+ if (RECOVERY_COMMAND_FILE.exists()) {
+ try {
+ mRebootWipe = new String(FileUtils.readTextFile(
+ RECOVERY_COMMAND_FILE, 0, null)).contains("wipe");
+ } catch (IOException e) {
+ }
+ }
if (mRebootUpdate) {
pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
pd.setMessage(context.getText(
@@ -271,22 +417,41 @@ public final class ShutdownThread extends Thread {
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setProgress(0);
pd.setIndeterminate(false);
- } else {
+ } else if (mRebootWipe) {
// Factory reset path. Set the dialog message accordingly.
pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
pd.setMessage(context.getText(
com.android.internal.R.string.reboot_to_reset_message));
pd.setIndeterminate(true);
+ } else {
+ pd.setTitle(context.getText(com.android.internal.R.string.reboot_title));
+ pd.setMessage(context.getText(com.android.internal.R.string.reboot_progress));
+ pd.setIndeterminate(true);
}
} else {
- pd.setTitle(context.getText(com.android.internal.R.string.power_off));
- pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
+ if (mReboot) {
+ pd.setTitle(context.getText(com.android.internal.R.string.reboot_title));
+ pd.setMessage(context.getText(com.android.internal.R.string.reboot_progress));
+ } else {
+ pd.setTitle(context.getText(com.android.internal.R.string.power_off));
+ pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
+ }
pd.setIndeterminate(true);
}
- pd.setCancelable(false);
- pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
- pd.show();
+ //acquire audio focus to make the other apps to stop playing muisc
+ mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mAudioManager.requestAudioFocus(null,
+ AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
+
+ if (!checkAnimationFileExist()) {
+ // throw up an indeterminate system dialog to indicate radio is
+ // shutting down.
+ pd.setCancelable(false);
+ pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+
+ pd.show();
+ }
sInstance.mProgressDialog = pd;
sInstance.mContext = context;
@@ -417,6 +582,40 @@ public final class ShutdownThread extends Thread {
sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
}
+ String shutDownFile = null;
+
+ //showShutdownAnimation() is called from here to sync
+ //music and animation properly
+ if(checkAnimationFileExist()) {
+ lockDevice();
+ showShutdownAnimation();
+
+ if (!isSilentMode()
+ && (shutDownFile = getShutdownMusicFilePath()) != null) {
+ isShutdownMusicPlaying = true;
+ shutdownMusicHandler.obtainMessage(0, shutDownFile).sendToTarget();
+ }
+ }
+
+ Log.i(TAG, "wait for shutdown music");
+ final long endTimeForMusic = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
+ synchronized (mActionDoneSync) {
+ while (isShutdownMusicPlaying) {
+ long delay = endTimeForMusic - SystemClock.elapsedRealtime();
+ if (delay <= 0) {
+ Log.w(TAG, "play shutdown music timeout!");
+ break;
+ }
+ try {
+ mActionDoneSync.wait(delay);
+ } catch (InterruptedException e) {
+ }
+ }
+ if (!isShutdownMusicPlaying) {
+ Log.i(TAG, "play shutdown music complete.");
+ }
+ }
+
// Shutdown radios.
shutdownRadios(MAX_RADIO_WAIT_TIME);
if (mRebootUpdate) {
@@ -431,6 +630,16 @@ public final class ShutdownThread extends Thread {
}
};
+ final String cryptoStatus = SystemProperties.get("ro.crypto.state", "unsupported");
+ final boolean isEncrypted = "encrypted".equalsIgnoreCase(cryptoStatus);
+
+ if (mRebootUpdate && isEncrypted) {
+ sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
+
+ // If it's to reboot to install update, invoke uncrypt via init service.
+ uncrypt();
+ }
+
Log.i(TAG, "Shutting down MountService");
// Set initial variables and time out time.
@@ -466,12 +675,6 @@ public final class ShutdownThread extends Thread {
}
}
}
- if (mRebootUpdate) {
- sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
-
- // If it's to reboot to install update, invoke uncrypt via init service.
- uncrypt();
- }
rebootOrShutdown(mContext, mReboot, mRebootReason);
}
@@ -507,11 +710,10 @@ public final class ShutdownThread extends Thread {
ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
final IBluetoothManager bluetooth =
IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
- BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
+ BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
try {
- nfcOff = nfc == null ||
- nfc.getState() == NfcAdapter.STATE_OFF;
+ nfcOff = nfc == null || nfc.getState() == NfcAdapter.STATE_OFF;
if (!nfcOff) {
Log.w(TAG, "Turning off NFC...");
nfc.disable(false); // Don't persist new state
@@ -619,6 +821,7 @@ public final class ShutdownThread extends Thread {
* @param reason reason for reboot
*/
public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
+ deviceRebootOrShutdown(reboot, reason);
if (reboot) {
Log.i(TAG, "Rebooting, reason: " + reason);
PowerManagerService.lowLevelReboot(reason);
@@ -722,4 +925,90 @@ public final class ShutdownThread extends Thread {
Log.w(TAG, "Timed out waiting for uncrypt.");
}
}
+
+ private static void deviceRebootOrShutdown(boolean reboot, String reason) {
+ Class<?> cl;
+ String deviceShutdownClassName = "com.qti.server.power.ShutdownOem";
+ try {
+ cl = Class.forName(deviceShutdownClassName);
+ Method m;
+ try {
+ m = cl.getMethod("rebootOrShutdown", new Class[] {boolean.class, String.class});
+ m.invoke(cl.newInstance(), reboot, reason);
+ } catch (NoSuchMethodException ex) {
+ Log.e(TAG, "rebootOrShutdown method not found in class " + deviceShutdownClassName);
+ } catch (Exception ex) {
+ Log.e(TAG, "Unknown exception hit while trying to invode rebootOrShutdown");
+ }
+ } catch (ClassNotFoundException e) {
+ Log.e(TAG, "Unable to find class " + deviceShutdownClassName);
+ } catch (Exception e) {
+ Log.e(TAG, "Unknown exception while trying to invoke rebootOrShutdown");
+ }
+ }
+
+ private static boolean checkAnimationFileExist() {
+ if (new File(OEM_BOOTANIMATION_FILE).exists()
+ || new File(SYSTEM_BOOTANIMATION_FILE).exists()
+ || new File(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE).exists())
+ return true;
+ else
+ return false;
+ }
+
+ private static boolean isSilentMode() {
+ return mAudioManager.isSilentMode();
+ }
+
+ private static void showShutdownAnimation() {
+ /*
+ * When boot completed, "service.bootanim.exit" property is set to 1.
+ * Bootanimation checks this property to stop showing the boot animation.
+ * Since we use the same code for shutdown animation, we
+ * need to reset this property to 0. If this is not set to 0 then shutdown
+ * will stop and exit after displaying the first frame of the animation
+ */
+ SystemProperties.set("service.bootanim.exit", "0");
+
+ SystemProperties.set("ctl.start", "bootanim");
+ }
+
+ private Handler shutdownMusicHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ String path = (String) msg.obj;
+ mMediaPlayer = new MediaPlayer();
+ try
+ {
+ mMediaPlayer.reset();
+ mMediaPlayer.setDataSource(path);
+ mMediaPlayer.prepare();
+ mMediaPlayer.start();
+ mMediaPlayer.setOnCompletionListener(new OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ synchronized (mActionDoneSync) {
+ isShutdownMusicPlaying = false;
+ mActionDoneSync.notifyAll();
+ }
+ }
+ });
+ } catch (IOException e) {
+ Log.d(TAG, "play shutdown music error:" + e);
+ }
+ }
+ };
+
+ private static Context getUiContext(Context context) {
+ Context uiContext = null;
+ if (context != null) {
+ uiContext = ThemeUtils.createUiContext(context);
+ if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION)) {
+ uiContext.setTheme(com.android.internal.R.style.Theme_Leanback_Dialog_Alert);
+ } else {
+ uiContext.setTheme(com.android.internal.R.style.Theme_Power_Dialog);
+ }
+ }
+ return uiContext != null ? uiContext : context;
+ }
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index e9ace29..0e51953 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -249,7 +249,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub {
*/
@Override
public void disable2(int what, IBinder token, String pkg) {
- disableForUser(what, token, pkg, mCurrentUserId);
+ disable2ForUser(what, token, pkg, mCurrentUserId);
}
/**
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
index a035826..a775c5a 100644
--- a/services/core/java/com/android/server/tv/TvInputHal.java
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -146,7 +146,7 @@ final class TvInputHal implements Handler.Callback {
private void firstFrameCapturedFromNative(int deviceId, int streamId) {
mHandler.sendMessage(
- mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, streamId));
+ mHandler.obtainMessage(EVENT_FIRST_FRAME_CAPTURED, deviceId, streamId));
}
// Handler.Callback implementation
diff --git a/services/core/java/com/android/server/twilight/TwilightService.java b/services/core/java/com/android/server/twilight/TwilightService.java
index a71961c..2185b19 100644
--- a/services/core/java/com/android/server/twilight/TwilightService.java
+++ b/services/core/java/com/android/server/twilight/TwilightService.java
@@ -309,6 +309,13 @@ public final class TwilightService extends SystemService {
}
sendEmptyMessageDelayed(MSG_ENABLE_LOCATION_UPDATES, mLastUpdateInterval);
}
+
+ if (!networkLocationEnabled && mLocation == null) {
+ if (DEBUG) {
+ Slog.d(TAG, "Network location unavailable");
+ }
+ retrieveLocation();
+ }
break;
case MSG_DO_TWILIGHT_UPDATE:
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 7784884..ee926a4 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -106,6 +106,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
static final int MAX_WALLPAPER_COMPONENT_LOG_LENGTH = 128;
static final String WALLPAPER = "wallpaper";
static final String WALLPAPER_INFO = "wallpaper_info.xml";
+ static final String KEYGUARD_WALLPAPER = "keyguard_wallpaper";
+ static final String KEYGUARD_WALLPAPER_INFO = "keyguard_wallpaper_info.xml";
/**
* Observes the wallpaper for changes and notifies all IWallpaperServiceCallbacks
@@ -116,17 +118,21 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
private class WallpaperObserver extends FileObserver {
final WallpaperData mWallpaper;
+ final KeyguardWallpaperData mKeyguardWallpaper;
final File mWallpaperDir;
final File mWallpaperFile;
final File mWallpaperInfoFile;
+ final File mKeyguardWallpaperFile;
- public WallpaperObserver(WallpaperData wallpaper) {
+ public WallpaperObserver(WallpaperData wallpaper, KeyguardWallpaperData keyguardWallpaper) {
super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
CLOSE_WRITE | MOVED_TO | DELETE | DELETE_SELF);
mWallpaperDir = getWallpaperDir(wallpaper.userId);
mWallpaper = wallpaper;
mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
mWallpaperInfoFile = new File(mWallpaperDir, WALLPAPER_INFO);
+ mKeyguardWallpaper = keyguardWallpaper;
+ mKeyguardWallpaperFile = new File(mWallpaperDir, KEYGUARD_WALLPAPER);
}
@Override
@@ -145,6 +151,12 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
Binder.restoreCallingIdentity(origId);
}
if (mWallpaperFile.equals(changedFile)) {
+ // changing the wallpaper means we'll need to back up the new one
+ long origId = Binder.clearCallingIdentity();
+ BackupManager bm = new BackupManager(mContext);
+ bm.dataChanged();
+ Binder.restoreCallingIdentity(origId);
+
notifyCallbacksLocked(mWallpaper);
final boolean written = (event == CLOSE_WRITE || event == MOVED_TO);
if (mWallpaper.wallpaperComponent == null
@@ -157,6 +169,13 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
false, mWallpaper, null);
saveSettingsLocked(mWallpaper);
}
+ } else if (mKeyguardWallpaperFile.equals(changedFile)) {
+ notifyCallbacksLocked(mKeyguardWallpaper);
+ if (event == CLOSE_WRITE
+ || mKeyguardWallpaper.imageWallpaperPending) {
+ mKeyguardWallpaper.imageWallpaperPending = false;
+ saveSettingsLocked(mKeyguardWallpaper);
+ }
}
}
}
@@ -176,6 +195,8 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
final ComponentName mImageWallpaper;
SparseArray<WallpaperData> mWallpaperMap = new SparseArray<WallpaperData>();
+ SparseArray<KeyguardWallpaperData> mKeyguardWallpaperMap
+ = new SparseArray<KeyguardWallpaperData>();
int mCurrentUserId;
@@ -227,6 +248,37 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ static class KeyguardWallpaperData {
+
+ int userId;
+
+ File wallpaperFile;
+
+ /**
+ * Client is currently writing a new image wallpaper.
+ */
+ boolean imageWallpaperPending;
+
+ /**
+ * Resource name if using a picture from the wallpaper gallery
+ */
+ String name = "";
+
+ /**
+ * List of callbacks registered they should each be notified when the wallpaper is changed.
+ */
+ private RemoteCallbackList<IWallpaperManagerCallback> callbacks
+ = new RemoteCallbackList<IWallpaperManagerCallback>();
+
+ int width = -1;
+ int height = -1;
+
+ KeyguardWallpaperData(int userId) {
+ this.userId = userId;
+ wallpaperFile = new File(getWallpaperDir(userId), KEYGUARD_WALLPAPER);
+ }
+ }
+
class WallpaperConnection extends IWallpaperConnection.Stub
implements ServiceConnection {
final WallpaperInfo mInfo;
@@ -486,6 +538,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
mMonitor.register(context, null, UserHandle.ALL, true);
getWallpaperDir(UserHandle.USER_OWNER).mkdirs();
loadSettingsLocked(UserHandle.USER_OWNER);
+ loadKeyguardSettingsLocked(UserHandle.USER_OWNER);
}
private static File getWallpaperDir(int userId) {
@@ -504,8 +557,9 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
public void systemRunning() {
if (DEBUG) Slog.v(TAG, "systemReady");
WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_OWNER);
+ KeyguardWallpaperData keyguardWallpaper = mKeyguardWallpaperMap.get(UserHandle.USER_OWNER);
switchWallpaper(wallpaper, null);
- wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
+ wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper, keyguardWallpaper);
wallpaper.wallpaperObserver.startWatching();
IntentFilter userFilter = new IntentFilter();
@@ -572,6 +626,7 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
wallpaper.wallpaperObserver = null;
}
mWallpaperMap.remove(userId);
+ mKeyguardWallpaperMap.remove(userId);
}
}
}
@@ -584,6 +639,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
wallpaperFile.delete();
File wallpaperInfoFile = new File(getWallpaperDir(userId), WALLPAPER_INFO);
wallpaperInfoFile.delete();
+ File keyguardWallpaperFile = new File(getWallpaperDir(userId), KEYGUARD_WALLPAPER);
+ keyguardWallpaperFile.delete();
+ File keyguardWallpaperInfoFile = new File(getWallpaperDir(userId),
+ KEYGUARD_WALLPAPER_INFO);
+ keyguardWallpaperInfoFile.delete();
}
}
@@ -591,9 +651,10 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
synchronized (mLock) {
mCurrentUserId = userId;
WallpaperData wallpaper = getWallpaperSafeLocked(userId);
+ KeyguardWallpaperData keyguardWallpaper = getKeyguardWallpaperSafeLocked(userId);
// Not started watching yet, in case wallpaper data was loaded for other reasons.
if (wallpaper.wallpaperObserver == null) {
- wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
+ wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper, keyguardWallpaper);
wallpaper.wallpaperObserver.startWatching();
}
switchWallpaper(wallpaper, reply);
@@ -669,6 +730,40 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ /**
+ * @hide
+ */
+ public void clearKeyguardWallpaper() {
+ if (DEBUG) Slog.v(TAG, "clearWallpaper");
+ synchronized (mLock) {
+ clearKeyguardWallpaperLocked(UserHandle.getCallingUserId(), null);
+ }
+ }
+
+ void clearKeyguardWallpaperLocked(int userId, IRemoteCallback reply) {
+ KeyguardWallpaperData wallpaper = mKeyguardWallpaperMap.get(userId);
+ final long ident = Binder.clearCallingIdentity();
+ wallpaper.imageWallpaperPending = false;
+ wallpaper.height = -1;
+ wallpaper.width = -1;
+ wallpaper.name = "";
+
+ File f = new File(getWallpaperDir(userId), KEYGUARD_WALLPAPER);
+ if (f.exists()) {
+ f.delete();
+ }
+ if (userId != mCurrentUserId)
+ return;
+ Binder.restoreCallingIdentity(ident);
+
+ if (reply != null) {
+ try {
+ reply.sendResult(null);
+ } catch (RemoteException e1) {
+ }
+ }
+ }
+
public boolean hasNamedWallpaper(String name) {
synchronized (mLock) {
List<UserInfo> users;
@@ -837,6 +932,31 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ /** @hide */
+ public ParcelFileDescriptor getKeyguardWallpaper(IWallpaperManagerCallback cb,
+ Bundle outParams) {
+ synchronized (mLock) {
+ int wallpaperUserId = mCurrentUserId;
+ KeyguardWallpaperData wallpaper = mKeyguardWallpaperMap.get(wallpaperUserId);
+ try {
+ if (outParams != null) {
+ outParams.putInt("width", wallpaper.width);
+ outParams.putInt("height", wallpaper.height);
+ }
+ wallpaper.callbacks.register(cb);
+ File f = new File(getWallpaperDir(wallpaperUserId), KEYGUARD_WALLPAPER);
+ if (!f.exists()) {
+ return null;
+ }
+ return ParcelFileDescriptor.open(f, MODE_READ_ONLY);
+ } catch (FileNotFoundException e) {
+ /* Shouldn't happen as we check to see if the file exists */
+ Slog.w(TAG, "Error getting wallpaper", e);
+ }
+ return null;
+ }
+ }
+
public WallpaperInfo getWallpaperInfo() {
int userId = UserHandle.getCallingUserId();
synchronized (mLock) {
@@ -848,6 +968,15 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ /** @hide */
+ public boolean isKeyguardWallpaperSet() {
+ int userId = UserHandle.getCallingUserId();
+ synchronized (mLock) {
+ KeyguardWallpaperData data = mKeyguardWallpaperMap.get(userId);
+ return data.wallpaperFile.exists();
+ }
+ }
+
public ParcelFileDescriptor setWallpaper(String name, String callingPackage) {
checkPermission(android.Manifest.permission.SET_WALLPAPER);
if (!isWallpaperSupported(callingPackage)) {
@@ -901,6 +1030,58 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ public ParcelFileDescriptor setKeyguardWallpaper(String name, String callingPackage) {
+ checkPermission(android.Manifest.permission.SET_KEYGUARD_WALLPAPER);
+ if (!isWallpaperSupported(callingPackage)) {
+ return null;
+ }
+ synchronized (mLock) {
+ if (DEBUG) Slog.v(TAG, "setKeyguardWallpaper");
+ int userId = UserHandle.getCallingUserId();
+ KeyguardWallpaperData wallpaper = mKeyguardWallpaperMap.get(userId);
+ if (wallpaper == null) {
+ throw new IllegalStateException("Keyguard wallpaper not yet initialized for user "
+ + userId);
+ }
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ ParcelFileDescriptor pfd = updateKeyguardWallpaperBitmapLocked(name, wallpaper);
+ if (pfd != null) {
+ wallpaper.imageWallpaperPending = true;
+ }
+ return pfd;
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ public ParcelFileDescriptor updateKeyguardWallpaperBitmapLocked(String name,
+ KeyguardWallpaperData wallpaper) {
+ if (name == null) name = "";
+ try {
+ File dir = getWallpaperDir(wallpaper.userId);
+ if (!dir.exists()) {
+ dir.mkdir();
+ FileUtils.setPermissions(
+ dir.getPath(),
+ FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
+ -1, -1);
+ }
+ File file = new File(dir, KEYGUARD_WALLPAPER);
+ ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
+ MODE_CREATE|MODE_READ_WRITE|MODE_TRUNCATE);
+ if (!SELinux.restorecon(file)) {
+ return null;
+ }
+ wallpaper.name = name;
+ return fd;
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "Error setting wallpaper", e);
+ }
+ return null;
+ }
+
// ToDo: Remove this version of the function
public void setWallpaperComponent(ComponentName name) {
checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT);
@@ -1125,6 +1306,22 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
}
+ private void notifyCallbacksLocked(KeyguardWallpaperData wallpaper) {
+ final int n = wallpaper.callbacks.beginBroadcast();
+ for (int i = 0; i < n; i++) {
+ try {
+ wallpaper.callbacks.getBroadcastItem(i).onKeyguardWallpaperChanged();
+ } catch (RemoteException e) {
+
+ // The RemoteCallbackList will take care of removing
+ // the dead object for us.
+ }
+ }
+ wallpaper.callbacks.finishBroadcast();
+ final Intent intent = new Intent(Intent.ACTION_KEYGUARD_WALLPAPER_CHANGED);
+ mContext.sendBroadcastAsUser(intent, new UserHandle(mCurrentUserId));
+ }
+
private void checkPermission(String permission) {
if (PackageManager.PERMISSION_GRANTED!= mContext.checkCallingOrSelfPermission(permission)) {
throw new SecurityException("Access denied to process: " + Binder.getCallingPid()
@@ -1142,7 +1339,11 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
private static JournaledFile makeJournaledFile(int userId) {
- final String base = new File(getWallpaperDir(userId), WALLPAPER_INFO).getAbsolutePath();
+ return makeJournaledFile(WALLPAPER_INFO, userId);
+ }
+
+ private static JournaledFile makeJournaledFile(String name, int userId) {
+ final String base = new File(getWallpaperDir(userId), name).getAbsolutePath();
return new JournaledFile(new File(base), new File(base + ".tmp"));
}
@@ -1195,6 +1396,36 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ private void saveSettingsLocked(KeyguardWallpaperData wallpaper) {
+ JournaledFile journal = makeJournaledFile(KEYGUARD_WALLPAPER_INFO, wallpaper.userId);
+ FileOutputStream stream = null;
+ try {
+ stream = new FileOutputStream(journal.chooseForWrite(), false);
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(stream, "utf-8");
+ out.startDocument(null, true);
+
+ out.startTag(null, "kwp");
+ out.attribute(null, "width", Integer.toString(wallpaper.width));
+ out.attribute(null, "height", Integer.toString(wallpaper.height));
+ out.attribute(null, "name", wallpaper.name);
+ out.endTag(null, "kwp");
+
+ out.endDocument();
+ stream.close();
+ journal.commit();
+ } catch (IOException e) {
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException ex) {
+ // Ignore
+ }
+ journal.rollback();
+ }
+ }
+
private void migrateFromOld() {
File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
@@ -1232,6 +1463,16 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
return wallpaper;
}
+ private KeyguardWallpaperData getKeyguardWallpaperSafeLocked(int userId) {
+ KeyguardWallpaperData wallpaper = mKeyguardWallpaperMap.get(userId);
+ if (wallpaper == null) {
+ loadKeyguardSettingsLocked(userId);
+ wallpaper = mKeyguardWallpaperMap.get(userId);
+ }
+ return wallpaper;
+ }
+
+
private void loadSettingsLocked(int userId) {
if (DEBUG) Slog.v(TAG, "loadSettingsLocked");
@@ -1326,6 +1567,80 @@ public class WallpaperManagerService extends IWallpaperManager.Stub {
}
}
+ private void loadKeyguardSettingsLocked(int userId) {
+ if (DEBUG) Slog.v(TAG, "loadKeyguardSettingsLocked");
+
+ JournaledFile journal = makeJournaledFile(KEYGUARD_WALLPAPER_INFO, userId);
+ FileInputStream stream = null;
+ File file = journal.chooseForRead();
+ KeyguardWallpaperData keyguardWallpaper = mKeyguardWallpaperMap.get(userId);
+ if (keyguardWallpaper == null) {
+ keyguardWallpaper = new KeyguardWallpaperData(userId);
+ mKeyguardWallpaperMap.put(userId, keyguardWallpaper);
+ }
+ boolean success = false;
+ try {
+ stream = new FileInputStream(file);
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, null);
+
+ int type;
+ do {
+ type = parser.next();
+ if (type == XmlPullParser.START_TAG) {
+ String tag = parser.getName();
+ if ("kwp".equals(tag)) {
+ keyguardWallpaper.width = Integer.parseInt(parser.getAttributeValue(null,
+ "width"));
+ keyguardWallpaper.height = Integer.parseInt(parser
+ .getAttributeValue(null, "height"));
+ keyguardWallpaper.name = parser.getAttributeValue(null, "name");
+ if (DEBUG) {
+ Slog.v(TAG, "mWidth:" + keyguardWallpaper.width);
+ Slog.v(TAG, "mHeight:" + keyguardWallpaper.height);
+ Slog.v(TAG, "mName:" + keyguardWallpaper.name);
+ }
+ }
+ }
+ } while (type != XmlPullParser.END_DOCUMENT);
+ success = true;
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "no current wallpaper -- first boot?");
+ } catch (NullPointerException e) {
+ Slog.w(TAG, "failed parsing " + file + " " + e);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "failed parsing " + file + " " + e);
+ } catch (XmlPullParserException e) {
+ Slog.w(TAG, "failed parsing " + file + " " + e);
+ } catch (IOException e) {
+ Slog.w(TAG, "failed parsing " + file + " " + e);
+ } catch (IndexOutOfBoundsException e) {
+ Slog.w(TAG, "failed parsing " + file + " " + e);
+ }
+ try {
+ if (stream != null) {
+ stream.close();
+ }
+ } catch (IOException e) {
+ // Ignore
+ }
+
+ if (!success) {
+ keyguardWallpaper.width = -1;
+ keyguardWallpaper.height = -1;
+ keyguardWallpaper.name = "";
+ }
+
+ // We always want to have some reasonable width hint.
+ int baseSize = getMaximumSizeDimension();
+ if (keyguardWallpaper.width < baseSize) {
+ keyguardWallpaper.width = baseSize;
+ }
+ if (keyguardWallpaper.height < baseSize) {
+ keyguardWallpaper.height = baseSize;
+ }
+ }
+
private int getMaximumSizeDimension() {
WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
Display d = wm.getDefaultDisplay();
diff --git a/services/core/java/com/android/server/wm/BlurLayer.java b/services/core/java/com/android/server/wm/BlurLayer.java
new file mode 100644
index 0000000..b54ac9e
--- /dev/null
+++ b/services/core/java/com/android/server/wm/BlurLayer.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.view.DisplayInfo;
+import android.view.SurfaceControl;
+
+import java.io.PrintWriter;
+
+public class BlurLayer {
+ private static final String TAG = "BlurLayer";
+ private static final boolean DEBUG = false;
+
+ /** Reference to the owner of this object. */
+ final DisplayContent mDisplayContent;
+
+ /** Actual surface that blurs */
+ SurfaceControl mBlurSurface;
+
+ /** Last value passed to mBlurSurface.setBlur() */
+ float mBlur = 0;
+
+ /** Last value passed to mBlurSurface.setLayer() */
+ int mLayer = -1;
+
+ /** Next values to pass to mBlurSurface.setPosition() and mBlurSurface.setSize() */
+ Rect mBounds = new Rect();
+
+ /** Last values passed to mBlurSurface.setPosition() and mBlurSurface.setSize() */
+ Rect mLastBounds = new Rect();
+
+ /** True after mBlurSurface.show() has been called, false after mBlurSurface.hide(). */
+ private boolean mShowing = false;
+
+ /** Value of mBlur when beginning transition to mTargetBlur */
+ float mStartBlur = 0;
+
+ /** Final value of mBlur following transition */
+ float mTargetBlur = 0;
+
+ /** Time in units of SystemClock.uptimeMillis() at which the current transition started */
+ long mStartTime;
+
+ /** Time in milliseconds to take to transition from mStartBlur to mTargetBlur */
+ long mDuration;
+
+ /** Owning stack */
+ final TaskStack mStack;
+
+ BlurLayer(WindowManagerService service, TaskStack stack, DisplayContent displayContent) {
+ mStack = stack;
+ mDisplayContent = displayContent;
+ final int displayId = mDisplayContent.getDisplayId();
+ if (DEBUG) Slog.v(TAG, "Ctor: displayId=" + displayId);
+ SurfaceControl.openTransaction();
+ try {
+ if (WindowManagerService.DEBUG_SURFACE_TRACE) {
+ mBlurSurface = new WindowStateAnimator.SurfaceTrace(service.mFxSession,
+ "BlurSurface",
+ 16, 16, PixelFormat.OPAQUE,
+ SurfaceControl.FX_SURFACE_BLUR | SurfaceControl.HIDDEN);
+ } else {
+ mBlurSurface = new SurfaceControl(service.mFxSession, TAG,
+ 16, 16, PixelFormat.OPAQUE,
+ SurfaceControl.FX_SURFACE_BLUR | SurfaceControl.HIDDEN);
+ }
+ if (WindowManagerService.SHOW_TRANSACTIONS ||
+ WindowManagerService.SHOW_SURFACE_ALLOC) Slog.i(TAG,
+ " BLUR " + mBlurSurface + ": CREATE");
+ mBlurSurface.setLayerStack(displayId);
+ } catch (Exception e) {
+ Slog.e(WindowManagerService.TAG, "Exception creating Blur surface", e);
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
+ }
+
+ /** Return true if blur layer is showing */
+ boolean isBlurring() {
+ return mTargetBlur != 0;
+ }
+
+ /** Return true if in a transition period */
+ boolean isAnimating() {
+ return mTargetBlur != mBlur;
+ }
+
+ float getTargetBlur() {
+ return mTargetBlur;
+ }
+
+ void setLayer(int layer) {
+ if (mLayer != layer) {
+ mLayer = layer;
+ mBlurSurface.setLayer(layer);
+ }
+ }
+
+ int getLayer() {
+ return mLayer;
+ }
+
+ private void setBlur(float blur) {
+ if (mBlur != blur) {
+ if (DEBUG) Slog.v(TAG, "setBlur blur=" + blur);
+ try {
+ mBlurSurface.setBlur(blur);
+ if (blur == 0 && mShowing) {
+ if (DEBUG) Slog.v(TAG, "setBlur hiding");
+ mBlurSurface.hide();
+ mShowing = false;
+ } else if (blur > 0 && !mShowing) {
+ if (DEBUG) Slog.v(TAG, "setBlur showing");
+ mBlurSurface.show();
+ mShowing = true;
+ }
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failure setting blur immediately", e);
+ }
+ mBlur = blur;
+ }
+ }
+
+ void adjustBounds() {
+ final int dw, dh;
+ final float xPos, yPos;
+ if (!mStack.isFullscreen()) {
+ dw = mBounds.width();
+ dh = mBounds.height();
+ xPos = mBounds.left;
+ yPos = mBounds.top;
+ } else {
+ // Set surface size to screen size.
+ final DisplayInfo info = mDisplayContent.getDisplayInfo();
+ dw = info.logicalWidth;
+ dh = info.logicalHeight;
+ xPos = 0;
+ yPos = 0;
+ }
+
+ mBlurSurface.setPosition(xPos, yPos);
+ mBlurSurface.setSize(dw, dh);
+ mLastBounds.set(mBounds);
+ }
+
+ void setBounds(Rect bounds) {
+ mBounds.set(bounds);
+ if (isBlurring() && !mLastBounds.equals(bounds)) {
+ try {
+ SurfaceControl.openTransaction();
+ adjustBounds();
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Failure setting size", e);
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
+ }
+ }
+
+ /**
+ * @param duration The time to test.
+ * @return True if the duration would lead to an earlier end to the current animation.
+ */
+ private boolean durationEndsEarlier(long duration) {
+ return SystemClock.uptimeMillis() + duration < mStartTime + mDuration;
+ }
+
+ /** Jump to the end of the animation.
+ * NOTE: Must be called with Surface transaction open. */
+ void show() {
+ if (isAnimating()) {
+ if (DEBUG) Slog.v(TAG, "show: immediate");
+ show(mLayer, mTargetBlur, 0);
+ }
+ }
+
+ /**
+ * Begin an animation to a new dim value.
+ * NOTE: Must be called with Surface transaction open.
+ *
+ * @param layer The layer to set the surface to.
+ * @param blur The dim value to end at.
+ * @param duration How long to take to get there in milliseconds.
+ */
+ void show(int layer, float blur, long duration) {
+ if (DEBUG) Slog.v(TAG, "show: layer=" + layer + " blur=" + blur
+ + " duration=" + duration);
+ if (mBlurSurface == null) {
+ Slog.e(TAG, "show: no Surface");
+ // Make sure isAnimating() returns false.
+ mTargetBlur = mBlur = 0;
+ return;
+ }
+
+ if (!mLastBounds.equals(mBounds)) {
+ adjustBounds();
+ }
+ setLayer(layer);
+
+ long curTime = SystemClock.uptimeMillis();
+ final boolean animating = isAnimating();
+ if ((animating && (mTargetBlur != blur || durationEndsEarlier(duration)))
+ || (!animating && mBlur != blur)) {
+ if (duration <= 0) {
+ // No animation required, just set values.
+ setBlur(blur);
+ } else {
+ // Start or continue animation with new parameters.
+ mStartBlur = mBlur;
+ mStartTime = curTime;
+ mDuration = duration;
+ }
+ }
+ if (DEBUG) Slog.v(TAG, "show: mStartBlur=" + mStartBlur + " mStartTime=" + mStartTime);
+ mTargetBlur = blur;
+ }
+
+ /** Immediate hide.
+ * NOTE: Must be called with Surface transaction open. */
+ void hide() {
+ if (mShowing) {
+ if (DEBUG) Slog.v(TAG, "hide: immediate");
+ hide(0);
+ }
+ }
+
+ /**
+ * Gradually fade to transparent.
+ * NOTE: Must be called with Surface transaction open.
+ *
+ * @param duration Time to fade in milliseconds.
+ */
+ void hide(long duration) {
+ if (mShowing && (mTargetBlur != 0 || durationEndsEarlier(duration))) {
+ if (DEBUG) Slog.v(TAG, "hide: duration=" + duration);
+ show(mLayer, 0, duration);
+ }
+ }
+
+ /**
+ * Advance the dimming per the last #show(int, float, long) call.
+ * NOTE: Must be called with Surface transaction open.
+ *
+ * @return True if animation is still required after this step.
+ */
+ boolean stepAnimation() {
+ if (mBlurSurface == null) {
+ Slog.e(TAG, "stepAnimation: null Surface");
+ // Ensure that isAnimating() returns false;
+ mTargetBlur = mBlur = 0;
+ return false;
+ }
+
+ if (isAnimating()) {
+ final long curTime = SystemClock.uptimeMillis();
+ final float blurDelta = mTargetBlur - mStartBlur;
+ float blur = mStartBlur + blurDelta * (curTime - mStartTime) / mDuration;
+ if (blurDelta > 0 && blur > mTargetBlur ||
+ blurDelta < 0 && blur < mTargetBlur) {
+ // Don't exceed limits.
+ blur = mTargetBlur;
+ }
+ if (DEBUG) Slog.v(TAG, "stepAnimation: curTime=" + curTime + " blur=" + blur);
+ setBlur(blur);
+ }
+
+ return isAnimating();
+ }
+
+ /** Cleanup */
+ void destroySurface() {
+ if (DEBUG) Slog.v(TAG, "destroySurface.");
+ if (mBlurSurface != null) {
+ mBlurSurface.destroy();
+ mBlurSurface = null;
+ }
+ }
+
+ public void printTo(String prefix, PrintWriter pw) {
+ pw.print(prefix); pw.print("mBlurSurface="); pw.print(mBlurSurface);
+ pw.print(" mLayer="); pw.print(mLayer);
+ pw.print(" mBlur="); pw.println(mBlur);
+ pw.print(prefix); pw.print("mLastBounds="); pw.print(mLastBounds.toShortString());
+ pw.print(" mBounds="); pw.println(mBounds.toShortString());
+ pw.print(prefix); pw.print("Last animation: ");
+ pw.print(" mDuration="); pw.print(mDuration);
+ pw.print(" mStartTime="); pw.print(mStartTime);
+ pw.print(" curTime="); pw.println(SystemClock.uptimeMillis());
+ pw.print(prefix); pw.print(" mStartBlur="); pw.print(mStartBlur);
+ pw.print(" mTargetBlur="); pw.println(mTargetBlur);
+ }
+}
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index 7c2da2d..be3e922 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -56,7 +56,7 @@ class CircularDisplayMask {
int screenOffset, int maskThickness) {
mScreenSize = new Point();
display.getSize(mScreenSize);
- if (mScreenSize.x != mScreenSize.y) {
+ if (mScreenSize.x != mScreenSize.y + screenOffset) {
Slog.w(TAG, "Screen dimensions of displayId = " + display.getDisplayId() +
"are not equal, circularMask will not be drawn.");
mDimensionsUnequal = true;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4db0b1e..2edf552 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -116,6 +116,7 @@ class DisplayContent {
display.getDisplayInfo(mDisplayInfo);
isDefaultDisplay = mDisplayId == Display.DEFAULT_DISPLAY;
mService = service;
+ initializeDisplayBaseInfo();
}
int getDisplayId() {
@@ -176,6 +177,21 @@ class DisplayContent {
}
}
+ void initializeDisplayBaseInfo() {
+ synchronized(mDisplaySizeLock) {
+ // Bootstrap the default logical display from the display manager.
+ final DisplayInfo newDisplayInfo =
+ mService.mDisplayManagerInternal.getDisplayInfo(mDisplayId);
+ if (newDisplayInfo != null) {
+ mDisplayInfo.copyFrom(newDisplayInfo);
+ }
+ mBaseDisplayWidth = mInitialDisplayWidth = mDisplayInfo.logicalWidth;
+ mBaseDisplayHeight = mInitialDisplayHeight = mDisplayInfo.logicalHeight;
+ mBaseDisplayDensity = mInitialDisplayDensity = mDisplayInfo.logicalDensityDpi;
+ mBaseDisplayRect.set(0, 0, mBaseDisplayWidth, mBaseDisplayHeight);
+ }
+ }
+
void getLogicalDisplayRect(Rect out) {
// Uses same calculation as in LogicalDisplay#configureDisplayInTransactionLocked.
final int orientation = mDisplayInfo.rotation;
@@ -301,6 +317,35 @@ class DisplayContent {
}
}
+ boolean animateBlurLayers() {
+ boolean result = false;
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ result |= mStacks.get(stackNdx).animateBlurLayers();
+ }
+ return result;
+ }
+
+ void resetBlurring() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ mStacks.get(stackNdx).resetBlurringTag();
+ }
+ }
+
+ boolean isBlurring() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ if (mStacks.get(stackNdx).isBlurring()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void stopBlurringIfNeeded() {
+ for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+ mStacks.get(stackNdx).stopBlurringIfNeeded();
+ }
+ }
+
void close() {
for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
mStacks.get(stackNdx).close();
diff --git a/services/core/java/com/android/server/wm/DisplaySettings.java b/services/core/java/com/android/server/wm/DisplaySettings.java
index 01f878c..80526f2 100644
--- a/services/core/java/com/android/server/wm/DisplaySettings.java
+++ b/services/core/java/com/android/server/wm/DisplaySettings.java
@@ -79,17 +79,20 @@ public class DisplaySettings {
}
}
- public void setOverscanLocked(String name, int left, int top, int right, int bottom) {
+ public void setOverscanLocked(String uniqueId, String name, int left, int top, int right,
+ int bottom) {
if (left == 0 && top == 0 && right == 0 && bottom == 0) {
// Right now all we are storing is overscan; if there is no overscan,
// we have no need for the entry.
+ mEntries.remove(uniqueId);
+ // Legacy name might have been in used, so we need to clear it.
mEntries.remove(name);
return;
}
- Entry entry = mEntries.get(name);
+ Entry entry = mEntries.get(uniqueId);
if (entry == null) {
- entry = new Entry(name);
- mEntries.put(name, entry);
+ entry = new Entry(uniqueId);
+ mEntries.put(uniqueId, entry);
}
entry.overscanLeft = left;
entry.overscanTop = top;
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 7dd716e..7e5c807 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -51,6 +51,7 @@ class ScreenRotationAnimation {
BlackFrame mExitingBlackFrame;
BlackFrame mEnteringBlackFrame;
int mWidth, mHeight;
+ int mSnapshotRotation;
int mOriginalRotation;
int mOriginalWidth, mOriginalHeight;
@@ -221,13 +222,26 @@ class ScreenRotationAnimation {
originalWidth = displayInfo.logicalWidth;
originalHeight = displayInfo.logicalHeight;
}
- if (originalRotation == Surface.ROTATION_90
- || originalRotation == Surface.ROTATION_270) {
- mWidth = originalHeight;
- mHeight = originalWidth;
+ // Allow for abnormal hardware orientation
+ mSnapshotRotation = (4 - android.os.SystemProperties.getInt("ro.sf.hwrotation",0) / 90) % 4;
+ if (mSnapshotRotation == Surface.ROTATION_0 || mSnapshotRotation == Surface.ROTATION_180) {
+ if (originalRotation == Surface.ROTATION_90
+ || originalRotation == Surface.ROTATION_270) {
+ mWidth = originalHeight;
+ mHeight = originalWidth;
+ } else {
+ mWidth = originalWidth;
+ mHeight = originalHeight;
+ }
} else {
- mWidth = originalWidth;
- mHeight = originalHeight;
+ if (originalRotation == Surface.ROTATION_90
+ || originalRotation == Surface.ROTATION_270) {
+ mWidth = originalWidth;
+ mHeight = originalHeight;
+ } else {
+ mWidth = originalHeight;
+ mHeight = originalWidth;
+ }
}
mOriginalRotation = originalRotation;
@@ -346,7 +360,7 @@ class ScreenRotationAnimation {
// Compute the transformation matrix that must be applied
// to the snapshot to make it stay in the same original position
// with the current screen rotation.
- int delta = DisplayContent.deltaRotation(rotation, Surface.ROTATION_0);
+ int delta = DisplayContent.deltaRotation(rotation, mSnapshotRotation);
createRotationMatrix(delta, mWidth, mHeight, mSnapshotInitialMatrix);
if (DEBUG_STATE) Slog.v(TAG, "**** ROTATION: " + delta);
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index a8ba0f9..f0793b8 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -429,6 +429,34 @@ final class Session extends IWindowSession.Stub
}
}
+ /**
+ * @hide
+ */
+ public int getLastWallpaperX() {
+ synchronized(mService.mWindowMap) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return mService.getLastWallpaperX();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public int getLastWallpaperY() {
+ synchronized(mService.mWindowMap) {
+ long ident = Binder.clearCallingIdentity();
+ try {
+ return mService.getLastWallpaperY();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
int z, Bundle extras, boolean sync) {
synchronized(mService.mWindowMap) {
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 985bbfb..7e437c7 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -37,6 +37,8 @@ public class TaskStack {
/** Amount of time in milliseconds to animate the dim surface from one value to another,
* when no window animation is driving it. */
private static final int DEFAULT_DIM_DURATION = 200;
+ private static final int DEFAULT_BLUR_DURATION = 50;
+ private static final float MAX_BLUR_AMOUNT = 1.0f;
/** Unique identifier */
final int mStackId;
@@ -78,6 +80,10 @@ public class TaskStack {
* then stop any dimming. */
boolean mDimmingTag;
+ boolean mBlurringTag;
+ BlurLayer mBlurLayer;
+ WindowStateAnimator mBlurWinAnimator;
+
/** Application tokens that are exiting, but still on screen for animations. */
final AppTokenList mExitingAppTokens = new AppTokenList();
@@ -162,6 +168,7 @@ public class TaskStack {
}
mDimLayer.setBounds(bounds);
+ mBlurLayer.setBounds(bounds);
mAnimationBackgroundSurface.setBounds(bounds);
mBounds.set(bounds);
mRotation = rotation;
@@ -363,6 +370,7 @@ public class TaskStack {
mDisplayContent = displayContent;
mDimLayer = new DimLayer(mService, this, displayContent);
mAnimationBackgroundSurface = new DimLayer(mService, this, displayContent);
+ mBlurLayer = new BlurLayer(mService, this, displayContent);
updateDisplayInfo();
}
@@ -386,13 +394,18 @@ public class TaskStack {
if (doAnotherLayoutPass) {
mService.requestTraversalLocked();
}
-
close();
}
void resetAnimationBackgroundAnimator() {
mAnimationBackgroundAnimator = null;
- mAnimationBackgroundSurface.hide();
+ if (mAnimationBackgroundSurface != null) {
+ mAnimationBackgroundSurface.hide();
+ }
+ }
+
+ private long getBlurBehindFadeDuration(long duration) {
+ return getDimBehindFadeDuration(duration);
}
private long getDimBehindFadeDuration(long duration) {
@@ -457,11 +470,14 @@ public class TaskStack {
}
boolean isDimming() {
+ if (mDimLayer == null) {
+ return false;
+ }
return mDimLayer.isDimming();
}
boolean isDimming(WindowStateAnimator winAnimator) {
- return mDimWinAnimator == winAnimator && mDimLayer.isDimming();
+ return mDimWinAnimator == winAnimator && isDimming();
}
void startDimmingIfNeeded(WindowStateAnimator newWinAnimator) {
@@ -498,6 +514,82 @@ public class TaskStack {
}
}
+ boolean animateBlurLayers() {
+ boolean result = false;
+ final int blurLayer;
+ final float blurAmount;
+ if (mBlurWinAnimator == null) {
+ blurLayer = mBlurLayer.getLayer();
+ blurAmount = 0;
+ } else {
+ blurLayer = mBlurWinAnimator.mAnimLayer - WindowManagerService.LAYER_OFFSET_BLUR;
+ blurAmount = mBlurWinAnimator.mWin.mAttrs.blurAmount;
+ }
+ final float targetBlur = mBlurLayer.getTargetBlur();
+ if (targetBlur != blurAmount) {
+ if (mBlurWinAnimator == null) {
+ mBlurLayer.hide(DEFAULT_BLUR_DURATION);
+ } else {
+ long duration = (mBlurWinAnimator.mAnimating && mBlurWinAnimator.mAnimation != null)
+ ? mBlurWinAnimator.mAnimation.computeDurationHint()
+ : DEFAULT_BLUR_DURATION;
+ if (targetBlur > blurAmount) {
+ duration = getBlurBehindFadeDuration(duration);
+ }
+ if (duration > DEFAULT_BLUR_DURATION)
+ duration = DEFAULT_BLUR_DURATION;
+ mBlurLayer.show(blurLayer, blurAmount, duration);
+ }
+ } else if (mBlurLayer.getLayer() != blurLayer) {
+ mBlurLayer.setLayer(blurLayer);
+ }
+ if (mBlurLayer.isAnimating()) {
+ if (!mService.okToDisplay()) {
+ // Jump to the end of the animation.
+ mBlurLayer.show();
+ } else {
+ result = mBlurLayer.stepAnimation();
+ }
+ }
+ return result;
+ }
+
+ void resetBlurringTag() {
+ mBlurringTag = false;
+ }
+
+ void setBlurringTag() {
+ mBlurringTag = true;
+ }
+
+ boolean testBlurringTag() {
+ return mBlurringTag;
+ }
+
+ boolean isBlurring() {
+ return mBlurLayer.isBlurring();
+ }
+
+ boolean isBlurring(WindowStateAnimator winAnimator) {
+ return mBlurWinAnimator == winAnimator && mBlurLayer.isBlurring();
+ }
+
+ void stopBlurringIfNeeded() {
+ if (!mBlurringTag && isBlurring()) {
+ mBlurWinAnimator = null;
+ }
+ }
+
+ void startBlurringIfNeeded(WindowStateAnimator newWinAnimator) {
+ final WindowStateAnimator existingBlurWinAnimator = mBlurWinAnimator;
+ // Don't turn on for an unshown surface, or for any layer but the highest blur layer.
+ if (newWinAnimator.mSurfaceShown && (existingBlurWinAnimator == null
+ || !existingBlurWinAnimator.mSurfaceShown
+ || existingBlurWinAnimator.mAnimLayer < newWinAnimator.mAnimLayer)) {
+ mBlurWinAnimator = newWinAnimator;
+ }
+ }
+
void switchUser() {
int top = mTasks.size();
for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
@@ -519,6 +611,10 @@ public class TaskStack {
mDimLayer.destroySurface();
mDimLayer = null;
}
+ if (mBlurLayer != null) {
+ mBlurLayer.destroySurface();
+ mBlurLayer = null;
+ }
mDisplayContent = null;
}
@@ -537,6 +633,11 @@ public class TaskStack {
mDimLayer.printTo(prefix + " ", pw);
pw.print(prefix); pw.print("mDimWinAnimator="); pw.println(mDimWinAnimator);
}
+ if (mBlurLayer.isBlurring()) {
+ pw.print(prefix); pw.println("mBlurLayer:");
+ mBlurLayer.printTo(prefix, pw);
+ pw.print(prefix); pw.print("mBlurWinAnimator="); pw.println(mBlurWinAnimator);
+ }
if (!mExitingAppTokens.isEmpty()) {
pw.println();
pw.println(" Exiting application tokens:");
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index e226e3d..3ae783e 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -23,6 +23,7 @@ import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.Paint.FontMetricsInt;
+import android.os.SystemProperties;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
@@ -76,6 +77,14 @@ class Watermark {
else c2 -= '0';
builder.append((char)(255-((c1*16)+c2)));
}
+
+ int appendDisplayVersion = (WindowManagerService.getPropertyInt(tokens, 10,
+ TypedValue.COMPLEX_UNIT_PX, 0, dm));
+ if (appendDisplayVersion != 0) {
+ builder.append(" - ");
+ builder.append(SystemProperties.get("ro.cm.display.version"));
+ }
+
mText = builder.toString();
if (false) {
Log.i(WindowManagerService.TAG, "Final text: " + mText);
@@ -149,27 +158,32 @@ class Watermark {
if (c != null) {
c.drawColor(0, PorterDuff.Mode.CLEAR);
- int deltaX = mDeltaX;
- int deltaY = mDeltaY;
-
- // deltaX shouldn't be close to a round fraction of our
- // x step, or else things will line up too much.
- int div = (dw+mTextWidth)/deltaX;
- int rem = (dw+mTextWidth) - (div*deltaX);
- int qdelta = deltaX/4;
- if (rem < qdelta || rem > (deltaX-qdelta)) {
- deltaX += deltaX/3;
- }
+ if (mDeltaX != 0 || mDeltaY != 0) {
+ int deltaX = mDeltaX;
+ int deltaY = mDeltaY;
+
+ // deltaX shouldn't be close to a round fraction of our
+ // x step, or else things will line up too much.
+ int div = (dw+mTextWidth)/deltaX;
+ int rem = (dw+mTextWidth) - (div*deltaX);
+ int qdelta = deltaX/4;
+ if (rem < qdelta || rem > (deltaX-qdelta)) {
+ deltaX += deltaX/3;
+ }
- int y = -mTextHeight;
- int x = -mTextWidth;
- while (y < (dh+mTextHeight)) {
- c.drawText(mText, x, y, mTextPaint);
- x += deltaX;
- if (x >= dw) {
- x -= (dw+mTextWidth);
- y += deltaY;
+ int y = -mTextHeight;
+ int x = -mTextWidth;
+ while (y < (dh+mTextHeight)) {
+ c.drawText(mText, x, y, mTextPaint);
+ x += deltaX;
+ if (x >= dw) {
+ x -= (dw+mTextWidth);
+ y += deltaY;
+ }
}
+ } else {
+ c.drawText(mText, dw - mTextWidth,
+ dh - mTextHeight*4, mTextPaint);
}
mSurface.unlockCanvasAndPost(c);
}
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 85a9624..362e959 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -30,12 +30,15 @@ import static com.android.server.wm.WindowManagerService.LayoutFields.SET_ORIENT
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_WALLPAPER_ACTION_PENDING;
import android.content.Context;
+import android.database.ContentObserver;
+import android.os.Handler;
import android.os.RemoteException;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TimeUtils;
import android.view.Display;
import android.view.SurfaceControl;
+import android.view.WindowManager;
import android.view.WindowManagerPolicy;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
@@ -43,6 +46,8 @@ import android.view.Choreographer;
import com.android.server.wm.WindowManagerService.LayoutFields;
+import cyanogenmod.providers.CMSettings;
+
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -92,10 +97,13 @@ public class WindowAnimator {
boolean mKeyguardGoingAway;
boolean mKeyguardGoingAwayToNotificationShade;
boolean mKeyguardGoingAwayDisableWindowAnimations;
+ boolean mKeyguardGoingAwayShowingMedia;
/** Use one animation for all entering activities after keyguard is dismissed. */
Animation mPostKeyguardExitAnimation;
+ private boolean mKeyguardBlurEnabled;
+
// forceHiding states.
static final int KEYGUARD_NOT_SHOWN = 0;
static final int KEYGUARD_SHOWN = 1;
@@ -116,6 +124,14 @@ public class WindowAnimator {
mContext = service.mContext;
mPolicy = service.mPolicy;
+ boolean blurUiEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_ui_blur_enabled);
+
+ if (blurUiEnabled) {
+ SettingsObserver observer = new SettingsObserver(new Handler());
+ observer.observe(mContext);
+ }
+
mAnimationFrameCallback = new Choreographer.FrameCallback() {
public void doFrame(long frameTimeNs) {
synchronized (mService.mWindowMap) {
@@ -203,6 +219,10 @@ public class WindowAnimator {
allowWhenLocked |= (win.mIsImWindow || imeTarget == win) && showImeOverKeyguard;
// Show SHOW_WHEN_LOCKED windows that turn on the screen
allowWhenLocked |= (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 && win.mTurnOnScreen;
+ // Show windows that use TYPE_STATUS_BAR_SUB_PANEL when locked
+ allowWhenLocked |= win.mAttrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD_PANEL &&
+ winShowWhenLocked == null;
+
if (appShowWhenLocked != null) {
allowWhenLocked |= appShowWhenLocked == win.mAppToken
@@ -215,6 +235,13 @@ public class WindowAnimator {
// Only hide windows if the keyguard is active and not animating away.
boolean keyguardOn = mPolicy.isKeyguardShowingOrOccluded()
&& mForceHiding != KEYGUARD_ANIMATING_OUT;
+
+ final WindowState winKeyguardPanel = (WindowState) mPolicy.getWinKeyguardPanelLw();
+ // If a keyguard panel is currently being shown, we should
+ // continue to hide the windows as if blur is disabled.
+ if (winKeyguardPanel == null) {
+ keyguardOn &= !mKeyguardBlurEnabled;
+ }
return keyguardOn && !allowWhenLocked && (win.getDisplayId() == Display.DEFAULT_DISPLAY);
}
@@ -223,7 +250,7 @@ public class WindowAnimator {
final WindowList windows = mService.getWindowListLocked(displayId);
- if (mKeyguardGoingAway) {
+ if (mKeyguardGoingAway && !mKeyguardBlurEnabled) {
for (int i = windows.size() - 1; i >= 0; i--) {
WindowState win = windows.get(i);
if (!mPolicy.isKeyguardHostWindow(win.mAttrs)) {
@@ -237,7 +264,8 @@ public class WindowAnimator {
// Create a new animation to delay until keyguard is gone on its own.
winAnimator.mAnimation = new AlphaAnimation(1.0f, 1.0f);
- winAnimator.mAnimation.setDuration(KEYGUARD_ANIM_TIMEOUT_MS);
+ winAnimator.mAnimation.setDuration(
+ mKeyguardBlurEnabled ? 0 : KEYGUARD_ANIM_TIMEOUT_MS);
winAnimator.mAnimationIsEntrance = false;
winAnimator.mAnimationStartTime = -1;
winAnimator.mKeyguardGoingAwayAnimation = true;
@@ -327,7 +355,8 @@ public class WindowAnimator {
if (nowAnimating && win.mWinAnimator.mKeyguardGoingAwayAnimation) {
mForceHiding = KEYGUARD_ANIMATING_OUT;
} else {
- mForceHiding = win.isDrawnLw() ? KEYGUARD_SHOWN : KEYGUARD_NOT_SHOWN;
+ mForceHiding = win.isDrawnLw() && !mKeyguardBlurEnabled ?
+ KEYGUARD_SHOWN : KEYGUARD_NOT_SHOWN;
}
}
if (DEBUG_KEYGUARD || WindowManagerService.DEBUG_VISIBILITY) Slog.v(TAG,
@@ -486,7 +515,7 @@ public class WindowAnimator {
&& !mKeyguardGoingAwayDisableWindowAnimations) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "updateWindowsLocked: wallpaper animating away");
Animation a = mPolicy.createForceHideWallpaperExitAnimation(
- mKeyguardGoingAwayToNotificationShade);
+ mKeyguardGoingAwayToNotificationShade, mKeyguardGoingAwayShowingMedia);
if (a != null) {
wallpaper.mWinAnimator.setAnimation(a);
}
@@ -697,6 +726,7 @@ public class WindowAnimator {
}
mAnimating |= mService.getDisplayContentLocked(displayId).animateDimLayers();
+ mAnimating |= mService.getDisplayContentLocked(displayId).animateBlurLayers();
//TODO (multidisplay): Magnification is supported only for the default display.
if (mService.mAccessibilityController != null
@@ -878,4 +908,30 @@ public class WindowAnimator {
private class DisplayContentsAnimator {
ScreenRotationAnimation mScreenRotationAnimation = null;
}
+
+ private class SettingsObserver extends ContentObserver {
+ public SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ public void observe(Context context) {
+ context.getContentResolver().registerContentObserver(
+ CMSettings.Secure.getUriFor(CMSettings.Secure.LOCK_SCREEN_BLUR_ENABLED),
+ false,
+ this);
+
+ onChange(true);
+ }
+
+ public void unobserve(Context context) {
+ context.getContentResolver().unregisterContentObserver(this);
+ }
+
+ @Override
+ public void onChange(boolean selfChange) {
+ // default to being enabled since we are here because the blur config was set to true
+ mKeyguardBlurEnabled = CMSettings.Secure.getInt(mContext.getContentResolver(),
+ CMSettings.Secure.LOCK_SCREEN_BLUR_ENABLED, 1) == 1;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index d695d93..3d79757 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -29,6 +29,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
@@ -129,6 +130,8 @@ import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
import com.android.server.input.InputManagerService;
+import com.android.server.lights.Light;
+import com.android.server.lights.LightsManager;
import com.android.server.policy.PhoneWindowManager;
import com.android.server.power.ShutdownThread;
@@ -155,7 +158,9 @@ import java.util.List;
import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_BLUR_BEHIND;
import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
@@ -248,7 +253,17 @@ public class WindowManagerService extends IWindowManager.Stub
/**
* Dim surface layer is immediately below target window.
*/
- static final int LAYER_OFFSET_DIM = 1;
+ static final int LAYER_OFFSET_DIM = 1+1;
+
+ /**
+ * Blur surface layer is immediately below dim layer.
+ */
+ static final int LAYER_OFFSET_BLUR = 2+1;
+
+ /**
+ * Blur_with_masking layer is immediately below blur layer.
+ */
+ static final int LAYER_OFFSET_BLUR_WITH_MASKING = 1;
/**
* FocusedStackFrame layer is immediately above focused window.
@@ -299,6 +314,7 @@ public class WindowManagerService extends IWindowManager.Stub
private static final String SYSTEM_SECURE = "ro.secure";
private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
+ private static final String PERSIST_SYS_LCD_DENSITY = "persist.sys.lcd_density";
private static final String DENSITY_OVERRIDE = "ro.config.density_override";
private static final String SIZE_OVERRIDE = "ro.config.size_override";
@@ -308,6 +324,8 @@ public class WindowManagerService extends IWindowManager.Stub
final private KeyguardDisableHandler mKeyguardDisableHandler;
+ private final int mSfHwRotation;
+
final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -998,7 +1016,9 @@ public class WindowManagerService extends IWindowManager.Stub
SurfaceControl.closeTransaction();
}
- updateCircularDisplayMaskIfNeeded();
+ // Load hardware rotation from prop
+ mSfHwRotation = android.os.SystemProperties.getInt("ro.sf.hwrotation",0) / 90;
+
showEmulatorDisplayOverlayIfNeeded();
}
@@ -2146,6 +2166,36 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ public int getLastWallpaperX() {
+ int curTokenIndex = mWallpaperTokens.size();
+ while (curTokenIndex > 0) {
+ curTokenIndex--;
+ WindowToken token = mWallpaperTokens.get(curTokenIndex);
+ int curWallpaperIndex = token.windows.size();
+ while (curWallpaperIndex > 0) {
+ curWallpaperIndex--;
+ WindowState wallpaperWin = token.windows.get(curWallpaperIndex);
+ return wallpaperWin.mXOffset;
+ }
+ }
+ return -1;
+ }
+
+ public int getLastWallpaperY() {
+ int curTokenIndex = mWallpaperTokens.size();
+ while (curTokenIndex > 0) {
+ curTokenIndex--;
+ WindowToken token = mWallpaperTokens.get(curTokenIndex);
+ int curWallpaperIndex = token.windows.size();
+ while (curWallpaperIndex > 0) {
+ curWallpaperIndex--;
+ WindowState wallpaperWin = token.windows.get(curWallpaperIndex);
+ return wallpaperWin.mYOffset;
+ }
+ }
+ return -1;
+ }
+
boolean updateWallpaperOffsetLocked(WindowState wallpaperWin, int dw, int dh,
boolean sync) {
boolean changed = false;
@@ -2735,7 +2785,7 @@ public class WindowManagerService extends IWindowManager.Stub
// to hold off on removing the window until the animation is done.
// If the display is frozen, just remove immediately, since the
// animation wouldn't be seen.
- if (win.mHasSurface && okToDisplay()) {
+ if (win.mHasSurface && okToDisplay() && !win.mBinderDied) {
// If we are not currently running the exit animation, we
// need to see about starting one.
wasVisible = win.isWinVisibleLw();
@@ -3116,7 +3166,9 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (attrs != null) {
+ Binder.restoreCallingIdentity(origId);
mPolicy.adjustWindowParamsLw(attrs);
+ origId = Binder.clearCallingIdentity();
}
// if they don't have the permission, mask out the status bar bits
@@ -3801,7 +3853,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
public int getOrientationLocked() {
- if (mDisplayFrozen) {
+ if (mDisplayFrozen || mAppsFreezingScreen > 0) {
if (mLastWindowForcedOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
if (DEBUG_ORIENTATION) Slog.v(TAG, "Display is frozen, return "
+ mLastWindowForcedOrientation);
@@ -3826,8 +3878,8 @@ public class WindowManagerService extends IWindowManager.Stub
continue;
}
int req = win.mAttrs.screenOrientation;
- if((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) ||
- (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
+ if ((req == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) ||
+ (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)) {
continue;
}
@@ -3857,7 +3909,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
if (DEBUG_ORIENTATION) Slog.v(TAG,
"No one is requesting an orientation when the screen is locked");
- return mLastKeyguardForcedOrientation;
+ return mLastWindowForcedOrientation = mLastKeyguardForcedOrientation;
}
}
@@ -4314,7 +4366,7 @@ public class WindowManagerService extends IWindowManager.Stub
AppWindowToken ttoken = findAppWindowToken(transferFrom);
if (ttoken != null) {
WindowState startingWindow = ttoken.startingWindow;
- if (startingWindow != null) {
+ if (startingWindow != null && ttoken.startingView != null) {
// In this case, the starting icon has already been displayed, so start
// letting windows get shown immediately without any more transitions.
mSkipAppTransitionAnimation = true;
@@ -4443,13 +4495,8 @@ public class WindowManagerService extends IWindowManager.Stub
+ " ShowWallpaper="
+ ent.array.getBoolean(
com.android.internal.R.styleable.Window_windowShowWallpaper, false));
- final boolean windowIsTranslucentDefined = ent.array.hasValue(
- com.android.internal.R.styleable.Window_windowIsTranslucent);
- final boolean windowIsTranslucent = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowIsTranslucent, false);
- final boolean windowSwipeToDismiss = ent.array.getBoolean(
- com.android.internal.R.styleable.Window_windowSwipeToDismiss, false);
- if (windowIsTranslucent || (!windowIsTranslucentDefined && windowSwipeToDismiss)) {
+ if (ent.array.getBoolean(
+ com.android.internal.R.styleable.Window_windowIsTranslucent, false)) {
return;
}
if (ent.array.getBoolean(
@@ -5467,7 +5514,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void keyguardGoingAway(boolean disableWindowAnimations,
- boolean keyguardGoingToNotificationShade) {
+ boolean keyguardGoingToNotificationShade, boolean keyguardShowingMedia) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires DISABLE_KEYGUARD permission");
@@ -5478,6 +5525,7 @@ public class WindowManagerService extends IWindowManager.Stub
mAnimator.mKeyguardGoingAway = true;
mAnimator.mKeyguardGoingAwayToNotificationShade = keyguardGoingToNotificationShade;
mAnimator.mKeyguardGoingAwayDisableWindowAnimations = disableWindowAnimations;
+ mAnimator.mKeyguardGoingAwayShowingMedia = keyguardShowingMedia;
requestTraversalLocked();
}
}
@@ -5622,6 +5670,11 @@ public class WindowManagerService extends IWindowManager.Stub
mPointerEventDispatcher.unregisterInputEventListener(listener);
}
+ @Override
+ public void addSystemUIVisibilityFlag(int flags) {
+ mLastStatusBarVisibility |= flags;
+ }
+
// Called by window manager policy. Not exposed externally.
@Override
public int getLidState() {
@@ -5778,7 +5831,7 @@ public class WindowManagerService extends IWindowManager.Stub
boolean wallpaperEnabled = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableWallpaperService)
&& !mOnlyCore;
- boolean haveKeyguard = true;
+ boolean haveKeyguard = false;
// TODO(multidisplay): Expand to all displays?
final WindowList windows = getDefaultWindowListLocked();
final int N = windows.size();
@@ -5881,6 +5934,17 @@ public class WindowManagerService extends IWindowManager.Stub
mPolicy.enableScreenAfterBoot();
+ // clear any intrusive lighting which may still be on from the
+ // crypto landing ui
+ LightsManager lm = LocalServices.getService(LightsManager.class);
+ Light batteryLight = lm.getLight(LightsManager.LIGHT_ID_BATTERY);
+ Light notifLight = lm.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS);
+ if (batteryLight != null) {
+ batteryLight.turnOff();
+ }
+ if (notifLight != null) {
+ notifLight.turnOff();
+ }
// Make sure the last requested orientation has been applied.
updateRotationUnchecked(false, false);
}
@@ -5897,13 +5961,18 @@ public class WindowManagerService extends IWindowManager.Stub
return true;
}
- public void showBootMessage(final CharSequence msg, final boolean always) {
+ public void updateBootProgress(final int stage, final ApplicationInfo optimizedApp,
+ final int currentAppPos, final int totalAppCount, final boolean always) {
boolean first = false;
synchronized(mWindowMap) {
if (DEBUG_BOOT) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
- Slog.i(TAG, "showBootMessage: msg=" + msg + " always=" + always
+ Slog.i(TAG, "updateBootProgress: stage=" + stage
+ + " optimizedApp=" + optimizedApp
+ + " currentAppPos=" + currentAppPos
+ + " totalAppCount=" + totalAppCount
+ + " always=" + always
+ " mAllowBootMessages=" + mAllowBootMessages
+ " mShowingBootMessages=" + mShowingBootMessages
+ " mSystemBooted=" + mSystemBooted, here);
@@ -5921,7 +5990,7 @@ public class WindowManagerService extends IWindowManager.Stub
return;
}
mShowingBootMessages = true;
- mPolicy.showBootMessage(msg, always);
+ mPolicy.updateBootProgress(stage, optimizedApp, currentAppPos, totalAppCount);
}
if (first) {
performEnableScreen();
@@ -5950,7 +6019,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- public void updateCircularDisplayMaskIfNeeded() {
+ private void updateCircularDisplayMaskIfNeeded() {
// we're fullscreen and not hosted in an ActivityView
if (mContext.getResources().getConfiguration().isScreenRound()
&& mContext.getResources().getBoolean(
@@ -5990,8 +6059,8 @@ public class WindowManagerService extends IWindowManager.Stub
if (visible) {
// TODO(multi-display): support multiple displays
if (mCircularDisplayMask == null) {
- int screenOffset = mContext.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.circular_display_mask_offset);
+ int screenOffset = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_windowOutsetBottom);
int maskThickness = mContext.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.circular_display_mask_thickness);
@@ -6203,10 +6272,13 @@ public class WindowManagerService extends IWindowManager.Stub
int retryCount = 0;
WindowState appWin = null;
- final boolean appIsImTarget = mInputMethodTarget != null
- && mInputMethodTarget.mAppToken != null
- && mInputMethodTarget.mAppToken.appToken != null
- && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
+ boolean appIsImTarget;
+ synchronized(mWindowMap) {
+ appIsImTarget = mInputMethodTarget != null
+ && mInputMethodTarget.mAppToken != null
+ && mInputMethodTarget.mAppToken.appToken != null
+ && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
+ }
final int aboveAppLayer = (mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
@@ -6360,6 +6432,8 @@ public class WindowManagerService extends IWindowManager.Stub
// The screenshot API does not apply the current screen rotation.
int rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
+ // Allow for abnormal hardware orientation
+ rot = (rot + mSfHwRotation) % 4;
if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
@@ -7674,6 +7748,8 @@ public class WindowManagerService extends IWindowManager.Stub
mActivityManager.updateConfiguration(null);
} catch (RemoteException e) {
}
+
+ updateCircularDisplayMaskIfNeeded();
}
private void displayReady(int displayId) {
@@ -7681,22 +7757,7 @@ public class WindowManagerService extends IWindowManager.Stub
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null) {
mAnimator.addDisplayLocked(displayId);
- synchronized(displayContent.mDisplaySizeLock) {
- // Bootstrap the default logical display from the display manager.
- final DisplayInfo displayInfo = displayContent.getDisplayInfo();
- DisplayInfo newDisplayInfo = mDisplayManagerInternal.getDisplayInfo(displayId);
- if (newDisplayInfo != null) {
- displayInfo.copyFrom(newDisplayInfo);
- }
- displayContent.mInitialDisplayWidth = displayInfo.logicalWidth;
- displayContent.mInitialDisplayHeight = displayInfo.logicalHeight;
- displayContent.mInitialDisplayDensity = displayInfo.logicalDensityDpi;
- displayContent.mBaseDisplayWidth = displayContent.mInitialDisplayWidth;
- displayContent.mBaseDisplayHeight = displayContent.mInitialDisplayHeight;
- displayContent.mBaseDisplayDensity = displayContent.mInitialDisplayDensity;
- displayContent.mBaseDisplayRect.set(0, 0,
- displayContent.mBaseDisplayWidth, displayContent.mBaseDisplayHeight);
- }
+ displayContent.initializeDisplayBaseInfo();
}
}
}
@@ -8565,6 +8626,9 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public int getInitialDisplayDensity(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ return DisplayMetrics.DENSITY_DEVICE_DEFAULT;
+ }
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
@@ -8578,6 +8642,9 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public int getBaseDisplayDensity(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ return DisplayMetrics.DENSITY_PREFERRED;
+ }
synchronized (mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
@@ -8605,6 +8672,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null) {
+ SystemProperties.set(PERSIST_SYS_LCD_DENSITY, Integer.toString(density));
setForcedDisplayDensityLocked(displayContent, density);
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.DISPLAY_DENSITY_FORCED, Integer.toString(density));
@@ -8613,6 +8681,10 @@ public class WindowManagerService extends IWindowManager.Stub
} finally {
Binder.restoreCallingIdentity(ident);
}
+ try {
+ ActivityManagerNative.getDefault().restart();
+ } catch (RemoteException e) {
+ }
}
// displayContent must not be null
@@ -8641,6 +8713,7 @@ public class WindowManagerService extends IWindowManager.Stub
synchronized(mWindowMap) {
final DisplayContent displayContent = getDisplayContentLocked(displayId);
if (displayContent != null) {
+ SystemProperties.set(PERSIST_SYS_LCD_DENSITY, null);
setForcedDisplayDensityLocked(displayContent,
displayContent.mInitialDisplayDensity);
Settings.Global.putString(mContext.getContentResolver(),
@@ -8650,6 +8723,10 @@ public class WindowManagerService extends IWindowManager.Stub
} finally {
Binder.restoreCallingIdentity(ident);
}
+ try {
+ ActivityManagerNative.getDefault().restart();
+ } catch (RemoteException e) {
+ }
}
// displayContent must not be null
@@ -8719,7 +8796,8 @@ public class WindowManagerService extends IWindowManager.Stub
displayInfo.overscanBottom = bottom;
}
- mDisplaySettings.setOverscanLocked(displayInfo.uniqueId, left, top, right, bottom);
+ mDisplaySettings.setOverscanLocked(displayInfo.uniqueId, displayInfo.name, left, top,
+ right, bottom);
mDisplaySettings.writeSettingsLocked();
reconfigureDisplayLocked(displayContent);
@@ -8909,7 +8987,7 @@ public class WindowManagerService extends IWindowManager.Stub
anyLayerChanged = true;
}
final TaskStack stack = w.getStack();
- if (layerChanged && stack != null && stack.isDimming(winAnimator)) {
+ if (layerChanged && stack != null && (stack.isDimming(winAnimator) || stack.isBlurring(winAnimator))) {
// Force an animation pass just to update the mDimLayer layer.
scheduleAnimationLocked();
}
@@ -9811,6 +9889,31 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ private void handleFlagBlurBehind(WindowState w) {
+ final WindowManager.LayoutParams attrs = w.mAttrs;
+ if ((attrs.flags & FLAG_BLUR_BEHIND) != 0
+ && w.isDisplayedLw()
+ && !w.mExiting) {
+ final WindowStateAnimator winAnimator = w.mWinAnimator;
+ final TaskStack stack = w.getStack();
+ if (stack == null) {
+ return;
+ }
+ stack.setBlurringTag();
+ if (!stack.isBlurring(winAnimator)) {
+ if (localLOGV) Slog.v(TAG, "Win " + w + " start blurring");
+ stack.startBlurringIfNeeded(winAnimator);
+ }
+ }
+ }
+
+ private void handlePrivateFlagBlurWithMasking(WindowState w) {
+ final WindowManager.LayoutParams attrs = w.mAttrs;
+ boolean hideForced = !w.isDisplayedLw() || w.mExiting;
+ final WindowStateAnimator winAnimator = w.mWinAnimator;
+ winAnimator.updateBlurWithMaskingState(attrs, hideForced);
+ }
+
private void updateAllDrawnLocked(DisplayContent displayContent) {
// See if any windows have been drawn, so they (and others
// associated with them) can now be shown.
@@ -9986,6 +10089,7 @@ public class WindowManagerService extends IWindowManager.Stub
mInnerFields.mObscured = false;
mInnerFields.mSyswin = false;
displayContent.resetDimming();
+ displayContent.resetBlurring();
// Only used if default window
final boolean someoneLosingFocus = !mLosingFocus.isEmpty();
@@ -10010,6 +10114,11 @@ public class WindowManagerService extends IWindowManager.Stub
handleFlagDimBehind(w);
}
+ if (stack != null && !stack.testBlurringTag()) {
+ handleFlagBlurBehind(w);
+ }
+ handlePrivateFlagBlurWithMasking(w);
+
if (isDefaultDisplay && obscuredChanged && (mWallpaperTarget == w)
&& w.isVisibleLw()) {
// This is the wallpaper target and its obscured state
@@ -10085,6 +10194,7 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
+ winAnimator.computeShownFrameLocked();
winAnimator.setSurfaceBoundariesLocked(recoveringMemory);
}
@@ -10149,6 +10259,7 @@ public class WindowManagerService extends IWindowManager.Stub
true /* inTraversal, must call performTraversalInTrans... below */);
getDisplayContentLocked(displayId).stopDimmingIfNeeded();
+ getDisplayContentLocked(displayId).stopBlurringIfNeeded();
if (updateAllDrawn) {
updateAllDrawnLocked(displayContent);
@@ -11104,6 +11215,16 @@ public class WindowManagerService extends IWindowManager.Stub
}
@Override
+ public boolean hasPermanentMenuKey() {
+ return mPolicy.hasPermanentMenuKey();
+ }
+
+ @Override
+ public boolean needsNavigationBar() {
+ return mPolicy.needsNavigationBar();
+ }
+
+ @Override
public void lockNow(Bundle options) {
mPolicy.lockNow(options);
}
@@ -11859,6 +11980,14 @@ public class WindowManagerService extends IWindowManager.Stub
return mWindowMap;
}
+ @Override
+ public void setLiveLockscreenEdgeDetector(boolean enable) {
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR)
+ == PackageManager.PERMISSION_GRANTED) {
+ mPolicy.setLiveLockscreenEdgeDetector(enable);
+ }
+ }
+
private final class LocalService extends WindowManagerInternal {
@Override
public void requestTraversalFromDisplayManager() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c2548de..3bef41b 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -333,6 +333,9 @@ final class WindowState implements WindowManagerPolicy.WindowState {
/** Is this window now (or just being) removed? */
boolean mRemoved;
+ /** Is this window going to be removed from binderDied callback? */
+ boolean mBinderDied;
+
/**
* Temp for keeping track of windows that have been removed when
* rebuilding window list.
@@ -1209,9 +1212,11 @@ final class WindowState implements WindowManagerPolicy.WindowState {
WindowState win = mService.windowForClientLocked(mSession, mClient, false);
Slog.i(TAG, "WIN DEATH: " + win);
if (win != null) {
+ win.mBinderDied = true;
mService.removeWindowLocked(win);
} else if (mHasSurface) {
Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
+ WindowState.this.mBinderDied = true;
mService.removeWindowLocked(WindowState.this);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 42042b9..342a2ac 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -29,6 +29,7 @@ import static com.android.server.wm.WindowManagerService.SHOW_SURFACE_ALLOC;
import static com.android.server.wm.WindowManagerService.localLOGV;
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_ORIENTATION_CHANGE_COMPLETE;
import static com.android.server.wm.WindowManagerService.LayoutFields.SET_TURN_ON_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
import android.content.Context;
import android.graphics.Matrix;
@@ -94,6 +95,13 @@ class WindowStateAnimator {
SurfaceControl mSurfaceControl;
SurfaceControl mPendingDestroySurface;
+ int mLayerStack;
+
+ SurfaceControl mSurfaceControlBlur;
+ SurfaceControl mPendingDestroySurfaceBlur;
+ boolean mSurfaceBlurShown; // last value
+ boolean mSurfaceBlurScaleNeeded;
+ final static int BLUR_LAYER_OFFSET = WindowManagerService.LAYER_OFFSET_BLUR_WITH_MASKING;
/**
* Set when we have changed the size of the surface, to know that
@@ -503,6 +511,12 @@ class WindowStateAnimator {
} catch (RuntimeException e) {
Slog.w(TAG, "Exception hiding surface in " + mWin);
}
+ if (mSurfaceControlBlur != null) {
+ try { mSurfaceControlBlur.hide(); }
+ catch (RuntimeException e) {
+ Slog.w(TAG, "Exception hiding surface blur in " + mWin);
+ }
+ }
}
}
}
@@ -791,6 +805,24 @@ class WindowStateAnimator {
flags |= SurfaceControl.SECURE;
}
+ final boolean consumingNavBar =
+ (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
+ && (attrs.systemUiVisibility & View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
+ && (attrs.systemUiVisibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
+
+ final DisplayContent displayContent = w.getDisplayContent();
+
+ int defaultWidth = 1;
+ int defaultHeight = 1;
+ if (displayContent != null) {
+ final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+ // When we need to expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
+ // set the default width and height of the window to the size of the display
+ // we can use.
+ defaultWidth = consumingNavBar ? displayInfo.logicalWidth : displayInfo.appWidth;
+ defaultHeight = consumingNavBar ? displayInfo.logicalHeight : displayInfo.appHeight;
+ }
+
int width;
int height;
if ((attrs.flags & LayoutParams.FLAG_SCALED) != 0) {
@@ -799,17 +831,17 @@ class WindowStateAnimator {
width = w.mRequestedWidth;
height = w.mRequestedHeight;
} else {
- width = w.mCompatFrame.width();
- height = w.mCompatFrame.height();
+ width = consumingNavBar ? defaultWidth : w.mCompatFrame.width();
+ height = consumingNavBar ? defaultHeight : w.mCompatFrame.height();
}
// Something is wrong and SurfaceFlinger will not like this,
// try to revert to sane values
if (width <= 0) {
- width = 1;
+ width = defaultWidth;
}
if (height <= 0) {
- height = 1;
+ height = defaultHeight;
}
float left = w.mFrame.left + w.mXOffset;
@@ -916,9 +948,9 @@ class WindowStateAnimator {
try {
mSurfaceControl.setPosition(left, top);
mSurfaceLayer = mAnimLayer;
- final DisplayContent displayContent = w.getDisplayContent();
if (displayContent != null) {
- mSurfaceControl.setLayerStack(displayContent.getDisplay().getLayerStack());
+ mLayerStack = displayContent.getDisplay().getLayerStack();
+ mSurfaceControl.setLayerStack(mLayerStack);
}
mSurfaceControl.setLayer(mAnimLayer);
mSurfaceControl.setAlpha(0);
@@ -935,6 +967,7 @@ class WindowStateAnimator {
}
if (WindowManagerService.localLOGV) Slog.v(
TAG, "Created surface " + this);
+ updateBlurWithMaskingState(attrs, false);
}
return mSurfaceControl;
}
@@ -978,6 +1011,13 @@ class WindowStateAnimator {
}
mPendingDestroySurface = mSurfaceControl;
}
+
+ if (mSurfaceControlBlur != null && mPendingDestroySurfaceBlur != mSurfaceControlBlur) {
+ if (mPendingDestroySurfaceBlur != null) {
+ mPendingDestroySurfaceBlur.destroy();
+ }
+ mPendingDestroySurfaceBlur = mSurfaceControlBlur;
+ }
} else {
if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) {
RuntimeException e = null;
@@ -988,6 +1028,9 @@ class WindowStateAnimator {
WindowManagerService.logSurface(mWin, "DESTROY", e);
}
mSurfaceControl.destroy();
+ if (mSurfaceControlBlur != null) {
+ mSurfaceControlBlur.destroy();
+ }
}
mService.hideWallpapersLocked(mWin);
} catch (RuntimeException e) {
@@ -998,6 +1041,7 @@ class WindowStateAnimator {
mSurfaceShown = false;
mSurfaceControl = null;
+ mSurfaceControlBlur = null;
mWin.mHasSurface = false;
mDrawState = NO_SURFACE;
}
@@ -1015,6 +1059,9 @@ class WindowStateAnimator {
WindowManagerService.logSurface(mWin, "DESTROY PENDING", e);
}
mPendingDestroySurface.destroy();
+ if (mPendingDestroySurfaceBlur != null) {
+ mPendingDestroySurfaceBlur.destroy();
+ }
mService.hideWallpapersLocked(mWin);
}
} catch (RuntimeException e) {
@@ -1024,6 +1071,7 @@ class WindowStateAnimator {
}
mSurfaceDestroyDeferred = false;
mPendingDestroySurface = null;
+ mPendingDestroySurfaceBlur = null;
}
void computeShownFrameLocked() {
@@ -1400,6 +1448,9 @@ class WindowStateAnimator {
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
"POS " + left + ", " + top, null);
mSurfaceControl.setPosition(left, top);
+ if (mSurfaceControlBlur != null) {
+ mSurfaceControlBlur.setPosition(left, top);
+ }
} catch (RuntimeException e) {
Slog.w(TAG, "Error positioning surface of " + w
+ " pos=(" + left + "," + top + ")", e);
@@ -1419,6 +1470,9 @@ class WindowStateAnimator {
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
"SIZE " + width + "x" + height, null);
mSurfaceControl.setSize(width, height);
+ if (mSurfaceControlBlur != null) {
+ mSurfaceControlBlur.setSize(width, height);
+ }
mSurfaceControl.setMatrix(
mDsDx * w.mHScale, mDtDx * w.mVScale,
mDsDy * w.mHScale, mDtDy * w.mVScale);
@@ -1430,6 +1484,12 @@ class WindowStateAnimator {
stack.startDimmingIfNeeded(this);
}
}
+ if ((w.mAttrs.flags & LayoutParams.FLAG_BLUR_BEHIND) != 0) {
+ final TaskStack stack = w.getStack();
+ if (stack != null) {
+ stack.startBlurringIfNeeded(this);
+ }
+ }
} catch (RuntimeException e) {
// If something goes wrong with the surface (such
// as running out of memory), don't take down the
@@ -1515,6 +1575,14 @@ class WindowStateAnimator {
mDsDx * w.mHScale, mDtDx * w.mVScale,
mDsDy * w.mHScale, mDtDy * w.mVScale);
+ if (mSurfaceControlBlur != null) {
+ mSurfaceControlBlur.setAlpha(mShownAlpha);
+ mSurfaceControlBlur.setLayer(mAnimLayer-BLUR_LAYER_OFFSET);
+ mSurfaceControlBlur.setMatrix(
+ mDsDx*w.mHScale, mDtDx*w.mVScale,
+ mDsDy*w.mHScale, mDtDy*w.mVScale);
+ }
+
if (mLastHidden && mDrawState == HAS_DRAWN) {
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(w,
"SHOW (performLayout)", null);
@@ -1605,6 +1673,9 @@ class WindowStateAnimator {
if (WindowManagerService.SHOW_TRANSACTIONS) WindowManagerService.logSurface(mWin,
"POS " + left + ", " + top, null);
mSurfaceControl.setPosition(mWin.mFrame.left + left, mWin.mFrame.top + top);
+ if (mSurfaceControlBlur != null) {
+ mSurfaceControlBlur.setPosition(mWin.mFrame.left + left, mWin.mFrame.top + top);
+ }
updateSurfaceWindowCrop(false);
} catch (RuntimeException e) {
Slog.w(TAG, "Error positioning surface of " + mWin
@@ -1783,6 +1854,9 @@ class WindowStateAnimator {
if (mSurfaceControl != null) {
mSurfaceShown = true;
mSurfaceControl.show();
+ if (mSurfaceControlBlur != null) {
+ mSurfaceControlBlur.show();
+ }
if (mWin.mTurnOnScreen) {
if (DEBUG_VISIBILITY) Slog.v(TAG,
"Show surface turning screen on: " + mWin);
@@ -1977,4 +2051,66 @@ class WindowStateAnimator {
sb.append('}');
return sb.toString();
}
+
+ void updateBlurWithMaskingState(WindowManager.LayoutParams attrs, boolean hideForced) {
+ boolean blurVisible = !hideForced && 0 != (attrs.privateFlags &
+ (WindowManager.LayoutParams.PRIVATE_FLAG_BLUR_WITH_MASKING |
+ WindowManager.LayoutParams.PRIVATE_FLAG_BLUR_WITH_MASKING_SCALED) );
+ boolean blurScaleNeeded = blurVisible && 0 != (attrs.privateFlags &
+ WindowManager.LayoutParams.PRIVATE_FLAG_BLUR_WITH_MASKING_SCALED);
+
+ if (mSurfaceBlurShown == blurVisible && mSurfaceBlurScaleNeeded == blurScaleNeeded) return;
+ mSurfaceBlurShown = blurVisible;
+ mSurfaceBlurScaleNeeded = blurScaleNeeded;
+
+ if (!blurVisible) {
+ // we don't destroy mSurfaceControlBlur
+ if (mSurfaceControlBlur != null) {
+ mSurfaceControlBlur.hide();
+ } else {
+ // nothing to do
+ }
+ return;
+ }
+
+ if (mSurfaceControl == null) return;
+
+ if (blurVisible) {
+ if (null == mSurfaceControlBlur) {
+ int flags = SurfaceControl.HIDDEN | SurfaceControl.FX_SURFACE_BLUR;
+ final boolean isHwAccelerated = (attrs.flags &
+ WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
+ final int format = isHwAccelerated ? PixelFormat.TRANSLUCENT : attrs.format;
+ if (!PixelFormat.formatHasAlpha(attrs.format)) {
+ flags |= SurfaceControl.OPAQUE;
+ }
+
+ mSurfaceControlBlur = new SurfaceControl(
+ mSession.mSurfaceSession,
+ attrs.getTitle().toString() + " blur",
+ (int)mSurfaceW, (int)mSurfaceH, format, flags);
+ }
+
+ SurfaceControl.openTransaction();
+ try {
+ mSurfaceControlBlur.setPosition(mSurfaceX, mSurfaceY);
+ mSurfaceControlBlur.setLayerStack(mLayerStack);
+ mSurfaceControlBlur.setLayer(mAnimLayer-BLUR_LAYER_OFFSET);
+ mSurfaceControlBlur.setAlpha(mShownAlpha);
+ mSurfaceControlBlur.setBlur(1.0f);
+ mSurfaceControlBlur.setBlurMaskSurface(mSurfaceControl);
+ final int BLUR_MASKING_SAMPLING = 4;
+ mSurfaceControlBlur.setBlurMaskSampling(blurScaleNeeded ? BLUR_MASKING_SAMPLING : 1);
+ mSurfaceControlBlur.setBlurMaskAlphaThreshold(attrs.blurMaskAlphaThreshold);
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Error creating blur surface", e);
+ } finally {
+ SurfaceControl.closeTransaction();
+ }
+
+ if (mSurfaceShown) {
+ mSurfaceControlBlur.show();
+ }
+ }
+ }
}
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index 98d8d08..69488b1 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -72,3 +72,10 @@ LOCAL_SHARED_LIBRARIES += \
libGLESv2 \
libnetutils \
+ifeq ($(BOARD_USES_QC_TIME_SERVICES),true)
+LOCAL_CFLAGS += -DHAVE_QC_TIME_SERVICES=1
+LOCAL_SHARED_LIBRARIES += libtime_genoff
+$(shell mkdir -p $(OUT)/obj/SHARED_LIBRARIES/libtime_genoff_intermediates/)
+$(shell touch $(OUT)/obj/SHARED_LIBRARIES/libtime_genoff_intermediates/export_includes)
+endif
+
diff --git a/services/core/jni/com_android_server_AlarmManagerService.cpp b/services/core/jni/com_android_server_AlarmManagerService.cpp
index 3fd0f84..34d863c 100644
--- a/services/core/jni/com_android_server_AlarmManagerService.cpp
+++ b/services/core/jni/com_android_server_AlarmManagerService.cpp
@@ -40,6 +40,14 @@
#include <linux/android_alarm.h>
#include <linux/rtc.h>
+#include <memory>
+
+#if HAVE_QC_TIME_SERVICES
+extern "C" {
+#include <private/time_genoff.h>
+}
+#endif
+
namespace android {
static const size_t N_ANDROID_TIMERFDS = ANDROID_ALARM_TYPE_COUNT + 1;
@@ -49,6 +57,7 @@ static const clockid_t android_alarm_to_clockid[N_ANDROID_TIMERFDS] = {
CLOCK_BOOTTIME_ALARM,
CLOCK_BOOTTIME,
CLOCK_MONOTONIC,
+ CLOCK_POWEROFF_ALARM,
CLOCK_REALTIME,
};
/* to match the legacy alarm driver implementation, we need an extra
@@ -61,6 +70,7 @@ public:
virtual ~AlarmImpl();
virtual int set(int type, struct timespec *ts) = 0;
+ virtual int clear(int type, struct timespec *ts) = 0;
virtual int setTime(struct timeval *tv) = 0;
virtual int waitForAlarm() = 0;
@@ -75,6 +85,7 @@ public:
AlarmImplAlarmDriver(int fd) : AlarmImpl(&fd, 1) { }
int set(int type, struct timespec *ts);
+ int clear(int type, struct timespec *ts);
int setTime(struct timeval *tv);
int waitForAlarm();
};
@@ -87,6 +98,7 @@ public:
~AlarmImplTimerFd();
int set(int type, struct timespec *ts);
+ int clear(int type, struct timespec *ts);
int setTime(struct timeval *tv);
int waitForAlarm();
@@ -114,6 +126,30 @@ int AlarmImplAlarmDriver::set(int type, struct timespec *ts)
return ioctl(fds[0], ANDROID_ALARM_SET(type), ts);
}
+int AlarmImplAlarmDriver::clear(int type, struct timespec *ts)
+{
+ return ioctl(fds[0], ANDROID_ALARM_CLEAR(type), ts);
+}
+
+#if HAVE_QC_TIME_SERVICES
+static int setTimeServicesTime(time_bases_type base, long int secs)
+{
+ int rc = 0;
+ time_genoff_info_type time_set;
+ uint64_t value = secs;
+ time_set.base = base;
+ time_set.unit = TIME_SECS;
+ time_set.operation = T_SET;
+ time_set.ts_val = &value;
+ rc = time_genoff_operation(&time_set);
+ if (rc) {
+ ALOGE("Error setting generic offset: %d. Still setting system time\n", rc);
+ rc = -1;
+ }
+ return rc;
+}
+#endif
+
int AlarmImplAlarmDriver::setTime(struct timeval *tv)
{
struct timespec ts;
@@ -122,6 +158,10 @@ int AlarmImplAlarmDriver::setTime(struct timeval *tv)
ts.tv_sec = tv->tv_sec;
ts.tv_nsec = tv->tv_usec * 1000;
res = ioctl(fds[0], ANDROID_ALARM_SET_RTC, &ts);
+#if HAVE_QC_TIME_SERVICES
+ setTimeServicesTime(ATS_USER, (tv->tv_sec));
+#endif
+
if (res < 0)
ALOGV("ANDROID_ALARM_SET_RTC ioctl failed: %s\n", strerror(errno));
return res;
@@ -160,6 +200,23 @@ int AlarmImplTimerFd::set(int type, struct timespec *ts)
return timerfd_settime(fds[type], TFD_TIMER_ABSTIME, &spec, NULL);
}
+int AlarmImplTimerFd::clear(int type, struct timespec *ts)
+{
+ if (type > ANDROID_ALARM_TYPE_COUNT) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ ts->tv_sec = 0;
+ ts->tv_nsec = 0;
+
+ struct itimerspec spec;
+ memset(&spec, 0, sizeof(spec));
+ memcpy(&spec.it_value, ts, sizeof(spec.it_value));
+
+ return timerfd_settime(fds[type], TFD_TIMER_ABSTIME, &spec, NULL);
+}
+
int AlarmImplTimerFd::setTime(struct timeval *tv)
{
struct rtc_time rtc;
@@ -323,14 +380,14 @@ static bool rtc_is_hctosys(unsigned int rtc_id)
static int wall_clock_rtc()
{
- DIR *dir = opendir(rtc_sysfs);
- if (!dir) {
+ std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(rtc_sysfs), closedir);
+ if (!dir.get()) {
ALOGE("failed to open %s: %s", rtc_sysfs, strerror(errno));
return -1;
}
struct dirent *dirent;
- while (errno = 0, dirent = readdir(dir)) {
+ while (errno = 0, dirent = readdir(dir.get())) {
unsigned int rtc_id;
int matched = sscanf(dirent->d_name, "rtc%u", &rtc_id);
@@ -367,6 +424,10 @@ static jlong init_timerfd()
for (size_t i = 0; i < N_ANDROID_TIMERFDS; i++) {
fds[i] = timerfd_create(android_alarm_to_clockid[i], 0);
+ if ((fds[i] < 0) && (android_alarm_to_clockid[i] == CLOCK_POWEROFF_ALARM)) {
+ ALOGV("timerfd does not support CLOCK_POWEROFF_ALARM, using CLOCK_REALTIME_ALARM instead");
+ fds[i] = timerfd_create(CLOCK_REALTIME_ALARM, 0);
+ }
if (fds[i] < 0) {
ALOGV("timerfd_create(%u) failed: %s", android_alarm_to_clockid[i],
strerror(errno));
@@ -441,6 +502,23 @@ static void android_server_AlarmManagerService_set(JNIEnv*, jobject, jlong nativ
}
}
+static void android_server_AlarmManagerService_clear(JNIEnv*, jobject, jlong nativeData, jint type,
+jlong seconds, jlong nanoseconds)
+{
+ AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
+ struct timespec ts;
+ ts.tv_sec = seconds;
+ ts.tv_nsec = nanoseconds;
+
+ int result = impl->clear(type, &ts);
+ if (result < 0)
+ {
+ ALOGE("Unable to clear alarm %lld.%09lld: %s\n",
+ static_cast<long long>(seconds),
+ static_cast<long long>(nanoseconds), strerror(errno));
+ }
+}
+
static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv*, jobject, jlong nativeData)
{
AlarmImpl *impl = reinterpret_cast<AlarmImpl *>(nativeData);
@@ -465,6 +543,7 @@ static JNINativeMethod sMethods[] = {
{"init", "()J", (void*)android_server_AlarmManagerService_init},
{"close", "(J)V", (void*)android_server_AlarmManagerService_close},
{"set", "(JIJJ)V", (void*)android_server_AlarmManagerService_set},
+ {"clear", "(JIJJ)V", (void*)android_server_AlarmManagerService_clear},
{"waitForAlarm", "(J)I", (void*)android_server_AlarmManagerService_waitForAlarm},
{"setKernelTime", "(JJ)I", (void*)android_server_AlarmManagerService_setKernelTime},
{"setKernelTimezone", "(JI)I", (void*)android_server_AlarmManagerService_setKernelTimezone},
diff --git a/services/core/jni/com_android_server_PersistentDataBlockService.cpp b/services/core/jni/com_android_server_PersistentDataBlockService.cpp
index e842eeb..9b0393b 100644
--- a/services/core/jni/com_android_server_PersistentDataBlockService.cpp
+++ b/services/core/jni/com_android_server_PersistentDataBlockService.cpp
@@ -17,6 +17,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <JNIHelp.h>
#include <jni.h>
+#include <ScopedUtfChars.h>
#include <utils/misc.h>
#include <sys/ioctl.h>
@@ -77,23 +78,31 @@ namespace android {
static jlong com_android_server_PersistentDataBlockService_getBlockDeviceSize(JNIEnv *env, jclass, jstring jpath)
{
- const char *path = env->GetStringUTFChars(jpath, 0);
- int fd = open(path, O_RDONLY);
+ ScopedUtfChars path(env, jpath);
+ int fd = open(path.c_str(), O_RDONLY);
if (fd < 0)
return 0;
- return get_block_device_size(fd);
+ const uint64_t size = get_block_device_size(fd);
+
+ close(fd);
+
+ return size;
}
static int com_android_server_PersistentDataBlockService_wipe(JNIEnv *env, jclass, jstring jpath) {
- const char *path = env->GetStringUTFChars(jpath, 0);
- int fd = open(path, O_WRONLY);
+ ScopedUtfChars path(env, jpath);
+ int fd = open(path.c_str(), O_WRONLY);
if (fd < 0)
return 0;
- return wipe_block_device(fd);
+ const int ret = wipe_block_device(fd);
+
+ close(fd);
+
+ return ret;
}
static JNINativeMethod sMethods[] = {
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 64514a9..3f46ec6 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -25,12 +25,24 @@
namespace android {
+void* sensorInit(void *arg) {
+ ALOGI("System server: starting sensor init.\n");
+ // Start the sensor service
+ SensorService::instantiate();
+ ALOGI("System server: sensor init done.\n");
+ return NULL;
+}
+
static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jobject /* clazz */) {
char propBuf[PROPERTY_VALUE_MAX];
+ pthread_t sensor_init_thread;
+
property_get("system_init.startsensorservice", propBuf, "1");
if (strcmp(propBuf, "1") == 0) {
- // Start the sensor service
- SensorService::instantiate();
+ // We are safe to move this to a new thread because
+ // Android frame work has taken care to check whether the
+ // service is started or not before using it.
+ pthread_create( &sensor_init_thread, NULL, &sensorInit, NULL);
}
}
diff --git a/services/core/jni/com_android_server_UsbMidiDevice.cpp b/services/core/jni/com_android_server_UsbMidiDevice.cpp
index 06b9bc3..e12a016 100644
--- a/services/core/jni/com_android_server_UsbMidiDevice.cpp
+++ b/services/core/jni/com_android_server_UsbMidiDevice.cpp
@@ -43,12 +43,26 @@ android_server_UsbMidiDevice_get_subdevice_count(JNIEnv *env, jobject /* thiz */
jint card, jint device)
{
char path[100];
+ int fd;
+ const int kMaxRetries = 10;
+ const int kSleepMicroseconds = 2000;
snprintf(path, sizeof(path), "/dev/snd/controlC%d", card);
- int fd = open(path, O_RDWR);
- if (fd < 0) {
- ALOGE("could not open %s", path);
- return 0;
+ // This control device may not have been created yet. So we should
+ // try to open it several times to prevent intermittent failure
+ // from a race condition.
+ int retryCounter = 0;
+ while ((fd = open(path, O_RDWR)) < 0) {
+ if (++retryCounter > kMaxRetries) {
+ ALOGE("timed out after %d tries, could not open %s", retryCounter, path);
+ return 0;
+ } else {
+ ALOGW("attempt #%d, could not open %s", retryCounter, path);
+ // Increase the sleep interval each time.
+ // 10 retries will total 2 * sum(1..10) = 110 milliseconds.
+ // Typically the device should be ready in 5-10 milliseconds.
+ usleep(kSleepMicroseconds * retryCounter);
+ }
}
struct snd_rawmidi_info info;
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e29d0a9..94467a5 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -197,6 +197,8 @@ public:
void setSystemUiVisibility(int32_t visibility);
void setPointerSpeed(int32_t speed);
void setShowTouches(bool enabled);
+ void setStylusIconEnabled(bool enabled);
+ void setVolumeKeysRotation(int mode);
void setInteractive(bool interactive);
void reloadCalibration();
@@ -263,6 +265,12 @@ private:
// Show touches feature enable/disable.
bool showTouches;
+ // Show icon when stylus is used
+ bool stylusIconEnabled;
+
+ // Volume keys rotation mode (0 - off, 1 - phone, 2 - tablet)
+ int32_t volumeKeysRotationMode;
+
// Sprite controller singleton, created on first use.
sp<SpriteController> spriteController;
@@ -299,6 +307,8 @@ NativeInputManager::NativeInputManager(jobject contextObj,
mLocked.pointerSpeed = 0;
mLocked.pointerGesturesEnabled = true;
mLocked.showTouches = false;
+ mLocked.stylusIconEnabled = false;
+ mLocked.volumeKeysRotationMode = 0;
}
mInteractive = true;
@@ -446,6 +456,8 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon
outConfig->pointerGesturesEnabled = mLocked.pointerGesturesEnabled;
outConfig->showTouches = mLocked.showTouches;
+ outConfig->stylusIconEnabled = mLocked.stylusIconEnabled;
+ outConfig->volumeKeysRotationMode = mLocked.volumeKeysRotationMode;
outConfig->setDisplayInfo(false /*external*/, mLocked.internalViewport);
outConfig->setDisplayInfo(true /*external*/, mLocked.externalViewport);
@@ -770,6 +782,38 @@ void NativeInputManager::setShowTouches(bool enabled) {
InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
}
+void NativeInputManager::setStylusIconEnabled(bool enabled) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (mLocked.stylusIconEnabled == enabled) {
+ return;
+ }
+
+ ALOGI("Setting stylus icon enabled to %s.", enabled ? "enabled" : "disabled");
+ mLocked.stylusIconEnabled = enabled;
+ } // release lock
+
+ mInputManager->getReader()->requestRefreshConfiguration(
+ InputReaderConfiguration::CHANGE_STYLUS_ICON_ENABLED);
+}
+
+void NativeInputManager::setVolumeKeysRotation(int mode) {
+ { // acquire lock
+ AutoMutex _l(mLock);
+
+ if (mLocked.volumeKeysRotationMode == mode) {
+ return;
+ }
+
+ ALOGI("Volume keys: rotation mode set to %d.", mode);
+ mLocked.volumeKeysRotationMode = mode;
+ } // release lock
+
+ mInputManager->getReader()->requestRefreshConfiguration(
+ InputReaderConfiguration::CHANGE_VOLUME_KEYS_ROTATION);
+}
+
void NativeInputManager::setInteractive(bool interactive) {
mInteractive = interactive;
}
@@ -1292,6 +1336,20 @@ static void nativeSetShowTouches(JNIEnv* /* env */,
im->setShowTouches(enabled);
}
+static void nativeSetStylusIconEnabled(JNIEnv* env,
+ jclass clazz, jlong ptr, jboolean enabled) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->setStylusIconEnabled(enabled);
+}
+
+static void nativeSetVolumeKeysRotation(JNIEnv* env,
+ jclass clazz, jlong ptr, int mode) {
+ NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+ im->setVolumeKeysRotation(mode);
+}
+
static void nativeSetInteractive(JNIEnv* env,
jclass clazz, jlong ptr, jboolean interactive) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -1409,6 +1467,10 @@ static JNINativeMethod gInputManagerMethods[] = {
(void*) nativeSetPointerSpeed },
{ "nativeSetShowTouches", "(JZ)V",
(void*) nativeSetShowTouches },
+ { "nativeSetStylusIconEnabled", "(JZ)V",
+ (void*) nativeSetStylusIconEnabled },
+ { "nativeSetVolumeKeysRotation", "(JI)V",
+ (void*) nativeSetVolumeKeysRotation },
{ "nativeSetInteractive", "(JZ)V",
(void*) nativeSetInteractive },
{ "nativeReloadCalibration", "(J)V",
diff --git a/services/core/jni/com_android_server_lights_LightsService.cpp b/services/core/jni/com_android_server_lights_LightsService.cpp
index b2b2783..f8051f0 100644
--- a/services/core/jni/com_android_server_lights_LightsService.cpp
+++ b/services/core/jni/com_android_server_lights_LightsService.cpp
@@ -1,5 +1,6 @@
/*
* Copyright (C) 2009 The Android Open Source Project
+ * Copyright (C) 2015 The CyanogenMod Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,6 +42,8 @@ enum {
LIGHT_INDEX_ATTENTION = 5,
LIGHT_INDEX_BLUETOOTH = 6,
LIGHT_INDEX_WIFI = 7,
+ LIGHT_INDEX_CAPS = 8,
+ LIGHT_INDEX_FUNC = 9,
LIGHT_COUNT
};
@@ -86,6 +89,10 @@ static jlong init_native(JNIEnv* /* env */, jobject /* clazz */)
= get_device(module, LIGHT_ID_BLUETOOTH);
devices->lights[LIGHT_INDEX_WIFI]
= get_device(module, LIGHT_ID_WIFI);
+ devices->lights[LIGHT_INDEX_CAPS]
+ = get_device(module, LIGHT_ID_CAPS);
+ devices->lights[LIGHT_INDEX_FUNC]
+ = get_device(module, LIGHT_ID_FUNC);
} else {
memset(devices, 0, sizeof(Devices));
}
@@ -104,21 +111,34 @@ static void finalize_native(JNIEnv* /* env */, jobject /* clazz */, jlong ptr)
}
static void setLight_native(JNIEnv* /* env */, jobject /* clazz */, jlong ptr,
- jint light, jint colorARGB, jint flashMode, jint onMS, jint offMS, jint brightnessMode)
+ jint light, jint colorARGB, jint flashMode, jint onMS, jint offMS, jint brightnessMode,
+ jint brightnessLevel, jint multipleLeds)
{
Devices* devices = (Devices*)ptr;
light_state_t state;
+ int colorAlpha;
if (light < 0 || light >= LIGHT_COUNT || devices->lights[light] == NULL) {
return ;
}
+ if (brightnessLevel > 0 && brightnessLevel <= 0xFF) {
+ colorAlpha = (colorARGB & 0xFF000000) >> 24;
+ if (colorAlpha == 0x00) {
+ colorAlpha = 0xFF;
+ }
+ colorAlpha = (colorAlpha * brightnessLevel) / 0xFF;
+ colorARGB = (colorAlpha << 24) + (colorARGB & 0x00FFFFFF);
+ }
+
memset(&state, 0, sizeof(light_state_t));
state.color = colorARGB;
state.flashMode = flashMode;
state.flashOnMS = onMS;
state.flashOffMS = offMS;
state.brightnessMode = brightnessMode;
+ state.ledsModes = 0 |
+ (multipleLeds ? LIGHT_MODE_MULTIPLE_LEDS : 0);
{
ALOGD_IF_SLOW(50, "Excessive delay setting light");
@@ -129,7 +149,7 @@ static void setLight_native(JNIEnv* /* env */, jobject /* clazz */, jlong ptr,
static JNINativeMethod method_table[] = {
{ "init_native", "()J", (void*)init_native },
{ "finalize_native", "(J)V", (void*)finalize_native },
- { "setLight_native", "(JIIIIII)V", (void*)setLight_native },
+ { "setLight_native", "(JIIIIIIII)V", (void*)setLight_native },
};
int register_android_server_LightsService(JNIEnv *env)
diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
index 5c27b1f..91eeb8b 100644
--- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -641,7 +641,7 @@ static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, job
}
static void android_location_GpsLocationProvider_agps_set_reference_location_cellid(
- JNIEnv* /* env */, jobject /* obj */, jint type, jint mcc, jint mnc, jint lac, jint cid)
+ JNIEnv* /* env */, jobject /* obj */, jint type, jint mcc, jint mnc, jint lac, jint cid, jint psc)
{
AGpsRefLocation location;
@@ -657,6 +657,11 @@ static void android_location_GpsLocationProvider_agps_set_reference_location_cel
location.u.cellID.mcc = mcc;
location.u.cellID.mnc = mnc;
location.u.cellID.lac = lac;
+#ifdef AGPS_USE_PSC
+ location.u.cellID.psc = psc;
+#else
+ (void)psc;
+#endif
location.u.cellID.cid = cid;
break;
default:
@@ -1477,7 +1482,7 @@ static JNINativeMethod sMethods[] = {
"(ILjava/lang/String;)V",
(void*)android_location_GpsLocationProvider_agps_set_id},
{"native_agps_set_ref_location_cellid",
- "(IIIII)V",
+ "(IIIIII)V",
(void*)android_location_GpsLocationProvider_agps_set_reference_location_cellid},
{"native_set_agps_server",
"(ILjava/lang/String;I)V",
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 1662755..509a6ca 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -148,11 +148,7 @@ static void nativeSendPowerHint(JNIEnv *env, jclass clazz, jint hintId, jint dat
int data_param = data;
if (gPowerModule && gPowerModule->powerHint) {
- if(data)
- gPowerModule->powerHint(gPowerModule, (power_hint_t)hintId, &data_param);
- else {
- gPowerModule->powerHint(gPowerModule, (power_hint_t)hintId, NULL);
- }
+ gPowerModule->powerHint(gPowerModule, (power_hint_t)hintId, &data_param);
}
}
@@ -164,6 +160,16 @@ static void nativeSetFeature(JNIEnv *env, jclass clazz, jint featureId, jint dat
}
}
+static jint nativeGetFeature(JNIEnv *env, jclass clazz, jint featureId) {
+ int value = -1;
+
+ if (gPowerModule && gPowerModule->getFeature) {
+ value = gPowerModule->getFeature(gPowerModule, (feature_t)featureId);
+ }
+
+ return (jint)value;
+}
+
// ----------------------------------------------------------------------------
static JNINativeMethod gPowerManagerServiceMethods[] = {
@@ -182,6 +188,8 @@ static JNINativeMethod gPowerManagerServiceMethods[] = {
(void*) nativeSendPowerHint },
{ "nativeSetFeature", "(II)V",
(void*) nativeSetFeature },
+ { "nativeGetFeature", "(I)I",
+ (void*) nativeGetFeature },
};
#define FIND_CLASS(var, className) \
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index 507bc9c..b7c19db 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -32,6 +32,8 @@
#include <utils/NativeHandle.h>
#include <hardware/tv_input.h>
+#include <jni/TvInputHalExtensions.h>
+
namespace android {
static struct {
@@ -71,37 +73,6 @@ static struct {
////////////////////////////////////////////////////////////////////////////////
-class BufferProducerThread : public Thread {
-public:
- BufferProducerThread(tv_input_device_t* device, int deviceId, const tv_stream_t* stream);
-
- virtual status_t readyToRun();
-
- void setSurface(const sp<Surface>& surface);
- void onCaptured(uint32_t seq, bool succeeded);
- void shutdown();
-
-private:
- Mutex mLock;
- Condition mCondition;
- sp<Surface> mSurface;
- tv_input_device_t* mDevice;
- int mDeviceId;
- tv_stream_t mStream;
- sp<ANativeWindowBuffer_t> mBuffer;
- enum {
- CAPTURING,
- CAPTURED,
- RELEASED,
- } mBufferState;
- uint32_t mSeq;
- bool mShutdown;
-
- virtual bool threadLoop();
-
- void setSurfaceLocked(const sp<Surface>& surface);
-};
-
BufferProducerThread::BufferProducerThread(
tv_input_device_t* device, int deviceId, const tv_stream_t* stream)
: Thread(false),
@@ -132,14 +103,14 @@ status_t BufferProducerThread::readyToRun() {
return NO_ERROR;
}
-void BufferProducerThread::setSurface(const sp<Surface>& surface) {
+int BufferProducerThread::setSurface(const sp<Surface>& surface) {
Mutex::Autolock autoLock(&mLock);
- setSurfaceLocked(surface);
+ return setSurfaceLocked(surface);
}
-void BufferProducerThread::setSurfaceLocked(const sp<Surface>& surface) {
+int BufferProducerThread::setSurfaceLocked(const sp<Surface>& surface) {
if (surface == mSurface) {
- return;
+ return NO_ERROR;
}
if (mBufferState == CAPTURING) {
@@ -157,6 +128,8 @@ void BufferProducerThread::setSurfaceLocked(const sp<Surface>& surface) {
mSurface = surface;
mCondition.broadcast();
+
+ return NO_ERROR;
}
void BufferProducerThread::onCaptured(uint32_t seq, bool succeeded) {
@@ -390,15 +363,37 @@ int JTvInputHal::addOrUpdateStream(int deviceId, int streamId, const sp<Surface>
if (connection.mThread != NULL) {
connection.mThread->shutdown();
}
- connection.mThread = new BufferProducerThread(mDevice, deviceId, &stream);
- connection.mThread->run();
+
+ connection.mThread = TvInputHalFactory::get()->createBufferProducerThread(mDevice, deviceId, &stream);
+ if (connection.mThread == NULL) {
+ ALOGE("No memory for BufferProducerThread");
+
+ // clean up
+ if (mDevice->close_stream(mDevice, deviceId, streamId) != 0) {
+ ALOGE("Couldn't remove stream");
+ }
+ return NO_MEMORY;
+ }
}
}
connection.mSurface = surface;
if (connection.mStreamType == TV_STREAM_TYPE_INDEPENDENT_VIDEO_SOURCE) {
connection.mSurface->setSidebandStream(connection.mSourceHandle);
} else if (connection.mStreamType == TV_STREAM_TYPE_BUFFER_PRODUCER) {
- connection.mThread->setSurface(surface);
+ if (NO_ERROR != connection.mThread->setSurface(surface))
+ {
+ ALOGE("failed to setSurface");
+ // clean up
+ connection.mThread.clear();
+ if (mDevice->close_stream(mDevice, deviceId, streamId) != 0) {
+ ALOGE("Couldn't remove stream");
+ }
+ if (connection.mSurface != NULL) {
+ connection.mSurface.clear();
+ }
+ return UNKNOWN_ERROR;
+ }
+ connection.mThread->run();
}
return NO_ERROR;
}
@@ -413,13 +408,6 @@ int JTvInputHal::removeStream(int deviceId, int streamId) {
// Nothing to do
return NO_ERROR;
}
- if (Surface::isValid(connection.mSurface)) {
- connection.mSurface.clear();
- }
- if (connection.mSurface != NULL) {
- connection.mSurface->setSidebandStream(NULL);
- connection.mSurface.clear();
- }
if (connection.mThread != NULL) {
connection.mThread->shutdown();
connection.mThread.clear();
@@ -431,6 +419,13 @@ int JTvInputHal::removeStream(int deviceId, int streamId) {
if (connection.mSourceHandle != NULL) {
connection.mSourceHandle.clear();
}
+ if (Surface::isValid(connection.mSurface)) {
+ connection.mSurface.clear();
+ }
+ if (connection.mSurface != NULL) {
+ connection.mSurface->setSidebandStream(NULL);
+ connection.mSurface.clear();
+ }
return NO_ERROR;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index dedf1d9..302d23a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -95,6 +95,7 @@ import android.security.IKeyChainAliasCallback;
import android.security.IKeyChainService;
import android.security.KeyChain;
import android.security.KeyChain.KeyChainConnection;
+import android.security.KeyStore;
import android.service.persistentdata.PersistentDataBlockManager;
import android.text.TextUtils;
import android.util.Log;
@@ -4198,6 +4199,26 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
}
}
+ @Override
+ public boolean requireSecureKeyguard(int userHandle) {
+ if (!mHasFeature) {
+ return false;
+ }
+
+ int passwordQuality = getPasswordQuality(null, userHandle);
+ if (passwordQuality > DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) {
+ return true;
+ }
+
+ int encryptionStatus = getStorageEncryptionStatus(userHandle);
+ if (encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE
+ || encryptionStatus == DevicePolicyManager.ENCRYPTION_STATUS_ACTIVATING) {
+ return true;
+ }
+ final int keyguardDisabledFeatures = getKeyguardDisabledFeatures(null, userHandle);
+ return (keyguardDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
+ }
+
// Returns the active device owner or null if there is no device owner.
private ActiveAdmin getDeviceOwnerAdmin() {
String deviceOwnerPackageName = getDeviceOwner();
@@ -4236,6 +4257,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
mDeviceOwner.clearDeviceOwner();
mDeviceOwner.writeOwnerFile();
updateDeviceOwnerLocked();
+ // Restore backup manager.
+ long ident = Binder.clearCallingIdentity();
+ try {
+ IBackupManager ibm = IBackupManager.Stub.asInterface(
+ ServiceManager.getService(Context.BACKUP_SERVICE));
+ ibm.setBackupServiceActive(UserHandle.USER_OWNER, true);
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed activating backup service.", e);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 7dd16d1..d19f8ac 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -18,6 +18,7 @@ package com.android.server;
import android.app.ActivityManagerNative;
import android.app.ActivityThread;
+import android.app.IActivityManager;
import android.app.IAlarmManager;
import android.app.INotificationManager;
import android.app.usage.UsageStatsManagerInternal;
@@ -25,9 +26,11 @@ import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources.Theme;
+import android.database.ContentObserver;
import android.os.Build;
import android.os.Environment;
import android.os.FactoryTest;
@@ -61,6 +64,7 @@ import com.android.server.display.DisplayManagerService;
import com.android.server.dreams.DreamManagerService;
import com.android.server.fingerprint.FingerprintService;
import com.android.server.hdmi.HdmiControlService;
+import com.android.server.gesture.GestureService;
import com.android.server.input.InputManagerService;
import com.android.server.job.JobSchedulerService;
import com.android.server.lights.LightsService;
@@ -92,9 +96,13 @@ import com.android.server.wallpaper.WallpaperManagerService;
import com.android.server.webkit.WebViewUpdateService;
import com.android.server.wm.WindowManagerService;
+import cyanogenmod.providers.CMSettings;
import dalvik.system.VMRuntime;
import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;
@@ -173,6 +181,19 @@ public final class SystemServer {
mFactoryTestMode = FactoryTest.getMode();
}
+ private class AdbPortObserver extends ContentObserver {
+ public AdbPortObserver() {
+ super(null);
+ }
+ @Override
+ public void onChange(boolean selfChange) {
+ int adbPort = CMSettings.Secure.getInt(mContentResolver,
+ CMSettings.Secure.ADB_PORT, 0);
+ // setting this will control whether ADB runs on TCP/IP or USB
+ SystemProperties.set("adb.network.port", Integer.toString(adbPort));
+ }
+ }
+
private void run() {
// If a device's clock is before 1970 (before 0), a lot of
// APIs crash dealing with negative numbers, notably
@@ -309,7 +330,7 @@ public final class SystemServer {
private void createSystemContext() {
ActivityThread activityThread = ActivityThread.systemMain();
mSystemContext = activityThread.getSystemContext();
- mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
+ mSystemContext.setTheme(com.android.internal.R.style.Theme_Power_Dialog);
}
/**
@@ -438,6 +459,9 @@ public final class SystemServer {
boolean disableNetwork = SystemProperties.getBoolean("config.disable_network", false);
boolean disableNetworkTime = SystemProperties.getBoolean("config.disable_networktime", false);
boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
+ String externalServer = context.getResources().getString(
+ org.cyanogenmod.platform.internal.R.string.config_externalSystemServer);
+ boolean disableAtlas = SystemProperties.getBoolean("config.disable_atlas", false);
try {
Slog.i(TAG, "Reading configuration...");
@@ -499,7 +523,7 @@ public final class SystemServer {
Slog.i(TAG, "Window Manager");
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
- !mFirstBoot, mOnlyCore);
+ true, mOnlyCore);
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
@@ -542,6 +566,7 @@ public final class SystemServer {
LockSettingsService lockSettings = null;
AssetAtlasService atlas = null;
MediaRouterService mediaRouter = null;
+ GestureService gestureService = null;
// Bring up services needed for UI.
if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
@@ -596,10 +621,8 @@ public final class SystemServer {
}
try {
- ActivityManagerNative.getDefault().showBootMessage(
- context.getResources().getText(
- com.android.internal.R.string.android_upgrading_starting_apps),
- false);
+ ActivityManagerNative.getDefault().updateBootProgress(
+ IActivityManager.BOOT_STAGE_STARTING_APPS, null, 0, 0, false);
} catch (RemoteException e) {
}
@@ -825,6 +848,11 @@ public final class SystemServer {
if (!disableNonCoreServices) {
mSystemServiceManager.startService(DockObserver.class);
+
+ if (context.getPackageManager().hasSystemFeature
+ (PackageManager.FEATURE_WATCH)) {
+ mSystemServiceManager.startService(ThermalObserver.class);
+ }
}
try {
@@ -932,7 +960,7 @@ public final class SystemServer {
mSystemServiceManager.startService(DreamManagerService.class);
}
- if (!disableNonCoreServices) {
+ if (!disableNonCoreServices && !disableAtlas) {
try {
Slog.i(TAG, "Assets Atlas Service");
atlas = new AssetAtlasService(context);
@@ -947,6 +975,17 @@ public final class SystemServer {
new GraphicsStatsService(context));
}
+ if (context.getResources().getBoolean(
+ com.android.internal.R.bool.config_enableGestureService)) {
+ try {
+ Slog.i(TAG, "Gesture Sensor Service");
+ gestureService = new GestureService(context, inputManager);
+ ServiceManager.addService("gesture", gestureService);
+ } catch (Throwable e) {
+ Slog.e(TAG, "Failure starting Gesture Sensor Service", e);
+ }
+ }
+
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
}
@@ -992,6 +1031,15 @@ public final class SystemServer {
mSystemServiceManager.startService(MediaProjectionManagerService.class);
}
+ // make sure the ADB_ENABLED setting value matches the secure property value
+ CMSettings.Secure.putInt(mContentResolver, CMSettings.Secure.ADB_PORT,
+ Integer.parseInt(SystemProperties.get("service.adb.tcp.port", "-1")));
+
+ // register observer to listen for settings changes
+ mContentResolver.registerContentObserver(
+ CMSettings.Secure.getUriFor(CMSettings.Secure.ADB_PORT),
+ false, new AdbPortObserver());
+
// Before things start rolling, be sure we have decided whether
// we are in safe mode.
final boolean safeMode = wm.detectSafeMode();
@@ -1007,6 +1055,24 @@ public final class SystemServer {
// MMS service broker
mmsService = mSystemServiceManager.startService(MmsServiceBroker.class);
+ final Class<?> serverClazz;
+ try {
+ serverClazz = Class.forName(externalServer);
+ final Constructor<?> constructor = serverClazz.getDeclaredConstructor(Context.class);
+ constructor.setAccessible(true);
+ final Object baseObject = constructor.newInstance(mSystemContext);
+ final Method method = baseObject.getClass().getDeclaredMethod("run");
+ method.setAccessible(true);
+ method.invoke(baseObject);
+ } catch (ClassNotFoundException
+ | IllegalAccessException
+ | InvocationTargetException
+ | InstantiationException
+ | NoSuchMethodException e) {
+ Slog.wtf(TAG, "Unable to start " + externalServer);
+ Slog.wtf(TAG, e);
+ }
+
// It is now time to start up the app processes...
try {
@@ -1047,6 +1113,12 @@ public final class SystemServer {
w.getDefaultDisplay().getMetrics(metrics);
context.getResources().updateConfiguration(config, metrics);
+ // The system context's theme may be configuration-dependent.
+ final Theme systemTheme = context.getTheme();
+ if (systemTheme.getChangingConfigurations() != 0) {
+ systemTheme.rebase();
+ }
+
try {
// TODO: use boot phase
mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
@@ -1067,6 +1139,14 @@ public final class SystemServer {
reportWtf("making Display Manager Service ready", e);
}
+ if (gestureService != null) {
+ try {
+ gestureService.systemReady();
+ } catch (Throwable e) {
+ reportWtf("making Gesture Sensor Service ready", e);
+ }
+ }
+
// These are needed to propagate to the runnable below.
final NetworkManagementService networkManagementF = networkManagement;
final NetworkStatsService networkStatsF = networkStats;
diff --git a/services/libtvextensions/Android.mk b/services/libtvextensions/Android.mk
new file mode 100644
index 0000000..0a9e9bf
--- /dev/null
+++ b/services/libtvextensions/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ jni/TvInputHalFactory.cpp \
+
+LOCAL_C_INCLUDES:= \
+ $(TOP)/frameworks/base/services/libtvextensions \
+
+LOCAL_CFLAGS += -Wno-multichar
+
+ifeq ($(TARGET_ENABLE_QC_TVINPUT_HAL_EXTENSIONS),true)
+ LOCAL_CFLAGS += -DENABLE_TVINPUT_HAL_EXTENSIONS
+endif
+
+LOCAL_MODULE:= libTvInputHalExtensions
+
+LOCAL_MODULE_TAGS := optional
+
+include $(BUILD_STATIC_LIBRARY)
+
diff --git a/services/libtvextensions/common/ExtensionsLoader.hpp b/services/libtvextensions/common/ExtensionsLoader.hpp
new file mode 100644
index 0000000..010e614
--- /dev/null
+++ b/services/libtvextensions/common/ExtensionsLoader.hpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include <dlfcn.h>
+#include <common/TvInputHalExtensionsCommon.h>
+
+namespace android {
+
+/*
+ * Create strongly-typed objects of type T
+ * If the customization library exists and does contain a "named" constructor,
+ * invoke and create an instance
+ * Else create the object of type T itself
+ *
+ * Contains a static instance to dlopen'd library, But may end up
+ * opening the library mutiple times. Following snip from dlopen man page is
+ * reassuring "...Only a single copy of an object file is brought into the
+ * address space, even if dlopen() is invoked multiple times in reference to
+ * the file, and even if different pathnames are used to reference the file.."
+ */
+
+template <typename T>
+T *ExtensionsLoader<T>::createInstance(const char *createFunctionName) {
+ ALOGV("createInstance(%dbit) : %s", sizeof(intptr_t)*8, createFunctionName);
+ // create extended object if extensions-lib is available and
+ // TVINPUT_HAL_EXTENSIONS is enabled
+#if ENABLE_TVINPUT_HAL_EXTENSIONS
+ createFunction_t createFunc = loadCreateFunction(createFunctionName);
+ if (createFunc) {
+ return reinterpret_cast<T *>((*createFunc)());
+ }
+#endif
+ // Else, create the default object
+ return new T;
+}
+
+template <typename T>
+void ExtensionsLoader<T>::loadLib() {
+ if (!mLibHandle) {
+ mLibHandle = ::dlopen(CUSTOMIZATION_LIB_NAME, RTLD_LAZY);
+ if (!mLibHandle) {
+ ALOGV("%s", dlerror());
+ return;
+ }
+ ALOGV("Opened %s", CUSTOMIZATION_LIB_NAME);
+ }
+}
+
+template <typename T>
+createFunction_t ExtensionsLoader<T>::loadCreateFunction(const char *createFunctionName) {
+ loadLib();
+ if (!mLibHandle) {
+ return NULL;
+ }
+ createFunction_t func = (createFunction_t)dlsym(mLibHandle, createFunctionName);
+ if (!func) {
+ ALOGW("symbol %s not found: %s",createFunctionName, dlerror());
+ }
+ return func;
+}
+
+//static
+template <typename T>
+void *ExtensionsLoader<T>::mLibHandle = NULL;
+
+} //namespace android
diff --git a/services/libtvextensions/common/TvInputHalExtensionsCommon.h b/services/libtvextensions/common/TvInputHalExtensionsCommon.h
new file mode 100644
index 0000000..3db4c7b
--- /dev/null
+++ b/services/libtvextensions/common/TvInputHalExtensionsCommon.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _TVINPUTHAL_EXTENSIONS_COMMON_H_
+#define _TVINPUTHAL_EXTENSIONS_COMMON_H_
+
+namespace android {
+
+static const char * CUSTOMIZATION_LIB_NAME = "libTvInputHalEnhancements.so";
+
+typedef void *(*createFunction_t)(void);
+
+template <typename T>
+struct ExtensionsLoader {
+
+ static T *createInstance(const char *createFunctionName);
+
+private:
+ static void loadLib();
+ static createFunction_t loadCreateFunction(const char *createFunctionName);
+ static void *mLibHandle;
+};
+
+/*
+ * Boiler-plate to declare the class as a singleton (with a static getter)
+ * which can be loaded (dlopen'd) via ExtensionsLoader
+ */
+#define DECLARE_LOADABLE_SINGLETON(className) \
+protected: \
+ className(); \
+ virtual ~className(); \
+ static className *sInst; \
+private: \
+ className(const className&); \
+ className &operator=(className &); \
+public: \
+ static className *get() { \
+ return sInst; \
+ } \
+ friend struct ExtensionsLoader<className>;
+
+} //namespace android
+
+#endif // _TVINPUTHAL_EXTENSIONS_COMMON_H_
diff --git a/services/libtvextensions/jni/BufferProducerThread.h b/services/libtvextensions/jni/BufferProducerThread.h
new file mode 100644
index 0000000..6699edc
--- /dev/null
+++ b/services/libtvextensions/jni/BufferProducerThread.h
@@ -0,0 +1,89 @@
+/* Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Not a contribution.
+ */
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TVINPUT_HAL_INTERFACE_H_
+#define TVINPUT_HAL_INTERFACE_H_
+
+#include "jni.h"
+
+#include <gui/Surface.h>
+#include <hardware/tv_input.h>
+
+namespace android {
+
+class BufferProducerThread : public Thread {
+public:
+ BufferProducerThread(tv_input_device_t* device, int deviceId, const tv_stream_t* stream);
+
+ virtual status_t readyToRun();
+
+ virtual int setSurface(const sp<Surface>& surface);
+ virtual void onCaptured(uint32_t seq, bool succeeded);
+ virtual void shutdown();
+
+protected:
+ Mutex mLock;
+ Condition mCondition;
+ sp<Surface> mSurface;
+ tv_input_device_t* mDevice;
+ int mDeviceId;
+ tv_stream_t mStream;
+ sp<ANativeWindowBuffer_t> mBuffer;
+ enum {
+ CAPTURING,
+ CAPTURED,
+ RELEASED,
+ } mBufferState;
+ uint32_t mSeq;
+ bool mShutdown;
+
+ virtual bool threadLoop();
+
+ virtual int setSurfaceLocked(const sp<Surface>& surface);
+};
+
+} // namespace android
+
+#endif // TVINPUT_HAL_INTERFACE_H_
diff --git a/services/libtvextensions/jni/TvInputHalExtensions.h b/services/libtvextensions/jni/TvInputHalExtensions.h
new file mode 100644
index 0000000..479e544
--- /dev/null
+++ b/services/libtvextensions/jni/TvInputHalExtensions.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef _TVINPUTHAL_EXTENSIONS_H_
+#define _TVINPUTHAL_EXTENSIONS_H_
+
+#include <utils/RefBase.h>
+#include <common/TvInputHalExtensionsCommon.h>
+#include <jni/BufferProducerThread.h>
+
+namespace android {
+
+class BufferProducerThread;
+
+/*
+ * Factory to create objects of base-classes in libstagefright
+ */
+struct TvInputHalFactory {
+ virtual sp<BufferProducerThread> createBufferProducerThread(tv_input_device_t* device,
+ int deviceId,
+ const tv_stream_t* stream);
+
+ // ----- NO TRESSPASSING BEYOND THIS LINE ------
+ DECLARE_LOADABLE_SINGLETON(TvInputHalFactory);
+};
+
+}
+
+#endif // _TVINPUTHAL_EXTENSIONS_H_
diff --git a/services/libtvextensions/jni/TvInputHalFactory.cpp b/services/libtvextensions/jni/TvInputHalFactory.cpp
new file mode 100644
index 0000000..b752066
--- /dev/null
+++ b/services/libtvextensions/jni/TvInputHalFactory.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2013 - 2015, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define LOG_TAG "TvInputHalFactory"
+#include <utils/Log.h>
+
+#include "common/ExtensionsLoader.hpp"
+#include "jni/TvInputHalExtensions.h"
+
+namespace android {
+
+ sp<BufferProducerThread> TvInputHalFactory::createBufferProducerThread(tv_input_device_t* device,
+ int deviceId,
+ const tv_stream_t* stream) {
+ return new BufferProducerThread(device, deviceId, stream);
+}
+
+// ----- NO TRESSPASSING BEYOND THIS LINE ------
+TvInputHalFactory::TvInputHalFactory() {
+}
+
+TvInputHalFactory::~TvInputHalFactory() {
+}
+
+//static
+TvInputHalFactory *TvInputHalFactory::sInst =
+ ExtensionsLoader<TvInputHalFactory>::createInstance("createExtendedFactory");
+
+} //namespace android
+
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 8927bfa..f97df83 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -602,7 +602,12 @@ abstract class DhcpPacket {
protected void addCommonClientTlvs(ByteBuffer buf) {
addTlv(buf, DHCP_MAX_MESSAGE_SIZE, (short) MAX_LENGTH);
addTlv(buf, DHCP_VENDOR_CLASS_ID, "android-dhcp-" + Build.VERSION.RELEASE);
- addTlv(buf, DHCP_HOST_NAME, SystemProperties.get("net.hostname"));
+
+ /* the default 'android-dhcp' is there to make sure the hostname is
+ * never empty, because the DHCP standard forbids it (RFC2132, section
+ * 3.14) and certain DHCP forwarders and servers ignore such malformed
+ * requests */
+ addTlv(buf, DHCP_HOST_NAME, SystemProperties.get("net.hostname", "android-dhcp"));
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index ed1db6f..aa8b37f 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -23,15 +23,28 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import android.content.Context;
import android.content.pm.PackageParser;
+import android.content.res.AssetManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.SystemProperties;
+import android.os.UserHandle;
import android.test.AndroidTestCase;
+import android.test.mock.MockContext;
+import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.LongSparseArray;
import com.android.internal.os.AtomicFile;
import java.lang.reflect.Constructor;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import com.android.internal.R;
+
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -160,6 +173,12 @@ public class PackageManagerSettingsTests extends AndroidTestCase {
folder.delete();
}
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
+ }
+
private void writeOldFiles() {
deleteSystemFolder();
writePackagesXml();
@@ -245,7 +264,7 @@ public class PackageManagerSettingsTests extends AndroidTestCase {
writeOldFiles();
createUserManagerServiceRef();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
- assertEquals(true, settings.readLPw(null, null, 0, false));
+ assertEquals(true, settings.readLPw(null, null, 0, false, null));
verifyKeySetMetaData(settings);
}
@@ -257,11 +276,11 @@ public class PackageManagerSettingsTests extends AndroidTestCase {
writeOldFiles();
createUserManagerServiceRef();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
- assertEquals(true, settings.readLPw(null, null, 0, false));
+ assertEquals(true, settings.readLPw(null, null, 0, false, null));
/* write out, read back in and verify the same */
settings.writeLPr();
- assertEquals(true, settings.readLPw(null, null, 0, false));
+ assertEquals(true, settings.readLPw(null, null, 0, false, null));
verifyKeySetMetaData(settings);
}
@@ -269,7 +288,7 @@ public class PackageManagerSettingsTests extends AndroidTestCase {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
- assertEquals(true, settings.readLPw(null, null, 0, false));
+ assertEquals(true, settings.readLPw(null, null, 0, false, null));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_3));
assertNotNull(settings.peekPackageLPr(PACKAGE_NAME_1));
@@ -289,12 +308,12 @@ public class PackageManagerSettingsTests extends AndroidTestCase {
writeOldFiles();
createUserManagerServiceRef();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
- assertEquals(true, settings.readLPw(null, null, 0, false));
+ assertEquals(true, settings.readLPw(null, null, 0, false, null));
settings.writeLPr();
// Create Settings again to make it read from the new files
settings = new Settings(getContext().getFilesDir(), new Object());
- assertEquals(true, settings.readLPw(null, null, 0, false));
+ assertEquals(true, settings.readLPw(null, null, 0, false, null));
PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_2);
assertEquals(COMPONENT_ENABLED_STATE_DISABLED_USER, ps.getEnabled(0));
@@ -305,7 +324,7 @@ public class PackageManagerSettingsTests extends AndroidTestCase {
// Write the package files and make sure they're parsed properly the first time
writeOldFiles();
Settings settings = new Settings(getContext().getFilesDir(), new Object());
- assertEquals(true, settings.readLPw(null, null, 0, false));
+ assertEquals(true, settings.readLPw(null, null, 0, false, null));
// Enable/Disable a package
PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_1);
@@ -335,4 +354,101 @@ public class PackageManagerSettingsTests extends AndroidTestCase {
hasEnabled = ps.getEnabledComponents(0) != null && ps.getEnabledComponents(0).size() > 0;
assertEquals(false, hasEnabled);
}
+
+ // Checks if a package that is locked to a different region is rejected
+ // from being installed
+ public void testPrebundledDifferentRegionReject() {
+ Settings settings = new Settings(getContext().getFilesDir());
+ String expectedPackageNeededForRegion = "org.cyanogenmod.restricted.package";
+ Resources resources = Mockito.mock(Resources.class);
+ String[] regionRestrictedPackages = new String[] {
+ expectedPackageNeededForRegion
+ };
+ Mockito.when(resources.getStringArray(R.array.config_restrict_to_region_locked_devices))
+ .thenReturn(regionRestrictedPackages);
+ assertFalse(settings.shouldPrebundledPackageBeInstalledForRegion(resources,
+ expectedPackageNeededForRegion, resources));
+ }
+
+ // Checks if a package that is locked to the current region is accepted
+ // This also covers the test for a package that needs to be installed on a
+ // non region locked device
+ public void testPrebundledMatchingRegionAccept() {
+ Settings settings = new Settings(getContext().getFilesDir());
+ String expectedPackageNeededForRegion = "org.cyanogenmod.restricted.package";
+ Resources resources = Mockito.mock(Resources.class);
+ String[] regionLockedPackages = new String[] {
+ expectedPackageNeededForRegion
+ };
+ Mockito.when(resources.getStringArray(R.array.config_region_locked_packages))
+ .thenReturn(regionLockedPackages);
+
+ Mockito.when(resources.getStringArray(R.array.config_restrict_to_region_locked_devices))
+ .thenReturn(regionLockedPackages);
+ assertTrue(settings.shouldPrebundledPackageBeInstalledForRegion(resources,
+ expectedPackageNeededForRegion, resources));
+ }
+
+ // Shamelessly kanged from KeySetManagerServiceTest
+ public PackageSetting generateFakePackageSetting(String name) {
+ return new PackageSetting(name, name, new File(mContext.getCacheDir(), "fakeCodePath"),
+ new File(mContext.getCacheDir(), "fakeResPath"), "", "", "",
+ "", 1, 0, 0);
+ }
+
+ // Checks if a package that was installed and currently isn't installed for the owner
+ // is accepted for a secondary user
+ public void testPrebundledSecondaryUserAccept() {
+ Settings settings = new Settings(getContext().getFilesDir());
+ String expectedPackageToBeInstalled = "org.cyanogenmod.secondaryuser.package";
+
+ PackageSetting packageSetting =
+ generateFakePackageSetting(expectedPackageToBeInstalled);
+
+ int userOwner = UserHandle.USER_OWNER;
+ int userSecondary = 1000;
+
+ // Return true that the package was installed for the owner at some point
+ settings.markPrebundledPackageInstalledLPr(userOwner, expectedPackageToBeInstalled);
+ assertTrue(settings.wasPrebundledPackageInstalledLPr(userOwner,
+ expectedPackageToBeInstalled));
+
+ // Return false that the package was installed for the secondary user at some point
+ // DON'T MARK PREBUNDLED PACKAGE INSTALLED
+
+ // Return false that the package is currently not installed for the owner
+ packageSetting.setInstalled(false, userOwner);
+ assertFalse(packageSetting.getInstalled(userOwner));
+
+ // Return false that the package is currently not installed for the secondary user
+ packageSetting.setInstalled(false, userSecondary);
+ assertFalse(packageSetting.getInstalled(userSecondary));
+
+ assertFalse(settings.shouldPrebundledPackageBeInstalledForUserLPr(packageSetting,
+ userSecondary, expectedPackageToBeInstalled));
+ }
+
+ // Checks if a package that was installed for a secondary user and currently isn't installed
+ // for the user is accepted to be reinstalled
+ public void testPrebundledSecondaryUserReinstallAccept() {
+ Settings settings = new Settings(getContext().getFilesDir());
+ String expectedPackageToBeInstalled = "org.cyanogenmod.secondaryuser.package";
+
+ PackageSetting packageSetting =
+ generateFakePackageSetting(expectedPackageToBeInstalled);
+
+ int userSecondary = 1000;
+
+ // Return true that the package was installed for the secondary user at some point
+ settings.markPrebundledPackageInstalledLPr(userSecondary, expectedPackageToBeInstalled);
+ assertTrue(settings.wasPrebundledPackageInstalledLPr(userSecondary,
+ expectedPackageToBeInstalled));
+
+ // Return false that the package is currently not installed for the secondary user
+ packageSetting.setInstalled(false, userSecondary);
+ assertFalse(packageSetting.getInstalled(userSecondary));
+
+ assertFalse(settings.shouldPrebundledPackageBeInstalledForUserLPr(packageSetting,
+ userSecondary, expectedPackageToBeInstalled));
+ }
}
diff --git a/services/usb/Android.mk b/services/usb/Android.mk
index feabf0a..2a9c382 100644
--- a/services/usb/Android.mk
+++ b/services/usb/Android.mk
@@ -9,4 +9,6 @@ LOCAL_SRC_FILES += \
LOCAL_JAVA_LIBRARIES := services.core
+LOCAL_JAVA_LIBRARIES += org.cyanogenmod.platform.internal
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index 31763e7..701272e 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -439,7 +439,7 @@ public final class UsbAlsaManager {
UsbAudioDevice audioDevice = mAudioDevices.remove(usbDevice);
if (audioDevice != null) {
- if (audioDevice.mHasPlayback || audioDevice.mHasPlayback) {
+ if (audioDevice.mHasPlayback || audioDevice.mHasCapture) {
notifyDeviceState(audioDevice, false);
// if there any external devices left, select one of them
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index a4a4d84..2886ce6 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -32,6 +32,7 @@ import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
+import android.os.Binder;
import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
@@ -52,6 +53,15 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.FgThread;
+import cyanogenmod.providers.CMSettings;
+
+import cyanogenmod.app.CMStatusBarManager;
+import cyanogenmod.app.CustomTile;
+
+import org.cyanogenmod.internal.util.QSUtils;
+import org.cyanogenmod.internal.util.QSUtils.OnQSChanged;
+import org.cyanogenmod.internal.util.QSConstants;
+
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -161,6 +171,13 @@ public class UsbDeviceManager {
}
}
+ private final OnQSChanged mQSListener = new OnQSChanged() {
+ @Override
+ public void onQSChanged() {
+ mHandler.processQSChangedLocked();
+ }
+ };
+
/*
* Listens for uevent messages from the kernel to monitor the USB state
*/
@@ -233,7 +250,8 @@ public class UsbDeviceManager {
final StorageManager storageManager = StorageManager.from(mContext);
final StorageVolume primary = storageManager.getPrimaryVolume();
massStorageSupported = primary != null && primary.allowMassStorage();
- mUseUsbNotification = !massStorageSupported;
+ mUseUsbNotification = !massStorageSupported && mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_usbChargingMessage);
// make sure the ADB_ENABLED setting value matches the current state
try {
@@ -322,7 +340,7 @@ public class UsbDeviceManager {
private boolean mCurrentFunctionsApplied;
private UsbAccessory mCurrentAccessory;
private int mUsbNotificationId;
- private boolean mAdbNotificationShown;
+ private int mAdbNotificationId;
private int mCurrentUser = UserHandle.USER_NULL;
public UsbHandler(Looper looper) {
@@ -348,9 +366,25 @@ public class UsbDeviceManager {
Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
false, new AdbSettingsObserver());
+ ContentObserver adbNotificationObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateAdbNotification();
+ }
+ };
+
+ mContentResolver.registerContentObserver(
+ CMSettings.Secure.getUriFor(CMSettings.Secure.ADB_PORT),
+ false, adbNotificationObserver);
+ mContentResolver.registerContentObserver(
+ CMSettings.Secure.getUriFor(CMSettings.Secure.ADB_NOTIFY),
+ false, adbNotificationObserver);
+
// Watch for USB configuration changes
mUEventObserver.startObserving(USB_STATE_MATCH);
mUEventObserver.startObserving(ACCESSORY_START_MATCH);
+
+ QSUtils.registerObserverForQSChanges(mContext, mQSListener);
} catch (Exception e) {
Slog.e(TAG, "Error initializing UsbHandler", e);
}
@@ -546,7 +580,13 @@ public class UsbDeviceManager {
if (mConfigured && enteringAccessoryMode) {
// successfully entered accessory mode
-
+ if (mCurrentAccessory != null) {
+ Slog.w(TAG, "USB accessory re-attached, detach was not announced!");
+ if (mBootCompleted) {
+ getCurrentSettings().accessoryDetached(mCurrentAccessory);
+ }
+ mCurrentAccessory = null;
+ }
if (mAccessoryStrings != null) {
mCurrentAccessory = new UsbAccessory(mAccessoryStrings);
Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
@@ -794,15 +834,35 @@ public class UsbDeviceManager {
private void updateAdbNotification() {
if (mNotificationManager == null) return;
- final int id = com.android.internal.R.string.adb_active_notification_title;
- if (mAdbEnabled && mConnected) {
- if ("0".equals(SystemProperties.get("persist.adb.notify"))) return;
+ final int id;
+ boolean usbAdbActive = mAdbEnabled && mConnected;
+ boolean netAdbActive = mAdbEnabled &&
+ CMSettings.Secure.getInt(mContentResolver, CMSettings.Secure.ADB_PORT, -1) > 0;
+ boolean hideNotification = "0".equals(SystemProperties.get("persist.adb.notify"))
+ || CMSettings.Secure.getInt(mContext.getContentResolver(),
+ CMSettings.Secure.ADB_NOTIFY, 1) == 0;
+
+ if (hideNotification) {
+ id = 0;
+ } else if (usbAdbActive && netAdbActive) {
+ id = com.android.internal.R.string.adb_both_active_notification_title;
+ } else if (usbAdbActive) {
+ id = com.android.internal.R.string.adb_active_notification_title;
+ } else if (netAdbActive) {
+ id = com.android.internal.R.string.adb_net_active_notification_title;
+ } else {
+ id = 0;
+ }
- if (!mAdbNotificationShown) {
+ if (id != mAdbNotificationId) {
+ if (mAdbNotificationId != 0) {
+ mNotificationManager.cancelAsUser(null, mAdbNotificationId, UserHandle.ALL);
+ }
+ if (id != 0) {
Resources r = mContext.getResources();
CharSequence title = r.getText(id);
CharSequence message = r.getText(
- com.android.internal.R.string.adb_active_notification_message);
+ com.android.internal.R.string.adb_active_generic_notification_message);
Intent intent = Intent.makeRestartActivityTask(
new ComponentName("com.android.settings",
@@ -824,19 +884,24 @@ public class UsbDeviceManager {
.setContentIntent(pi)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.build();
- mAdbNotificationShown = true;
+
mNotificationManager.notifyAsUser(null, id, notification,
UserHandle.ALL);
}
- } else if (mAdbNotificationShown) {
- mAdbNotificationShown = false;
- mNotificationManager.cancelAsUser(null, id, UserHandle.ALL);
+ mAdbNotificationId = id;
+ }
+
+ if (id > 0) {
+ publishAdbCustomTile();
+ } else {
+ unpublishAdbCustomTile();
}
}
private String getDefaultFunctions() {
String func = SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY,
UsbManager.USB_FUNCTION_NONE);
+ func = UsbManager.removeFunction(func, "charging");
if (UsbManager.USB_FUNCTION_NONE.equals(func)) {
func = UsbManager.USB_FUNCTION_MTP;
}
@@ -860,6 +925,88 @@ public class UsbDeviceManager {
pw.println("IOException: " + e);
}
}
+
+ private void publishAdbCustomTile() {
+ // This action should be performed as system
+ final int userId = UserHandle.myUserId();
+ long token = Binder.clearCallingIdentity();
+ try {
+ final UserHandle user = new UserHandle(userId);
+ final int icon = QSUtils.getDynamicQSTileResIconId(mContext, userId,
+ QSConstants.DYNAMIC_TILE_ADB);
+ final String contentDesc = QSUtils.getDynamicQSTileLabel(mContext, userId,
+ QSConstants.DYNAMIC_TILE_ADB);
+ final Context resourceContext = QSUtils.getQSTileContext(mContext, userId);
+
+ CMStatusBarManager statusBarManager = CMStatusBarManager.getInstance(mContext);
+ CustomTile tile = new CustomTile.Builder(resourceContext)
+ .setLabel(getAdbCustomTileLabel())
+ .setContentDescription(contentDesc)
+ .setIcon(icon)
+ .setOnClickIntent(getCustomTilePendingIntent())
+ .build();
+ statusBarManager.publishTileAsUser(QSConstants.DYNAMIC_TILE_ADB,
+ UsbDeviceManager.class.hashCode(), tile, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private void unpublishAdbCustomTile() {
+ // This action should be performed as system
+ final int userId = UserHandle.myUserId();
+ long token = Binder.clearCallingIdentity();
+ try {
+ CMStatusBarManager statusBarManager = CMStatusBarManager.getInstance(mContext);
+ statusBarManager.removeTileAsUser(QSConstants.DYNAMIC_TILE_ADB,
+ UsbDeviceManager.class.hashCode(), new UserHandle(userId));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ private PendingIntent getCustomTilePendingIntent() {
+ Intent i = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ return PendingIntent.getActivity(mContext, 0, i, PendingIntent.FLAG_UPDATE_CURRENT, null);
+ }
+
+ private String getAdbCustomTileLabel() {
+ boolean usbAdbActive = mAdbEnabled && mConnected;
+ boolean netAdbActive = mAdbEnabled &&
+ CMSettings.Secure.getInt(mContentResolver, CMSettings.Secure.ADB_PORT, -1) > 0;
+
+ int id = 0;
+ if (usbAdbActive && netAdbActive) {
+ id = com.android.internal.R.string.adb_active_custom_tile_both;
+ } else if (usbAdbActive) {
+ id = com.android.internal.R.string.adb_active_custom_tile_usb;
+ } else if (netAdbActive) {
+ id = com.android.internal.R.string.adb_active_custom_tile_net;
+ }
+
+ Resources res = mContext.getResources();
+ return res.getString(
+ com.android.internal.R.string.adb_active_custom_tile,
+ res.getString(id));
+ }
+
+ private void processQSChangedLocked() {
+ final int userId = UserHandle.myUserId();
+ boolean usbAdbActive = mAdbEnabled && mConnected;
+ boolean netAdbActive = mAdbEnabled &&
+ CMSettings.Secure.getInt(mContentResolver, CMSettings.Secure.ADB_PORT, -1) > 0;
+ boolean notifEnabled = "1".equals(SystemProperties.get("persist.adb.notify"))
+ || CMSettings.Secure.getInt(mContext.getContentResolver(),
+ CMSettings.Secure.ADB_NOTIFY, 1) == 1;
+ boolean isActive = notifEnabled && (usbAdbActive || netAdbActive);
+ boolean enabled = (userId == UserHandle.USER_OWNER) && isActive;
+ if (enabled) {
+ publishAdbCustomTile();
+ } else {
+ unpublishAdbCustomTile();
+ }
+ }
}
/* returns the currently attached USB accessory */
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 2cf42f0..674952c 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -739,7 +739,7 @@ class UsbSettingsManager {
}
// Send broadcast to running activity with registered intent
- mUserContext.sendBroadcast(intent);
+ mUserContext.sendBroadcastAsUser(intent, UserHandle.ALL);
// Start activity with registered intent
resolveActivity(intent, matches, defaultPackage, device, null);