diff options
Diffstat (limited to 'services')
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>"technology" - 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>"dock_scale" - int, the maximum value for the charge level</p> + * <p>"dock_level" - int, charge level, from 0 through "scale" inclusive</p> + * <p>"dock_status" - String, the current charging status.<br /> + * <p>"dock_health" - String, the current battery health.<br /> + * <p>"dock_present" - boolean, true if the battery is present<br /> + * <p>"dock_icon-small" - int, suggested small icon to use for this state</p> + * <p>"dock_plugged" - 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>"dock_voltage" - int, current battery voltage in millivolts</p> + * <p>"dock_temperature" - int, current battery temperature in tenths of + * a degree Centigrade</p> + * <p>"dock_technology" - 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); |