diff options
31 files changed, 2259 insertions, 1581 deletions
diff --git a/services/java/Android.mk b/services/java/Android.mk index 8c3d0f0..9651239 100644 --- a/services/java/Android.mk +++ b/services/java/Android.mk @@ -1,18 +1,18 @@ LOCAL_PATH:= $(call my-dir) -# the library +# Build services.jar # ============================================================ include $(CLEAR_VARS) -LOCAL_SRC_FILES := \ - $(call all-subdir-java-files) \ - com/android/server/EventLogTags.logtags \ - com/android/server/am/EventLogTags.logtags - LOCAL_MODULE:= services +LOCAL_MODULE_CLASS := JAVA_LIBRARIES + +LOCAL_SRC_FILES := \ + $(call all-subdir-java-files) \ + com/android/server/EventLogTags.logtags \ + com/android/server/am/EventLogTags.logtags LOCAL_JAVA_LIBRARIES := android.policy conscrypt telephony-common include $(BUILD_JAVA_LIBRARY) - include $(BUILD_DROIDDOC) diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index 5ae9a6d..1fb164d 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -31,6 +31,7 @@ import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.os.Message; import android.os.PowerManager; import android.os.SystemClock; @@ -64,54 +65,51 @@ import static android.app.AlarmManager.ELAPSED_REALTIME; import com.android.internal.util.LocalLog; -class AlarmManagerService extends IAlarmManager.Stub { +class AlarmManagerService extends SystemService { // The threshold for how long an alarm can be late before we print a // warning message. The time duration is in milliseconds. private static final long LATE_ALARM_THRESHOLD = 10 * 1000; 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_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP; private static final int ELAPSED_REALTIME_MASK = 1 << ELAPSED_REALTIME; - private static final int TIME_CHANGED_MASK = 1 << 16; - private static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK|ELAPSED_REALTIME_WAKEUP_MASK; + static final int TIME_CHANGED_MASK = 1 << 16; + static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK|ELAPSED_REALTIME_WAKEUP_MASK; // Mask for testing whether a given alarm type is wakeup vs non-wakeup - private static final int TYPE_NONWAKEUP_MASK = 0x1; // low bit => non-wakeup - - private static final String TAG = "AlarmManager"; - private static final String ClockReceiver_TAG = "ClockReceiver"; - private static final boolean localLOGV = false; - private static final boolean DEBUG_BATCH = localLOGV || false; - private static final boolean DEBUG_VALIDATE = localLOGV || false; - private static final int ALARM_EVENT = 1; - private static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; + static final int TYPE_NONWAKEUP_MASK = 0x1; // low bit => non-wakeup + + static final String TAG = "AlarmManager"; + static final String ClockReceiver_TAG = "ClockReceiver"; + static final boolean localLOGV = false; + static final boolean DEBUG_BATCH = localLOGV || false; + static final boolean DEBUG_VALIDATE = localLOGV || false; + static final int ALARM_EVENT = 1; + static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; - private static final Intent mBackgroundIntent + static final Intent mBackgroundIntent = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND); - private static final IncreasingTimeOrder sIncreasingTimeOrder = new IncreasingTimeOrder(); + static final IncreasingTimeOrder sIncreasingTimeOrder = new IncreasingTimeOrder(); - private static final boolean WAKEUP_STATS = false; + static final boolean WAKEUP_STATS = false; - private final Context mContext; + final LocalLog mLog = new LocalLog(TAG); - private final LocalLog mLog = new LocalLog(TAG); + final Object mLock = new Object(); - private Object mLock = new Object(); - - private int mDescriptor; + int mDescriptor; private long mNextWakeup; private long mNextNonWakeup; - private int mBroadcastRefCount = 0; - private PowerManager.WakeLock mWakeLock; - private ArrayList<InFlight> mInFlight = new ArrayList<InFlight>(); - private final AlarmThread mWaitThread = new AlarmThread(); - private final AlarmHandler mHandler = new AlarmHandler(); - private ClockReceiver mClockReceiver; + int mBroadcastRefCount = 0; + PowerManager.WakeLock mWakeLock; + ArrayList<InFlight> mInFlight = new ArrayList<InFlight>(); + final AlarmHandler mHandler = new AlarmHandler(); + ClockReceiver mClockReceiver; private UninstallReceiver mUninstallReceiver; - private final ResultReceiver mResultReceiver = new ResultReceiver(); - private final PendingIntent mTimeTickSender; - private final PendingIntent mDateChangeSender; + final ResultReceiver mResultReceiver = new ResultReceiver(); + PendingIntent mTimeTickSender; + PendingIntent mDateChangeSender; class WakeupEvent { public long when; @@ -125,8 +123,8 @@ class AlarmManagerService extends IAlarmManager.Stub { } } - private final LinkedList<WakeupEvent> mRecentWakeups = new LinkedList<WakeupEvent>(); - private final long RECENT_WAKEUP_PERIOD = 1000L * 60 * 60 * 24; // one day + final LinkedList<WakeupEvent> mRecentWakeups = new LinkedList<WakeupEvent>(); + final long RECENT_WAKEUP_PERIOD = 1000L * 60 * 60 * 24; // one day static final class Batch { long start; // These endpoints are always in ELAPSED @@ -317,9 +315,9 @@ class AlarmManagerService extends IAlarmManager.Stub { } // minimum recurrence period or alarm futurity for us to be able to fuzz it - private static final long MIN_FUZZABLE_INTERVAL = 10000; - private static final BatchTimeOrder sBatchOrder = new BatchTimeOrder(); - private final ArrayList<Batch> mAlarmBatches = new ArrayList<Batch>(); + static final long MIN_FUZZABLE_INTERVAL = 10000; + static final BatchTimeOrder sBatchOrder = new BatchTimeOrder(); + final ArrayList<Batch> mAlarmBatches = new ArrayList<Batch>(); static long convertToElapsed(long when, int type) { final boolean isRtc = (type == RTC || type == RTC_WAKEUP); @@ -403,7 +401,7 @@ class AlarmManagerService extends IAlarmManager.Stub { } } - private static final class InFlight extends Intent { + static final class InFlight extends Intent { final PendingIntent mPendingIntent; final WorkSource mWorkSource; final Pair<String, ComponentName> mTarget; @@ -427,7 +425,7 @@ class AlarmManagerService extends IAlarmManager.Stub { } } - private static final class FilterStats { + static final class FilterStats { final BroadcastStats mBroadcastStats; final Pair<String, ComponentName> mTarget; @@ -443,7 +441,7 @@ class AlarmManagerService extends IAlarmManager.Stub { } } - private static final class BroadcastStats { + static final class BroadcastStats { final String mPackageName; long aggregateTime; @@ -459,47 +457,48 @@ class AlarmManagerService extends IAlarmManager.Stub { } } - private final HashMap<String, BroadcastStats> mBroadcastStats + final HashMap<String, BroadcastStats> mBroadcastStats = new HashMap<String, BroadcastStats>(); - public AlarmManagerService(Context context) { - mContext = context; + @Override + public void onStart() { mDescriptor = init(); mNextWakeup = mNextNonWakeup = 0; // We have to set current TimeZone info to kernel // because kernel doesn't keep this after reboot - String tz = SystemProperties.get(TIMEZONE_PROPERTY); - if (tz != null) { - setTimeZone(tz); - } + setTimeZoneImpl(SystemProperties.get(TIMEZONE_PROPERTY)); - PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); + PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); - mTimeTickSender = PendingIntent.getBroadcastAsUser(context, 0, + mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0, new Intent(Intent.ACTION_TIME_TICK).addFlags( Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND), 0, UserHandle.ALL); Intent intent = new Intent(Intent.ACTION_DATE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - mDateChangeSender = PendingIntent.getBroadcastAsUser(context, 0, intent, + mDateChangeSender = PendingIntent.getBroadcastAsUser(getContext(), 0, intent, Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT, UserHandle.ALL); // now that we have initied the driver schedule the alarm - mClockReceiver= new ClockReceiver(); + mClockReceiver = new ClockReceiver(); mClockReceiver.scheduleTimeTickEvent(); mClockReceiver.scheduleDateChangedEvent(); mUninstallReceiver = new UninstallReceiver(); if (mDescriptor != -1) { - mWaitThread.start(); + AlarmThread waitThread = new AlarmThread(); + waitThread.start(); } else { Slog.w(TAG, "Failed to open alarm driver. Falling back to a handler."); } + + publishBinderService(Context.ALARM_SERVICE, mService); } - + + @Override protected void finalize() throws Throwable { try { close(mDescriptor); @@ -508,19 +507,51 @@ class AlarmManagerService extends IAlarmManager.Stub { } } - @Override - public void set(int type, long triggerAtTime, long windowLength, long interval, - PendingIntent operation, WorkSource workSource) { - if (workSource != null) { - mContext.enforceCallingPermission( - android.Manifest.permission.UPDATE_DEVICE_STATS, - "AlarmManager.set"); + void setTimeZoneImpl(String tz) { + if (TextUtils.isEmpty(tz)) { + return; + } + + TimeZone zone = TimeZone.getTimeZone(tz); + // Prevent reentrant calls from stepping on each other when writing + // the time zone property + boolean timeZoneWasChanged = false; + synchronized (this) { + String current = SystemProperties.get(TIMEZONE_PROPERTY); + if (current == null || !current.equals(zone.getID())) { + if (localLOGV) { + Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID()); + } + timeZoneWasChanged = true; + SystemProperties.set(TIMEZONE_PROPERTY, zone.getID()); + } + + // Update the kernel timezone information + // Kernel tracks time offsets as 'minutes west of GMT' + int gmtOffset = zone.getOffset(System.currentTimeMillis()); + setKernelTimezone(mDescriptor, -(gmtOffset / 60000)); } - set(type, triggerAtTime, windowLength, interval, operation, false, workSource); + TimeZone.setDefault(null); + + if (timeZoneWasChanged) { + Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED); + intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); + intent.putExtra("time-zone", zone.getID()); + getContext().sendBroadcastAsUser(intent, UserHandle.ALL); + } } - public void set(int type, long triggerAtTime, long windowLength, long interval, + void removeImpl(PendingIntent operation) { + if (operation == null) { + return; + } + synchronized (mLock) { + removeLocked(operation); + } + } + + void setImpl(int type, long triggerAtTime, long windowLength, long interval, PendingIntent operation, boolean isStandalone, WorkSource workSource) { if (operation == null) { Slog.w(TAG, "set/setRepeating ignored because there is no intent"); @@ -606,231 +637,64 @@ class AlarmManagerService extends IAlarmManager.Stub { rescheduleKernelAlarmsLocked(); } - private void logBatchesLocked() { - ByteArrayOutputStream bs = new ByteArrayOutputStream(2048); - PrintWriter pw = new PrintWriter(bs); - final long nowRTC = System.currentTimeMillis(); - final long nowELAPSED = SystemClock.elapsedRealtime(); - final int NZ = mAlarmBatches.size(); - for (int iz = 0; iz < NZ; iz++) { - Batch bz = mAlarmBatches.get(iz); - pw.append("Batch "); pw.print(iz); pw.append(": "); pw.println(bz); - dumpAlarmList(pw, bz.alarms, " ", nowELAPSED, nowRTC); - pw.flush(); - Slog.v(TAG, bs.toString()); - bs.reset(); - } - } - - private boolean validateConsistencyLocked() { - if (DEBUG_VALIDATE) { - long lastTime = Long.MIN_VALUE; - final int N = mAlarmBatches.size(); - for (int i = 0; i < N; i++) { - Batch b = mAlarmBatches.get(i); - if (b.start >= lastTime) { - // duplicate start times are okay because of standalone batches - lastTime = b.start; - } else { - Slog.e(TAG, "CONSISTENCY FAILURE: Batch " + i + " is out of order"); - logBatchesLocked(); - return false; - } - } - } - return true; - } - - private Batch findFirstWakeupBatchLocked() { - final int N = mAlarmBatches.size(); - for (int i = 0; i < N; i++) { - Batch b = mAlarmBatches.get(i); - if (b.hasWakeups()) { - return b; + private final IBinder mService = new IAlarmManager.Stub() { + @Override + public void set(int type, long triggerAtTime, long windowLength, long interval, + PendingIntent operation, WorkSource workSource) { + if (workSource != null) { + getContext().enforceCallingPermission( + android.Manifest.permission.UPDATE_DEVICE_STATS, + "AlarmManager.set"); } - } - return null; - } - private void rescheduleKernelAlarmsLocked() { - // Schedule the next upcoming wakeup alarm. If there is a deliverable batch - // prior to that which contains no wakeups, we schedule that as well. - if (mAlarmBatches.size() > 0) { - final Batch firstWakeup = findFirstWakeupBatchLocked(); - final Batch firstBatch = mAlarmBatches.get(0); - if (firstWakeup != null && mNextWakeup != firstWakeup.start) { - mNextWakeup = firstWakeup.start; - setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start); - } - if (firstBatch != firstWakeup && mNextNonWakeup != firstBatch.start) { - mNextNonWakeup = firstBatch.start; - setLocked(ELAPSED_REALTIME, firstBatch.start); - } + setImpl(type, triggerAtTime, windowLength, interval, operation, + false, workSource); } - } - - public void setTime(long millis) { - mContext.enforceCallingOrSelfPermission( - "android.permission.SET_TIME", - "setTime"); - - SystemClock.setCurrentTimeMillis(millis); - } - - public void setTimeZone(String tz) { - mContext.enforceCallingOrSelfPermission( - "android.permission.SET_TIME_ZONE", - "setTimeZone"); - - long oldId = Binder.clearCallingIdentity(); - try { - if (TextUtils.isEmpty(tz)) return; - TimeZone zone = TimeZone.getTimeZone(tz); - // Prevent reentrant calls from stepping on each other when writing - // the time zone property - boolean timeZoneWasChanged = false; - synchronized (this) { - String current = SystemProperties.get(TIMEZONE_PROPERTY); - if (current == null || !current.equals(zone.getID())) { - if (localLOGV) { - Slog.v(TAG, "timezone changed: " + current + ", new=" + zone.getID()); - } - timeZoneWasChanged = true; - SystemProperties.set(TIMEZONE_PROPERTY, zone.getID()); - } - - // Update the kernel timezone information - // Kernel tracks time offsets as 'minutes west of GMT' - int gmtOffset = zone.getOffset(System.currentTimeMillis()); - setKernelTimezone(mDescriptor, -(gmtOffset / 60000)); - } - - TimeZone.setDefault(null); - if (timeZoneWasChanged) { - Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED); - intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); - intent.putExtra("time-zone", zone.getID()); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); - } - } finally { - Binder.restoreCallingIdentity(oldId); - } - } - - public void remove(PendingIntent operation) { - if (operation == null) { - return; - } - synchronized (mLock) { - removeLocked(operation); - } - } - - public void removeLocked(PendingIntent operation) { - boolean didRemove = false; - for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { - Batch b = mAlarmBatches.get(i); - didRemove |= b.remove(operation); - if (b.size() == 0) { - mAlarmBatches.remove(i); - } - } + @Override + public void setTime(long millis) { + getContext().enforceCallingOrSelfPermission( + "android.permission.SET_TIME", + "setTime"); - if (didRemove) { - if (DEBUG_BATCH) { - Slog.v(TAG, "remove(operation) changed bounds; rebatching"); - } - rebatchAllAlarmsLocked(true); - rescheduleKernelAlarmsLocked(); + SystemClock.setCurrentTimeMillis(millis); } - } - public void removeLocked(String packageName) { - boolean didRemove = false; - for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { - Batch b = mAlarmBatches.get(i); - didRemove |= b.remove(packageName); - if (b.size() == 0) { - mAlarmBatches.remove(i); + @Override + public void setTimeZone(String tz) { + getContext().enforceCallingOrSelfPermission( + "android.permission.SET_TIME_ZONE", + "setTimeZone"); + + final long oldId = Binder.clearCallingIdentity(); + try { + setTimeZoneImpl(tz); + } finally { + Binder.restoreCallingIdentity(oldId); } } - if (didRemove) { - if (DEBUG_BATCH) { - Slog.v(TAG, "remove(package) changed bounds; rebatching"); - } - rebatchAllAlarmsLocked(true); - rescheduleKernelAlarmsLocked(); - } - } + @Override + public void remove(PendingIntent operation) { + removeImpl(operation); - public void removeUserLocked(int userHandle) { - boolean didRemove = false; - for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { - Batch b = mAlarmBatches.get(i); - didRemove |= b.remove(userHandle); - if (b.size() == 0) { - mAlarmBatches.remove(i); - } } - if (didRemove) { - if (DEBUG_BATCH) { - Slog.v(TAG, "remove(user) changed bounds; rebatching"); + @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 AlarmManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; } - rebatchAllAlarmsLocked(true); - rescheduleKernelAlarmsLocked(); - } - } - public boolean lookForPackageLocked(String packageName) { - for (int i = 0; i < mAlarmBatches.size(); i++) { - Batch b = mAlarmBatches.get(i); - if (b.hasPackage(packageName)) { - return true; - } + dumpImpl(pw); } - return false; - } + }; - private void setLocked(int type, long when) - { - if (mDescriptor != -1) - { - // The kernel never triggers alarms with negative wakeup times - // so we ensure they are positive. - long alarmSeconds, alarmNanoseconds; - if (when < 0) { - alarmSeconds = 0; - alarmNanoseconds = 0; - } else { - alarmSeconds = when / 1000; - alarmNanoseconds = (when % 1000) * 1000 * 1000; - } - - set(mDescriptor, type, alarmSeconds, alarmNanoseconds); - } - else - { - Message msg = Message.obtain(); - msg.what = ALARM_EVENT; - - mHandler.removeMessages(ALARM_EVENT); - mHandler.sendMessageAtTime(msg, when); - } - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump AlarmManager from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return; - } - + void dumpImpl(PrintWriter pw) { synchronized (mLock) { pw.println("Current Alarm Manager state:"); final long nowRTC = System.currentTimeMillis(); @@ -980,6 +844,159 @@ class AlarmManagerService extends IAlarmManager.Stub { } } + private void logBatchesLocked() { + ByteArrayOutputStream bs = new ByteArrayOutputStream(2048); + PrintWriter pw = new PrintWriter(bs); + final long nowRTC = System.currentTimeMillis(); + final long nowELAPSED = SystemClock.elapsedRealtime(); + final int NZ = mAlarmBatches.size(); + for (int iz = 0; iz < NZ; iz++) { + Batch bz = mAlarmBatches.get(iz); + pw.append("Batch "); pw.print(iz); pw.append(": "); pw.println(bz); + dumpAlarmList(pw, bz.alarms, " ", nowELAPSED, nowRTC); + pw.flush(); + Slog.v(TAG, bs.toString()); + bs.reset(); + } + } + + private boolean validateConsistencyLocked() { + if (DEBUG_VALIDATE) { + long lastTime = Long.MIN_VALUE; + final int N = mAlarmBatches.size(); + for (int i = 0; i < N; i++) { + Batch b = mAlarmBatches.get(i); + if (b.start >= lastTime) { + // duplicate start times are okay because of standalone batches + lastTime = b.start; + } else { + Slog.e(TAG, "CONSISTENCY FAILURE: Batch " + i + " is out of order"); + logBatchesLocked(); + return false; + } + } + } + return true; + } + + private Batch findFirstWakeupBatchLocked() { + final int N = mAlarmBatches.size(); + for (int i = 0; i < N; i++) { + Batch b = mAlarmBatches.get(i); + if (b.hasWakeups()) { + return b; + } + } + return null; + } + + void rescheduleKernelAlarmsLocked() { + // Schedule the next upcoming wakeup alarm. If there is a deliverable batch + // prior to that which contains no wakeups, we schedule that as well. + if (mAlarmBatches.size() > 0) { + final Batch firstWakeup = findFirstWakeupBatchLocked(); + final Batch firstBatch = mAlarmBatches.get(0); + if (firstWakeup != null && mNextWakeup != firstWakeup.start) { + mNextWakeup = firstWakeup.start; + setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start); + } + if (firstBatch != firstWakeup && mNextNonWakeup != firstBatch.start) { + mNextNonWakeup = firstBatch.start; + setLocked(ELAPSED_REALTIME, firstBatch.start); + } + } + } + + private void removeLocked(PendingIntent operation) { + boolean didRemove = false; + for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { + Batch b = mAlarmBatches.get(i); + didRemove |= b.remove(operation); + if (b.size() == 0) { + mAlarmBatches.remove(i); + } + } + + if (didRemove) { + if (DEBUG_BATCH) { + Slog.v(TAG, "remove(operation) changed bounds; rebatching"); + } + rebatchAllAlarmsLocked(true); + rescheduleKernelAlarmsLocked(); + } + } + + void removeLocked(String packageName) { + boolean didRemove = false; + for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { + Batch b = mAlarmBatches.get(i); + didRemove |= b.remove(packageName); + if (b.size() == 0) { + mAlarmBatches.remove(i); + } + } + + if (didRemove) { + if (DEBUG_BATCH) { + Slog.v(TAG, "remove(package) changed bounds; rebatching"); + } + rebatchAllAlarmsLocked(true); + rescheduleKernelAlarmsLocked(); + } + } + + void removeUserLocked(int userHandle) { + boolean didRemove = false; + for (int i = mAlarmBatches.size() - 1; i >= 0; i--) { + Batch b = mAlarmBatches.get(i); + didRemove |= b.remove(userHandle); + if (b.size() == 0) { + mAlarmBatches.remove(i); + } + } + + if (didRemove) { + if (DEBUG_BATCH) { + Slog.v(TAG, "remove(user) changed bounds; rebatching"); + } + rebatchAllAlarmsLocked(true); + rescheduleKernelAlarmsLocked(); + } + } + + boolean lookForPackageLocked(String packageName) { + for (int i = 0; i < mAlarmBatches.size(); i++) { + Batch b = mAlarmBatches.get(i); + if (b.hasPackage(packageName)) { + return true; + } + } + return false; + } + + private void setLocked(int type, long when) { + if (mDescriptor != -1) { + // The kernel never triggers alarms with negative wakeup times + // so we ensure they are positive. + long alarmSeconds, alarmNanoseconds; + if (when < 0) { + alarmSeconds = 0; + alarmNanoseconds = 0; + } else { + alarmSeconds = when / 1000; + alarmNanoseconds = (when % 1000) * 1000 * 1000; + } + + set(mDescriptor, type, alarmSeconds, alarmNanoseconds); + } else { + Message msg = Message.obtain(); + msg.what = ALARM_EVENT; + + mHandler.removeMessages(ALARM_EVENT); + mHandler.sendMessageAtTime(msg, when); + } + } + private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list, String prefix, String label, long now) { for (int i=list.size()-1; i>=0; i--) { @@ -1020,7 +1037,7 @@ class AlarmManagerService extends IAlarmManager.Stub { private native int waitForAlarm(int fd); private native int setKernelTimezone(int fd, int minuteswest); - private void triggerAlarmsLocked(ArrayList<Alarm> triggerList, long nowELAPSED, long nowRTC) { + void triggerAlarmsLocked(ArrayList<Alarm> triggerList, long nowELAPSED, long nowRTC) { // batches are temporally sorted, so we need only pull from the // start of the list until we either empty it or hit a batch // that is not yet deliverable @@ -1166,13 +1183,13 @@ class AlarmManagerService extends IAlarmManager.Stub { if (DEBUG_BATCH) { Slog.v(TAG, "Time changed notification from kernel; rebatching"); } - remove(mTimeTickSender); + removeImpl(mTimeTickSender); rebatchAllAlarms(); mClockReceiver.scheduleTimeTickEvent(); Intent intent = new Intent(Intent.ACTION_TIME_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + getContext().sendBroadcastAsUser(intent, UserHandle.ALL); } synchronized (mLock) { @@ -1206,7 +1223,7 @@ class AlarmManagerService extends IAlarmManager.Stub { Alarm alarm = triggerList.get(i); try { if (localLOGV) Slog.v(TAG, "sending alarm " + alarm); - alarm.operation.send(mContext, 0, + alarm.operation.send(getContext(), 0, mBackgroundIntent.putExtra( Intent.EXTRA_ALARM_COUNT, alarm.count), mResultReceiver, mHandler); @@ -1248,7 +1265,7 @@ class AlarmManagerService extends IAlarmManager.Stub { if (alarm.repeatInterval > 0) { // This IntentSender is no longer valid, but this // is a repeating alarm, so toss the hoser. - remove(alarm.operation); + removeImpl(alarm.operation); } } catch (RuntimeException e) { Slog.w(TAG, "Failure sending alarm.", e); @@ -1310,7 +1327,7 @@ class AlarmManagerService extends IAlarmManager.Stub { if (alarm.repeatInterval > 0) { // This IntentSender is no longer valid, but this // is a repeating alarm, so toss the hoser. - remove(alarm.operation); + removeImpl(alarm.operation); } } } @@ -1323,7 +1340,7 @@ class AlarmManagerService extends IAlarmManager.Stub { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_TIME_TICK); filter.addAction(Intent.ACTION_DATE_CHANGED); - mContext.registerReceiver(this, filter); + getContext().registerReceiver(this, filter); } @Override @@ -1354,7 +1371,7 @@ class AlarmManagerService extends IAlarmManager.Stub { final long tickEventDelay = nextTime - currentTime; final WorkSource workSource = null; // Let system take blame for time tick events. - set(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0, + setImpl(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0, 0, mTimeTickSender, true, workSource); } @@ -1368,7 +1385,7 @@ class AlarmManagerService extends IAlarmManager.Stub { calendar.add(Calendar.DAY_OF_MONTH, 1); final WorkSource workSource = null; // Let system take blame for date change events. - set(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, true, workSource); + setImpl(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, true, workSource); } } @@ -1379,12 +1396,12 @@ class AlarmManagerService extends IAlarmManager.Stub { filter.addAction(Intent.ACTION_PACKAGE_RESTARTED); filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); filter.addDataScheme("package"); - mContext.registerReceiver(this, filter); + getContext().registerReceiver(this, filter); // Register for events related to sdcard installation. IntentFilter sdFilter = new IntentFilter(); sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); sdFilter.addAction(Intent.ACTION_USER_STOPPED); - mContext.registerReceiver(this, sdFilter); + getContext().registerReceiver(this, sdFilter); } @Override diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java index b234a4e..6e72e24 100644 --- a/services/java/com/android/server/BatteryService.java +++ b/services/java/com/android/server/BatteryService.java @@ -19,6 +19,8 @@ package com.android.server; import android.os.BatteryStats; import com.android.internal.app.IBatteryStats; import com.android.server.am.BatteryStatsService; +import com.android.server.lights.Light; +import com.android.server.lights.LightsManager; import android.app.ActivityManagerNative; import android.content.ContentResolver; @@ -134,13 +136,10 @@ public final class BatteryService extends Binder { private boolean mSentLowBatteryBroadcast = false; - private BatteryListener mBatteryPropertiesListener; - private IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar; - - public BatteryService(Context context, LightsService lights) { + public BatteryService(Context context, LightsManager lightsManager) { mContext = context; mHandler = new Handler(true /*async*/); - mLed = new Led(context, lights); + mLed = new Led(context, lightsManager); mBatteryStats = BatteryStatsService.getService(); mCriticalBatteryLevel = mContext.getResources().getInteger( @@ -158,13 +157,11 @@ public final class BatteryService extends Binder { "DEVPATH=/devices/virtual/switch/invalid_charger"); } - mBatteryPropertiesListener = new BatteryListener(); - IBinder b = ServiceManager.getService("batteryproperties"); - mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface(b); - + final IBatteryPropertiesRegistrar batteryPropertiesRegistrar = + IBatteryPropertiesRegistrar.Stub.asInterface(b); try { - mBatteryPropertiesRegistrar.registerListener(mBatteryPropertiesListener); + batteryPropertiesRegistrar.registerListener(new BatteryListener()); } catch (RemoteException e) { // Should never happen. } @@ -677,7 +674,7 @@ public final class BatteryService extends Binder { }; private final class Led { - private final LightsService.Light mBatteryLight; + private final Light mBatteryLight; private final int mBatteryLowARGB; private final int mBatteryMediumARGB; @@ -685,8 +682,8 @@ public final class BatteryService extends Binder { private final int mBatteryLedOn; private final int mBatteryLedOff; - public Led(Context context, LightsService lights) { - mBatteryLight = lights.getLight(LightsService.LIGHT_ID_BATTERY); + public Led(Context context, LightsManager lights) { + mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY); mBatteryLowARGB = context.getResources().getInteger( com.android.internal.R.integer.config_notificationsBatteryLowARGB); @@ -712,7 +709,7 @@ public final class BatteryService extends Binder { mBatteryLight.setColor(mBatteryLowARGB); } else { // Flash red when battery is low and not charging - mBatteryLight.setFlashing(mBatteryLowARGB, LightsService.LIGHT_FLASH_TIMED, + mBatteryLight.setFlashing(mBatteryLowARGB, Light.LIGHT_FLASH_TIMED, mBatteryLedOn, mBatteryLedOff); } } else if (status == BatteryManager.BATTERY_STATUS_CHARGING @@ -732,8 +729,14 @@ public final class BatteryService extends Binder { } private final class BatteryListener extends IBatteryPropertiesListener.Stub { + @Override public void batteryPropertiesChanged(BatteryProperties props) { - BatteryService.this.update(props); + final long identity = Binder.clearCallingIdentity(); + try { + BatteryService.this.update(props); + } finally { + Binder.restoreCallingIdentity(identity); + } } } } diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index f061149..0b3eb12 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -30,7 +30,7 @@ import com.android.internal.view.IInputMethodClient; import com.android.internal.view.IInputMethodManager; import com.android.internal.view.IInputMethodSession; import com.android.internal.view.InputBindResult; -import com.android.server.EventLogTags; +import com.android.server.statusbar.StatusBarManagerService; import com.android.server.wm.WindowManagerService; import org.xmlpull.v1.XmlPullParser; diff --git a/services/java/com/android/server/LocalServices.java b/services/java/com/android/server/LocalServices.java new file mode 100644 index 0000000..deff79d --- /dev/null +++ b/services/java/com/android/server/LocalServices.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.util.ArrayMap; + +/** + * This class is used in a similar way as ServiceManager, except the services registered here + * are not Binder objects and are only available in the same process. + * + * Once all services are converted to the SystemService interface, this class can be absorbed + * into SystemServiceManager. + */ +public final class LocalServices { + private LocalServices() {} + + private static final ArrayMap<Class<?>, Object> sLocalServiceObjects = + new ArrayMap<Class<?>, Object>(); + + /** + * Returns a local service instance that implements the specified interface. + * + * @param type The type of service. + * @return The service object. + */ + @SuppressWarnings("unchecked") + public static <T> T getService(Class<T> type) { + synchronized (sLocalServiceObjects) { + return (T) sLocalServiceObjects.get(type); + } + } + + /** + * Adds a service instance of the specified interface to the global registry of local services. + */ + public static <T> void addService(Class<T> type, T service) { + synchronized (sLocalServiceObjects) { + if (sLocalServiceObjects.containsKey(type)) { + throw new IllegalStateException("Overriding service registration"); + } + sLocalServiceObjects.put(type, service); + } + } +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index c745269..38f4c78 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -17,6 +17,8 @@ package com.android.server; import android.app.ActivityManagerNative; +import android.app.IAlarmManager; +import android.app.INotificationManager; import android.bluetooth.BluetoothAdapter; import android.content.ComponentName; import android.content.ContentResolver; @@ -58,9 +60,12 @@ import com.android.server.devicepolicy.DevicePolicyManagerService; import com.android.server.display.DisplayManagerService; import com.android.server.dreams.DreamManagerService; import com.android.server.input.InputManagerService; +import com.android.server.lights.LightsManager; +import com.android.server.lights.LightsService; import com.android.server.media.MediaRouterService; import com.android.server.net.NetworkPolicyManagerService; import com.android.server.net.NetworkStatsService; +import com.android.server.notification.NotificationManagerService; import com.android.server.os.SchedulingPolicyService; import com.android.server.pm.Installer; import com.android.server.pm.PackageManagerService; @@ -69,6 +74,10 @@ import com.android.server.power.PowerManagerService; import com.android.server.power.ShutdownThread; import com.android.server.print.PrintManagerService; import com.android.server.search.SearchManagerService; +import com.android.server.statusbar.StatusBarManagerService; +import com.android.server.storage.DeviceStorageMonitorService; +import com.android.server.twilight.TwilightManager; +import com.android.server.twilight.TwilightService; import com.android.server.usb.UsbService; import com.android.server.wallpaper.WallpaperManagerService; import com.android.server.wifi.WifiService; @@ -131,12 +140,12 @@ class ServerThread { Installer installer = null; AccountManagerService accountManager = null; ContentService contentService = null; - LightsService lights = null; + LightsManager lights = null; PowerManagerService power = null; DisplayManagerService display = null; BatteryService battery = null; VibratorService vibrator = null; - AlarmManagerService alarm = null; + IAlarmManager alarm = null; MountService mountService = null; NetworkManagementService networkManagement = null; NetworkStatsService networkStats = null; @@ -152,8 +161,7 @@ class ServerThread { DockObserver dock = null; UsbService usb = null; SerialService serial = null; - TwilightService twilight = null; - UiModeManagerService uiMode = null; + TwilightManager twilight = null; RecognitionManagerService recognition = null; NetworkTimeUpdateService networkTimeUpdater = null; CommonTimeManagementService commonTimeMgmtService = null; @@ -203,6 +211,8 @@ class ServerThread { Slog.e("System", "************ Failure starting bootstrap service", e); } + final SystemServiceManager systemServiceManager = new SystemServiceManager(context); + boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false); boolean disableMedia = SystemProperties.getBoolean("config.disable_media", false); boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false); @@ -278,8 +288,8 @@ class ServerThread { Slog.i(TAG, "System Content Providers"); ActivityManagerService.installSystemProviders(); - Slog.i(TAG, "Lights Service"); - lights = new LightsService(context); + systemServiceManager.startService(LightsService.class); + lights = LocalServices.getService(LightsManager.class); Slog.i(TAG, "Battery Service"); battery = new BatteryService(context, lights); @@ -295,17 +305,16 @@ class ServerThread { // only initialize the power service after we have started the // lights service, content providers and the battery service. - power.init(context, lights, ActivityManagerService.self(), battery, + power.init(context, lights, battery, BatteryStatsService.getService(), ActivityManagerService.self().getAppOpsService(), display); - Slog.i(TAG, "Alarm Manager"); - alarm = new AlarmManagerService(context); - ServiceManager.addService(Context.ALARM_SERVICE, alarm); + systemServiceManager.startService(AlarmManagerService.class); + alarm = IAlarmManager.Stub.asInterface( + ServiceManager.getService(Context.ALARM_SERVICE)); Slog.i(TAG, "Init Watchdog"); - Watchdog.getInstance().init(context, battery, power, alarm, - ActivityManagerService.self()); + Watchdog.getInstance().init(context, ActivityManagerService.self()); Watchdog.getInstance().addThread(wmHandler, "WindowManager thread"); Slog.i(TAG, "Input Manager"); @@ -350,9 +359,9 @@ class ServerThread { DevicePolicyManagerService devicePolicy = null; StatusBarManagerService statusBar = null; + INotificationManager notification = null; InputMethodManagerService imm = null; AppWidgetService appWidget = null; - NotificationManagerService notification = null; WallpaperManagerService wallpaper = null; LocationManagerService location = null; CountryDetectorService countryDetector = null; @@ -401,7 +410,7 @@ class ServerThread { ActivityManagerNative.getDefault().showBootMessage( context.getResources().getText( com.android.internal.R.string.android_upgrading_starting_apps), - false); + false); } catch (RemoteException e) { } @@ -571,22 +580,12 @@ class ServerThread { reportWtf("making Content Service ready", e); } - try { - Slog.i(TAG, "Notification Manager"); - notification = new NotificationManagerService(context, statusBar, lights); - ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification); - networkPolicy.bindNotificationManager(notification); - } catch (Throwable e) { - reportWtf("starting Notification Manager", e); - } + systemServiceManager.startService(NotificationManagerService.class); + notification = INotificationManager.Stub.asInterface( + ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + networkPolicy.bindNotificationManager(notification); - try { - Slog.i(TAG, "Device Storage Monitor"); - ServiceManager.addService(DeviceStorageMonitorService.SERVICE, - new DeviceStorageMonitorService(context)); - } catch (Throwable e) { - reportWtf("starting DeviceStorageMonitor service", e); - } + systemServiceManager.startService(DeviceStorageMonitorService.class); if (!disableLocation) { try { @@ -685,20 +684,10 @@ class ServerThread { } } - try { - Slog.i(TAG, "Twilight Service"); - twilight = new TwilightService(context); - } catch (Throwable e) { - reportWtf("starting TwilightService", e); - } + systemServiceManager.startService(TwilightService.class); + twilight = LocalServices.getService(TwilightManager.class); - try { - Slog.i(TAG, "UI Mode Manager Service"); - // Listen for UI mode changes - uiMode = new UiModeManagerService(context, twilight); - } catch (Throwable e) { - reportWtf("starting UiModeManagerService", e); - } + systemServiceManager.startService(UiModeManagerService.class); if (!disableNonCoreServices) { try { @@ -858,13 +847,7 @@ class ServerThread { } } - if (notification != null) { - try { - notification.systemReady(); - } catch (Throwable e) { - reportWtf("making Notification Service ready", e); - } - } + systemServiceManager.startBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY); try { wm.systemReady(); @@ -913,8 +896,6 @@ class ServerThread { final ConnectivityService connectivityF = connectivity; final DockObserver dockF = dock; final UsbService usbF = usb; - final TwilightService twilightF = twilight; - final UiModeManagerService uiModeF = uiMode; final AppWidgetService appWidgetF = appWidget; final WallpaperManagerService wallpaperF = wallpaper; final InputMethodManagerService immF = imm; @@ -992,16 +973,6 @@ class ServerThread { reportWtf("making USB Service ready", e); } try { - if (twilightF != null) twilightF.systemReady(); - } catch (Throwable e) { - reportWtf("makin Twilight Service ready", e); - } - try { - if (uiModeF != null) uiModeF.systemReady(); - } catch (Throwable e) { - reportWtf("making UI Mode Service ready", e); - } - try { if (recognitionF != null) recognitionF.systemReady(); } catch (Throwable e) { reportWtf("making Recognition Service ready", e); @@ -1010,6 +981,7 @@ class ServerThread { // It is now okay to let the various system services start their // third party code... + systemServiceManager.startBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START); try { if (appWidgetF != null) appWidgetF.systemRunning(safeMode); @@ -1086,6 +1058,8 @@ class ServerThread { } catch (Throwable e) { reportWtf("Notifying MediaRouterService running", e); } + + systemServiceManager.startBootPhase(SystemService.PHASE_BOOT_COMPLETE); } }); diff --git a/services/java/com/android/server/SystemService.java b/services/java/com/android/server/SystemService.java new file mode 100644 index 0000000..37afb4f --- /dev/null +++ b/services/java/com/android/server/SystemService.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.content.Context; +import android.os.IBinder; +import android.os.ServiceManager; + +/** + * System services respond to lifecycle events that help the services know what + */ +public abstract class SystemService { + /* + * Boot Phases + */ + public static final int PHASE_SYSTEM_SERVICES_READY = 500; + public static final int PHASE_THIRD_PARTY_APPS_CAN_START = 600; + public static final int PHASE_BOOT_COMPLETE = 1000; + + private SystemServiceManager mManager; + private Context mContext; + + final void init(Context context, SystemServiceManager manager) { + mContext = context; + mManager = manager; + onCreate(context); + } + + /** + * Services are not yet available. This is a good place to do setup work that does + * not require other services. + * + * @param context The system context. + */ + public void onCreate(Context context) {} + + /** + * Called when the dependencies listed in the @Service class-annotation are available + * and after the chosen start phase. + * When this method returns, the service should be published. + */ + public abstract void onStart(); + + /** + * Called on each phase of the boot process. Phases before the service's start phase + * (as defined in the @Service annotation) are never received. + * + * @param phase The current boot phase. + */ + public void onBootPhase(int phase) {} + + /** + * Publish the service so it is accessible to other services and apps. + */ + protected final void publishBinderService(String name, IBinder service) { + ServiceManager.addService(name, service); + } + + /** + * Get a binder service by its name. + */ + protected final IBinder getBinderService(String name) { + return ServiceManager.getService(name); + } + + /** + * Publish the service so it is only accessible to the system process. + */ + protected final <T> void publishLocalService(Class<T> type, T service) { + LocalServices.addService(type, service); + } + + /** + * Get a local service by interface. + */ + protected final <T> T getLocalService(Class<T> type) { + return LocalServices.getService(type); + } + + public final Context getContext() { + return mContext; + } + +// /** +// * Called when a new user has been created. If your service deals with multiple users, this +// * method should be overridden. +// * +// * @param userHandle The user that was created. +// */ +// public void onUserCreated(int userHandle) { +// } +// +// /** +// * Called when an existing user has started a new session. If your service deals with multiple +// * users, this method should be overridden. +// * +// * @param userHandle The user who started a new session. +// */ +// public void onUserStarted(int userHandle) { +// } +// +// /** +// * Called when a background user session has entered the foreground. If your service deals with +// * multiple users, this method should be overridden. +// * +// * @param userHandle The user who's session entered the foreground. +// */ +// public void onUserForeground(int userHandle) { +// } +// +// /** +// * Called when a foreground user session has entered the background. If your service deals with +// * multiple users, this method should be overridden; +// * +// * @param userHandle The user who's session entered the background. +// */ +// public void onUserBackground(int userHandle) { +// } +// +// /** +// * Called when a user's active session has stopped. If your service deals with multiple users, +// * this method should be overridden. +// * +// * @param userHandle The user who's session has stopped. +// */ +// public void onUserStopped(int userHandle) { +// } +// +// /** +// * Called when a user has been removed from the system. If your service deals with multiple +// * users, this method should be overridden. +// * +// * @param userHandle The user who has been removed. +// */ +// public void onUserRemoved(int userHandle) { +// } +} diff --git a/services/java/com/android/server/SystemServiceManager.java b/services/java/com/android/server/SystemServiceManager.java new file mode 100644 index 0000000..648975a --- /dev/null +++ b/services/java/com/android/server/SystemServiceManager.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.content.Context; +import android.util.Log; +import android.util.Slog; + +import java.util.ArrayList; + +/** + * Manages creating, starting, and other lifecycle events of system services. + */ +public class SystemServiceManager { + private static final String TAG = "SystemServiceManager"; + + private final Context mContext; + + // Services that should receive lifecycle events. + private final ArrayList<SystemService> mServices = new ArrayList<SystemService>(); + + private int mCurrentPhase = -1; + + public SystemServiceManager(Context context) { + mContext = context; + } + + /** + * Creates and starts a system service. The class must be a subclass of + * {@link com.android.server.SystemService}. + * + * @param serviceClass A Java class that implements the SystemService interface. + * @throws RuntimeException if the service fails to start. + */ + public void startService(Class<?> serviceClass) { + final SystemService serviceInstance = createInstance(serviceClass); + try { + Slog.i(TAG, "Creating " + serviceClass.getSimpleName()); + serviceInstance.init(mContext, this); + } catch (Throwable e) { + throw new RuntimeException("Failed to create service " + serviceClass.getName(), e); + } + + mServices.add(serviceInstance); + + try { + Slog.i(TAG, "Starting " + serviceClass.getSimpleName()); + serviceInstance.onStart(); + } catch (Throwable e) { + throw new RuntimeException("Failed to start service " + serviceClass.getName(), e); + } + } + + /** + * Starts the specified boot phase for all system services that have been started up to + * this point. + * + * @param phase The boot phase to start. + */ + public void startBootPhase(final int phase) { + if (phase <= mCurrentPhase) { + throw new IllegalArgumentException("Next phase must be larger than previous"); + } + mCurrentPhase = phase; + + Slog.i(TAG, "Starting phase " + mCurrentPhase); + + final int serviceLen = mServices.size(); + for (int i = 0; i < serviceLen; i++) { + final SystemService service = mServices.get(i); + try { + service.onBootPhase(mCurrentPhase); + } catch (Throwable e) { + reportWtf("Service " + service.getClass().getName() + + " threw an Exception processing boot phase " + mCurrentPhase, e); + } + } + } + + /** + * Outputs the state of this manager to the System log. + */ + public void dump() { + StringBuilder builder = new StringBuilder(); + builder.append("Current phase: ").append(mCurrentPhase).append("\n"); + builder.append("Services:\n"); + final int startedLen = mServices.size(); + for (int i = 0; i < startedLen; i++) { + final SystemService service = mServices.get(i); + builder.append("\t") + .append(service.getClass().getSimpleName()) + .append("\n"); + } + + Slog.e(TAG, builder.toString()); + } + + private SystemService createInstance(Class<?> clazz) { + // Make sure it's a type we expect + if (!SystemService.class.isAssignableFrom(clazz)) { + reportWtf("Class " + clazz.getName() + " does not extend " + + SystemService.class.getName()); + } + + try { + return (SystemService) clazz.newInstance(); + } catch (InstantiationException e) { + reportWtf("Class " + clazz.getName() + " is abstract", e); + } catch (IllegalAccessException e) { + reportWtf("Class " + clazz.getName() + + " must have a public no-arg constructor", e); + } + return null; + } + + private static void reportWtf(String message) { + reportWtf(message, null); + } + + private static void reportWtf(String message, Throwable e) { + Slog.i(TAG, "******************************"); + Log.wtf(TAG, message, e); + + // Make sure we die + throw new RuntimeException(message, e); + } +} diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java index 062be01..de912dc 100644 --- a/services/java/com/android/server/UiModeManagerService.java +++ b/services/java/com/android/server/UiModeManagerService.java @@ -34,9 +34,9 @@ import android.content.res.Configuration; import android.os.BatteryManager; import android.os.Binder; import android.os.Handler; +import android.os.IBinder; import android.os.PowerManager; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; import android.service.dreams.Sandman; @@ -47,9 +47,11 @@ import java.io.PrintWriter; import com.android.internal.R; import com.android.internal.app.DisableCarModeActivity; -import com.android.server.TwilightService.TwilightState; +import com.android.server.twilight.TwilightListener; +import com.android.server.twilight.TwilightManager; +import com.android.server.twilight.TwilightState; -final class UiModeManagerService extends IUiModeManager.Stub { +final class UiModeManagerService extends SystemService { private static final String TAG = UiModeManager.class.getSimpleName(); private static final boolean LOG = false; @@ -57,40 +59,36 @@ final class UiModeManagerService extends IUiModeManager.Stub { private static final boolean ENABLE_LAUNCH_CAR_DOCK_APP = true; private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = true; - private final Context mContext; - private final TwilightService mTwilightService; - private final Handler mHandler = new Handler(); - final Object mLock = new Object(); - private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + private int mLastBroadcastState = Intent.EXTRA_DOCK_STATE_UNDOCKED; + int mNightMode = UiModeManager.MODE_NIGHT_NO; - private int mNightMode = UiModeManager.MODE_NIGHT_NO; private boolean mCarModeEnabled = false; private boolean mCharging = false; - private final int mDefaultUiModeType; - private final boolean mCarModeKeepsScreenOn; - private final boolean mDeskModeKeepsScreenOn; - private final boolean mTelevision; - + private int mDefaultUiModeType; + private boolean mCarModeKeepsScreenOn; + private boolean mDeskModeKeepsScreenOn; + private boolean mTelevision; private boolean mComputedNightMode; - private int mCurUiMode = 0; - private int mSetUiMode = 0; + int mCurUiMode = 0; + private int mSetUiMode = 0; private boolean mHoldingConfiguration = false; + private Configuration mConfiguration = new Configuration(); + boolean mSystemReady; - private boolean mSystemReady; + private final Handler mHandler = new Handler(); + private TwilightManager mTwilightManager; private NotificationManager mNotificationManager; - private StatusBarManager mStatusBarManager; - private final PowerManager mPowerManager; - private final PowerManager.WakeLock mWakeLock; + private PowerManager.WakeLock mWakeLock; - static Intent buildHomeIntent(String category) { + private static Intent buildHomeIntent(String category) { Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(category); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK @@ -142,28 +140,26 @@ final class UiModeManagerService extends IUiModeManager.Stub { } }; - private final TwilightService.TwilightListener mTwilightListener = - new TwilightService.TwilightListener() { + private final TwilightListener mTwilightListener = new TwilightListener() { @Override public void onTwilightStateChanged() { updateTwilight(); } }; - public UiModeManagerService(Context context, TwilightService twilight) { - mContext = context; - mTwilightService = twilight; - - ServiceManager.addService(Context.UI_MODE_SERVICE, this); - - mContext.registerReceiver(mDockModeReceiver, + @Override + public void onStart() { + final Context context = getContext(); + mTwilightManager = getLocalService(TwilightManager.class); + final PowerManager powerManager = + (PowerManager) context.getSystemService(Context.POWER_SERVICE); + mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG); + + context.registerReceiver(mDockModeReceiver, new IntentFilter(Intent.ACTION_DOCK_EVENT)); - mContext.registerReceiver(mBatteryReceiver, + context.registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); - mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); - mWakeLock = mPowerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, TAG); - mConfiguration.setToDefaults(); mDefaultUiModeType = context.getResources().getInteger( @@ -175,101 +171,139 @@ final class UiModeManagerService extends IUiModeManager.Stub { mTelevision = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_TELEVISION); - mNightMode = Settings.Secure.getInt(mContext.getContentResolver(), + mNightMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.UI_NIGHT_MODE, UiModeManager.MODE_NIGHT_AUTO); - mTwilightService.registerListener(mTwilightListener, mHandler); + mTwilightManager.registerListener(mTwilightListener, mHandler); + + publishBinderService(Context.UI_MODE_SERVICE, mService); } - @Override // Binder call - public void disableCarMode(int flags) { - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - setCarModeLocked(false); - if (mSystemReady) { - updateLocked(0, flags); + private final IBinder mService = new IUiModeManager.Stub() { + @Override + public void enableCarMode(int flags) { + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + setCarModeLocked(true); + if (mSystemReady) { + updateLocked(flags, 0); + } } + } finally { + Binder.restoreCallingIdentity(ident); } - } finally { - Binder.restoreCallingIdentity(ident); } - } - @Override // Binder call - public void enableCarMode(int flags) { - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - setCarModeLocked(true); - if (mSystemReady) { - updateLocked(flags, 0); + @Override + public void disableCarMode(int flags) { + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + setCarModeLocked(false); + if (mSystemReady) { + updateLocked(0, flags); + } } + } finally { + Binder.restoreCallingIdentity(ident); } - } finally { - Binder.restoreCallingIdentity(ident); } - } - @Override // Binder call - public int getCurrentModeType() { - final long ident = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - return mCurUiMode & Configuration.UI_MODE_TYPE_MASK; + @Override + public int getCurrentModeType() { + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + return mCurUiMode & Configuration.UI_MODE_TYPE_MASK; + } + } finally { + Binder.restoreCallingIdentity(ident); } - } finally { - Binder.restoreCallingIdentity(ident); } - } - @Override // Binder call - public void setNightMode(int mode) { - switch (mode) { - case UiModeManager.MODE_NIGHT_NO: - case UiModeManager.MODE_NIGHT_YES: - case UiModeManager.MODE_NIGHT_AUTO: - break; - default: - throw new IllegalArgumentException("Unknown mode: " + mode); + @Override + public void setNightMode(int mode) { + switch (mode) { + case UiModeManager.MODE_NIGHT_NO: + case UiModeManager.MODE_NIGHT_YES: + case UiModeManager.MODE_NIGHT_AUTO: + break; + default: + throw new IllegalArgumentException("Unknown mode: " + mode); + } + + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + if (isDoingNightModeLocked() && mNightMode != mode) { + Settings.Secure.putInt(getContext().getContentResolver(), + Settings.Secure.UI_NIGHT_MODE, mode); + mNightMode = mode; + updateLocked(0, 0); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } } - final long ident = Binder.clearCallingIdentity(); - try { + @Override + public int getNightMode() { synchronized (mLock) { - if (isDoingNightModeLocked() && mNightMode != mode) { - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.UI_NIGHT_MODE, mode); - mNightMode = mode; - updateLocked(0, 0); - } + return mNightMode; } - } finally { - Binder.restoreCallingIdentity(ident); } - } - @Override // Binder call - public int getNightMode() { + @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 uimode service from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + dumpImpl(pw); + } + }; + + void dumpImpl(PrintWriter pw) { synchronized (mLock) { - return mNightMode; + pw.println("Current UI Mode Service state:"); + pw.print(" mDockState="); pw.print(mDockState); + pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState); + pw.print(" mNightMode="); pw.print(mNightMode); + pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled); + pw.print(" mComputedNightMode="); pw.println(mComputedNightMode); + pw.print(" mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode)); + pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode)); + pw.print(" mHoldingConfiguration="); pw.print(mHoldingConfiguration); + pw.print(" mSystemReady="); pw.println(mSystemReady); + pw.print(" mTwilightService.getCurrentState()="); + pw.println(mTwilightManager.getCurrentState()); } } - void systemReady() { - synchronized (mLock) { - mSystemReady = true; - mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR; - updateComputedNightModeLocked(); - updateLocked(0, 0); + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + synchronized (mLock) { + mSystemReady = true; + mCarModeEnabled = mDockState == Intent.EXTRA_DOCK_STATE_CAR; + updateComputedNightModeLocked(); + updateLocked(0, 0); + } } } - private boolean isDoingNightModeLocked() { + boolean isDoingNightModeLocked() { return mCarModeEnabled || mDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED; } - private void setCarModeLocked(boolean enabled) { + void setCarModeLocked(boolean enabled) { if (mCarModeEnabled != enabled) { mCarModeEnabled = enabled; } @@ -344,7 +378,7 @@ final class UiModeManagerService extends IUiModeManager.Stub { } } - private void updateLocked(int enableFlags, int disableFlags) { + void updateLocked(int enableFlags, int disableFlags) { String action = null; String oldAction = null; if (mLastBroadcastState == Intent.EXTRA_DOCK_STATE_CAR) { @@ -359,7 +393,7 @@ final class UiModeManagerService extends IUiModeManager.Stub { adjustStatusBarCarModeLocked(); if (oldAction != null) { - mContext.sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL); + getContext().sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL); } mLastBroadcastState = Intent.EXTRA_DOCK_STATE_CAR; action = UiModeManager.ACTION_ENTER_CAR_MODE; @@ -367,7 +401,7 @@ final class UiModeManagerService extends IUiModeManager.Stub { } else if (isDeskDockState(mDockState)) { if (!isDeskDockState(mLastBroadcastState)) { if (oldAction != null) { - mContext.sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL); + getContext().sendBroadcastAsUser(new Intent(oldAction), UserHandle.ALL); } mLastBroadcastState = mDockState; action = UiModeManager.ACTION_ENTER_DESK_MODE; @@ -393,7 +427,7 @@ final class UiModeManagerService extends IUiModeManager.Stub { Intent intent = new Intent(action); intent.putExtra("enableFlags", enableFlags); intent.putExtra("disableFlags", disableFlags); - mContext.sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, + getContext().sendOrderedBroadcastAsUser(intent, UserHandle.CURRENT, null, mResultReceiver, null, Activity.RESULT_OK, null, null); // Attempting to make this transition a little more clean, we are going @@ -491,7 +525,7 @@ final class UiModeManagerService extends IUiModeManager.Stub { // activity manager take care of both the start and config // change. Intent homeIntent = buildHomeIntent(category); - if (Sandman.shouldStartDockApp(mContext, homeIntent)) { + if (Sandman.shouldStartDockApp(getContext(), homeIntent)) { try { int result = ActivityManagerNative.getDefault().startActivityWithConfig( null, null, homeIntent, null, null, null, 0, 0, @@ -513,14 +547,15 @@ final class UiModeManagerService extends IUiModeManager.Stub { // If we did not start a dock app, then start dreaming if supported. if (category != null && !dockAppStarted) { - Sandman.startDreamWhenDockedIfAppropriate(mContext); + Sandman.startDreamWhenDockedIfAppropriate(getContext()); } } private void adjustStatusBarCarModeLocked() { + final Context context = getContext(); if (mStatusBarManager == null) { mStatusBarManager = (StatusBarManager) - mContext.getSystemService(Context.STATUS_BAR_SERVICE); + context.getSystemService(Context.STATUS_BAR_SERVICE); } // Fear not: StatusBarManagerService manages a list of requests to disable @@ -536,12 +571,12 @@ final class UiModeManagerService extends IUiModeManager.Stub { if (mNotificationManager == null) { mNotificationManager = (NotificationManager) - mContext.getSystemService(Context.NOTIFICATION_SERVICE); + context.getSystemService(Context.NOTIFICATION_SERVICE); } if (mNotificationManager != null) { if (mCarModeEnabled) { - Intent carModeOffIntent = new Intent(mContext, DisableCarModeActivity.class); + Intent carModeOffIntent = new Intent(context, DisableCarModeActivity.class); Notification n = new Notification(); n.icon = R.drawable.stat_notify_car_mode; @@ -549,10 +584,10 @@ final class UiModeManagerService extends IUiModeManager.Stub { n.flags = Notification.FLAG_ONGOING_EVENT; n.when = 0; n.setLatestEventInfo( - mContext, - mContext.getString(R.string.car_mode_disable_notification_title), - mContext.getString(R.string.car_mode_disable_notification_message), - PendingIntent.getActivityAsUser(mContext, 0, carModeOffIntent, 0, + context, + context.getString(R.string.car_mode_disable_notification_title), + context.getString(R.string.car_mode_disable_notification_message), + PendingIntent.getActivityAsUser(context, 0, carModeOffIntent, 0, null, UserHandle.CURRENT)); mNotificationManager.notifyAsUser(null, R.string.car_mode_disable_notification_title, n, UserHandle.ALL); @@ -563,7 +598,7 @@ final class UiModeManagerService extends IUiModeManager.Stub { } } - private void updateTwilight() { + void updateTwilight() { synchronized (mLock) { if (isDoingNightModeLocked() && mNightMode == UiModeManager.MODE_NIGHT_AUTO) { updateComputedNightModeLocked(); @@ -573,36 +608,11 @@ final class UiModeManagerService extends IUiModeManager.Stub { } private void updateComputedNightModeLocked() { - TwilightState state = mTwilightService.getCurrentState(); + TwilightState state = mTwilightManager.getCurrentState(); if (state != null) { mComputedNightMode = state.isNight(); } } - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump uimode service from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return; - } - - synchronized (mLock) { - pw.println("Current UI Mode Service state:"); - pw.print(" mDockState="); pw.print(mDockState); - pw.print(" mLastBroadcastState="); pw.println(mLastBroadcastState); - pw.print(" mNightMode="); pw.print(mNightMode); - pw.print(" mCarModeEnabled="); pw.print(mCarModeEnabled); - pw.print(" mComputedNightMode="); pw.println(mComputedNightMode); - pw.print(" mCurUiMode=0x"); pw.print(Integer.toHexString(mCurUiMode)); - pw.print(" mSetUiMode=0x"); pw.println(Integer.toHexString(mSetUiMode)); - pw.print(" mHoldingConfiguration="); pw.print(mHoldingConfiguration); - pw.print(" mSystemReady="); pw.println(mSystemReady); - pw.print(" mTwilightService.getCurrentState()="); - pw.println(mTwilightService.getCurrentState()); - } - } } diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index 3e90078..e0d6505 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -76,9 +76,6 @@ public class Watchdog extends Thread { final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<HandlerChecker>(); final HandlerChecker mMonitorChecker; ContentResolver mResolver; - BatteryService mBattery; - PowerManagerService mPower; - AlarmManagerService mAlarm; ActivityManagerService mActivity; int mPhonePid; @@ -230,13 +227,8 @@ public class Watchdog extends Thread { "i/o thread", DEFAULT_TIMEOUT)); } - public void init(Context context, BatteryService battery, - PowerManagerService power, AlarmManagerService alarm, - ActivityManagerService activity) { + public void init(Context context, ActivityManagerService activity) { mResolver = context.getContentResolver(); - mBattery = battery; - mPower = power; - mAlarm = alarm; mActivity = activity; context.registerReceiver(new RebootRequestReceiver(), diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java index 80e6e94..cb04835 100644 --- a/services/java/com/android/server/am/ServiceRecord.java +++ b/services/java/com/android/server/am/ServiceRecord.java @@ -18,7 +18,8 @@ package com.android.server.am; import com.android.internal.app.ProcessStats; import com.android.internal.os.BatteryStatsImpl; -import com.android.server.NotificationManagerService; +import com.android.server.LocalServices; +import com.android.server.notification.NotificationManagerInternal; import android.app.INotificationManager; import android.app.Notification; @@ -427,8 +428,8 @@ final class ServiceRecord extends Binder { final Notification localForegroundNoti = foregroundNoti; ams.mHandler.post(new Runnable() { public void run() { - NotificationManagerService nm = - (NotificationManagerService) NotificationManager.getService(); + NotificationManagerInternal nm = LocalServices.getService( + NotificationManagerInternal.class); if (nm == null) { return; } @@ -479,7 +480,7 @@ final class ServiceRecord extends Binder { throw new RuntimeException("icon must be non-zero"); } int[] outId = new int[1]; - nm.enqueueNotificationInternal(localPackageName, localPackageName, + nm.enqueueNotification(localPackageName, localPackageName, appUid, appPid, null, localForegroundId, localForegroundNoti, outId, userId); } catch (RuntimeException e) { diff --git a/services/java/com/android/server/lights/Light.java b/services/java/com/android/server/lights/Light.java new file mode 100644 index 0000000..b496b4c --- /dev/null +++ b/services/java/com/android/server/lights/Light.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.lights; + +public abstract class Light { + public static final int LIGHT_FLASH_NONE = 0; + public static final int LIGHT_FLASH_TIMED = 1; + public static final int LIGHT_FLASH_HARDWARE = 2; + + /** + * Light brightness is managed by a user setting. + */ + public static final int BRIGHTNESS_MODE_USER = 0; + + /** + * Light brightness is managed by a light sensor. + */ + public static final int BRIGHTNESS_MODE_SENSOR = 1; + + public abstract void setBrightness(int brightness); + 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 pulse(); + public abstract void pulse(int color, int onMS); + public abstract void turnOff(); +}
\ No newline at end of file diff --git a/services/java/com/android/server/lights/LightsManager.java b/services/java/com/android/server/lights/LightsManager.java new file mode 100644 index 0000000..2f20509 --- /dev/null +++ b/services/java/com/android/server/lights/LightsManager.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.lights; + +public abstract class LightsManager { + public static final int LIGHT_ID_BACKLIGHT = 0; + public static final int LIGHT_ID_KEYBOARD = 1; + public static final int LIGHT_ID_BUTTONS = 2; + public static final int LIGHT_ID_BATTERY = 3; + public static final int LIGHT_ID_NOTIFICATIONS = 4; + 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 abstract Light getLight(int id); +} diff --git a/services/java/com/android/server/LightsService.java b/services/java/com/android/server/lights/LightsService.java index 89bfcac..d814785 100644 --- a/services/java/com/android/server/LightsService.java +++ b/services/java/com/android/server/lights/LightsService.java @@ -14,59 +14,38 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.lights; + +import com.android.server.SystemService; import android.content.Context; import android.content.pm.PackageManager; import android.os.Handler; import android.os.IHardwareService; -import android.os.ServiceManager; import android.os.Message; import android.util.Slog; import java.io.FileInputStream; import java.io.FileOutputStream; -public class LightsService { - private static final String TAG = "LightsService"; - private static final boolean DEBUG = false; - - public static final int LIGHT_ID_BACKLIGHT = 0; - public static final int LIGHT_ID_KEYBOARD = 1; - public static final int LIGHT_ID_BUTTONS = 2; - public static final int LIGHT_ID_BATTERY = 3; - public static final int LIGHT_ID_NOTIFICATIONS = 4; - 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_FLASH_NONE = 0; - public static final int LIGHT_FLASH_TIMED = 1; - public static final int LIGHT_FLASH_HARDWARE = 2; - - /** - * Light brightness is managed by a user setting. - */ - public static final int BRIGHTNESS_MODE_USER = 0; - - /** - * Light brightness is managed by a light sensor. - */ - public static final int BRIGHTNESS_MODE_SENSOR = 1; +public class LightsService extends SystemService { + static final String TAG = "LightsService"; + static final boolean DEBUG = false; - private final Light mLights[] = new Light[LIGHT_ID_COUNT]; + final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT]; - public final class Light { + private final class LightImpl extends Light { - private Light(int id) { + private LightImpl(int id) { mId = id; } + @Override public void setBrightness(int brightness) { setBrightness(brightness, BRIGHTNESS_MODE_USER); } + @Override public void setBrightness(int brightness, int brightnessMode) { synchronized (this) { int color = brightness & 0x000000ff; @@ -75,23 +54,26 @@ public class LightsService { } } + @Override public void setColor(int color) { synchronized (this) { setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, 0); } } + @Override public void setFlashing(int color, int mode, int onMS, int offMS) { synchronized (this) { setLightLocked(color, mode, onMS, offMS, BRIGHTNESS_MODE_USER); } } - + @Override public void pulse() { pulse(0x00ffffff, 7); } + @Override public void pulse(int color, int onMS) { synchronized (this) { if (mColor == 0 && !mFlashing) { @@ -101,6 +83,7 @@ public class LightsService { } } + @Override public void turnOff() { synchronized (this) { setLightLocked(0, LIGHT_FLASH_NONE, 0, 0, 0); @@ -153,9 +136,10 @@ public class LightsService { } public void setFlashlightEnabled(boolean on) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.FLASHLIGHT) + final Context context = getContext(); + if (context.checkCallingOrSelfPermission(android.Manifest.permission.FLASHLIGHT) != PackageManager.PERMISSION_GRANTED && - mContext.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST) + context.checkCallingOrSelfPermission(android.Manifest.permission.HARDWARE_TEST) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Requires FLASHLIGHT or HARDWARE_TEST permission"); } @@ -172,31 +156,41 @@ public class LightsService { } }; - LightsService(Context context) { - + @Override + public void onCreate(Context context) { mNativePointer = init_native(); - mContext = context; - - ServiceManager.addService("hardware", mLegacyFlashlightHack); - for (int i = 0; i < LIGHT_ID_COUNT; i++) { - mLights[i] = new Light(i); + for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++) { + mLights[i] = new LightImpl(i); } } + @Override + public void onStart() { + publishBinderService("hardware", mLegacyFlashlightHack); + publishLocalService(LightsManager.class, mService); + } + + private final LightsManager mService = new LightsManager() { + @Override + public com.android.server.lights.Light getLight(int id) { + if (id < LIGHT_ID_COUNT) { + return mLights[id]; + } else { + return null; + } + } + }; + protected void finalize() throws Throwable { finalize_native(mNativePointer); super.finalize(); } - public Light getLight(int id) { - return mLights[id]; - } - private Handler mH = new Handler() { @Override public void handleMessage(Message msg) { - Light light = (Light)msg.obj; + LightImpl light = (LightImpl)msg.obj; light.stopFlashing(); } }; @@ -204,10 +198,8 @@ public class LightsService { private static native int init_native(); private static native void finalize_native(int ptr); - private static native void setLight_native(int ptr, int light, int color, int mode, + static native void setLight_native(int ptr, int light, int color, int mode, int onMS, int offMS, int brightnessMode); - private final Context mContext; - - private int mNativePointer; + int mNativePointer; } diff --git a/services/java/com/android/server/notification/NotificationDelegate.java b/services/java/com/android/server/notification/NotificationDelegate.java new file mode 100644 index 0000000..df2aaca --- /dev/null +++ b/services/java/com/android/server/notification/NotificationDelegate.java @@ -0,0 +1,27 @@ +/** + * Copyright (c) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.notification; + +public interface NotificationDelegate { + void onSetDisabled(int status); + void onClearAll(); + void onNotificationClick(String pkg, String tag, int id); + void onNotificationClear(String pkg, String tag, int id); + void onNotificationError(String pkg, String tag, int id, + int uid, int initialPid, String message); + void onPanelRevealed(); +} diff --git a/services/java/com/android/server/notification/NotificationManagerInternal.java b/services/java/com/android/server/notification/NotificationManagerInternal.java new file mode 100644 index 0000000..92ffdcc --- /dev/null +++ b/services/java/com/android/server/notification/NotificationManagerInternal.java @@ -0,0 +1,8 @@ +package com.android.server.notification; + +import android.app.Notification; + +public interface NotificationManagerInternal { + void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid, + String tag, int id, Notification notification, int[] idReceived, int userId); +} diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/notification/NotificationManagerService.java index 0438675..db4cf31d 100644 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/notification/NotificationManagerService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.notification; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; @@ -47,7 +47,6 @@ import android.content.res.Resources; import android.database.ContentObserver; import android.graphics.Bitmap; import android.media.AudioManager; -import android.media.IAudioService; import android.media.IRingtonePlayer; import android.net.Uri; import android.os.Binder; @@ -56,9 +55,7 @@ import android.os.IBinder; import android.os.Message; import android.os.Process; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; -import android.os.UserManager; import android.os.Vibrator; import android.provider.Settings; import android.service.notification.INotificationListener; @@ -78,6 +75,12 @@ import android.widget.Toast; import com.android.internal.R; import com.android.internal.notification.NotificationScorer; +import com.android.server.EventLogTags; +import com.android.server.statusbar.StatusBarManagerInternal; +import com.android.server.SystemService; +import com.android.server.lights.Light; +import com.android.server.lights.LightsManager; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -99,66 +102,61 @@ import java.util.Set; import libcore.io.IoUtils; - /** {@hide} */ -public class NotificationManagerService extends INotificationManager.Stub -{ - private static final String TAG = "NotificationService"; - private static final boolean DBG = false; +public class NotificationManagerService extends SystemService { + static final String TAG = "NotificationService"; + static final boolean DBG = false; - private static final int MAX_PACKAGE_NOTIFICATIONS = 50; + static final int MAX_PACKAGE_NOTIFICATIONS = 50; // message codes - private static final int MESSAGE_TIMEOUT = 2; + static final int MESSAGE_TIMEOUT = 2; - private static final int LONG_DELAY = 3500; // 3.5 seconds - private static final int SHORT_DELAY = 2000; // 2 seconds + static final int LONG_DELAY = 3500; // 3.5 seconds + static final int SHORT_DELAY = 2000; // 2 seconds - private static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; - private static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps + static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250}; + static final int VIBRATE_PATTERN_MAXLEN = 8 * 2 + 1; // up to eight bumps - private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; - private static final boolean SCORE_ONGOING_HIGHER = false; + static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_NOTIFICATION; + static final boolean SCORE_ONGOING_HIGHER = false; - private static final int JUNK_SCORE = -1000; - private static final int NOTIFICATION_PRIORITY_MULTIPLIER = 10; - private static final int SCORE_DISPLAY_THRESHOLD = Notification.PRIORITY_MIN * NOTIFICATION_PRIORITY_MULTIPLIER; + 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; // Notifications with scores below this will not interrupt the user, either via LED or // sound or vibration - private static final int SCORE_INTERRUPTION_THRESHOLD = + static final int SCORE_INTERRUPTION_THRESHOLD = Notification.PRIORITY_LOW * NOTIFICATION_PRIORITY_MULTIPLIER; - private static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true; - private static final boolean ENABLE_BLOCKED_TOASTS = true; + static final boolean ENABLE_BLOCKED_NOTIFICATIONS = true; + static final boolean ENABLE_BLOCKED_TOASTS = true; - private static final String ENABLED_NOTIFICATION_LISTENERS_SEPARATOR = ":"; + static final String ENABLED_NOTIFICATION_LISTENERS_SEPARATOR = ":"; - final Context mContext; - final IActivityManager mAm; - final UserManager mUserManager; - final IBinder mForegroundToken = new Binder(); + private IActivityManager mAm; + AudioManager mAudioManager; + StatusBarManagerInternal mStatusBar; + Vibrator mVibrator; + final IBinder mForegroundToken = new Binder(); private WorkerHandler mHandler; - private StatusBarManagerService mStatusBar; - private LightsService.Light mNotificationLight; - private LightsService.Light mAttentionLight; + private Light mNotificationLight; + Light mAttentionLight; private int mDefaultNotificationColor; private int mDefaultNotificationLedOn; - private int mDefaultNotificationLedOff; + private int mDefaultNotificationLedOff; private long[] mDefaultVibrationPattern; - private long[] mFallbackVibrationPattern; - - private boolean mSystemReady; - private int mDisabledNotifications; - private NotificationRecord mSoundNotification; - private NotificationRecord mVibrateNotification; + private long[] mFallbackVibrationPattern; + boolean mSystemReady; - private IAudioService mAudioService; - private Vibrator mVibrator; + int mDisabledNotifications; + NotificationRecord mSoundNotification; + NotificationRecord mVibrateNotification; // for enabling and disabling notification pulse behavior private boolean mScreenOn = true; @@ -166,15 +164,15 @@ public class NotificationManagerService extends INotificationManager.Stub private boolean mNotificationPulseEnabled; // used as a mutex for access to all active notifications & listeners - private final ArrayList<NotificationRecord> mNotificationList = + final ArrayList<NotificationRecord> mNotificationList = new ArrayList<NotificationRecord>(); - private ArrayList<ToastRecord> mToastQueue; + final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); - private ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>(); - private NotificationRecord mLedNotification; + ArrayList<NotificationRecord> mLights = new ArrayList<NotificationRecord>(); + NotificationRecord mLedNotification; - private final AppOpsManager mAppOps; + private AppOpsManager mAppOps; // contains connections to all connected listeners, including app services // and system listeners @@ -202,9 +200,9 @@ public class NotificationManagerService extends INotificationManager.Stub private static final String TAG_PACKAGE = "package"; private static final String ATTR_NAME = "name"; - private final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>(); + final ArrayList<NotificationScorer> mScorers = new ArrayList<NotificationScorer>(); - private class NotificationListenerInfo implements DeathRecipient { + private class NotificationListenerInfo implements IBinder.DeathRecipient { INotificationListener listener; ComponentName component; int userid; @@ -262,7 +260,7 @@ public class NotificationManagerService extends INotificationManager.Stub public void binderDied() { if (connection == null) { // This is not a service; it won't be recreated. We can give up this connection. - unregisterListener(this.listener, this.userid); + unregisterListenerImpl(this.listener, this.userid); } } @@ -400,12 +398,14 @@ public class NotificationManagerService extends INotificationManager.Stub tag = parser.getName(); if (type == START_TAG) { if (TAG_BODY.equals(tag)) { - version = Integer.parseInt(parser.getAttributeValue(null, ATTR_VERSION)); + version = Integer.parseInt( + parser.getAttributeValue(null, ATTR_VERSION)); } else if (TAG_BLOCKED_PKGS.equals(tag)) { while ((type = parser.next()) != END_DOCUMENT) { tag = parser.getName(); if (TAG_PACKAGE.equals(tag)) { - mBlockedPackages.add(parser.getAttributeValue(null, ATTR_NAME)); + mBlockedPackages.add( + parser.getAttributeValue(null, ATTR_NAME)); } else if (TAG_BLOCKED_PKGS.equals(tag) && type == END_TAG) { break; } @@ -428,15 +428,6 @@ public class NotificationManagerService extends INotificationManager.Stub } } - /** - * Use this when you just want to know if notifications are OK for this package. - */ - public boolean areNotificationsEnabledForPackage(String pkg, int uid) { - checkCallerIsSystem(); - return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) - == AppOpsManager.MODE_ALLOWED); - } - /** Use this when you actually want to post a notification or toast. * * Unchecked. Not exposed via Binder, but can be called in the course of enqueue*(). @@ -450,21 +441,6 @@ public class NotificationManagerService extends INotificationManager.Stub return true; } - public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { - checkCallerIsSystem(); - - Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); - - mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, - enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); - - // Now, cancel any outstanding notifications that are part of a just-disabled app - if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) { - cancelAllNotificationsInt(pkg, 0, 0, true, UserHandle.getUserId(uid)); - } - } - - private static String idDebugString(Context baseContext, String packageName, int id) { Context c = null; @@ -490,57 +466,6 @@ public class NotificationManagerService extends INotificationManager.Stub } } - /** - * System-only API for getting a list of current (i.e. not cleared) notifications. - * - * Requires ACCESS_NOTIFICATIONS which is signature|system. - */ - @Override - public StatusBarNotification[] getActiveNotifications(String callingPkg) { - // enforce() will ensure the calling uid has the correct permission - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS, - "NotificationManagerService.getActiveNotifications"); - - StatusBarNotification[] tmp = null; - int uid = Binder.getCallingUid(); - - // noteOp will check to make sure the callingPkg matches the uid - if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) - == AppOpsManager.MODE_ALLOWED) { - synchronized (mNotificationList) { - tmp = new StatusBarNotification[mNotificationList.size()]; - final int N = mNotificationList.size(); - for (int i=0; i<N; i++) { - tmp[i] = mNotificationList.get(i).sbn; - } - } - } - return tmp; - } - - /** - * System-only API for getting a list of recent (cleared, no longer shown) notifications. - * - * Requires ACCESS_NOTIFICATIONS which is signature|system. - */ - @Override - public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { - // enforce() will ensure the calling uid has the correct permission - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NOTIFICATIONS, - "NotificationManagerService.getHistoricalNotifications"); - - StatusBarNotification[] tmp = null; - int uid = Binder.getCallingUid(); - - // noteOp will check to make sure the callingPkg matches the uid - if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) - == AppOpsManager.MODE_ALLOWED) { - synchronized (mArchive) { - tmp = mArchive.getArray(count); - } - } - return tmp; - } /** * Remove notification access for any services that no longer exist. @@ -548,12 +473,12 @@ public class NotificationManagerService extends INotificationManager.Stub void disableNonexistentListeners() { int currentUser = ActivityManager.getCurrentUser(); String flatIn = Settings.Secure.getStringForUser( - mContext.getContentResolver(), + getContext().getContentResolver(), Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, currentUser); if (!TextUtils.isEmpty(flatIn)) { if (DBG) Slog.v(TAG, "flat before: " + flatIn); - PackageManager pm = mContext.getPackageManager(); + PackageManager pm = getContext().getPackageManager(); List<ResolveInfo> installedServices = pm.queryIntentServicesAsUser( new Intent(NotificationListenerService.SERVICE_INTERFACE), PackageManager.GET_SERVICES | PackageManager.GET_META_DATA, @@ -589,7 +514,7 @@ public class NotificationManagerService extends INotificationManager.Stub } if (DBG) Slog.v(TAG, "flat after: " + flatOut); if (!flatIn.equals(flatOut)) { - Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.putStringForUser(getContext().getContentResolver(), Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, flatOut, currentUser); } @@ -603,7 +528,7 @@ public class NotificationManagerService extends INotificationManager.Stub void rebindListenerServices() { final int currentUser = ActivityManager.getCurrentUser(); String flat = Settings.Secure.getStringForUser( - mContext.getContentResolver(), + getContext().getContentResolver(), Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, currentUser); @@ -652,28 +577,6 @@ public class NotificationManagerService extends INotificationManager.Stub } } - /** - * Register a listener binder directly with the notification manager. - * - * Only works with system callers. Apps should extend - * {@link android.service.notification.NotificationListenerService}. - */ - @Override - public void registerListener(final INotificationListener listener, - final ComponentName component, final int userid) { - checkCallerIsSystem(); - - synchronized (mNotificationList) { - try { - NotificationListenerInfo info - = new NotificationListenerInfo(listener, component, userid, true); - listener.asBinder().linkToDeath(info, 0); - mListeners.add(info); - } catch (RemoteException e) { - // already dead - } - } - } /** * Version of registerListener that takes the name of a @@ -703,7 +606,7 @@ public class NotificationManagerService extends INotificationManager.Stub if (DBG) Slog.v(TAG, " disconnecting old listener: " + info.listener); mListeners.remove(i); if (info.connection != null) { - mContext.unbindService(info.connection); + getContext().unbindService(info.connection); } } } @@ -713,21 +616,25 @@ public class NotificationManagerService extends INotificationManager.Stub intent.putExtra(Intent.EXTRA_CLIENT_LABEL, R.string.notification_listener_binding_label); - intent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity( - mContext, 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0)); + + final PendingIntent pendingIntent = PendingIntent.getActivity( + getContext(), 0, new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS), 0); + intent.putExtra(Intent.EXTRA_CLIENT_INTENT, pendingIntent); try { if (DBG) Slog.v(TAG, "binding: " + intent); - if (!mContext.bindServiceAsUser(intent, + if (!getContext().bindServiceAsUser(intent, new ServiceConnection() { INotificationListener mListener; + @Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mNotificationList) { mServicesBinding.remove(servicesBindingTag); try { mListener = INotificationListener.Stub.asInterface(service); - NotificationListenerInfo info = new NotificationListenerInfo( + NotificationListenerInfo info + = new NotificationListenerInfo( mListener, name, userid, this); service.linkToDeath(info, 0); mListeners.add(info); @@ -756,28 +663,6 @@ public class NotificationManagerService extends INotificationManager.Stub } } - /** - * Remove a listener binder directly - */ - @Override - public void unregisterListener(INotificationListener listener, int userid) { - // no need to check permissions; if your listener binder is in the list, - // that's proof that you had permission to add it in the first place - - synchronized (mNotificationList) { - final int N = mListeners.size(); - for (int i=N-1; i>=0; i--) { - final NotificationListenerInfo info = mListeners.get(i); - if (info.listener.asBinder() == listener.asBinder() - && info.userid == userid) { - mListeners.remove(i); - if (info.connection != null) { - mContext.unbindService(info.connection); - } - } - } - } - } /** * Remove a listener service for the given user by ComponentName @@ -794,7 +679,7 @@ public class NotificationManagerService extends INotificationManager.Stub mListeners.remove(i); if (info.connection != null) { try { - mContext.unbindService(info.connection); + getContext().unbindService(info.connection); } catch (IllegalArgumentException ex) { // something happened to the service: we think we have a connection // but it's bogus. @@ -809,7 +694,7 @@ public class NotificationManagerService extends INotificationManager.Stub /** * asynchronously notify all listeners about a new notification */ - private void notifyPostedLocked(NotificationRecord n) { + void notifyPostedLocked(NotificationRecord n) { // make a copy in case changes are made to the underlying Notification object final StatusBarNotification sbn = n.sbn.clone(); for (final NotificationListenerInfo info : mListeners) { @@ -824,7 +709,7 @@ public class NotificationManagerService extends INotificationManager.Stub /** * asynchronously notify all listeners about a removed notification */ - private void notifyRemovedLocked(NotificationRecord n) { + void notifyRemovedLocked(NotificationRecord n) { // make a copy in case changes are made to the underlying Notification object // NOTE: this copy is lightweight: it doesn't include heavyweight parts of the notification final StatusBarNotification sbn_light = n.sbn.cloneLight(); @@ -850,66 +735,7 @@ public class NotificationManagerService extends INotificationManager.Stub throw new SecurityException("Disallowed call from unknown listener: " + listener); } - /** - * Allow an INotificationListener to simulate a "clear all" operation. - * - * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} - * - * @param token The binder for the listener, to check that the caller is allowed - */ - public void cancelAllNotificationsFromListener(INotificationListener token) { - NotificationListenerInfo info = checkListenerToken(token); - long identity = Binder.clearCallingIdentity(); - try { - cancelAll(info.userid); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Allow an INotificationListener to simulate clearing (dismissing) a single notification. - * - * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} - * - * @param token The binder for the listener, to check that the caller is allowed - */ - public void cancelNotificationFromListener(INotificationListener token, String pkg, String tag, int id) { - NotificationListenerInfo info = checkListenerToken(token); - long identity = Binder.clearCallingIdentity(); - try { - cancelNotification(pkg, tag, id, 0, - Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, - true, - info.userid); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - /** - * Allow an INotificationListener to request the list of outstanding notifications seen by - * the current user. Useful when starting up, after which point the listener callbacks should - * be used. - * - * @param token The binder for the listener, to check that the caller is allowed - */ - public StatusBarNotification[] getActiveNotificationsFromListener(INotificationListener token) { - NotificationListenerInfo info = checkListenerToken(token); - - StatusBarNotification[] result = new StatusBarNotification[0]; - ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>(); - synchronized (mNotificationList) { - final int N = mNotificationList.size(); - for (int i=0; i<N; i++) { - StatusBarNotification sbn = mNotificationList.get(i).sbn; - if (info.enabledAndUserMatches(sbn)) { - list.add(sbn); - } - } - } - return list.toArray(result); - } // -- end of listener APIs -- @@ -992,8 +818,8 @@ public class NotificationManagerService extends INotificationManager.Stub return String.format( "NotificationRecord(0x%08x: pkg=%s user=%s id=%d tag=%s score=%d: %s)", System.identityHashCode(this), - this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(), this.sbn.getTag(), - this.sbn.getScore(), this.sbn.getNotification()); + this.sbn.getPackageName(), this.sbn.getUser(), this.sbn.getId(), + this.sbn.getTag(), this.sbn.getScore(), this.sbn.getNotification()); } } @@ -1031,9 +857,9 @@ public class NotificationManagerService extends INotificationManager.Stub } } - private StatusBarManagerService.NotificationCallbacks mNotificationCallbacks - = new StatusBarManagerService.NotificationCallbacks() { + private final NotificationDelegate mNotificationDelegate = new NotificationDelegate() { + @Override public void onSetDisabled(int status) { synchronized (mNotificationList) { mDisabledNotifications = status; @@ -1041,7 +867,7 @@ public class NotificationManagerService extends INotificationManager.Stub // cancel whatever's going on long identity = Binder.clearCallingIdentity(); try { - final IRingtonePlayer player = mAudioService.getRingtonePlayer(); + final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); if (player != null) { player.stopAsync(); } @@ -1060,12 +886,14 @@ public class NotificationManagerService extends INotificationManager.Stub } } + @Override public void onClearAll() { // XXX to be totally correct, the caller should tell us which user // this is for. cancelAll(ActivityManager.getCurrentUser()); } + @Override public void onNotificationClick(String pkg, String tag, int id) { // XXX to be totally correct, the caller should tell us which user // this is for. @@ -1074,6 +902,7 @@ public class NotificationManagerService extends INotificationManager.Stub ActivityManager.getCurrentUser()); } + @Override public void onNotificationClear(String pkg, String tag, int id) { // XXX to be totally correct, the caller should tell us which user // this is for. @@ -1082,6 +911,7 @@ public class NotificationManagerService extends INotificationManager.Stub true, ActivityManager.getCurrentUser()); } + @Override public void onPanelRevealed() { synchronized (mNotificationList) { // sound @@ -1089,7 +919,7 @@ public class NotificationManagerService extends INotificationManager.Stub long identity = Binder.clearCallingIdentity(); try { - final IRingtonePlayer player = mAudioService.getRingtonePlayer(); + final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); if (player != null) { player.stopAsync(); } @@ -1114,6 +944,7 @@ public class NotificationManagerService extends INotificationManager.Stub } } + @Override public void onNotificationError(String pkg, String tag, int id, int uid, int initialPid, String message) { Slog.d(TAG, "onNotification error pkg=" + pkg + " tag=" + tag + " id=" + id @@ -1168,7 +999,7 @@ public class NotificationManagerService extends INotificationManager.Stub if (packageChanged) { // We cancel notifications for packages which have just been disabled try { - final int enabled = mContext.getPackageManager() + final int enabled = getContext().getPackageManager() .getApplicationEnabledSetting(pkgName); if (enabled == PackageManager.COMPONENT_ENABLED_STATE_ENABLED || enabled == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { @@ -1244,7 +1075,7 @@ public class NotificationManagerService extends INotificationManager.Stub } void observe() { - ContentResolver resolver = mContext.getContentResolver(); + ContentResolver resolver = getContext().getContentResolver(); resolver.registerContentObserver(NOTIFICATION_LIGHT_PULSE_URI, false, this, UserHandle.USER_ALL); resolver.registerContentObserver(ENABLED_NOTIFICATION_LISTENERS_URI, @@ -1257,7 +1088,7 @@ public class NotificationManagerService extends INotificationManager.Stub } public void update(Uri uri) { - ContentResolver resolver = mContext.getContentResolver(); + 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; @@ -1287,28 +1118,24 @@ public class NotificationManagerService extends INotificationManager.Stub return out; } - NotificationManagerService(Context context, StatusBarManagerService statusBar, - LightsService lights) - { - super(); - mContext = context; - mVibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE); + @Override + public void onStart() { mAm = ActivityManagerNative.getDefault(); - mUserManager = (UserManager)context.getSystemService(Context.USER_SERVICE); - mToastQueue = new ArrayList<ToastRecord>(); - mHandler = new WorkerHandler(); + mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); + mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); - mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE); + mHandler = new WorkerHandler(); importOldBlockDb(); - mStatusBar = statusBar; - statusBar.setNotificationCallbacks(mNotificationCallbacks); + mStatusBar = getLocalService(StatusBarManagerInternal.class); + mStatusBar.setNotificationDelegate(mNotificationDelegate); - mNotificationLight = lights.getLight(LightsService.LIGHT_ID_NOTIFICATIONS); - mAttentionLight = lights.getLight(LightsService.LIGHT_ID_ATTENTION); + final LightsManager lights = getLocalService(LightsManager.class); + mNotificationLight = lights.getLight(LightsManager.LIGHT_ID_NOTIFICATIONS); + mAttentionLight = lights.getLight(LightsManager.LIGHT_ID_ATTENTION); - Resources resources = mContext.getResources(); + Resources resources = getContext().getResources(); mDefaultNotificationColor = resources.getColor( R.color.config_defaultNotificationColor); mDefaultNotificationLedOn = resources.getInteger( @@ -1330,7 +1157,7 @@ public class NotificationManagerService extends INotificationManager.Stub // After that, including subsequent boots, init with notifications turned on. // This works on the first boot because the setup wizard will toggle this // flag at least once and we'll go back to 0 after that. - if (0 == Settings.Global.getInt(mContext.getContentResolver(), + if (0 == Settings.Global.getInt(getContext().getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0)) { mDisabledNotifications = StatusBarManager.DISABLE_NOTIFICATION_ALERTS; } @@ -1343,7 +1170,7 @@ public class NotificationManagerService extends INotificationManager.Stub filter.addAction(Intent.ACTION_USER_PRESENT); filter.addAction(Intent.ACTION_USER_STOPPED); filter.addAction(Intent.ACTION_USER_SWITCHED); - mContext.registerReceiver(mIntentReceiver, filter); + getContext().registerReceiver(mIntentReceiver, filter); IntentFilter pkgFilter = new IntentFilter(); pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); @@ -1351,9 +1178,9 @@ public class NotificationManagerService extends INotificationManager.Stub pkgFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED); pkgFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART); pkgFilter.addDataScheme("package"); - mContext.registerReceiver(mIntentReceiver, pkgFilter); + getContext().registerReceiver(mIntentReceiver, pkgFilter); IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); - mContext.registerReceiver(mIntentReceiver, sdFilter); + getContext().registerReceiver(mIntentReceiver, sdFilter); mSettingsObserver = new SettingsObserver(mHandler); mSettingsObserver.observe(); @@ -1363,9 +1190,9 @@ public class NotificationManagerService extends INotificationManager.Stub R.array.config_notificationScorers); for (String scorerName : notificationScorerNames) { try { - Class<?> scorerClass = mContext.getClassLoader().loadClass(scorerName); + Class<?> scorerClass = getContext().getClassLoader().loadClass(scorerName); NotificationScorer scorer = (NotificationScorer) scorerClass.newInstance(); - scorer.initialize(mContext); + scorer.initialize(getContext()); mScorers.add(scorer); } catch (ClassNotFoundException e) { Slog.w(TAG, "Couldn't find scorer " + scorerName + ".", e); @@ -1375,6 +1202,9 @@ public class NotificationManagerService extends INotificationManager.Stub Slog.w(TAG, "Problem accessing scorer " + scorerName + ".", e); } } + + publishBinderService(Context.NOTIFICATION_SERVICE, mService); + publishLocalService(NotificationManagerInternal.class, mInternalService); } /** @@ -1383,12 +1213,12 @@ public class NotificationManagerService extends INotificationManager.Stub private void importOldBlockDb() { loadBlockDb(); - PackageManager pm = mContext.getPackageManager(); + PackageManager pm = getContext().getPackageManager(); for (String pkg : mBlockedPackages) { PackageInfo info = null; try { info = pm.getPackageInfo(pkg, 0); - setNotificationsEnabledForPackage(pkg, info.applicationInfo.uid, false); + setNotificationsEnabledForPackageImpl(pkg, info.applicationInfo.uid, false); } catch (NameNotFoundException e) { // forget you } @@ -1399,244 +1229,421 @@ public class NotificationManagerService extends INotificationManager.Stub } } - void systemReady() { - mAudioService = IAudioService.Stub.asInterface( - ServiceManager.getService(Context.AUDIO_SERVICE)); + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + // no beeping until we're basically done booting + mSystemReady = true; - // no beeping until we're basically done booting - mSystemReady = true; + // Grab our optional AudioService + mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); - // make sure our listener services are properly bound - rebindListenerServices(); + // make sure our listener services are properly bound + rebindListenerServices(); + } } - // Toasts - // ============================================================================ - public void enqueueToast(String pkg, ITransientNotification callback, int duration) - { - if (DBG) Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + " duration=" + duration); + void setNotificationsEnabledForPackageImpl(String pkg, int uid, boolean enabled) { + Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg); + + mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, + enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); - if (pkg == null || callback == null) { - Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); - return ; + // Now, cancel any outstanding notifications that are part of a just-disabled app + if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) { + cancelAllNotificationsInt(pkg, 0, 0, true, UserHandle.getUserId(uid)); } + } - final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg)); + private final IBinder mService = new INotificationManager.Stub() { + // Toasts + // ============================================================================ - if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { - if (!isSystemToast) { - Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); - return; + @Override + public void enqueueToast(String pkg, ITransientNotification callback, int duration) + { + if (DBG) { + Slog.i(TAG, "enqueueToast pkg=" + pkg + " callback=" + callback + + " duration=" + duration); } - } - synchronized (mToastQueue) { - int callingPid = Binder.getCallingPid(); - long callingId = Binder.clearCallingIdentity(); - try { - ToastRecord record; - int index = indexOfToastLocked(pkg, callback); - // If it's already in the queue, we update it in place, we don't - // move it to the end of the queue. - if (index >= 0) { - record = mToastQueue.get(index); - record.update(duration); - } else { - // Limit the number of toasts that any given package except the android - // package can enqueue. Prevents DOS attacks and deals with leaks. - if (!isSystemToast) { - int count = 0; - final int N = mToastQueue.size(); - for (int i=0; i<N; i++) { - final ToastRecord r = mToastQueue.get(i); - if (r.pkg.equals(pkg)) { - count++; - if (count >= MAX_PACKAGE_NOTIFICATIONS) { - Slog.e(TAG, "Package has already posted " + count - + " toasts. Not showing more. Package=" + pkg); - return; + if (pkg == null || callback == null) { + Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback); + return ; + } + + final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg)); + + if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) { + if (!isSystemToast) { + Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request."); + return; + } + } + + synchronized (mToastQueue) { + int callingPid = Binder.getCallingPid(); + long callingId = Binder.clearCallingIdentity(); + try { + ToastRecord record; + int index = indexOfToastLocked(pkg, callback); + // If it's already in the queue, we update it in place, we don't + // move it to the end of the queue. + if (index >= 0) { + record = mToastQueue.get(index); + record.update(duration); + } else { + // Limit the number of toasts that any given package except the android + // package can enqueue. Prevents DOS attacks and deals with leaks. + if (!isSystemToast) { + int count = 0; + final int N = mToastQueue.size(); + for (int i=0; i<N; i++) { + final ToastRecord r = mToastQueue.get(i); + if (r.pkg.equals(pkg)) { + count++; + if (count >= MAX_PACKAGE_NOTIFICATIONS) { + Slog.e(TAG, "Package has already posted " + count + + " toasts. Not showing more. Package=" + pkg); + return; + } } - } + } } + + record = new ToastRecord(callingPid, pkg, callback, duration); + mToastQueue.add(record); + index = mToastQueue.size() - 1; + keepProcessAliveLocked(callingPid); + } + // If it's at index 0, it's the current toast. It doesn't matter if it's + // new or just been updated. Call back and tell it to show itself. + // If the callback fails, this will remove it from the list, so don't + // assume that it's valid after this. + if (index == 0) { + showNextToastLocked(); } + } finally { + Binder.restoreCallingIdentity(callingId); + } + } + } + + @Override + public void cancelToast(String pkg, ITransientNotification callback) { + Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); + + if (pkg == null || callback == null) { + Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); + return ; + } - record = new ToastRecord(callingPid, pkg, callback, duration); - mToastQueue.add(record); - index = mToastQueue.size() - 1; - keepProcessAliveLocked(callingPid); + synchronized (mToastQueue) { + long callingId = Binder.clearCallingIdentity(); + try { + int index = indexOfToastLocked(pkg, callback); + if (index >= 0) { + cancelToastLocked(index); + } else { + Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + + " callback=" + callback); + } + } finally { + Binder.restoreCallingIdentity(callingId); } - // If it's at index 0, it's the current toast. It doesn't matter if it's - // new or just been updated. Call back and tell it to show itself. - // If the callback fails, this will remove it from the list, so don't - // assume that it's valid after this. - if (index == 0) { - showNextToastLocked(); + } + } + + @Override + public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id, + Notification notification, int[] idOut, int userId) throws RemoteException { + enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(), + Binder.getCallingPid(), tag, id, notification, idOut, userId); + } + + @Override + public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { + checkCallerIsSystemOrSameApp(pkg); + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); + // Don't allow client applications to cancel foreground service notis. + cancelNotification(pkg, tag, id, 0, + Binder.getCallingUid() == Process.SYSTEM_UID + ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId); + } + + @Override + public void cancelAllNotifications(String pkg, int userId) { + checkCallerIsSystemOrSameApp(pkg); + + userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), + Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); + + // Calling from user space, don't allow the canceling of actively + // running foreground services. + cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId); + } + + @Override + public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { + checkCallerIsSystem(); + + setNotificationsEnabledForPackageImpl(pkg, uid, enabled); + } + + /** + * Use this when you just want to know if notifications are OK for this package. + */ + @Override + public boolean areNotificationsEnabledForPackage(String pkg, int uid) { + checkCallerIsSystem(); + return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg) + == AppOpsManager.MODE_ALLOWED); + } + + /** + * System-only API for getting a list of current (i.e. not cleared) notifications. + * + * Requires ACCESS_NOTIFICATIONS which is signature|system. + */ + @Override + public StatusBarNotification[] getActiveNotifications(String callingPkg) { + // enforce() will ensure the calling uid has the correct permission + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NOTIFICATIONS, + "NotificationManagerService.getActiveNotifications"); + + StatusBarNotification[] tmp = null; + int uid = Binder.getCallingUid(); + + // noteOp will check to make sure the callingPkg matches the uid + if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) + == AppOpsManager.MODE_ALLOWED) { + synchronized (mNotificationList) { + tmp = new StatusBarNotification[mNotificationList.size()]; + final int N = mNotificationList.size(); + for (int i=0; i<N; i++) { + tmp[i] = mNotificationList.get(i).sbn; + } } - } finally { - Binder.restoreCallingIdentity(callingId); } + return tmp; } - } - public void cancelToast(String pkg, ITransientNotification callback) { - Slog.i(TAG, "cancelToast pkg=" + pkg + " callback=" + callback); + /** + * System-only API for getting a list of recent (cleared, no longer shown) notifications. + * + * Requires ACCESS_NOTIFICATIONS which is signature|system. + */ + @Override + public StatusBarNotification[] getHistoricalNotifications(String callingPkg, int count) { + // enforce() will ensure the calling uid has the correct permission + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_NOTIFICATIONS, + "NotificationManagerService.getHistoricalNotifications"); + + StatusBarNotification[] tmp = null; + int uid = Binder.getCallingUid(); + + // noteOp will check to make sure the callingPkg matches the uid + if (mAppOps.noteOpNoThrow(AppOpsManager.OP_ACCESS_NOTIFICATIONS, uid, callingPkg) + == AppOpsManager.MODE_ALLOWED) { + synchronized (mArchive) { + tmp = mArchive.getArray(count); + } + } + return tmp; + } - if (pkg == null || callback == null) { - Slog.e(TAG, "Not cancelling notification. pkg=" + pkg + " callback=" + callback); - return ; + /** + * Register a listener binder directly with the notification manager. + * + * Only works with system callers. Apps should extend + * {@link android.service.notification.NotificationListenerService}. + */ + @Override + public void registerListener(final INotificationListener listener, + final ComponentName component, final int userid) { + checkCallerIsSystem(); + registerListenerImpl(listener, component, userid); } - synchronized (mToastQueue) { - long callingId = Binder.clearCallingIdentity(); + /** + * Remove a listener binder directly + */ + @Override + public void unregisterListener(INotificationListener listener, int userid) { + // no need to check permissions; if your listener binder is in the list, + // that's proof that you had permission to add it in the first place + unregisterListenerImpl(listener, userid); + } + + /** + * Allow an INotificationListener to simulate a "clear all" operation. + * + * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onClearAllNotifications} + * + * @param token The binder for the listener, to check that the caller is allowed + */ + @Override + public void cancelAllNotificationsFromListener(INotificationListener token) { + NotificationListenerInfo info = checkListenerToken(token); + long identity = Binder.clearCallingIdentity(); try { - int index = indexOfToastLocked(pkg, callback); - if (index >= 0) { - cancelToastLocked(index); - } else { - Slog.w(TAG, "Toast already cancelled. pkg=" + pkg + " callback=" + callback); - } + cancelAll(info.userid); } finally { - Binder.restoreCallingIdentity(callingId); + Binder.restoreCallingIdentity(identity); } } - } - private void showNextToastLocked() { - ToastRecord record = mToastQueue.get(0); - while (record != null) { - if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); + /** + * Allow an INotificationListener to simulate clearing (dismissing) a single notification. + * + * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear} + * + * @param token The binder for the listener, to check that the caller is allowed + */ + @Override + public void cancelNotificationFromListener(INotificationListener token, String pkg, + String tag, int id) { + NotificationListenerInfo info = checkListenerToken(token); + long identity = Binder.clearCallingIdentity(); try { - record.callback.show(); - scheduleTimeoutLocked(record); - return; - } catch (RemoteException e) { - Slog.w(TAG, "Object died trying to show notification " + record.callback - + " in package " + record.pkg); - // remove it from the list and let the process die - int index = mToastQueue.indexOf(record); - if (index >= 0) { - mToastQueue.remove(index); - } - keepProcessAliveLocked(record.pid); - if (mToastQueue.size() > 0) { - record = mToastQueue.get(0); - } else { - record = null; + cancelNotification(pkg, tag, id, 0, + Notification.FLAG_ONGOING_EVENT | Notification.FLAG_FOREGROUND_SERVICE, + true, + info.userid); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + /** + * Allow an INotificationListener to request the list of outstanding notifications seen by + * the current user. Useful when starting up, after which point the listener callbacks + * should be used. + * + * @param token The binder for the listener, to check that the caller is allowed + */ + @Override + public StatusBarNotification[] getActiveNotificationsFromListener( + INotificationListener token) { + NotificationListenerInfo info = checkListenerToken(token); + + StatusBarNotification[] result = new StatusBarNotification[0]; + ArrayList<StatusBarNotification> list = new ArrayList<StatusBarNotification>(); + synchronized (mNotificationList) { + final int N = mNotificationList.size(); + for (int i=0; i<N; i++) { + StatusBarNotification sbn = mNotificationList.get(i).sbn; + if (info.enabledAndUserMatches(sbn)) { + list.add(sbn); + } } } + return list.toArray(result); } - } - private void cancelToastLocked(int index) { - ToastRecord record = mToastQueue.get(index); - try { - record.callback.hide(); - } catch (RemoteException e) { - Slog.w(TAG, "Object died trying to hide notification " + record.callback - + " in package " + record.pkg); - // don't worry about this, we're about to remove it from - // the list anyway + @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 NotificationManager from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + dumpImpl(pw); } - mToastQueue.remove(index); - keepProcessAliveLocked(record.pid); - if (mToastQueue.size() > 0) { - // Show the next one. If the callback fails, this will remove - // it from the list, so don't assume that the list hasn't changed - // after this point. - showNextToastLocked(); + }; + + void dumpImpl(PrintWriter pw) { + pw.println("Current Notification Manager state:"); + + pw.println(" Listeners (" + mEnabledListenersForCurrentUser.size() + + ") enabled for current user:"); + for (ComponentName cmpt : mEnabledListenersForCurrentUser) { + pw.println(" " + cmpt); } - } - private void scheduleTimeoutLocked(ToastRecord r) - { - mHandler.removeCallbacksAndMessages(r); - Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); - long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; - mHandler.sendMessageDelayed(m, delay); - } + pw.println(" Live listeners (" + mListeners.size() + "):"); + for (NotificationListenerInfo info : mListeners) { + pw.println(" " + info.component + + " (user " + info.userid + "): " + info.listener + + (info.isSystem?" SYSTEM":"")); + } + + int N; - private void handleTimeout(ToastRecord record) - { - if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); synchronized (mToastQueue) { - int index = indexOfToastLocked(record.pkg, record.callback); - if (index >= 0) { - cancelToastLocked(index); + N = mToastQueue.size(); + if (N > 0) { + pw.println(" Toast Queue:"); + for (int i=0; i<N; i++) { + mToastQueue.get(i).dump(pw, " "); + } + pw.println(" "); } + } - } - // lock on mToastQueue - private int indexOfToastLocked(String pkg, ITransientNotification callback) - { - IBinder cbak = callback.asBinder(); - ArrayList<ToastRecord> list = mToastQueue; - int len = list.size(); - for (int i=0; i<len; i++) { - ToastRecord r = list.get(i); - if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { - return i; + synchronized (mNotificationList) { + N = mNotificationList.size(); + if (N > 0) { + pw.println(" Notification List:"); + for (int i=0; i<N; i++) { + mNotificationList.get(i).dump(pw, " ", getContext()); + } + pw.println(" "); } - } - return -1; - } - // lock on mToastQueue - private void keepProcessAliveLocked(int pid) - { - int toastCount = 0; // toasts from this pid - ArrayList<ToastRecord> list = mToastQueue; - int N = list.size(); - for (int i=0; i<N; i++) { - ToastRecord r = list.get(i); - if (r.pid == pid) { - toastCount++; + N = mLights.size(); + if (N > 0) { + pw.println(" Lights List:"); + for (int i=0; i<N; i++) { + pw.println(" " + mLights.get(i)); + } + pw.println(" "); } - } - try { - mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); - } catch (RemoteException e) { - // Shouldn't happen. - } - } - private final class WorkerHandler extends Handler - { - @Override - public void handleMessage(Message msg) - { - switch (msg.what) - { - case MESSAGE_TIMEOUT: - handleTimeout((ToastRecord)msg.obj); + pw.println(" mSoundNotification=" + mSoundNotification); + pw.println(" mVibrateNotification=" + mVibrateNotification); + pw.println(" mDisabledNotifications=0x" + + Integer.toHexString(mDisabledNotifications)); + pw.println(" mSystemReady=" + mSystemReady); + pw.println(" mArchive=" + mArchive.toString()); + Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); + int i=0; + while (iter.hasNext()) { + pw.println(" " + iter.next()); + if (++i >= 5) { + if (iter.hasNext()) pw.println(" ..."); break; + } } - } - } - - // Notifications - // ============================================================================ - public void enqueueNotificationWithTag(String pkg, String basePkg, String tag, int id, - Notification notification, int[] idOut, int userId) - { - enqueueNotificationInternal(pkg, basePkg, Binder.getCallingUid(), Binder.getCallingPid(), - tag, id, notification, idOut, userId); - } - - private final static int clamp(int x, int low, int high) { - return (x < low) ? low : ((x > high) ? high : x); + } } - // Not exposed via Binder; for system use only (otherwise malicious apps could spoof the - // uid/pid of another application) + /** + * The private API only accessible to the system process. + */ + private final NotificationManagerInternal mInternalService = new NotificationManagerInternal() { + @Override + public void enqueueNotification(String pkg, String basePkg, int callingUid, int callingPid, + String tag, int id, Notification notification, int[] idReceived, int userId) { + enqueueNotificationInternal(pkg, basePkg, callingUid, callingPid, tag, id, notification, + idReceived, userId); + } + }; - public void enqueueNotificationInternal(final String pkg, String basePkg, final int callingUid, + void enqueueNotificationInternal(final String pkg, String basePkg, final int callingUid, final int callingPid, final String tag, final int id, final Notification notification, - int[] idOut, int incomingUserId) - { + int[] idOut, int incomingUserId) { if (DBG) { - Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + " notification=" + notification); + Slog.v(TAG, "enqueueNotificationInternal: pkg=" + pkg + " id=" + id + + " notification=" + notification); } checkCallerIsSystemOrSameApp(pkg); final boolean isSystemNotification = isUidSystem(callingUid) || ("android".equals(pkg)); @@ -1787,23 +1794,21 @@ public class NotificationManagerService extends INotificationManager.Stub if (notification.icon != 0) { if (old != null && old.statusBarKey != null) { r.statusBarKey = old.statusBarKey; - long identity = Binder.clearCallingIdentity(); + final long identity = Binder.clearCallingIdentity(); try { mStatusBar.updateNotification(r.statusBarKey, n); - } - finally { + } finally { Binder.restoreCallingIdentity(identity); } } else { - long identity = Binder.clearCallingIdentity(); + final long identity = Binder.clearCallingIdentity(); try { r.statusBarKey = mStatusBar.addNotification(n); if ((n.getNotification().flags & Notification.FLAG_SHOW_LIGHTS) != 0 && canInterrupt) { mAttentionLight.pulse(); } - } - finally { + } finally { Binder.restoreCallingIdentity(identity); } } @@ -1816,33 +1821,32 @@ public class NotificationManagerService extends INotificationManager.Stub } else { Slog.e(TAG, "Not posting notification with icon==0: " + notification); if (old != null && old.statusBarKey != null) { - long identity = Binder.clearCallingIdentity(); + final long identity = Binder.clearCallingIdentity(); try { mStatusBar.removeNotification(old.statusBarKey); - } - finally { + } finally { Binder.restoreCallingIdentity(identity); } notifyRemovedLocked(r); } // ATTENTION: in a future release we will bail out here - // so that we do not play sounds, show lights, etc. for invalid notifications + // so that we do not play sounds, show lights, etc. for invalid + // notifications Slog.e(TAG, "WARNING: In a future release this will crash the app: " + n.getPackageName()); } // If we're not supposed to beep, vibrate, etc. then don't. - if (((mDisabledNotifications & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) + if (((mDisabledNotifications + & StatusBarManager.DISABLE_NOTIFICATION_ALERTS) == 0) && (!(old != null && (notification.flags & Notification.FLAG_ONLY_ALERT_ONCE) != 0 )) && (r.getUserId() == UserHandle.USER_ALL || (r.getUserId() == userId && r.getUserId() == currentUser)) && canInterrupt - && mSystemReady) { - - final AudioManager audioManager = (AudioManager) mContext - .getSystemService(Context.AUDIO_SERVICE); + && mSystemReady + && mAudioManager != null) { // sound @@ -1861,7 +1865,7 @@ public class NotificationManagerService extends INotificationManager.Stub soundUri = Settings.System.DEFAULT_NOTIFICATION_URI; // check to see if the default notification sound is silent - ContentResolver resolver = mContext.getContentResolver(); + ContentResolver resolver = getContext().getContentResolver(); hasValidSound = Settings.System.getString(resolver, Settings.System.NOTIFICATION_SOUND) != null; } else if (notification.sound != null) { @@ -1870,7 +1874,8 @@ public class NotificationManagerService extends INotificationManager.Stub } if (hasValidSound) { - boolean looping = (notification.flags & Notification.FLAG_INSISTENT) != 0; + boolean looping = + (notification.flags & Notification.FLAG_INSISTENT) != 0; int audioStreamType; if (notification.audioStreamType >= 0) { audioStreamType = notification.audioStreamType; @@ -1880,11 +1885,12 @@ public class NotificationManagerService extends INotificationManager.Stub mSoundNotification = r; // do not play notifications if stream volume is 0 (typically because // ringer mode is silent) or if there is a user of exclusive audio focus - if ((audioManager.getStreamVolume(audioStreamType) != 0) - && !audioManager.isAudioFocusExclusive()) { + if ((mAudioManager.getStreamVolume(audioStreamType) != 0) + && !mAudioManager.isAudioFocusExclusive()) { final long identity = Binder.clearCallingIdentity(); try { - final IRingtonePlayer player = mAudioService.getRingtonePlayer(); + final IRingtonePlayer player = + mAudioManager.getRingtonePlayer(); if (player != null) { player.playAsync(soundUri, user, looping, audioStreamType); } @@ -1904,7 +1910,7 @@ public class NotificationManagerService extends INotificationManager.Stub final boolean convertSoundToVibration = !hasCustomVibrate && hasValidSound - && (audioManager.getRingerMode() + && (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE); // The DEFAULT_VIBRATE flag trumps any custom vibration AND the fallback. @@ -1912,7 +1918,7 @@ public class NotificationManagerService extends INotificationManager.Stub (notification.defaults & Notification.DEFAULT_VIBRATE) != 0; if ((useDefaultVibrate || convertSoundToVibration || hasCustomVibrate) - && !(audioManager.getRingerMode() + && !(mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT)) { mVibrateNotification = r; @@ -1966,8 +1972,158 @@ public class NotificationManagerService extends INotificationManager.Stub idOut[0] = id; } - private void sendAccessibilityEvent(Notification notification, CharSequence packageName) { - AccessibilityManager manager = AccessibilityManager.getInstance(mContext); + void registerListenerImpl(final INotificationListener listener, + final ComponentName component, final int userid) { + synchronized (mNotificationList) { + try { + NotificationListenerInfo info + = new NotificationListenerInfo(listener, component, userid, true); + listener.asBinder().linkToDeath(info, 0); + mListeners.add(info); + } catch (RemoteException e) { + // already dead + } + } + } + + void unregisterListenerImpl(final INotificationListener listener, final int userid) { + synchronized (mNotificationList) { + final int N = mListeners.size(); + for (int i=N-1; i>=0; i--) { + final NotificationListenerInfo info = mListeners.get(i); + if (info.listener.asBinder() == listener.asBinder() + && info.userid == userid) { + mListeners.remove(i); + if (info.connection != null) { + getContext().unbindService(info.connection); + } + } + } + } + } + + void showNextToastLocked() { + ToastRecord record = mToastQueue.get(0); + while (record != null) { + if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback); + try { + record.callback.show(); + scheduleTimeoutLocked(record); + return; + } catch (RemoteException e) { + Slog.w(TAG, "Object died trying to show notification " + record.callback + + " in package " + record.pkg); + // remove it from the list and let the process die + int index = mToastQueue.indexOf(record); + if (index >= 0) { + mToastQueue.remove(index); + } + keepProcessAliveLocked(record.pid); + if (mToastQueue.size() > 0) { + record = mToastQueue.get(0); + } else { + record = null; + } + } + } + } + + void cancelToastLocked(int index) { + ToastRecord record = mToastQueue.get(index); + try { + record.callback.hide(); + } catch (RemoteException e) { + Slog.w(TAG, "Object died trying to hide notification " + record.callback + + " in package " + record.pkg); + // don't worry about this, we're about to remove it from + // the list anyway + } + mToastQueue.remove(index); + keepProcessAliveLocked(record.pid); + if (mToastQueue.size() > 0) { + // Show the next one. If the callback fails, this will remove + // it from the list, so don't assume that the list hasn't changed + // after this point. + showNextToastLocked(); + } + } + + private void scheduleTimeoutLocked(ToastRecord r) + { + mHandler.removeCallbacksAndMessages(r); + Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r); + long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY; + mHandler.sendMessageDelayed(m, delay); + } + + private void handleTimeout(ToastRecord record) + { + if (DBG) Slog.d(TAG, "Timeout pkg=" + record.pkg + " callback=" + record.callback); + synchronized (mToastQueue) { + int index = indexOfToastLocked(record.pkg, record.callback); + if (index >= 0) { + cancelToastLocked(index); + } + } + } + + // lock on mToastQueue + int indexOfToastLocked(String pkg, ITransientNotification callback) + { + IBinder cbak = callback.asBinder(); + ArrayList<ToastRecord> list = mToastQueue; + int len = list.size(); + for (int i=0; i<len; i++) { + ToastRecord r = list.get(i); + if (r.pkg.equals(pkg) && r.callback.asBinder() == cbak) { + return i; + } + } + return -1; + } + + // lock on mToastQueue + void keepProcessAliveLocked(int pid) + { + int toastCount = 0; // toasts from this pid + ArrayList<ToastRecord> list = mToastQueue; + int N = list.size(); + for (int i=0; i<N; i++) { + ToastRecord r = list.get(i); + if (r.pid == pid) { + toastCount++; + } + } + try { + mAm.setProcessForeground(mForegroundToken, pid, toastCount > 0); + } catch (RemoteException e) { + // Shouldn't happen. + } + } + + private final class WorkerHandler extends Handler + { + @Override + public void handleMessage(Message msg) + { + switch (msg.what) + { + case MESSAGE_TIMEOUT: + handleTimeout((ToastRecord)msg.obj); + break; + } + } + } + + + // Notifications + // ============================================================================ + static int clamp(int x, int low, int high) { + return (x < low) ? low : ((x > high) ? high : x); + } + + void sendAccessibilityEvent(Notification notification, CharSequence packageName) { + AccessibilityManager manager = AccessibilityManager.getInstance(getContext()); if (!manager.isEnabled()) { return; } @@ -2001,11 +2157,10 @@ public class NotificationManagerService extends INotificationManager.Stub // status bar if (r.getNotification().icon != 0) { - long identity = Binder.clearCallingIdentity(); + final long identity = Binder.clearCallingIdentity(); try { mStatusBar.removeNotification(r.statusBarKey); - } - finally { + } finally { Binder.restoreCallingIdentity(identity); } r.statusBarKey = null; @@ -2017,7 +2172,7 @@ public class NotificationManagerService extends INotificationManager.Stub mSoundNotification = null; final long identity = Binder.clearCallingIdentity(); try { - final IRingtonePlayer player = mAudioService.getRingtonePlayer(); + final IRingtonePlayer player = mAudioManager.getRingtonePlayer(); if (player != null) { player.stopAsync(); } @@ -2053,7 +2208,7 @@ public class NotificationManagerService extends INotificationManager.Stub * Cancels a notification ONLY if it has all of the {@code mustHaveFlags} * and none of the {@code mustNotHaveFlags}. */ - private void cancelNotification(final String pkg, final String tag, final int id, + void cancelNotification(final String pkg, final String tag, final int id, final int mustHaveFlags, final int mustNotHaveFlags, final boolean sendDelete, final int userId) { // In enqueueNotificationInternal notifications are added by scheduling the @@ -2146,26 +2301,7 @@ public class NotificationManagerService extends INotificationManager.Stub } } - public void cancelNotificationWithTag(String pkg, String tag, int id, int userId) { - checkCallerIsSystemOrSameApp(pkg); - userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), - Binder.getCallingUid(), userId, true, false, "cancelNotificationWithTag", pkg); - // Don't allow client applications to cancel foreground service notis. - cancelNotification(pkg, tag, id, 0, - Binder.getCallingUid() == Process.SYSTEM_UID - ? 0 : Notification.FLAG_FOREGROUND_SERVICE, false, userId); - } - - public void cancelAllNotifications(String pkg, int userId) { - checkCallerIsSystemOrSameApp(pkg); - - userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), - Binder.getCallingUid(), userId, true, false, "cancelAllNotifications", pkg); - // Calling from user space, don't allow the canceling of actively - // running foreground services. - cancelAllNotificationsInt(pkg, 0, Notification.FLAG_FOREGROUND_SERVICE, true, userId); - } // Return true if the UID is a system or phone UID and therefore should not have // any notifications or toasts blocked. @@ -2225,7 +2361,7 @@ public class NotificationManagerService extends INotificationManager.Stub } // lock on mNotificationList - private void updateLightsLocked() + void updateLightsLocked() { // handle notification lights if (mLedNotification == null) { @@ -2251,14 +2387,14 @@ public class NotificationManagerService extends INotificationManager.Stub } if (mNotificationPulseEnabled) { // pulse repeatedly - mNotificationLight.setFlashing(ledARGB, LightsService.LIGHT_FLASH_TIMED, + mNotificationLight.setFlashing(ledARGB, Light.LIGHT_FLASH_TIMED, ledOnMS, ledOffMS); } } } // lock on mNotificationList - private int indexOfNotificationLocked(String pkg, String tag, int id, int userId) + int indexOfNotificationLocked(String pkg, String tag, int id, int userId) { ArrayList<NotificationRecord> list = mNotificationList; final int len = list.size(); @@ -2288,81 +2424,4 @@ public class NotificationManagerService extends INotificationManager.Stub updateLightsLocked(); } } - - // ====================================================================== - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump NotificationManager from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return; - } - - pw.println("Current Notification Manager state:"); - - pw.println(" Listeners (" + mEnabledListenersForCurrentUser.size() - + ") enabled for current user:"); - for (ComponentName cmpt : mEnabledListenersForCurrentUser) { - pw.println(" " + cmpt); - } - - pw.println(" Live listeners (" + mListeners.size() + "):"); - for (NotificationListenerInfo info : mListeners) { - pw.println(" " + info.component - + " (user " + info.userid + "): " + info.listener - + (info.isSystem?" SYSTEM":"")); - } - - int N; - - synchronized (mToastQueue) { - N = mToastQueue.size(); - if (N > 0) { - pw.println(" Toast Queue:"); - for (int i=0; i<N; i++) { - mToastQueue.get(i).dump(pw, " "); - } - pw.println(" "); - } - - } - - synchronized (mNotificationList) { - N = mNotificationList.size(); - if (N > 0) { - pw.println(" Notification List:"); - for (int i=0; i<N; i++) { - mNotificationList.get(i).dump(pw, " ", mContext); - } - pw.println(" "); - } - - N = mLights.size(); - if (N > 0) { - pw.println(" Lights List:"); - for (int i=0; i<N; i++) { - pw.println(" " + mLights.get(i)); - } - pw.println(" "); - } - - pw.println(" mSoundNotification=" + mSoundNotification); - pw.println(" mVibrateNotification=" + mVibrateNotification); - pw.println(" mDisabledNotifications=0x" + Integer.toHexString(mDisabledNotifications)); - pw.println(" mSystemReady=" + mSystemReady); - pw.println(" mArchive=" + mArchive.toString()); - Iterator<StatusBarNotification> iter = mArchive.descendingIterator(); - int i=0; - while (iter.hasNext()) { - pw.println(" " + iter.next()); - if (++i >= 5) { - if (iter.hasNext()) pw.println(" ..."); - break; - } - } - - } - } } diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 5761f6c..b11ebf5 100755 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -38,10 +38,10 @@ import com.android.internal.content.PackageHelper; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; -import com.android.server.DeviceStorageMonitorService; import com.android.server.EventLogTags; import com.android.server.IntentResolver; +import com.android.server.LocalServices; import com.android.server.Watchdog; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -92,6 +92,7 @@ import android.content.pm.VerificationParams; import android.content.pm.VerifierDeviceIdentity; import android.content.pm.VerifierInfo; import android.content.res.Resources; +import android.hardware.display.DisplayManager; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -127,7 +128,6 @@ import android.util.Slog; import android.util.SparseArray; import android.util.Xml; import android.view.Display; -import android.view.WindowManager; import java.io.BufferedOutputStream; import java.io.File; @@ -162,6 +162,7 @@ import libcore.io.Libcore; import libcore.io.StructStat; import com.android.internal.R; +import com.android.server.storage.DeviceStorageMonitorInternal; /** * Keep track of all those .apks everywhere. @@ -1067,6 +1068,12 @@ public class PackageManagerService extends IPackageManager.Stub { return res; } + private static void getDefaultDisplayMetrics(Context context, DisplayMetrics metrics) { + DisplayManager displayManager = (DisplayManager) context.getSystemService( + Context.DISPLAY_SERVICE); + displayManager.getDisplay(Display.DEFAULT_DISPLAY).getMetrics(metrics); + } + public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, @@ -1114,9 +1121,7 @@ public class PackageManagerService extends IPackageManager.Stub { mInstaller = installer; - WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); - Display d = wm.getDefaultDisplay(); - d.getMetrics(mMetrics); + getDefaultDisplayMetrics(context, mMetrics); synchronized (mInstallLock) { // writer @@ -7339,6 +7344,15 @@ public class PackageManagerService extends IPackageManager.Stub { return pkgLite.recommendedInstallLocation; } + private long getMemoryLowThreshold() { + final DeviceStorageMonitorInternal + dsm = LocalServices.getService(DeviceStorageMonitorInternal.class); + if (dsm == null) { + return 0L; + } + return dsm.getMemoryLowThreshold(); + } + /* * Invoke remote method to get package information and install * location values. Override install location based on default @@ -7356,15 +7370,9 @@ public class PackageManagerService extends IPackageManager.Stub { Slog.w(TAG, "Conflicting flags specified for installing on both internal and external"); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { - final long lowThreshold; - - final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager - .getService(DeviceStorageMonitorService.SERVICE); - if (dsm == null) { + final long lowThreshold = getMemoryLowThreshold(); + if (lowThreshold == 0L) { Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed"); - lowThreshold = 0L; - } else { - lowThreshold = dsm.getMemoryLowThreshold(); } try { @@ -7922,8 +7930,8 @@ public class PackageManagerService extends IPackageManager.Stub { boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException { final long lowThreshold; - final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager - .getService(DeviceStorageMonitorService.SERVICE); + final DeviceStorageMonitorInternal + dsm = LocalServices.getService(DeviceStorageMonitorInternal.class); if (dsm == null) { Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed"); lowThreshold = 0L; @@ -9738,10 +9746,10 @@ public class PackageManagerService extends IPackageManager.Stub { clearExternalStorageDataSync(packageName, userId, true); if (succeeded) { // invoke DeviceStorageMonitor's update method to clear any notifications - DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) - ServiceManager.getService(DeviceStorageMonitorService.SERVICE); + DeviceStorageMonitorInternal + dsm = LocalServices.getService(DeviceStorageMonitorInternal.class); if (dsm != null) { - dsm.updateMemory(); + dsm.checkMemory(); } } if(observer != null) { @@ -11566,12 +11574,17 @@ public class PackageManagerService extends IPackageManager.Stub { return true; } + @Override public boolean isStorageLow() { final long token = Binder.clearCallingIdentity(); try { - final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager - .getService(DeviceStorageMonitorService.SERVICE); - return dsm.isMemoryLow(); + final DeviceStorageMonitorInternal + dsm = LocalServices.getService(DeviceStorageMonitorInternal.class); + if (dsm != null) { + return dsm.isMemoryLow(); + } else { + return false; + } } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/java/com/android/server/power/DisplayPowerController.java b/services/java/com/android/server/power/DisplayPowerController.java index 60d44c7..b8a78e3 100644 --- a/services/java/com/android/server/power/DisplayPowerController.java +++ b/services/java/com/android/server/power/DisplayPowerController.java @@ -16,10 +16,11 @@ package com.android.server.power; -import com.android.server.LightsService; -import com.android.server.TwilightService; -import com.android.server.TwilightService.TwilightState; +import com.android.server.lights.LightsManager; +import com.android.server.twilight.TwilightListener; +import com.android.server.twilight.TwilightManager; import com.android.server.display.DisplayManagerService; +import com.android.server.twilight.TwilightState; import android.animation.Animator; import android.animation.ObjectAnimator; @@ -179,10 +180,10 @@ final class DisplayPowerController { private Handler mCallbackHandler; // The lights service. - private final LightsService mLights; + private final LightsManager mLights; // The twilight service. - private final TwilightService mTwilight; + private final TwilightManager mTwilight; // The display manager. private final DisplayManagerService mDisplayManager; @@ -351,7 +352,7 @@ final class DisplayPowerController { * Creates the display power controller. */ public DisplayPowerController(Looper looper, Context context, Notifier notifier, - LightsService lights, TwilightService twilight, SensorManager sensorManager, + LightsManager lights, TwilightManager twilight, SensorManager sensorManager, DisplayManagerService displayManager, SuspendBlocker displaySuspendBlocker, DisplayBlanker displayBlanker, Callbacks callbacks, Handler callbackHandler) { @@ -528,7 +529,7 @@ final class DisplayPowerController { private void initialize() { mPowerState = new DisplayPowerState( new ElectronBeam(mDisplayManager), mDisplayBlanker, - mLights.getLight(LightsService.LIGHT_ID_BACKLIGHT)); + mLights.getLight(LightsManager.LIGHT_ID_BACKLIGHT)); mElectronBeamOnAnimator = ObjectAnimator.ofFloat( mPowerState, DisplayPowerState.ELECTRON_BEAM_LEVEL, 0.0f, 1.0f); @@ -1368,8 +1369,7 @@ final class DisplayPowerController { } }; - private final TwilightService.TwilightListener mTwilightListener = - new TwilightService.TwilightListener() { + private final TwilightListener mTwilightListener = new TwilightListener() { @Override public void onTwilightStateChanged() { mTwilightChanged = true; diff --git a/services/java/com/android/server/power/DisplayPowerState.java b/services/java/com/android/server/power/DisplayPowerState.java index fa318f8..42af4b4 100644 --- a/services/java/com/android/server/power/DisplayPowerState.java +++ b/services/java/com/android/server/power/DisplayPowerState.java @@ -16,7 +16,7 @@ package com.android.server.power; -import com.android.server.LightsService; +import com.android.server.lights.Light; import android.os.AsyncTask; import android.os.Handler; @@ -56,7 +56,7 @@ final class DisplayPowerState { private final Choreographer mChoreographer; private final ElectronBeam mElectronBeam; private final DisplayBlanker mDisplayBlanker; - private final LightsService.Light mBacklight; + private final Light mBacklight; private final PhotonicModulator mPhotonicModulator; private boolean mScreenOn; @@ -72,7 +72,7 @@ final class DisplayPowerState { private Runnable mCleanListener; public DisplayPowerState(ElectronBeam electronBean, - DisplayBlanker displayBlanker, LightsService.Light backlight) { + DisplayBlanker displayBlanker, Light backlight) { mHandler = new Handler(true /*async*/); mChoreographer = Choreographer.getInstance(); mElectronBeam = electronBean; diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index da9548f..13f55e2 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -20,10 +20,11 @@ import com.android.internal.app.IAppOpsService; import com.android.internal.app.IBatteryStats; import com.android.server.BatteryService; import com.android.server.EventLogTags; -import com.android.server.LightsService; -import com.android.server.TwilightService; +import com.android.server.lights.LightsService; +import com.android.server.lights.Light; +import com.android.server.lights.LightsManager; +import com.android.server.twilight.TwilightManager; import com.android.server.Watchdog; -import com.android.server.am.ActivityManagerService; import com.android.server.display.DisplayManagerService; import com.android.server.dreams.DreamManagerService; @@ -168,7 +169,7 @@ public final class PowerManagerService extends IPowerManager.Stub private static final int DREAM_BATTERY_LEVEL_DRAIN_CUTOFF = 5; private Context mContext; - private LightsService mLightsService; + private LightsManager mLightsManager; private BatteryService mBatteryService; private DisplayManagerService mDisplayManagerService; private IBatteryStats mBatteryStats; @@ -181,7 +182,7 @@ public final class PowerManagerService extends IPowerManager.Stub private WirelessChargerDetector mWirelessChargerDetector; private SettingsObserver mSettingsObserver; private DreamManagerService mDreamManager; - private LightsService.Light mAttentionLight; + private Light mAttentionLight; private final Object mLock = new Object(); @@ -396,11 +397,11 @@ public final class PowerManagerService extends IPowerManager.Stub * Initialize the power manager. * Must be called before any other functions within the power manager are called. */ - public void init(Context context, LightsService ls, - ActivityManagerService am, BatteryService bs, IBatteryStats bss, + public void init(Context context, LightsManager ls, + BatteryService bs, IBatteryStats bss, IAppOpsService appOps, DisplayManagerService dm) { mContext = context; - mLightsService = ls; + mLightsManager = ls; mBatteryService = bs; mBatteryStats = bss; mAppOps = appOps; @@ -427,12 +428,12 @@ public final class PowerManagerService extends IPowerManager.Stub } } - public void systemReady(TwilightService twilight, DreamManagerService dreamManager) { + public void systemReady(TwilightManager twilight, DreamManagerService dreamManager) { synchronized (mLock) { mSystemReady = true; mDreamManager = dreamManager; - PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting(); mScreenBrightnessSettingMaximum = pm.getMaximumScreenBrightnessSetting(); mScreenBrightnessSettingDefault = pm.getDefaultScreenBrightnessSetting(); @@ -448,7 +449,7 @@ public final class PowerManagerService extends IPowerManager.Stub // The display power controller runs on the power manager service's // own handler thread to ensure timely operation. mDisplayPowerController = new DisplayPowerController(mHandler.getLooper(), - mContext, mNotifier, mLightsService, twilight, sensorManager, + mContext, mNotifier, mLightsManager, twilight, sensorManager, mDisplayManagerService, mDisplaySuspendBlocker, mDisplayBlanker, mDisplayPowerControllerCallbacks, mHandler); @@ -456,7 +457,7 @@ public final class PowerManagerService extends IPowerManager.Stub createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"), mHandler); mSettingsObserver = new SettingsObserver(mHandler); - mAttentionLight = mLightsService.getLight(LightsService.LIGHT_ID_ATTENTION); + mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION); // Register for broadcasts from other components of the system. IntentFilter filter = new IntentFilter(); @@ -2061,7 +2062,7 @@ public final class PowerManagerService extends IPowerManager.Stub } private void setAttentionLightInternal(boolean on, int color) { - LightsService.Light light; + Light light; synchronized (mLock) { if (!mSystemReady) { return; @@ -2070,7 +2071,7 @@ public final class PowerManagerService extends IPowerManager.Stub } // Control light outside of lock. - light.setFlashing(color, LightsService.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0); + light.setFlashing(color, Light.LIGHT_FLASH_HARDWARE, (on ? 3 : 0), 0); } /** diff --git a/services/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/java/com/android/server/statusbar/StatusBarManagerInternal.java new file mode 100644 index 0000000..4f75189 --- /dev/null +++ b/services/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -0,0 +1,29 @@ +/** + * Copyright (c) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.statusbar; + +import com.android.server.notification.NotificationDelegate; + +import android.os.IBinder; +import android.service.notification.StatusBarNotification; + +public interface StatusBarManagerInternal { + void setNotificationDelegate(NotificationDelegate delegate); + IBinder addNotification(StatusBarNotification notification); + void updateNotification(IBinder key, StatusBarNotification notification); + void removeNotification(IBinder key); +} diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/statusbar/StatusBarManagerService.java index f207c08..2ae467e 100644 --- a/services/java/com/android/server/StatusBarManagerService.java +++ b/services/java/com/android/server/statusbar/StatusBarManagerService.java @@ -14,26 +14,28 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.statusbar; import android.app.StatusBarManager; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; -import android.os.Binder; -import android.os.Handler; -import android.os.IBinder; -import android.os.RemoteException; -import android.os.UserHandle; import android.util.Slog; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIconList; +import com.android.server.LocalServices; +import com.android.server.notification.NotificationDelegate; import com.android.server.wm.WindowManagerService; import java.io.FileDescriptor; @@ -51,31 +53,31 @@ import java.util.Map; public class StatusBarManagerService extends IStatusBarService.Stub implements WindowManagerService.OnHardKeyboardStatusChangeListener { - static final String TAG = "StatusBarManagerService"; - static final boolean SPEW = false; - - final Context mContext; - final WindowManagerService mWindowManager; - Handler mHandler = new Handler(); - NotificationCallbacks mNotificationCallbacks; - volatile IStatusBar mBar; - StatusBarIconList mIcons = new StatusBarIconList(); - HashMap<IBinder,StatusBarNotification> mNotifications + private static final String TAG = "StatusBarManagerService"; + private static final boolean SPEW = false; + + private final Context mContext; + private final WindowManagerService mWindowManager; + private Handler mHandler = new Handler(); + private NotificationDelegate mNotificationDelegate; + private volatile IStatusBar mBar; + private StatusBarIconList mIcons = new StatusBarIconList(); + private HashMap<IBinder,StatusBarNotification> mNotifications = new HashMap<IBinder,StatusBarNotification>(); // for disabling the status bar - final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>(); - IBinder mSysUiVisToken = new Binder(); - int mDisabled = 0; + private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>(); + private IBinder mSysUiVisToken = new Binder(); + private int mDisabled = 0; - Object mLock = new Object(); + private Object mLock = new Object(); // encompasses lights-out mode and other flags defined on View - int mSystemUiVisibility = 0; - boolean mMenuVisible = false; - int mImeWindowVis = 0; - int mImeBackDisposition; - IBinder mImeToken = null; - int mCurrentUserId; + private int mSystemUiVisibility = 0; + private boolean mMenuVisible = false; + private int mImeWindowVis = 0; + private int mImeBackDisposition; + private IBinder mImeToken = null; + private int mCurrentUserId; private class DisableRecord implements IBinder.DeathRecipient { int userId; @@ -90,16 +92,6 @@ public class StatusBarManagerService extends IStatusBarService.Stub } } - public interface NotificationCallbacks { - void onSetDisabled(int status); - void onClearAll(); - void onNotificationClick(String pkg, String tag, int id); - void onNotificationClear(String pkg, String tag, int id); - void onPanelRevealed(); - void onNotificationError(String pkg, String tag, int id, - int uid, int initialPid, String message); - } - /** * Construct the service, add the status bar view to the window manager */ @@ -110,15 +102,74 @@ public class StatusBarManagerService extends IStatusBarService.Stub final Resources res = context.getResources(); mIcons.defineSlots(res.getStringArray(com.android.internal.R.array.config_statusBarIcons)); - } - public void setNotificationCallbacks(NotificationCallbacks listener) { - mNotificationCallbacks = listener; + LocalServices.addService(StatusBarManagerInternal.class, mInternalService); } + /** + * Private API used by NotificationManagerService. + */ + private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() { + @Override + public void setNotificationDelegate(NotificationDelegate delegate) { + synchronized (mNotifications) { + mNotificationDelegate = delegate; + } + } + + @Override + public IBinder addNotification(StatusBarNotification notification) { + synchronized (mNotifications) { + IBinder key = new Binder(); + mNotifications.put(key, notification); + if (mBar != null) { + try { + mBar.addNotification(key, notification); + } catch (RemoteException ex) { + } + } + return key; + } + } + + @Override + public void updateNotification(IBinder key, StatusBarNotification notification) { + synchronized (mNotifications) { + if (!mNotifications.containsKey(key)) { + throw new IllegalArgumentException("updateNotification key not found: " + key); + } + mNotifications.put(key, notification); + if (mBar != null) { + try { + mBar.updateNotification(key, notification); + } catch (RemoteException ex) { + } + } + } + } + + @Override + public void removeNotification(IBinder key) { + synchronized (mNotifications) { + final StatusBarNotification n = mNotifications.remove(key); + if (n == null) { + Slog.e(TAG, "removeNotification key not found: " + key); + return; + } + if (mBar != null) { + try { + mBar.removeNotification(key); + } catch (RemoteException ex) { + } + } + } + } + }; + // ================================================================================ // From IStatusBarService // ================================================================================ + @Override public void expandNotificationsPanel() { enforceExpandStatusBar(); @@ -130,6 +181,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub } } + @Override public void collapsePanels() { enforceExpandStatusBar(); @@ -141,6 +193,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub } } + @Override public void expandSettingsPanel() { enforceExpandStatusBar(); @@ -152,6 +205,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub } } + @Override public void disable(int what, IBinder token, String pkg) { disableInternal(mCurrentUserId, what, token, pkg); } @@ -177,7 +231,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub mDisabled = net; mHandler.post(new Runnable() { public void run() { - mNotificationCallbacks.onSetDisabled(net); + mNotificationDelegate.onSetDisabled(net); } }); if (mBar != null) { @@ -189,6 +243,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub } } + @Override public void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription) { enforceStatusBar(); @@ -214,6 +269,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub } } + @Override public void setIconVisibility(String slot, boolean visible) { enforceStatusBar(); @@ -241,6 +297,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub } } + @Override public void removeIcon(String slot) { enforceStatusBar(); @@ -265,6 +322,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub * Hide or show the on-screen Menu key. Only call this from the window manager, typically in * response to a window with FLAG_NEEDS_MENU_KEY set. */ + @Override public void topAppWindowChanged(final boolean menuVisible) { enforceStatusBar(); @@ -285,6 +343,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub } } + @Override public void setImeWindowStatus(final IBinder token, final int vis, final int backDisposition) { enforceStatusBar(); @@ -312,6 +371,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub } } + @Override public void setSystemUiVisibility(int vis, int mask) { // also allows calls from window manager which is in this process. enforceStatusBarService(); @@ -344,6 +404,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub } } + @Override public void setHardKeyboardEnabled(final boolean enabled) { mHandler.post(new Runnable() { public void run() { @@ -426,6 +487,7 @@ public class StatusBarManagerService extends IStatusBarService.Stub // ================================================================================ // Callbacks from the status bar service. // ================================================================================ + @Override public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList, List<IBinder> notificationKeys, List<StatusBarNotification> notifications, int switches[], List<IBinder> binders) { @@ -458,86 +520,64 @@ public class StatusBarManagerService extends IStatusBarService.Stub * The status bar service should call this each time the user brings the panel from * invisible to visible in order to clear the notification light. */ + @Override public void onPanelRevealed() { enforceStatusBarService(); - - // tell the notification manager to turn off the lights. - mNotificationCallbacks.onPanelRevealed(); + long identity = Binder.clearCallingIdentity(); + try { + // tell the notification manager to turn off the lights. + mNotificationDelegate.onPanelRevealed(); + } finally { + Binder.restoreCallingIdentity(identity); + } } + @Override public void onNotificationClick(String pkg, String tag, int id) { enforceStatusBarService(); - - mNotificationCallbacks.onNotificationClick(pkg, tag, id); + long identity = Binder.clearCallingIdentity(); + try { + mNotificationDelegate.onNotificationClick(pkg, tag, id); + } finally { + Binder.restoreCallingIdentity(identity); + } } + @Override public void onNotificationError(String pkg, String tag, int id, int uid, int initialPid, String message) { enforceStatusBarService(); - - // WARNING: this will call back into us to do the remove. Don't hold any locks. - mNotificationCallbacks.onNotificationError(pkg, tag, id, uid, initialPid, message); + long identity = Binder.clearCallingIdentity(); + try { + // WARNING: this will call back into us to do the remove. Don't hold any locks. + mNotificationDelegate.onNotificationError(pkg, tag, id, uid, initialPid, message); + } finally { + Binder.restoreCallingIdentity(identity); + } } + @Override public void onNotificationClear(String pkg, String tag, int id) { enforceStatusBarService(); - - mNotificationCallbacks.onNotificationClear(pkg, tag, id); + long identity = Binder.clearCallingIdentity(); + try { + mNotificationDelegate.onNotificationClear(pkg, tag, id); + } finally { + Binder.restoreCallingIdentity(identity); + } } + @Override public void onClearAllNotifications() { enforceStatusBarService(); - - mNotificationCallbacks.onClearAll(); - } - - // ================================================================================ - // Callbacks for NotificationManagerService. - // ================================================================================ - public IBinder addNotification(StatusBarNotification notification) { - synchronized (mNotifications) { - IBinder key = new Binder(); - mNotifications.put(key, notification); - if (mBar != null) { - try { - mBar.addNotification(key, notification); - } catch (RemoteException ex) { - } - } - return key; + long identity = Binder.clearCallingIdentity(); + try { + mNotificationDelegate.onClearAll(); + } finally { + Binder.restoreCallingIdentity(identity); } } - public void updateNotification(IBinder key, StatusBarNotification notification) { - synchronized (mNotifications) { - if (!mNotifications.containsKey(key)) { - throw new IllegalArgumentException("updateNotification key not found: " + key); - } - mNotifications.put(key, notification); - if (mBar != null) { - try { - mBar.updateNotification(key, notification); - } catch (RemoteException ex) { - } - } - } - } - - public void removeNotification(IBinder key) { - synchronized (mNotifications) { - final StatusBarNotification n = mNotifications.remove(key); - if (n == null) { - Slog.e(TAG, "removeNotification key not found: " + key); - return; - } - if (mBar != null) { - try { - mBar.removeNotification(key); - } catch (RemoteException ex) { - } - } - } - } // ================================================================================ // Can be called from any thread diff --git a/services/java/com/android/server/storage/DeviceStorageMonitorInternal.java b/services/java/com/android/server/storage/DeviceStorageMonitorInternal.java new file mode 100644 index 0000000..a91a81b --- /dev/null +++ b/services/java/com/android/server/storage/DeviceStorageMonitorInternal.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.storage; + +public interface DeviceStorageMonitorInternal { + boolean isMemoryLow(); + long getMemoryLowThreshold(); + void checkMemory(); +} + diff --git a/services/java/com/android/server/DeviceStorageMonitorService.java b/services/java/com/android/server/storage/DeviceStorageMonitorService.java index 016c561..8805084 100644 --- a/services/java/com/android/server/DeviceStorageMonitorService.java +++ b/services/java/com/android/server/storage/DeviceStorageMonitorService.java @@ -14,7 +14,10 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.storage; + +import com.android.server.EventLogTags; +import com.android.server.SystemService; import android.app.Notification; import android.app.NotificationManager; @@ -29,8 +32,8 @@ import android.os.Binder; import android.os.Environment; import android.os.FileObserver; import android.os.Handler; +import android.os.IBinder; import android.os.Message; -import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.StatFs; @@ -66,13 +69,13 @@ import java.io.PrintWriter; * settings parameter with a default value of 2MB), the free memory is * logged to the event log. */ -public class DeviceStorageMonitorService extends Binder { - private static final String TAG = "DeviceStorageMonitorService"; +public class DeviceStorageMonitorService extends SystemService { + static final String TAG = "DeviceStorageMonitorService"; - private static final boolean DEBUG = false; - private static final boolean localLOGV = false; + static final boolean DEBUG = false; + static final boolean localLOGV = false; - private static final int DEVICE_MEMORY_WHAT = 1; + static final int DEVICE_MEMORY_WHAT = 1; private static final int MONITOR_INTERVAL = 1; //in minutes private static final int LOW_MEMORY_NOTIFICATION_ID = 1; @@ -84,9 +87,8 @@ public class DeviceStorageMonitorService extends Binder { private long mFreeMemAfterLastCacheClear; // on /data private long mLastReportedFreeMem; private long mLastReportedFreeMemTime; - private boolean mLowMemFlag=false; + boolean mLowMemFlag=false; private boolean mMemFullFlag=false; - private Context mContext; private ContentResolver mResolver; private long mTotalMemory; // on /data private StatFs mDataFileStats; @@ -98,19 +100,19 @@ public class DeviceStorageMonitorService extends Binder { private static final File CACHE_PATH = Environment.getDownloadCacheDirectory(); private long mThreadStartTime = -1; - private boolean mClearSucceeded = false; - private boolean mClearingCache; + boolean mClearSucceeded = false; + boolean mClearingCache; private Intent mStorageLowIntent; private Intent mStorageOkIntent; private Intent mStorageFullIntent; private Intent mStorageNotFullIntent; private CachePackageDataObserver mClearCacheObserver; - private final CacheFileDeletedObserver mCacheFileDeletedObserver; + private CacheFileDeletedObserver mCacheFileDeletedObserver; private static final int _TRUE = 1; private static final int _FALSE = 0; // This is the raw threshold that has been set at which we consider // storage to be low. - private long mMemLowThreshold; + long mMemLowThreshold; // This is the threshold at which we start trying to flush caches // to get below the low threshold limit. It is less than the low // threshold; we will allow storage to get a bit beyond the limit @@ -126,13 +128,13 @@ public class DeviceStorageMonitorService extends Binder { /** * This string is used for ServiceManager access to this class. */ - public static final String SERVICE = "devicestoragemonitor"; + static final String SERVICE = "devicestoragemonitor"; /** * Handler that checks the amount of disk space on the device and sends a * notification if the device runs low on disk space */ - Handler mHandler = new Handler() { + private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { //don't handle an invalid message @@ -144,7 +146,7 @@ public class DeviceStorageMonitorService extends Binder { } }; - class CachePackageDataObserver extends IPackageDataObserver.Stub { + private class CachePackageDataObserver extends IPackageDataObserver.Stub { public void onRemoveCompleted(String packageName, boolean succeeded) { mClearSucceeded = succeeded; mClearingCache = false; @@ -154,7 +156,7 @@ public class DeviceStorageMonitorService extends Binder { } } - private final void restatDataDir() { + private void restatDataDir() { try { mDataFileStats.restat(DATA_PATH.getAbsolutePath()); mFreeMem = (long) mDataFileStats.getAvailableBlocks() * @@ -206,7 +208,7 @@ public class DeviceStorageMonitorService extends Binder { } } - private final void clearCache() { + private void clearCache() { if (mClearCacheObserver == null) { // Lazy instantiation mClearCacheObserver = new CachePackageDataObserver(); @@ -223,7 +225,7 @@ public class DeviceStorageMonitorService extends Binder { } } - private final void checkMemory(boolean checkCache) { + void checkMemory(boolean checkCache) { //if the thread that was started to clear cache is still running do nothing till its //finished clearing cache. Ideally this flag could be modified by clearCache // and should be accessed via a lock but even if it does this test will fail now and @@ -300,7 +302,7 @@ public class DeviceStorageMonitorService extends Binder { postCheckMemoryMsg(true, DEFAULT_CHECK_INTERVAL); } - private void postCheckMemoryMsg(boolean clearCache, long delay) { + void postCheckMemoryMsg(boolean clearCache, long delay) { // Remove queued messages mHandler.removeMessages(DEVICE_MEMORY_WHAT); mHandler.sendMessageDelayed(mHandler.obtainMessage(DEVICE_MEMORY_WHAT, @@ -312,10 +314,10 @@ public class DeviceStorageMonitorService extends Binder { * Constructor to run service. initializes the disk space threshold value * and posts an empty message to kickstart the process. */ - public DeviceStorageMonitorService(Context context) { + @Override + public void onCreate(Context context) { mLastReportedFreeMemTime = 0; - mContext = context; - mResolver = mContext.getContentResolver(); + mResolver = context.getContentResolver(); //create StatFs object mDataFileStats = new StatFs(DATA_PATH.getAbsolutePath()); mSystemFileStats = new StatFs(SYSTEM_PATH.getAbsolutePath()); @@ -331,9 +333,12 @@ public class DeviceStorageMonitorService extends Binder { mStorageFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); mStorageNotFullIntent = new Intent(Intent.ACTION_DEVICE_STORAGE_NOT_FULL); mStorageNotFullIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + } + @Override + public void onStart() { // cache storage thresholds - final StorageManager sm = StorageManager.from(context); + final StorageManager sm = StorageManager.from(getContext()); mMemLowThreshold = sm.getStorageLowBytes(DATA_PATH); mMemFullThreshold = sm.getStorageFullBytes(DATA_PATH); @@ -345,6 +350,78 @@ public class DeviceStorageMonitorService extends Binder { mCacheFileDeletedObserver = new CacheFileDeletedObserver(); mCacheFileDeletedObserver.startWatching(); + + publishBinderService(SERVICE, mRemoteService); + publishLocalService(DeviceStorageMonitorInternal.class, mLocalService); + } + + private final DeviceStorageMonitorInternal mLocalService = new DeviceStorageMonitorInternal() { + @Override + public void checkMemory() { + // force an early check + postCheckMemoryMsg(true, 0); + } + + @Override + public boolean isMemoryLow() { + return mLowMemFlag; + } + + @Override + public long getMemoryLowThreshold() { + return mMemLowThreshold; + } + }; + + private final IBinder mRemoteService = new 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 " + SERVICE + " from from pid=" + + Binder.getCallingPid() + + ", uid=" + Binder.getCallingUid()); + return; + } + + dumpImpl(pw); + } + }; + + void dumpImpl(PrintWriter pw) { + final Context context = getContext(); + + pw.println("Current DeviceStorageMonitor state:"); + + pw.print(" mFreeMem="); pw.print(Formatter.formatFileSize(context, mFreeMem)); + pw.print(" mTotalMemory="); + pw.println(Formatter.formatFileSize(context, mTotalMemory)); + + pw.print(" mFreeMemAfterLastCacheClear="); + pw.println(Formatter.formatFileSize(context, mFreeMemAfterLastCacheClear)); + + pw.print(" mLastReportedFreeMem="); + pw.print(Formatter.formatFileSize(context, mLastReportedFreeMem)); + pw.print(" mLastReportedFreeMemTime="); + TimeUtils.formatDuration(mLastReportedFreeMemTime, SystemClock.elapsedRealtime(), pw); + pw.println(); + + pw.print(" mLowMemFlag="); pw.print(mLowMemFlag); + pw.print(" mMemFullFlag="); pw.println(mMemFullFlag); + + pw.print(" mClearSucceeded="); pw.print(mClearSucceeded); + pw.print(" mClearingCache="); pw.println(mClearingCache); + + pw.print(" mMemLowThreshold="); + pw.print(Formatter.formatFileSize(context, mMemLowThreshold)); + pw.print(" mMemFullThreshold="); + pw.println(Formatter.formatFileSize(context, mMemFullThreshold)); + + pw.print(" mMemCacheStartTrimThreshold="); + pw.print(Formatter.formatFileSize(context, mMemCacheStartTrimThreshold)); + pw.print(" mMemCacheTrimToThreshold="); + pw.println(Formatter.formatFileSize(context, mMemCacheTrimToThreshold)); } /** @@ -352,7 +429,8 @@ public class DeviceStorageMonitorService extends Binder { * an error dialog indicating low disk space and launch the Installer * application */ - private final void sendNotification() { + private void sendNotification() { + final Context context = getContext(); if(localLOGV) Slog.i(TAG, "Sending low memory notification"); //log the event to event log with the amount of free storage(in bytes) left on the device EventLog.writeEvent(EventLogTags.LOW_STORAGE, mFreeMem); @@ -363,86 +441,58 @@ public class DeviceStorageMonitorService extends Binder { lowMemIntent.putExtra("memory", mFreeMem); lowMemIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); NotificationManager mNotificationMgr = - (NotificationManager)mContext.getSystemService( + (NotificationManager)context.getSystemService( Context.NOTIFICATION_SERVICE); - CharSequence title = mContext.getText( + CharSequence title = context.getText( com.android.internal.R.string.low_internal_storage_view_title); - CharSequence details = mContext.getText( + CharSequence details = context.getText( com.android.internal.R.string.low_internal_storage_view_text); - PendingIntent intent = PendingIntent.getActivityAsUser(mContext, 0, lowMemIntent, 0, + PendingIntent intent = PendingIntent.getActivityAsUser(context, 0, lowMemIntent, 0, null, UserHandle.CURRENT); Notification notification = new Notification(); notification.icon = com.android.internal.R.drawable.stat_notify_disk_full; notification.tickerText = title; notification.flags |= Notification.FLAG_NO_CLEAR; - notification.setLatestEventInfo(mContext, title, details, intent); + notification.setLatestEventInfo(context, title, details, intent); mNotificationMgr.notifyAsUser(null, LOW_MEMORY_NOTIFICATION_ID, notification, UserHandle.ALL); - mContext.sendStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL); + context.sendStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL); } /** * Cancels low storage notification and sends OK intent. */ - private final void cancelNotification() { + private void cancelNotification() { + final Context context = getContext(); if(localLOGV) Slog.i(TAG, "Canceling low memory notification"); NotificationManager mNotificationMgr = - (NotificationManager)mContext.getSystemService( + (NotificationManager)context.getSystemService( Context.NOTIFICATION_SERVICE); //cancel notification since memory has been freed mNotificationMgr.cancelAsUser(null, LOW_MEMORY_NOTIFICATION_ID, UserHandle.ALL); - mContext.removeStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL); - mContext.sendBroadcastAsUser(mStorageOkIntent, UserHandle.ALL); + context.removeStickyBroadcastAsUser(mStorageLowIntent, UserHandle.ALL); + context.sendBroadcastAsUser(mStorageOkIntent, UserHandle.ALL); } /** * Send a notification when storage is full. */ - private final void sendFullNotification() { + private void sendFullNotification() { if(localLOGV) Slog.i(TAG, "Sending memory full notification"); - mContext.sendStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL); + getContext().sendStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL); } /** * Cancels memory full notification and sends "not full" intent. */ - private final void cancelFullNotification() { + private void cancelFullNotification() { if(localLOGV) Slog.i(TAG, "Canceling memory full notification"); - mContext.removeStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL); - mContext.sendBroadcastAsUser(mStorageNotFullIntent, UserHandle.ALL); + getContext().removeStickyBroadcastAsUser(mStorageFullIntent, UserHandle.ALL); + getContext().sendBroadcastAsUser(mStorageNotFullIntent, UserHandle.ALL); } - public void updateMemory() { - int callingUid = getCallingUid(); - if(callingUid != Process.SYSTEM_UID) { - return; - } - // force an early check - postCheckMemoryMsg(true, 0); - } - - /** - * Callable from other things in the system service to obtain the low memory - * threshold. - * - * @return low memory threshold in bytes - */ - public long getMemoryLowThreshold() { - return mMemLowThreshold; - } - - /** - * Callable from other things in the system process to check whether memory - * is low. - * - * @return true is memory is low - */ - public boolean isMemoryLow() { - return mLowMemFlag; - } - - public static class CacheFileDeletedObserver extends FileObserver { + private static class CacheFileDeletedObserver extends FileObserver { public CacheFileDeletedObserver() { super(Environment.getDownloadCacheDirectory().getAbsolutePath(), FileObserver.DELETE); } @@ -452,40 +502,4 @@ public class DeviceStorageMonitorService extends Binder { EventLogTags.writeCacheFileDeleted(path); } } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - - pw.println("Permission Denial: can't dump " + SERVICE + " from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return; - } - - pw.println("Current DeviceStorageMonitor state:"); - pw.print(" mFreeMem="); pw.print(Formatter.formatFileSize(mContext, mFreeMem)); - pw.print(" mTotalMemory="); - pw.println(Formatter.formatFileSize(mContext, mTotalMemory)); - pw.print(" mFreeMemAfterLastCacheClear="); - pw.println(Formatter.formatFileSize(mContext, mFreeMemAfterLastCacheClear)); - pw.print(" mLastReportedFreeMem="); - pw.print(Formatter.formatFileSize(mContext, mLastReportedFreeMem)); - pw.print(" mLastReportedFreeMemTime="); - TimeUtils.formatDuration(mLastReportedFreeMemTime, SystemClock.elapsedRealtime(), pw); - pw.println(); - pw.print(" mLowMemFlag="); pw.print(mLowMemFlag); - pw.print(" mMemFullFlag="); pw.println(mMemFullFlag); - pw.print(" mClearSucceeded="); pw.print(mClearSucceeded); - pw.print(" mClearingCache="); pw.println(mClearingCache); - pw.print(" mMemLowThreshold="); - pw.print(Formatter.formatFileSize(mContext, mMemLowThreshold)); - pw.print(" mMemFullThreshold="); - pw.println(Formatter.formatFileSize(mContext, mMemFullThreshold)); - pw.print(" mMemCacheStartTrimThreshold="); - pw.print(Formatter.formatFileSize(mContext, mMemCacheStartTrimThreshold)); - pw.print(" mMemCacheTrimToThreshold="); - pw.println(Formatter.formatFileSize(mContext, mMemCacheTrimToThreshold)); - } } diff --git a/services/java/com/android/server/twilight/TwilightListener.java b/services/java/com/android/server/twilight/TwilightListener.java new file mode 100644 index 0000000..29ead44 --- /dev/null +++ b/services/java/com/android/server/twilight/TwilightListener.java @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.twilight; + +public interface TwilightListener { + void onTwilightStateChanged(); +}
\ No newline at end of file diff --git a/services/java/com/android/server/twilight/TwilightManager.java b/services/java/com/android/server/twilight/TwilightManager.java new file mode 100644 index 0000000..b3de58b --- /dev/null +++ b/services/java/com/android/server/twilight/TwilightManager.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.twilight; + +import android.os.Handler; + +public interface TwilightManager { + void registerListener(TwilightListener listener, Handler handler); + TwilightState getCurrentState(); +} diff --git a/services/java/com/android/server/TwilightService.java b/services/java/com/android/server/twilight/TwilightService.java index 0356faa..8feb97b 100644 --- a/services/java/com/android/server/TwilightService.java +++ b/services/java/com/android/server/twilight/TwilightService.java @@ -14,7 +14,10 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.twilight; + +import com.android.server.SystemService; +import com.android.server.TwilightCalculator; import android.app.AlarmManager; import android.app.PendingIntent; @@ -34,9 +37,7 @@ import android.text.format.DateUtils; import android.text.format.Time; import android.util.Slog; -import java.text.DateFormat; import java.util.ArrayList; -import java.util.Date; import java.util.Iterator; import libcore.util.Objects; @@ -47,78 +48,88 @@ import libcore.util.Objects; * Used by the UI mode manager and other components to adjust night mode * effects based on sunrise and sunset. */ -public final class TwilightService { - private static final String TAG = "TwilightService"; - - private static final boolean DEBUG = false; - - private static final String ACTION_UPDATE_TWILIGHT_STATE = +public final class TwilightService extends SystemService { + static final String TAG = "TwilightService"; + static final boolean DEBUG = false; + static final String ACTION_UPDATE_TWILIGHT_STATE = "com.android.server.action.UPDATE_TWILIGHT_STATE"; - private final Context mContext; - private final AlarmManager mAlarmManager; - private final LocationManager mLocationManager; - private final LocationHandler mLocationHandler; + final Object mLock = new Object(); - private final Object mLock = new Object(); + AlarmManager mAlarmManager; + LocationManager mLocationManager; + LocationHandler mLocationHandler; - private final ArrayList<TwilightListenerRecord> mListeners = + final ArrayList<TwilightListenerRecord> mListeners = new ArrayList<TwilightListenerRecord>(); - private boolean mSystemReady; + TwilightState mTwilightState; - private TwilightState mTwilightState; + @Override + public void onStart() { + mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE); + mLocationManager = (LocationManager) getContext().getSystemService( + Context.LOCATION_SERVICE); + mLocationHandler = new LocationHandler(); - public TwilightService(Context context) { - mContext = context; + IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); + filter.addAction(Intent.ACTION_TIME_CHANGED); + filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); + filter.addAction(ACTION_UPDATE_TWILIGHT_STATE); + getContext().registerReceiver(mUpdateLocationReceiver, filter); - mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE); - mLocationManager = (LocationManager)mContext.getSystemService(Context.LOCATION_SERVICE); - mLocationHandler = new LocationHandler(); + publishLocalService(TwilightManager.class, mService); } - void systemReady() { - synchronized (mLock) { - mSystemReady = true; + private static class TwilightListenerRecord implements Runnable { + private final TwilightListener mListener; + private final Handler mHandler; - IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED); - filter.addAction(Intent.ACTION_TIME_CHANGED); - filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); - filter.addAction(ACTION_UPDATE_TWILIGHT_STATE); - mContext.registerReceiver(mUpdateLocationReceiver, filter); + public TwilightListenerRecord(TwilightListener listener, Handler handler) { + mListener = listener; + mHandler = handler; + } - if (!mListeners.isEmpty()) { - mLocationHandler.enableLocationUpdates(); - } + public void postUpdate() { + mHandler.post(this); } - } - /** - * Gets the current twilight state. - * - * @return The current twilight state, or null if no information is available. - */ - public TwilightState getCurrentState() { - synchronized (mLock) { - return mTwilightState; + @Override + public void run() { + mListener.onTwilightStateChanged(); } + } - /** - * Listens for twilight time. - * - * @param listener The listener. - * @param handler The handler on which to post calls into the listener. - */ - public void registerListener(TwilightListener listener, Handler handler) { - synchronized (mLock) { - mListeners.add(new TwilightListenerRecord(listener, handler)); + private final TwilightManager mService = new TwilightManager() { + /** + * Gets the current twilight state. + * + * @return The current twilight state, or null if no information is available. + */ + @Override + public TwilightState getCurrentState() { + synchronized (mLock) { + return mTwilightState; + } + } - if (mSystemReady && mListeners.size() == 1) { - mLocationHandler.enableLocationUpdates(); + /** + * Listens for twilight time. + * + * @param listener The listener. + */ + @Override + public void registerListener(TwilightListener listener, Handler handler) { + synchronized (mLock) { + mListeners.add(new TwilightListenerRecord(listener, handler)); + + if (mListeners.size() == 1) { + mLocationHandler.enableLocationUpdates(); + } } } - } + }; private void setTwilightState(TwilightState state) { synchronized (mLock) { @@ -128,9 +139,10 @@ public final class TwilightService { } mTwilightState = state; - int count = mListeners.size(); - for (int i = 0; i < count; i++) { - mListeners.get(i).post(); + + final int listenerLen = mListeners.size(); + for (int i = 0; i < listenerLen; i++) { + mListeners.get(i).postUpdate(); } } } @@ -162,124 +174,6 @@ public final class TwilightService { return distance >= totalAccuracy; } - /** - * Describes whether it is day or night. - * This object is immutable. - */ - public static final class TwilightState { - private final boolean mIsNight; - private final long mYesterdaySunset; - private final long mTodaySunrise; - private final long mTodaySunset; - private final long mTomorrowSunrise; - - TwilightState(boolean isNight, - long yesterdaySunset, - long todaySunrise, long todaySunset, - long tomorrowSunrise) { - mIsNight = isNight; - mYesterdaySunset = yesterdaySunset; - mTodaySunrise = todaySunrise; - mTodaySunset = todaySunset; - mTomorrowSunrise = tomorrowSunrise; - } - - /** - * Returns true if it is currently night time. - */ - public boolean isNight() { - return mIsNight; - } - - /** - * Returns the time of yesterday's sunset in the System.currentTimeMillis() timebase, - * or -1 if the sun never sets. - */ - public long getYesterdaySunset() { - return mYesterdaySunset; - } - - /** - * Returns the time of today's sunrise in the System.currentTimeMillis() timebase, - * or -1 if the sun never rises. - */ - public long getTodaySunrise() { - return mTodaySunrise; - } - - /** - * Returns the time of today's sunset in the System.currentTimeMillis() timebase, - * or -1 if the sun never sets. - */ - public long getTodaySunset() { - return mTodaySunset; - } - - /** - * Returns the time of tomorrow's sunrise in the System.currentTimeMillis() timebase, - * or -1 if the sun never rises. - */ - public long getTomorrowSunrise() { - return mTomorrowSunrise; - } - - @Override - public boolean equals(Object o) { - return o instanceof TwilightState && equals((TwilightState)o); - } - - public boolean equals(TwilightState other) { - return other != null - && mIsNight == other.mIsNight - && mYesterdaySunset == other.mYesterdaySunset - && mTodaySunrise == other.mTodaySunrise - && mTodaySunset == other.mTodaySunset - && mTomorrowSunrise == other.mTomorrowSunrise; - } - - @Override - public int hashCode() { - return 0; // don't care - } - - @Override - public String toString() { - DateFormat f = DateFormat.getDateTimeInstance(); - return "{TwilightState: isNight=" + mIsNight - + ", mYesterdaySunset=" + f.format(new Date(mYesterdaySunset)) - + ", mTodaySunrise=" + f.format(new Date(mTodaySunrise)) - + ", mTodaySunset=" + f.format(new Date(mTodaySunset)) - + ", mTomorrowSunrise=" + f.format(new Date(mTomorrowSunrise)) - + "}"; - } - } - - /** - * Listener for changes in twilight state. - */ - public interface TwilightListener { - public void onTwilightStateChanged(); - } - - private static final class TwilightListenerRecord implements Runnable { - private final TwilightListener mListener; - private final Handler mHandler; - - public TwilightListenerRecord(TwilightListener listener, Handler handler) { - mListener = listener; - mHandler = handler; - } - - public void post() { - mHandler.post(this); - } - - @Override - public void run() { - mListener.onTwilightStateChanged(); - } - } - private final class LocationHandler extends Handler { private static final int MSG_ENABLE_LOCATION_UPDATES = 1; private static final int MSG_GET_NEW_LOCATION_UPDATE = 2; @@ -518,11 +412,12 @@ public final class TwilightService { } Intent updateIntent = new Intent(ACTION_UPDATE_TWILIGHT_STATE); - PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, updateIntent, 0); + PendingIntent pendingIntent = PendingIntent.getBroadcast( + getContext(), 0, updateIntent, 0); mAlarmManager.cancel(pendingIntent); mAlarmManager.setExact(AlarmManager.RTC, nextUpdate, pendingIntent); } - }; + } private final BroadcastReceiver mUpdateLocationReceiver = new BroadcastReceiver() { @Override diff --git a/services/java/com/android/server/twilight/TwilightState.java b/services/java/com/android/server/twilight/TwilightState.java new file mode 100644 index 0000000..91e24d7 --- /dev/null +++ b/services/java/com/android/server/twilight/TwilightState.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.twilight; + +import java.text.DateFormat; +import java.util.Date; + +/** + * Describes whether it is day or night. + * This object is immutable. + */ +public class TwilightState { + private final boolean mIsNight; + private final long mYesterdaySunset; + private final long mTodaySunrise; + private final long mTodaySunset; + private final long mTomorrowSunrise; + + TwilightState(boolean isNight, + long yesterdaySunset, + long todaySunrise, long todaySunset, + long tomorrowSunrise) { + mIsNight = isNight; + mYesterdaySunset = yesterdaySunset; + mTodaySunrise = todaySunrise; + mTodaySunset = todaySunset; + mTomorrowSunrise = tomorrowSunrise; + } + + /** + * Returns true if it is currently night time. + */ + public boolean isNight() { + return mIsNight; + } + + /** + * Returns the time of yesterday's sunset in the System.currentTimeMillis() timebase, + * or -1 if the sun never sets. + */ + public long getYesterdaySunset() { + return mYesterdaySunset; + } + + /** + * Returns the time of today's sunrise in the System.currentTimeMillis() timebase, + * or -1 if the sun never rises. + */ + public long getTodaySunrise() { + return mTodaySunrise; + } + + /** + * Returns the time of today's sunset in the System.currentTimeMillis() timebase, + * or -1 if the sun never sets. + */ + public long getTodaySunset() { + return mTodaySunset; + } + + /** + * Returns the time of tomorrow's sunrise in the System.currentTimeMillis() timebase, + * or -1 if the sun never rises. + */ + public long getTomorrowSunrise() { + return mTomorrowSunrise; + } + + @Override + public boolean equals(Object o) { + return o instanceof TwilightState && equals((TwilightState)o); + } + + public boolean equals(TwilightState other) { + return other != null + && mIsNight == other.mIsNight + && mYesterdaySunset == other.mYesterdaySunset + && mTodaySunrise == other.mTodaySunrise + && mTodaySunset == other.mTodaySunset + && mTomorrowSunrise == other.mTomorrowSunrise; + } + + @Override + public int hashCode() { + return 0; // don't care + } + + @Override + public String toString() { + DateFormat f = DateFormat.getDateTimeInstance(); + return "{TwilightState: isNight=" + mIsNight + + ", mYesterdaySunset=" + f.format(new Date(mYesterdaySunset)) + + ", mTodaySunrise=" + f.format(new Date(mTodaySunrise)) + + ", mTodaySunset=" + f.format(new Date(mTodaySunset)) + + ", mTomorrowSunrise=" + f.format(new Date(mTomorrowSunrise)) + + "}"; + } +} diff --git a/services/jni/Android.mk b/services/jni/Android.mk index 98e9b30..a98b1c3 100644 --- a/services/jni/Android.mk +++ b/services/jni/Android.mk @@ -8,7 +8,7 @@ LOCAL_SRC_FILES:= \ com_android_server_input_InputApplicationHandle.cpp \ com_android_server_input_InputManagerService.cpp \ com_android_server_input_InputWindowHandle.cpp \ - com_android_server_LightsService.cpp \ + com_android_server_lights_LightsService.cpp \ com_android_server_power_PowerManagerService.cpp \ com_android_server_SerialService.cpp \ com_android_server_SystemServer.cpp \ diff --git a/services/jni/com_android_server_LightsService.cpp b/services/jni/com_android_server_lights_LightsService.cpp index 401e1aa..ea03cfa 100644 --- a/services/jni/com_android_server_LightsService.cpp +++ b/services/jni/com_android_server_lights_LightsService.cpp @@ -134,7 +134,7 @@ static JNINativeMethod method_table[] = { int register_android_server_LightsService(JNIEnv *env) { - return jniRegisterNativeMethods(env, "com/android/server/LightsService", + return jniRegisterNativeMethods(env, "com/android/server/lights/LightsService", method_table, NELEM(method_table)); } |