Merge "Adding shell commands for grant/revoke of bind app widget permisison." into lmp-dev
diff --git a/api/current.txt b/api/current.txt
index f0ee69d..a49e0f7 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4588,6 +4588,7 @@
field public static final int DEFAULT_SOUND = 1; // 0x1
field public static final int DEFAULT_VIBRATE = 2; // 0x2
field public static final java.lang.String EXTRA_BACKGROUND_IMAGE_URI = "android.backgroundImageUri";
+ field public static final java.lang.String EXTRA_COMPACT_ACTIONS = "android.compactActions";
field public static final java.lang.String EXTRA_INFO_TEXT = "android.infoText";
field public static final java.lang.String EXTRA_LARGE_ICON = "android.largeIcon";
field public static final java.lang.String EXTRA_LARGE_ICON_BIG = "android.largeIcon.big";
@@ -5354,6 +5355,7 @@
method public int getPasswordMinimumSymbols(android.content.ComponentName);
method public int getPasswordMinimumUpperCase(android.content.ComponentName);
method public int getPasswordQuality(android.content.ComponentName);
+ method public boolean getScreenCaptureDisabled(android.content.ComponentName);
method public boolean getStorageEncryption(android.content.ComponentName);
method public int getStorageEncryptionStatus();
method public boolean hasAnyCaCertsInstalled();
@@ -5398,6 +5400,7 @@
method public void setProfileName(android.content.ComponentName, java.lang.String);
method public void setRecommendedGlobalProxy(android.content.ComponentName, android.net.ProxyInfo);
method public void setRestrictionsProvider(android.content.ComponentName, android.content.ComponentName);
+ method public void setScreenCaptureDisabled(android.content.ComponentName, boolean);
method public void setSecureSetting(android.content.ComponentName, java.lang.String, java.lang.String);
method public int setStorageEncryption(android.content.ComponentName, boolean);
method public boolean switchUser(android.content.ComponentName, android.os.UserHandle);
@@ -15017,6 +15020,7 @@
field public static final java.lang.String KEY_REFERENCE_CLOCK_ID = "reference-clock-id";
field public static final java.lang.String KEY_REPEAT_PREVIOUS_FRAME_AFTER = "repeat-previous-frame-after";
field public static final java.lang.String KEY_SAMPLE_RATE = "sample-rate";
+ field public static final java.lang.String KEY_TEMPORAL_LAYERING = "ts-schema";
field public static final java.lang.String KEY_WIDTH = "width";
field public static final java.lang.String MIMETYPE_AUDIO_AAC = "audio/mp4a-latm";
field public static final java.lang.String MIMETYPE_AUDIO_AC3 = "audio/ac3";
@@ -16592,7 +16596,7 @@
}
public static final class MediaController.VolumeInfo {
- method public int getAudioStream();
+ method public android.media.AudioAttributes getAudioAttributes();
method public int getCurrentVolume();
method public int getMaxVolume();
method public int getVolumeControl();
@@ -16617,7 +16621,7 @@
method public void setMediaRouter(android.media.routing.MediaRouter);
method public void setMetadata(android.media.MediaMetadata);
method public void setPlaybackState(android.media.session.PlaybackState);
- method public void setPlaybackToLocal(int);
+ method public void setPlaybackToLocal(android.media.AudioAttributes);
method public void setPlaybackToRemote(android.media.VolumeProvider);
field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
@@ -27276,6 +27280,7 @@
ctor public StatusBarNotification(android.os.Parcel);
method public android.service.notification.StatusBarNotification clone();
method public int describeContents();
+ method public java.lang.String getGroupKey();
method public int getId();
method public java.lang.String getKey();
method public android.app.Notification getNotification();
@@ -28689,15 +28694,15 @@
}
public class PhoneAccount implements android.os.Parcelable {
- ctor public PhoneAccount(android.telecomm.PhoneAccountHandle, android.net.Uri, java.lang.String, int, int, java.lang.String, java.lang.String, boolean);
+ ctor public PhoneAccount(android.telecomm.PhoneAccountHandle, android.net.Uri, java.lang.String, int, int, java.lang.CharSequence, java.lang.CharSequence, boolean);
method public int describeContents();
method public android.telecomm.PhoneAccountHandle getAccountHandle();
method public int getCapabilities();
method public android.net.Uri getHandle();
method public android.graphics.drawable.Drawable getIcon(android.content.Context);
method public int getIconResId();
- method public java.lang.String getLabel();
- method public java.lang.String getShortDescription();
+ method public java.lang.CharSequence getLabel();
+ method public java.lang.CharSequence getShortDescription();
method public java.lang.String getSubscriptionNumber();
method public boolean isVideoCallingSupported();
method public void writeToParcel(android.os.Parcel, int);
@@ -28791,13 +28796,13 @@
}
public final class StatusHints implements android.os.Parcelable {
- ctor public StatusHints(android.content.ComponentName, java.lang.String, int, android.os.Bundle);
+ ctor public StatusHints(android.content.ComponentName, java.lang.CharSequence, int, android.os.Bundle);
method public int describeContents();
method public android.content.ComponentName getComponentName();
method public android.os.Bundle getExtras();
method public android.graphics.drawable.Drawable getIcon(android.content.Context);
method public int getIconId();
- method public java.lang.String getLabel();
+ method public java.lang.CharSequence getLabel();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator CREATOR;
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index da59a03..5782edc 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -799,6 +799,12 @@
public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
/**
+ * {@link #extras} key: the indices of actions to be shown in the compact view,
+ * as supplied to (e.g.) {@link MediaStyle#setShowActionsInCompactView(int...)}.
+ */
+ public static final String EXTRA_COMPACT_ACTIONS = "android.compactActions";
+
+ /**
* Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be
* displayed in the heads up space.
*
@@ -3362,6 +3368,9 @@
if (mToken != null) {
extras.putParcelable(EXTRA_MEDIA_SESSION, mToken);
}
+ if (mActionsToShowInCompact != null) {
+ extras.putIntArray(EXTRA_COMPACT_ACTIONS, mActionsToShowInCompact);
+ }
}
private RemoteViews generateMediaActionButton(Action action) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index efeded5..a193a34 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1775,6 +1775,46 @@
}
/**
+ * Called by a device/profile owner to set whether the screen capture is disabled.
+ *
+ * <p>The calling device admin must be a device or profile owner. If it is not, a
+ * security exception will be thrown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ */
+ public void setScreenCaptureDisabled(ComponentName admin, boolean disabled) {
+ if (mService != null) {
+ try {
+ mService.setScreenCaptureDisabled(admin, UserHandle.myUserId(), disabled);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ }
+
+ /**
+ * Determine whether or not screen capture has been disabled by the current
+ * admin, if specified, or all admins.
+ * @param admin The name of the admin component to check, or null to check if any admins
+ * have disabled screen capture.
+ */
+ public boolean getScreenCaptureDisabled(ComponentName admin) {
+ return getScreenCaptureDisabled(admin, UserHandle.myUserId());
+ }
+
+ /** @hide per-user version */
+ public boolean getScreenCaptureDisabled(ComponentName admin, int userHandle) {
+ if (mService != null) {
+ try {
+ return mService.getScreenCaptureDisabled(admin, userHandle);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return false;
+ }
+
+ /**
* Called by an application that is administering the device to disable keyguard customizations,
* such as widgets. After setting this, keyguard features will be disabled according to the
* provided feature list.
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5fc8c5f..6499ae4 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -88,6 +88,9 @@
void setCameraDisabled(in ComponentName who, boolean disabled, int userHandle);
boolean getCameraDisabled(in ComponentName who, int userHandle);
+ void setScreenCaptureDisabled(in ComponentName who, int userHandle, boolean disabled);
+ boolean getScreenCaptureDisabled(in ComponentName who, int userHandle);
+
void setKeyguardDisabledFeatures(in ComponentName who, int which, int userHandle);
int getKeyguardDisabledFeatures(in ComponentName who, int userHandle);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index b7344b7..95c62e0 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2837,6 +2837,7 @@
* @see android.net.NetworkScoreManager
* @hide
*/
+ @SystemApi
public static final String NETWORK_SCORE_SERVICE = "network_score";
/**
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 38ddede..5face69 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1824,15 +1824,7 @@
remainder = languageTag.substring(separator);
}
- if ("id".equals(language)) {
- return "in" + remainder;
- } else if ("yi".equals(language)) {
- return "ji" + remainder;
- } else if ("he".equals(language)) {
- return "iw" + remainder;
- } else {
- return languageTag;
- }
+ return Locale.adjustLanguageCode(language) + remainder;
}
/**
diff --git a/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java b/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java
new file mode 100644
index 0000000..b51fa2b
--- /dev/null
+++ b/core/java/android/hardware/hdmi/HdmiTimerRecordSources.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.hdmi;
+
+import android.annotation.SystemApi;
+import android.hardware.hdmi.HdmiRecordSources.AnalogueServiceSource;
+import android.hardware.hdmi.HdmiRecordSources.DigitalServiceSource;
+import android.hardware.hdmi.HdmiRecordSources.ExternalPhysicalAddress;
+import android.hardware.hdmi.HdmiRecordSources.ExternalPlugData;
+import android.hardware.hdmi.HdmiRecordSources.RecordSource;
+import android.util.Log;
+
+/**
+ * Container for timer record source used for timer recording. Timer source consists of two parts,
+ * timer info and record source.
+ * <p>
+ * Timer info contains all timing information used for recording. It consists of the following
+ * values.
+ * <ul>
+ * <li>[Day of Month]
+ * <li>[Month of Year]
+ * <li>[Start Time]
+ * <li>[Duration]
+ * <li>[Recording Sequence]
+ * </ul>
+ * <p>
+ * Record source containers all program information used for recording.
+ * For more details, look at {@link HdmiRecordSources}.
+ * <p>
+ * Usage
+ * <pre>
+ * TimeOrDuration startTime = HdmiTimerRecordSources.ofTime(18, 00); // 6PM.
+ * TimeOrDuration duration = HdmiTimerRecordSource.ofDuration(1, 00); // 1 hour duration.
+ * // For 1 hour from 6PM, August 10th every SaturDay and Sunday.
+ * TimerInfo timerInfo = HdmiTimerRecordSource.timerInfoOf(10, 8, starTime, duration,
+ * HdmiTimerRecordSource.RECORDING_SEQUENCE_REPEAT_SATURDAY |
+ * HdmiTimerRecordSource.RECORDING_SEQUENCE_REPEAT_SUNDAY);
+ * // create digital source.
+ * DigitalServiceSource recordSource = HdmiRecordSource.ofDvb(...);
+ * TimerRecordSource source = ofDigitalSource(timerInfo, recordSource);
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+public class HdmiTimerRecordSources {
+ private static final String TAG = "HdmiTimerRecordingSources";
+
+ private HdmiTimerRecordSources() {}
+
+ /**
+ * Create {@link TimerRecordSource} for digital source which is used for <Set Digital
+ * Timer>.
+ *
+ * @param timerInfo timer info used for timer recording
+ * @param source digital source used for timer recording
+ * @return {@link TimerRecordSource}
+ * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null
+ */
+ public static TimerRecordSource ofDigitalSource(TimerInfo timerInfo,
+ DigitalServiceSource source) {
+ checkTimerRecordSourceInputs(timerInfo, source);
+ return new TimerRecordSource(timerInfo, source);
+ }
+
+ /**
+ * Create {@link TimerRecordSource} for analogue source which is used for <Set Analogue
+ * Timer>.
+ *
+ * @param timerInfo timer info used for timer recording
+ * @param source digital source used for timer recording
+ * @return {@link TimerRecordSource}
+ * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null
+ */
+ public static TimerRecordSource ofAnalogueSource(TimerInfo timerInfo,
+ AnalogueServiceSource source) {
+ checkTimerRecordSourceInputs(timerInfo, source);
+ return new TimerRecordSource(timerInfo, source);
+ }
+
+ /**
+ * Create {@link TimerRecordSource} for external plug which is used for <Set External
+ * Timer>.
+ *
+ * @param timerInfo timer info used for timer recording
+ * @param source digital source used for timer recording
+ * @return {@link TimerRecordSource}
+ * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null
+ */
+ public static TimerRecordSource ofExternalPlug(TimerInfo timerInfo, ExternalPlugData source) {
+ checkTimerRecordSourceInputs(timerInfo, source);
+ return new TimerRecordSource(timerInfo,
+ new ExternalSourceDecorator(source, EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG));
+ }
+
+ /**
+ * Create {@link TimerRecordSource} for external physical address which is used for <Set
+ * External Timer>.
+ *
+ * @param timerInfo timer info used for timer recording
+ * @param source digital source used for timer recording
+ * @return {@link TimerRecordSource}
+ * @throws IllegalArgumentException if {@code timerInfo} or {@code source} is null
+ */
+ public static TimerRecordSource ofExternalPhysicalAddress(TimerInfo timerInfo,
+ ExternalPhysicalAddress source) {
+ checkTimerRecordSourceInputs(timerInfo, source);
+ return new TimerRecordSource(timerInfo,
+ new ExternalSourceDecorator(source,
+ EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS));
+ }
+
+ private static void checkTimerRecordSourceInputs(TimerInfo timerInfo, RecordSource source) {
+ if (timerInfo == null) {
+ Log.w(TAG, "TimerInfo should not be null.");
+ throw new IllegalArgumentException("TimerInfo should not be null.");
+ }
+ if (source == null) {
+ Log.w(TAG, "source should not be null.");
+ throw new IllegalArgumentException("source should not be null.");
+ }
+ }
+
+ /**
+ * Create {@link Duration} for time value.
+ *
+ * @param hour hour in range of [0, 24]
+ * @param minute minute in range of [0, 60]
+ * @return {@link Duration}
+ * @throws IllegalArgumentException if hour or minute is out of range
+ */
+ public static Time ofTime(int hour, int minute) {
+ checkTimeValue(hour, minute);
+ return new Time(hour, minute);
+ }
+
+ private static void checkTimeValue(int hour, int minute) {
+ if (hour < 0 || hour > 24) {
+ throw new IllegalArgumentException("Hour should be in rage of [0, 24]:" + hour);
+ }
+ if (minute < 0 || minute > 60) {
+ throw new IllegalArgumentException("Minute should be in rage of [0, 60]:" + minute);
+ }
+ }
+
+ /**
+ * Create {@link Duration} for duration value.
+ *
+ * @param hour hour in range of [0, 90]
+ * @param minute minute in range of [0, 60]
+ * @return {@link Duration}
+ * @throws IllegalArgumentException if hour or minute is out of range
+ */
+ public static Duration ofDuration(int hour, int minute) {
+ checkDurationValue(hour, minute);
+ return new Duration(hour, minute);
+ }
+
+ private static void checkDurationValue(int hour, int minute) {
+ if (hour < 0 || hour > 99) {
+ throw new IllegalArgumentException("Hour should be in rage of [0, 99]:" + hour);
+ }
+ if (minute < 0 || minute > 60) {
+ throw new IllegalArgumentException("minute should be in rage of [0, 60]:" + minute);
+ }
+ }
+
+ private static class TimeUnit {
+ protected final int mHour;
+ protected final int mMinute;
+
+ protected TimeUnit(int hour, int minute) {
+ mHour = hour;
+ mMinute = minute;
+ }
+
+ protected int toByteArray(byte[] data, int index) {
+ data[index] = toBcdByte(mHour);
+ data[index + 1] = toBcdByte(mMinute);
+ return 2;
+ }
+
+ protected static byte toBcdByte(int value) {
+ int digitOfTen = (value / 10) % 10;
+ int digitOfOne = value % 10;
+ return (byte) ((digitOfTen << 4) | digitOfOne);
+ }
+ }
+
+ /**
+ * Place holder for time value.
+ */
+ public static class Time extends TimeUnit {
+ private Time(int hour, int minute) {
+ super(hour, minute);
+ }
+ }
+
+ /**
+ * Place holder for duration value.
+ */
+ public static class Duration extends TimeUnit {
+ private Duration(int hour, int minute) {
+ super(hour, minute);
+ }
+ }
+
+ /**
+ * Fields for recording sequence.
+ * The following can be merged by OR(|) operation.
+ */
+ public static final int RECORDING_SEQUENCE_REPEAT_ONCE_ONLY = 0;
+ public static final int RECORDING_SEQUENCE_REPEAT_SUNDAY = 1 << 0;
+ public static final int RECORDING_SEQUENCE_REPEAT_MONDAY = 1 << 1;
+ public static final int RECORDING_SEQUENCE_REPEAT_TUESDAY = 1 << 2;
+ public static final int RECORDING_SEQUENCE_REPEAT_WEDNESDAY = 1 << 3;
+ public static final int RECORDING_SEQUENCE_REPEAT_THURSDAY = 1 << 4;
+ public static final int RECORDING_SEQUENCE_REPEAT_FRIDAY = 1 << 5;
+ public static final int RECORDING_SEQUENCE_REPEAT_SATUREDAY = 1 << 6;
+
+ private static final int RECORDING_SEQUENCE_REPEAT_MASK =
+ (RECORDING_SEQUENCE_REPEAT_SUNDAY | RECORDING_SEQUENCE_REPEAT_MONDAY |
+ RECORDING_SEQUENCE_REPEAT_TUESDAY | RECORDING_SEQUENCE_REPEAT_WEDNESDAY |
+ RECORDING_SEQUENCE_REPEAT_THURSDAY | RECORDING_SEQUENCE_REPEAT_FRIDAY |
+ RECORDING_SEQUENCE_REPEAT_SATUREDAY);
+
+ /**
+ * Create {@link TimerInfo} with the given information.
+ *
+ * @param dayOfMonth day of month
+ * @param monthOfYear month of year
+ * @param startTime start time in {@link Time}
+ * @param duration duration in {@link Duration}
+ * @param recordingSequence recording sequence. Use RECORDING_SEQUENCE_REPEAT_ONCE_ONLY for no
+ * repeat. Otherwise use combination of {@link #RECORDING_SEQUENCE_REPEAT_SUNDAY},
+ * {@link #RECORDING_SEQUENCE_REPEAT_MONDAY},
+ * {@link #RECORDING_SEQUENCE_REPEAT_TUESDAY},
+ * {@link #RECORDING_SEQUENCE_REPEAT_WEDNESDAY},
+ * {@link #RECORDING_SEQUENCE_REPEAT_THURSDAY},
+ * {@link #RECORDING_SEQUENCE_REPEAT_FRIDAY},
+ * {@link #RECORDING_SEQUENCE_REPEAT_SATUREDAY}.
+ * @return {@link TimerInfo}.
+ * @throws IllegalArgumentException if input value is invalid
+ */
+ public static TimerInfo timerInfoOf(int dayOfMonth, int monthOfYear, Time startTime,
+ Duration duration, int recordingSequence) {
+ if (dayOfMonth < 0 || dayOfMonth > 31) {
+ throw new IllegalArgumentException(
+ "Day of month should be in range of [0, 31]:" + dayOfMonth);
+ }
+ if (monthOfYear < 1 || monthOfYear > 12) {
+ throw new IllegalArgumentException(
+ "Month of year should be in range of [1, 12]:" + monthOfYear);
+ }
+ checkTimeValue(startTime.mHour, startTime.mMinute);
+ checkDurationValue(duration.mHour, duration.mMinute);
+ // Recording sequence should use least 7 bits or no bits.
+ if ((recordingSequence != 0)
+ && ((recordingSequence & ~RECORDING_SEQUENCE_REPEAT_MASK) != 0)) {
+ throw new IllegalArgumentException(
+ "Invalid reecording sequence value:" + recordingSequence);
+ }
+
+ return new TimerInfo(dayOfMonth, monthOfYear, startTime, duration, recordingSequence);
+ }
+
+ /**
+ * Container basic timer information. It consists of the following fields.
+ * <ul>
+ * <li>[Day of Month]
+ * <li>[Month of Year]
+ * <li>[Start Time]
+ * <li>[Duration]
+ * <li>[Recording Sequence]
+ * </ul>
+ */
+ public static class TimerInfo {
+ private static final int DAY_OF_MONTH_SIZE = 1;
+ private static final int MONTH_OF_YEAR_SIZE = 1;
+ private static final int START_TIME_SIZE = 2; // 1byte for hour and 1byte for minute.
+ private static final int DURATION_SIZE = 2; // 1byte for hour and 1byte for minute.
+ private static final int RECORDING_SEQUENCE_SIZE = 1;
+ private static final int BASIC_INFO_SIZE = DAY_OF_MONTH_SIZE + MONTH_OF_YEAR_SIZE
+ + START_TIME_SIZE + DURATION_SIZE + RECORDING_SEQUENCE_SIZE;
+
+ /** Day of month. */
+ private final int mDayOfMonth;
+ /** Month of year. */
+ private final int mMonthOfYear;
+ /**
+ * Time of day.
+ * [Hour][Minute]. 0 <= Hour <= 24, 0 <= Minute <= 60 in BCD format.
+ */
+ private final Time mStartTime;
+ /**
+ * Duration. [Hour][Minute].
+ * 0 <= Hour <= 99, 0 <= Minute <= 60 in BCD format.
+ * */
+ private final Duration mDuration;
+ /**
+ * Indicates if recording is repeated and, if so, on which days. For repeated recordings,
+ * the recording sequence value is the bitwise OR of the days when recordings are required.
+ * [Recording Sequence] shall be set to 0x00 when the recording is not repeated. Bit 7 is
+ * reserved and shall be set to zero.
+ */
+ private final int mRecordingSequence;
+
+ private TimerInfo(int dayOfMonth, int monthOfYear, Time startTime,
+ Duration duration, int recordingSequence) {
+ mDayOfMonth = dayOfMonth;
+ mMonthOfYear = monthOfYear;
+ mStartTime = startTime;
+ mDuration = duration;
+ mRecordingSequence = recordingSequence;
+ }
+
+ int toByteArray(byte[] data, int index) {
+ // [Day of Month]
+ data[index] = (byte) mDayOfMonth;
+ index += DAY_OF_MONTH_SIZE;
+ // [Month of Year]
+ data[index] = (byte) mMonthOfYear;
+ index += MONTH_OF_YEAR_SIZE;
+ // [Start Time]
+ index += mStartTime.toByteArray(data, index);
+ index += mDuration.toByteArray(data, index);
+ // [Duration]
+ // [Recording Sequence]
+ data[index] = (byte) mRecordingSequence;
+ return getDataSize();
+ }
+
+ int getDataSize() {
+ return BASIC_INFO_SIZE;
+ }
+ }
+
+ /**
+ * Record source container for timer record. This is used to set parameter for <Set Digital
+ * Timer>, <Set Analogue Timer>, and <Set External Timer> message.
+ * <p>
+ * In order to create this from each source type, use one of helper method.
+ * <ul>
+ * <li>{@link #ofDigitalSource} for digital source
+ * <li>{@link #ofAnalogueSource} for analogue source
+ * <li>{@link #ofExternalPlug} for external plug type
+ * <li>{@link #ofExternalPhysicalAddress} for external physical address type.
+ * </ul>
+ */
+ public static class TimerRecordSource {
+ private final RecordSource mRecordSource;
+ private final TimerInfo mTimerInfo;
+
+ private TimerRecordSource(TimerInfo timerInfo, RecordSource recordSource) {
+ mTimerInfo = timerInfo;
+ mRecordSource = recordSource;
+ }
+
+ int getDataSize() {
+ return mTimerInfo.getDataSize() + mRecordSource.getDataSize(false);
+ }
+
+ int toByteArray(byte[] data, int index) {
+ // Basic infos including [Day of Month] [Month of Year] [Start Time] [Duration]
+ // [Recording Sequence]
+ index += mTimerInfo.toByteArray(data, index);
+ // [Record Source]
+ mRecordSource.toByteArray(false, data, index);
+ return getDataSize();
+ }
+ }
+
+ /**
+ * External source specifier types.
+ */
+ private static final int EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PLUG = 4;
+ private static final int EXTERNAL_SOURCE_SPECIFIER_EXTERNAL_PHYSICAL_ADDRESS = 5;
+
+ /**
+ * Decorator for external source. Beside digital or analogue source, external source starts with
+ * [External Source Specifier] because it covers both external plug type and external specifier.
+ */
+ private static class ExternalSourceDecorator extends RecordSource {
+ private final RecordSource mRecordSource;
+ private final int mExternalSourceSpecifier;
+
+ private ExternalSourceDecorator(RecordSource recordSource, int externalSourceSpecifier) {
+ // External source has one byte field for [External Source Specifier].
+ super(recordSource.mSourceType, recordSource.getDataSize(false) + 1);
+ mRecordSource = recordSource;
+ mExternalSourceSpecifier = externalSourceSpecifier;
+ }
+
+ @Override
+ int extraParamToByteArray(byte[] data, int index) {
+ data[index] = (byte) mExternalSourceSpecifier;
+ mRecordSource.toByteArray(false, data, index + 1);
+ return getDataSize(false);
+ }
+ }
+}
diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java
index 6d64a1d..c02ff8a 100644
--- a/core/java/android/hardware/hdmi/HdmiTvClient.java
+++ b/core/java/android/hardware/hdmi/HdmiTvClient.java
@@ -168,29 +168,56 @@
/**
* Set {@link RecordRequestListener} to hdmi control service.
*/
- public void setRecordRequestListener(RecordRequestListener listener) {
+ public void setOneTouchRecordRequestListener(RecordRequestListener listener) {
try {
- mService.setRecordRequestListener(getCallbackWrapper(listener));
+ mService.setOneTouchRecordRequestListener(getCallbackWrapper(listener));
} catch (RemoteException e) {
Log.e(TAG, "failed to set record request listener: ", e);
}
}
/**
- * Start recording with the given recorder address and recorder source.
- * <p>Usage
+ * Start one touch recording with the given recorder address and recorder source.
+ * <p>
+ * Usage
* <pre>
* HdmiTvClient tvClient = ....;
* // for own source.
* OwnSource ownSource = ownHdmiRecordSources.ownSource();
- * tvClient.startRecord(recorderAddress, ownSource);
+ * tvClient.startOneTouchRecord(recorderAddress, ownSource);
* </pre>
*/
- public void startRecord(int recorderAddress, HdmiRecordSources.RecordSource source) {
+ public void startOneTouchRecord(int recorderAddress, HdmiRecordSources.RecordSource source) {
try {
byte[] data = new byte[source.getDataSize(true)];
source.toByteArray(true, data, 0);
- mService.startRecord(recorderAddress, data);
+ mService.startOneTouchRecord(recorderAddress, data);
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to start record: ", e);
+ }
+ }
+
+ /**
+ * Start timer recording with the given recoder address and recorder source.
+ * <p>
+ * Usage
+ * <pre>
+ * HdmiTvClient tvClient = ....;
+ * // create timer info
+ * TimerInfo timerInfo = HdmiTimerRecourdSources.timerInfoOf(...);
+ * // for digital source.
+ * DigitalServiceSource recordSource = HdmiRecordSources.ofDigitalService(...);
+ * // create timer recording source.
+ * TimerRecordSource source = HdmiTimerRecourdSources.ofDigitalSource(timerInfo, recordSource);
+ * tvClient.startTimerRecording(recorderAddress, source);
+ * </pre>
+ */
+ public void startTimerRecording(int recorderAddress,
+ HdmiTimerRecordSources.TimerRecordSource source) {
+ try {
+ byte[] data = new byte[source.getDataSize()];
+ source.toByteArray(data, 0);
+ mService.startTimerRecording(recorderAddress, data);
} catch (RemoteException e) {
Log.e(TAG, "failed to start record: ", e);
}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 6f3763b..53b8b3f 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -55,13 +55,14 @@
void setArcMode(boolean enabled);
void setOption(int option, int value);
void setProhibitMode(boolean enabled);
- oneway void setSystemAudioVolume(int oldIndex, int newIndex, int maxIndex);
- oneway void setSystemAudioMute(boolean mute);
+ void setSystemAudioVolume(int oldIndex, int newIndex, int maxIndex);
+ void setSystemAudioMute(boolean mute);
void setInputChangeListener(IHdmiInputChangeListener listener);
List<HdmiCecDeviceInfo> getInputDevices();
void sendVendorCommand(int deviceType, int targetAddress, in byte[] params,
boolean hasVendorId);
void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType);
- void setRecordRequestListener(IHdmiRecordRequestListener listener);
- void startRecord(int recorderAddress, in byte[] recordSource);
+ void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener);
+ void startOneTouchRecord(int recorderAddress, in byte[] recordSource);
+ void startTimerRecording(int recorderAddress, in byte[] recordSource);
}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index aab7b1f..545b19a 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -346,10 +346,6 @@
public abstract void noteWifiBatchedScanStoppedLocked(long elapsedRealtime);
public abstract void noteWifiMulticastEnabledLocked(long elapsedRealtime);
public abstract void noteWifiMulticastDisabledLocked(long elapsedRealtime);
- public abstract void noteAudioTurnedOnLocked(long elapsedRealtime);
- public abstract void noteAudioTurnedOffLocked(long elapsedRealtime);
- public abstract void noteVideoTurnedOnLocked(long elapsedRealtime);
- public abstract void noteVideoTurnedOffLocked(long elapsedRealtime);
public abstract void noteActivityResumedLocked(long elapsedRealtime);
public abstract void noteActivityPausedLocked(long elapsedRealtime);
public abstract long getWifiRunningTime(long elapsedRealtimeUs, int which);
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index e7cdc4e..e5a5292 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -30,6 +30,7 @@
private final int id;
private final String tag;
private final String key;
+ private final String groupKey;
private final int uid;
private final String opPkg;
@@ -65,6 +66,7 @@
this.notification.setUser(user);
this.postTime = postTime;
this.key = key();
+ this.groupKey = groupKey();
}
public StatusBarNotification(Parcel in) {
@@ -84,12 +86,26 @@
this.notification.setUser(this.user);
this.postTime = in.readLong();
this.key = key();
+ this.groupKey = groupKey();
}
private String key() {
return user.getIdentifier() + "|" + pkg + "|" + id + "|" + tag + "|" + uid;
}
+ private String groupKey() {
+ final String group = getNotification().getGroup();
+ final String sortKey = getNotification().getSortKey();
+ if (group == null && sortKey == null) {
+ // a group of one
+ return key;
+ }
+ return user.getIdentifier() + "|" + pkg + "|" +
+ (group == null
+ ? "p:" + notification.priority
+ : "g:" + group);
+ }
+
public void writeToParcel(Parcel out, int flags) {
out.writeString(this.pkg);
out.writeString(this.opPkg);
@@ -240,4 +256,11 @@
public String getKey() {
return key;
}
+
+ /**
+ * A key that indicates the group with which this message ranks.
+ */
+ public String getGroupKey() {
+ return groupKey;
+ }
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index a61d771..5157c41 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -149,6 +149,12 @@
// boolean string as parsed by SystemProperties.getBoolean().
void setStrictModeVisualIndicatorPreference(String enabled);
+ /**
+ * Update the windowmanagers cached value of
+ * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled(null, userId)}
+ */
+ void updateScreenCaptureDisabled(int userId);
+
// These can only be called with the SET_ORIENTATION permission.
/**
* Update the current screen rotation based on the current state of
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index d91eb69..5655506 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -31,6 +31,8 @@
void noteStopVideo(int uid);
void noteStartAudio(int uid);
void noteStopAudio(int uid);
+ void noteResetVideo();
+ void noteResetAudio();
// Remaining methods are only used in Java.
byte[] getStatistics();
diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java
index a866ca7..229df8f 100644
--- a/core/java/com/android/internal/app/LocalePicker.java
+++ b/core/java/com/android/internal/app/LocalePicker.java
@@ -36,7 +36,6 @@
import android.widget.TextView;
import java.text.Collator;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -106,22 +105,21 @@
return constructAdapter(context, layoutId, fieldId, false /* disable pseudolocales */);
}
- public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
- final int layoutId, final int fieldId, final boolean isInDeveloperMode) {
+ public static List<LocaleInfo> getAllAssetLocales(Context context, boolean isInDeveloperMode) {
final Resources resources = context.getResources();
- String[] locales = Resources.getSystem().getAssets().getLocales();
+ final String[] locales = Resources.getSystem().getAssets().getLocales();
List<String> localeList = new ArrayList<String>(locales.length);
Collections.addAll(localeList, locales);
if (isInDeveloperMode) {
if (!localeList.contains("zz_ZZ")) {
localeList.add("zz_ZZ");
}
- /** - TODO: Enable when zz_ZY Pseudolocale is complete
- * if (!localeList.contains("zz_ZY")) {
- * localeList.add("zz_ZY");
- * }
- */
+ /** - TODO: Enable when zz_ZY Pseudolocale is complete
+ * if (!localeList.contains("zz_ZY")) {
+ * localeList.add("zz_ZY");
+ * }
+ */
}
Collections.sort(localeList);
@@ -179,6 +177,13 @@
}
Collections.sort(localeInfos);
+ return localeInfos;
+ }
+
+ public static ArrayAdapter<LocaleInfo> constructAdapter(Context context,
+ final int layoutId, final int fieldId, final boolean isInDeveloperMode) {
+ final List<LocaleInfo> localeInfos = getAllAssetLocales(context, isInDeveloperMode);
+
final LayoutInflater inflater =
(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
return new ArrayAdapter<LocaleInfo>(context, layoutId, fieldId, localeInfos) {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index dec94f2..aad1156 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -172,6 +172,8 @@
final ArrayList<StopwatchTimer> mWifiScanTimers = new ArrayList<StopwatchTimer>();
final SparseArray<ArrayList<StopwatchTimer>> mWifiBatchedScanTimers =
new SparseArray<ArrayList<StopwatchTimer>>();
+ final ArrayList<StopwatchTimer> mAudioTurnedOnTimers = new ArrayList<StopwatchTimer>();
+ final ArrayList<StopwatchTimer> mVideoTurnedOnTimers = new ArrayList<StopwatchTimer>();
// Last partial timers we use for distributing CPU usage.
final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<StopwatchTimer>();
@@ -256,10 +258,10 @@
boolean mPhoneOn;
StopwatchTimer mPhoneOnTimer;
- boolean mAudioOn;
+ int mAudioOnNesting;
StopwatchTimer mAudioOnTimer;
- boolean mVideoOn;
+ int mVideoOnNesting;
StopwatchTimer mVideoOnTimer;
boolean mFlashlightOn;
@@ -1472,6 +1474,13 @@
}
}
+ void stopAllRunningLocked(long elapsedRealtimeMs) {
+ if (mNesting > 0) {
+ mNesting = 1;
+ stopRunningLocked(elapsedRealtimeMs);
+ }
+ }
+
// Update the total time for all other running Timers with the same type as this Timer
// due to a change in timer count
private static long refreshTimersLocked(long batteryRealtime,
@@ -3187,27 +3196,29 @@
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
- if (!mAudioOn) {
+ if (mAudioOnNesting == 0) {
mHistoryCur.states |= HistoryItem.STATE_AUDIO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Audio on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
- mAudioOn = true;
mAudioOnTimer.startRunningLocked(elapsedRealtime);
}
+ mAudioOnNesting++;
getUidStatsLocked(uid).noteAudioTurnedOnLocked(elapsedRealtime);
}
public void noteAudioOffLocked(int uid) {
+ if (mAudioOnNesting == 0) {
+ return;
+ }
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
- if (mAudioOn) {
+ if (--mAudioOnNesting == 0) {
mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
- mAudioOn = false;
mAudioOnTimer.stopRunningLocked(elapsedRealtime);
}
getUidStatsLocked(uid).noteAudioTurnedOffLocked(elapsedRealtime);
@@ -3217,32 +3228,68 @@
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
- if (!mVideoOn) {
+ if (mVideoOnNesting == 0) {
mHistoryCur.states2 |= HistoryItem.STATE2_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video on to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
- mVideoOn = true;
mVideoOnTimer.startRunningLocked(elapsedRealtime);
}
+ mVideoOnNesting++;
getUidStatsLocked(uid).noteVideoTurnedOnLocked(elapsedRealtime);
}
public void noteVideoOffLocked(int uid) {
+ if (mVideoOnNesting == 0) {
+ return;
+ }
uid = mapUid(uid);
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
- if (mVideoOn) {
+ if (--mVideoOnNesting == 0) {
mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+ Integer.toHexString(mHistoryCur.states));
addHistoryRecordLocked(elapsedRealtime, uptime);
- mVideoOn = false;
mVideoOnTimer.stopRunningLocked(elapsedRealtime);
}
getUidStatsLocked(uid).noteVideoTurnedOffLocked(elapsedRealtime);
}
+ public void noteResetAudioLocked() {
+ if (mAudioOnNesting > 0) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ mAudioOnNesting = 0;
+ mHistoryCur.states &= ~HistoryItem.STATE_AUDIO_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Audio off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mAudioOnTimer.stopAllRunningLocked(elapsedRealtime);
+ for (int i=0; i<mUidStats.size(); i++) {
+ BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+ uid.noteResetAudioLocked(elapsedRealtime);
+ }
+ }
+ }
+
+ public void noteResetVideoLocked() {
+ if (mVideoOnNesting > 0) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ mAudioOnNesting = 0;
+ mHistoryCur.states2 &= ~HistoryItem.STATE2_VIDEO_ON_FLAG;
+ if (DEBUG_HISTORY) Slog.v(TAG, "Video off to: "
+ + Integer.toHexString(mHistoryCur.states));
+ addHistoryRecordLocked(elapsedRealtime, uptime);
+ mVideoOnTimer.stopAllRunningLocked(elapsedRealtime);
+ for (int i=0; i<mUidStats.size(); i++) {
+ BatteryStatsImpl.Uid uid = mUidStats.valueAt(i);
+ uid.noteResetVideoLocked(elapsedRealtime);
+ }
+ }
+ }
+
public void noteActivityResumedLocked(int uid) {
uid = mapUid(uid);
getUidStatsLocked(uid).noteActivityResumedLocked(SystemClock.elapsedRealtime());
@@ -3855,10 +3902,7 @@
boolean mWifiMulticastEnabled;
StopwatchTimer mWifiMulticastTimer;
- boolean mAudioTurnedOn;
StopwatchTimer mAudioTurnedOnTimer;
-
- boolean mVideoTurnedOn;
StopwatchTimer mVideoTurnedOnTimer;
StopwatchTimer mForegroundActivityTimer;
@@ -4073,52 +4117,48 @@
public StopwatchTimer createAudioTurnedOnTimerLocked() {
if (mAudioTurnedOnTimer == null) {
mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
- null, mOnBatteryTimeBase);
+ mAudioTurnedOnTimers, mOnBatteryTimeBase);
}
return mAudioTurnedOnTimer;
}
- @Override
public void noteAudioTurnedOnLocked(long elapsedRealtimeMs) {
- if (!mAudioTurnedOn) {
- mAudioTurnedOn = true;
- createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ createAudioTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ }
+
+ public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
+ if (mAudioTurnedOnTimer != null) {
+ mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
- @Override
- public void noteAudioTurnedOffLocked(long elapsedRealtimeMs) {
- if (mAudioTurnedOn) {
- mAudioTurnedOn = false;
- if (mAudioTurnedOnTimer != null) {
- mAudioTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
- }
+ public void noteResetAudioLocked(long elapsedRealtimeMs) {
+ if (mAudioTurnedOnTimer != null) {
+ mAudioTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
}
}
public StopwatchTimer createVideoTurnedOnTimerLocked() {
if (mVideoTurnedOnTimer == null) {
mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
- null, mOnBatteryTimeBase);
+ mVideoTurnedOnTimers, mOnBatteryTimeBase);
}
return mVideoTurnedOnTimer;
}
- @Override
public void noteVideoTurnedOnLocked(long elapsedRealtimeMs) {
- if (!mVideoTurnedOn) {
- mVideoTurnedOn = true;
- createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ createVideoTurnedOnTimerLocked().startRunningLocked(elapsedRealtimeMs);
+ }
+
+ public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
+ if (mVideoTurnedOnTimer != null) {
+ mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
}
}
- @Override
- public void noteVideoTurnedOffLocked(long elapsedRealtimeMs) {
- if (mVideoTurnedOn) {
- mVideoTurnedOn = false;
- if (mVideoTurnedOnTimer != null) {
- mVideoTurnedOnTimer.stopRunningLocked(elapsedRealtimeMs);
- }
+ public void noteResetVideoLocked(long elapsedRealtimeMs) {
+ if (mVideoTurnedOnTimer != null) {
+ mVideoTurnedOnTimer.stopAllRunningLocked(elapsedRealtimeMs);
}
}
@@ -4416,11 +4456,9 @@
}
if (mAudioTurnedOnTimer != null) {
active |= !mAudioTurnedOnTimer.reset(false);
- active |= mAudioTurnedOn;
}
if (mVideoTurnedOnTimer != null) {
active |= !mVideoTurnedOnTimer.reset(false);
- active |= mVideoTurnedOn;
}
if (mForegroundActivityTimer != null) {
active |= !mForegroundActivityTimer.reset(false);
@@ -4803,17 +4841,15 @@
} else {
mWifiMulticastTimer = null;
}
- mAudioTurnedOn = false;
if (in.readInt() != 0) {
mAudioTurnedOnTimer = new StopwatchTimer(Uid.this, AUDIO_TURNED_ON,
- null, mOnBatteryTimeBase, in);
+ mAudioTurnedOnTimers, mOnBatteryTimeBase, in);
} else {
mAudioTurnedOnTimer = null;
}
- mVideoTurnedOn = false;
if (in.readInt() != 0) {
mVideoTurnedOnTimer = new StopwatchTimer(Uid.this, VIDEO_TURNED_ON,
- null, mOnBatteryTimeBase, in);
+ mVideoTurnedOnTimers, mOnBatteryTimeBase, in);
} else {
mVideoTurnedOnTimer = null;
}
@@ -7554,11 +7590,9 @@
if (in.readInt() != 0) {
u.mWifiMulticastTimer.readSummaryFromParcelLocked(in);
}
- u.mAudioTurnedOn = false;
if (in.readInt() != 0) {
u.createAudioTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
}
- u.mVideoTurnedOn = false;
if (in.readInt() != 0) {
u.createVideoTurnedOnTimerLocked().readSummaryFromParcelLocked(in);
}
@@ -8071,9 +8105,9 @@
mBluetoothStateTimer[i] = new StopwatchTimer(null, -500-i,
null, mOnBatteryTimeBase, in);
}
- mAudioOn = false;
+ mAudioOnNesting = 0;
mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase);
- mVideoOn = false;
+ mVideoOnNesting = 0;
mVideoOnTimer = new StopwatchTimer(null, -8, null, mOnBatteryTimeBase);
mFlashlightOn = false;
mFlashlightOnTimer = new StopwatchTimer(null, -9, null, mOnBatteryTimeBase, in);
@@ -8125,6 +8159,8 @@
mWifiScanTimers.clear();
mWifiBatchedScanTimers.clear();
mWifiMulticastTimers.clear();
+ mAudioTurnedOnTimers.clear();
+ mVideoTurnedOnTimers.clear();
sNumSpeedSteps = in.readInt();
diff --git a/core/tests/coretests/src/android/net/NetworkKeyTest.java b/core/tests/coretests/src/android/net/NetworkKeyTest.java
deleted file mode 100644
index 9005188..0000000
--- a/core/tests/coretests/src/android/net/NetworkKeyTest.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.net;
-
-import android.os.Parcel;
-
-import junit.framework.TestCase;
-
-public class NetworkKeyTest extends TestCase {
- public void testValidWifiKey_utf8() {
- new WifiKey("\"quotedSsid\"", "AB:CD:01:EF:23:03");
- new WifiKey("\"\"", "AB:CD:01:EF:23:03");
- }
-
- public void testValidWifiKey_hex() {
- new WifiKey("0x1234abcd", "AB:CD:01:EF:23:03");
- }
-
- public void testInvalidWifiKey_empty() {
- try {
- new WifiKey("", "AB:CD:01:EF:23:03");
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected - empty SSID
- }
- }
-
- public void testInvalidWifiKey_unquotedUtf8() {
- try {
- new WifiKey("unquotedSsid", "AB:CD:01:EF:23:03");
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected - empty SSID
- }
- }
-
- public void testInvalidWifiKey_invalidHex() {
- try {
- new WifiKey("0x\"nothex\"", "AB:CD:01:EF:23:03");
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected - empty SSID
- }
- }
-
- public void testInvalidWifiKey_shortBssid() {
- try {
- new WifiKey("\"quotedSsid\"", "AB:CD:01:EF:23");
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected - BSSID too short
- }
- }
-
- public void testInvalidWifiKey_longBssid() {
- try {
- new WifiKey("\"quotedSsid\"", "AB:CD:01:EF:23:03:11");
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected - BSSID too long
- }
- }
-
- public void testParceling() {
- WifiKey wifiKey = new WifiKey("\"ssid\"", "00:00:00:00:00:00");
- NetworkKey networkKey = new NetworkKey(wifiKey);
- Parcel parcel = null;
- try {
- parcel = Parcel.obtain();
- parcel.writeParcelable(networkKey, 0);
- parcel.setDataPosition(0);
- networkKey = parcel.readParcelable(getClass().getClassLoader());
- } finally {
- if (parcel != null) {
- parcel.recycle();
- }
- }
-
- assertEquals(NetworkKey.TYPE_WIFI, networkKey.type);
- assertEquals("\"ssid\"", networkKey.wifiKey.ssid);
- assertEquals("00:00:00:00:00:00", networkKey.wifiKey.bssid);
- }
-}
diff --git a/core/tests/coretests/src/android/net/RssiCurveTest.java b/core/tests/coretests/src/android/net/RssiCurveTest.java
deleted file mode 100644
index d4438df..0000000
--- a/core/tests/coretests/src/android/net/RssiCurveTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.net;
-
-import junit.framework.TestCase;
-
-public class RssiCurveTest extends TestCase {
- public void testLookupScore_constantCurve() {
- RssiCurve curve = new RssiCurve(-100, 200, new byte[] { 10 });
- assertEquals(10, curve.lookupScore(-200));
- assertEquals(10, curve.lookupScore(-100));
- assertEquals(10, curve.lookupScore(0));
- assertEquals(10, curve.lookupScore(100));
- assertEquals(10, curve.lookupScore(200));
- }
-
- public void testLookupScore_changingCurve() {
- RssiCurve curve = new RssiCurve(-100, 100, new byte[] { -10, 10 });
- assertEquals(-10, curve.lookupScore(-200));
- assertEquals(-10, curve.lookupScore(-100));
- assertEquals(-10, curve.lookupScore(-50));
- assertEquals(10, curve.lookupScore(0));
- assertEquals(10, curve.lookupScore(50));
- assertEquals(10, curve.lookupScore(100));
- assertEquals(10, curve.lookupScore(200));
- }
-}
diff --git a/core/tests/coretests/src/android/net/ScoredNetworkTest.java b/core/tests/coretests/src/android/net/ScoredNetworkTest.java
deleted file mode 100644
index 7ab69ad..0000000
--- a/core/tests/coretests/src/android/net/ScoredNetworkTest.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.net;
-
-import android.os.Parcel;
-
-import junit.framework.TestCase;
-
-import java.util.Arrays;
-
-public class ScoredNetworkTest extends TestCase {
- private static final RssiCurve CURVE =
- new RssiCurve(-110, 10, new byte[] {0, 1, 2, 3, 4, 5, 6, 7});
-
- public void testInvalidCurve_nullBuckets() {
- try {
- new RssiCurve(-110, 10, null);
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected
- }
- }
-
- public void testInvalidCurve_emptyBuckets() {
- try {
- new RssiCurve(-110, 10, new byte[] {});
- fail("Should have thrown IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // expected
- }
- }
-
- public void testParceling() {
- NetworkKey key = new NetworkKey(new WifiKey("\"ssid\"", "00:00:00:00:00:00"));
- ScoredNetwork network = new ScoredNetwork(key, CURVE);
- Parcel parcel = null;
- try {
- parcel = Parcel.obtain();
- parcel.writeParcelable(network, 0);
- parcel.setDataPosition(0);
- network = parcel.readParcelable(getClass().getClassLoader());
- } finally {
- if (parcel != null) {
- parcel.recycle();
- }
- }
- assertEquals(CURVE.start, network.rssiCurve.start);
- assertEquals(CURVE.bucketWidth, network.rssiCurve.bucketWidth);
- assertTrue(Arrays.equals(CURVE.rssiBuckets, network.rssiCurve.rssiBuckets));
- }
-}
diff --git a/media/java/android/media/AudioAttributes.aidl b/media/java/android/media/AudioAttributes.aidl
new file mode 100644
index 0000000..04587f9
--- /dev/null
+++ b/media/java/android/media/AudioAttributes.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media;
+
+parcelable AudioAttributes;
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index b5cebe3..a95348f 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -213,6 +213,18 @@
public static final String KEY_I_FRAME_INTERVAL = "i-frame-interval";
/**
+ * A key describing the temporal layering schema. This is an optional parameter
+ * that applies only to video encoders. Use {@link MediaCodec#getInputFormat}
+ * after {@link MediaCodec#configure configure} to query if the encoder supports
+ * the desired schema. Supported values are {@code webrtc.vp8.1-layer},
+ * {@code webrtc.vp8.2-layer}, {@code webrtc.vp8.3-layer}, and {@code none}.
+ * If the encoder does not support temporal layering, the input format will
+ * not have an entry with this key.
+ * The associated value is a string.
+ */
+ public static final String KEY_TEMPORAL_LAYERING = "ts-schema";
+
+ /**
* @hide
*/
public static final String KEY_STRIDE = "stride";
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 7d1de24..7cda961 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -2220,7 +2220,9 @@
}
} else {
// We only know how to handle local and remote, fall back to local if not remote.
- session.setPlaybackToLocal(mPlaybackStream);
+ AudioAttributes.Builder bob = new AudioAttributes.Builder();
+ bob.setLegacyStreamType(mPlaybackStream);
+ session.setPlaybackToLocal(bob.build());
mSvp = null;
}
}
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index a92350b..d87a69e 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -16,6 +16,7 @@
package android.media.session;
import android.content.ComponentName;
+import android.media.AudioAttributes;
import android.media.MediaMetadata;
import android.media.routing.IMediaRouter;
import android.media.session.ISessionController;
@@ -42,6 +43,7 @@
void setRatingType(int type);
// These commands relate to volume handling
- void configureVolumeHandling(int type, int arg1, int arg2);
+ void setPlaybackToLocal(in AudioAttributes attributes);
+ void setPlaybackToRemote(int control, int max);
void setCurrentVolume(int currentVolume);
}
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 7fedd82..171f4c9 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
@@ -206,7 +207,7 @@
public @Nullable VolumeInfo getVolumeInfo() {
try {
ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
- return new VolumeInfo(result.volumeType, result.audioStream, result.controlType,
+ return new VolumeInfo(result.volumeType, result.audioAttrs, result.controlType,
result.maxVolume, result.currentVolume);
} catch (RemoteException e) {
@@ -216,8 +217,8 @@
}
/**
- * Set the volume of the stream or output this session is playing on. The
- * command will be ignored if it does not support
+ * Set the volume of the output this session is playing on. The command will
+ * be ignored if it does not support
* {@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}. The flags in
* {@link AudioManager} may be used to affect the handling.
*
@@ -234,8 +235,8 @@
}
/**
- * Adjust the volume of the stream or output this session is playing on. The
- * direction must be one of {@link AudioManager#ADJUST_LOWER},
+ * Adjust the volume of the output this session is playing on. The direction
+ * must be one of {@link AudioManager#ADJUST_LOWER},
* {@link AudioManager#ADJUST_RAISE}, or {@link AudioManager#ADJUST_SAME}.
* The command will be ignored if the session does not support
* {@link VolumeProvider#VOLUME_CONTROL_RELATIVE} or
@@ -570,17 +571,17 @@
*/
public static final class VolumeInfo {
private final int mVolumeType;
- private final int mAudioStream;
private final int mVolumeControl;
private final int mMaxVolume;
private final int mCurrentVolume;
+ private final AudioAttributes mAudioAttrs;
/**
* @hide
*/
- public VolumeInfo(int type, int stream, int control, int max, int current) {
+ public VolumeInfo(int type, AudioAttributes attrs, int control, int max, int current) {
mVolumeType = type;
- mAudioStream = stream;
+ mAudioAttrs = attrs;
mVolumeControl = control;
mMaxVolume = max;
mCurrentVolume = current;
@@ -600,14 +601,15 @@
}
/**
- * Get the stream this is currently controlling volume on. When the volume
- * type is {@link MediaSession#PLAYBACK_TYPE_REMOTE} this value does not
- * have meaning and should be ignored.
+ * Get the audio attributes for this session. The attributes will affect
+ * volume handling for the session. When the volume type is
+ * {@link MediaSession#PLAYBACK_TYPE_REMOTE} these may be ignored by the
+ * remote volume handler.
*
- * @return The stream this session is playing on.
+ * @return The attributes for this session.
*/
- public int getAudioStream() {
- return mAudioStream;
+ public AudioAttributes getAudioAttributes() {
+ return mAudioAttrs;
}
/**
@@ -679,7 +681,7 @@
public void onVolumeInfoChanged(ParcelableVolumeInfo pvi) {
MediaController controller = mController.get();
if (controller != null) {
- VolumeInfo info = new VolumeInfo(pvi.volumeType, pvi.audioStream, pvi.controlType,
+ VolumeInfo info = new VolumeInfo(pvi.volumeType, pvi.audioAttrs, pvi.controlType,
pvi.maxVolume, pvi.currentVolume);
controller.postMessage(MSG_UPDATE_VOLUME, info, null);
}
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 086cd23..6662303 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -22,6 +22,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
+import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
@@ -268,18 +269,22 @@
}
/**
- * Set the stream this session is playing on. This will affect the system's
- * volume handling for this session. If {@link #setPlaybackToRemote} was
- * previously called it will stop receiving volume commands and the system
- * will begin sending volume changes to the appropriate stream.
+ * Set the attributes for this session's audio. This will affect the
+ * system's volume handling for this session. If
+ * {@link #setPlaybackToRemote} was previously called it will stop receiving
+ * volume commands and the system will begin sending volume changes to the
+ * appropriate stream.
* <p>
- * By default sessions are on {@link AudioManager#STREAM_MUSIC}.
+ * By default sessions use attributes for media.
*
- * @param stream The {@link AudioManager} stream this session is playing on.
+ * @param attributes The {@link AudioAttributes} for this session's audio.
*/
- public void setPlaybackToLocal(int stream) {
+ public void setPlaybackToLocal(AudioAttributes attributes) {
+ if (attributes == null) {
+ throw new IllegalArgumentException("Attributes cannot be null for local playback.");
+ }
try {
- mBinder.configureVolumeHandling(PLAYBACK_TYPE_LOCAL, stream, 0);
+ mBinder.setPlaybackToLocal(attributes);
} catch (RemoteException e) {
Log.wtf(TAG, "Failure in setPlaybackToLocal.", e);
}
@@ -288,9 +293,10 @@
/**
* Configure this session to use remote volume handling. This must be called
* to receive volume button events, otherwise the system will adjust the
- * current stream volume for this session. If {@link #setPlaybackToLocal}
- * was previously called that stream will stop receiving volume changes for
- * this session.
+ * appropriate stream volume for this session. If
+ * {@link #setPlaybackToLocal} was previously called the system will stop
+ * handling volume changes for this session and pass them to the volume
+ * provider instead.
*
* @param volumeProvider The provider that will handle volume changes. May
* not be null.
@@ -308,7 +314,7 @@
});
try {
- mBinder.configureVolumeHandling(PLAYBACK_TYPE_REMOTE, volumeProvider.getVolumeControl(),
+ mBinder.setPlaybackToRemote(volumeProvider.getVolumeControl(),
volumeProvider.getMaxVolume());
} catch (RemoteException e) {
Log.wtf(TAG, "Failure in setPlaybackToRemote.", e);
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.java b/media/java/android/media/session/ParcelableVolumeInfo.java
index 166ccd3..e71b539 100644
--- a/media/java/android/media/session/ParcelableVolumeInfo.java
+++ b/media/java/android/media/session/ParcelableVolumeInfo.java
@@ -15,6 +15,7 @@
package android.media.session;
+import android.media.AudioAttributes;
import android.os.Parcel;
import android.os.Parcelable;
@@ -26,15 +27,16 @@
*/
public class ParcelableVolumeInfo implements Parcelable {
public int volumeType;
- public int audioStream;
+ public AudioAttributes audioAttrs;
public int controlType;
public int maxVolume;
public int currentVolume;
- public ParcelableVolumeInfo(int volumeType, int audioStream, int controlType, int maxVolume,
+ public ParcelableVolumeInfo(int volumeType, AudioAttributes audioAttrs, int controlType,
+ int maxVolume,
int currentVolume) {
this.volumeType = volumeType;
- this.audioStream = audioStream;
+ this.audioAttrs = audioAttrs;
this.controlType = controlType;
this.maxVolume = maxVolume;
this.currentVolume = currentVolume;
@@ -42,10 +44,10 @@
public ParcelableVolumeInfo(Parcel from) {
volumeType = from.readInt();
- audioStream = from.readInt();
controlType = from.readInt();
maxVolume = from.readInt();
currentVolume = from.readInt();
+ audioAttrs = AudioAttributes.CREATOR.createFromParcel(from);
}
@Override
@@ -56,10 +58,10 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(volumeType);
- dest.writeInt(audioStream);
dest.writeInt(controlType);
dest.writeInt(maxVolume);
dest.writeInt(currentVolume);
+ audioAttrs.writeToParcel(dest, flags);
}
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index e98c5c8..d2071ef 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.ContentUris;
@@ -120,6 +121,15 @@
}
/**
+ * Returns true, if {@code channelUri} is a channel URI for a passthrough TV input.
+ * @hide
+ */
+ @SystemApi
+ public static final boolean isChannelUriForPassthroughTvInput(Uri channelUri) {
+ return channelUri.toString().endsWith(PATH_PASSTHROUGH);
+ }
+
+ /**
* Builds a URI that points to a channel logo. See {@link Channels.Logo}.
*
* @param channelId The ID of the channel whose logo is pointed to.
@@ -1099,6 +1109,7 @@
* @return {@code true} if the genre is canonical, otherwise {@code false}.
* @hide
*/
+ @SystemApi
public static boolean isCanonical(String genre) {
return CANONICAL_GENRES.contains(genre);
}
diff --git a/media/java/android/media/tv/TvInputInfo.java b/media/java/android/media/tv/TvInputInfo.java
index 331283e..52c0534 100644
--- a/media/java/android/media/tv/TvInputInfo.java
+++ b/media/java/android/media/tv/TvInputInfo.java
@@ -16,6 +16,7 @@
package android.media.tv;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -28,6 +29,7 @@
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.hardware.hdmi.HdmiCecDeviceInfo;
+import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -40,6 +42,7 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.io.InputStream;
/**
* This class is used to specify meta information of a TV input.
@@ -107,6 +110,8 @@
private String mSetupActivity;
private String mSettingsActivity;
private int mType = TYPE_TUNER;
+ private String mLabel;
+ private Uri mIconUri;
static {
sHardwareTypeToTvInputType.put(TvInputHardwareInfo.TV_INPUT_TYPE_OTHER_HARDWARE,
@@ -134,7 +139,7 @@
throws XmlPullParserException, IOException {
return createTvInputInfo(context, service, generateInputIdForComponentName(
new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name)),
- null, TYPE_TUNER);
+ null, TYPE_TUNER, null, null);
}
/**
@@ -143,13 +148,21 @@
*
* @param service The ResolveInfo returned from the package manager about this TV input service.
* @param cecInfo The HdmiCecDeviceInfo for a HDMI CEC logical device.
+ * @param parentId The ID of this TV input's parent input. {@code null} if none exists.
+ * @param iconUri The {@link android.net.Uri} to load the icon image.
+ * {@see android.content.ContentResolver#openInputStream}. If it is null, the application
+ * icon of {@code service} will be loaded.
+ * @param label The label of this TvInputInfo. If it is null or empty, {@code service} label
+ * will be loaded.
* @hide
*/
+ @SystemApi
public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
- HdmiCecDeviceInfo cecInfo, String parentId) throws XmlPullParserException, IOException {
+ HdmiCecDeviceInfo cecInfo, String parentId, String label, Uri iconUri)
+ throws XmlPullParserException, IOException {
return createTvInputInfo(context, service, generateInputIdForHdmiCec(
new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
- cecInfo), parentId, TYPE_HDMI);
+ cecInfo), parentId, TYPE_HDMI, label, iconUri);
}
/**
@@ -158,18 +171,26 @@
*
* @param service The ResolveInfo returned from the package manager about this TV input service.
* @param hardwareInfo The TvInputHardwareInfo for a TV input hardware device.
+ * @param iconUri The {@link android.net.Uri} to load the icon image.
+ * {@see android.content.ContentResolver#openInputStream}. If it is null, the application
+ * icon of {@code service} will be loaded.
+ * @param label The label of this TvInputInfo. If it is null or empty, {@code service} label
+ * will be loaded.
* @hide
*/
+ @SystemApi
public static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
- TvInputHardwareInfo hardwareInfo) throws XmlPullParserException, IOException {
+ TvInputHardwareInfo hardwareInfo, String label, Uri iconUri)
+ throws XmlPullParserException, IOException {
int inputType = sHardwareTypeToTvInputType.get(hardwareInfo.getType(), TYPE_TUNER);
return createTvInputInfo(context, service, generateInputIdForHardware(
new ComponentName(service.serviceInfo.packageName, service.serviceInfo.name),
- hardwareInfo), null, inputType);
+ hardwareInfo), null, inputType, label, iconUri);
}
private static TvInputInfo createTvInputInfo(Context context, ResolveInfo service,
- String id, String parentId, int inputType) throws XmlPullParserException, IOException {
+ String id, String parentId, int inputType, String label, Uri iconUri)
+ throws XmlPullParserException, IOException {
ServiceInfo si = service.serviceInfo;
PackageManager pm = context.getPackageManager();
XmlResourceParser parser = null;
@@ -210,6 +231,8 @@
}
sa.recycle();
+ input.mLabel = label;
+ input.mIconUri = iconUri;
return input;
} catch (NameNotFoundException e) {
throw new XmlPullParserException("Unable to create context for: " + si.packageName);
@@ -331,7 +354,11 @@
* a label, its name is returned.
*/
public CharSequence loadLabel(Context context) {
- return mService.loadLabel(context.getPackageManager());
+ if (TextUtils.isEmpty(mLabel)) {
+ return mService.loadLabel(context.getPackageManager());
+ } else {
+ return mLabel;
+ }
}
/**
@@ -343,7 +370,19 @@
* returned.
*/
public Drawable loadIcon(Context context) {
- return mService.serviceInfo.loadIcon(context.getPackageManager());
+ if (mIconUri == null) {
+ return loadDefaultIcon(context);
+ }
+ try (InputStream is = context.getContentResolver().openInputStream(mIconUri)) {
+ Drawable drawable = Drawable.createFromStream(is, null);
+ if (drawable == null) {
+ return loadDefaultIcon(context);
+ }
+ return drawable;
+ } catch (IOException e) {
+ Log.w(TAG, "Loading the default icon due to a failure on loading " + mIconUri, e);
+ return loadDefaultIcon(context);
+ }
}
@Override
@@ -391,6 +430,12 @@
dest.writeString(mSetupActivity);
dest.writeString(mSettingsActivity);
dest.writeInt(mType);
+ dest.writeString(mIconUri == null ? null : mIconUri.toString());
+ dest.writeString(mLabel);
+ }
+
+ private Drawable loadDefaultIcon(Context context) {
+ return mService.serviceInfo.loadIcon(context.getPackageManager());
}
/**
@@ -452,5 +497,10 @@
mSetupActivity = in.readString();
mSettingsActivity = in.readString();
mType = in.readInt();
+ String mIconUriString = in.readString();
+ if (mIconUriString != null) {
+ mIconUri = Uri.parse(mIconUriString);
+ }
+ mLabel = in.readString();
}
}
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index 5235a53..5bed40b 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -126,6 +126,7 @@
* Interface used to receive the created session.
* @hide
*/
+ @SystemApi
public abstract static class SessionCallback {
/**
* This is called after {@link TvInputManager#createSession} has been processed.
@@ -214,6 +215,7 @@
* @param eventArgs Optional arguments of the event.
* @hide
*/
+ @SystemApi
public void onSessionEvent(Session session, String eventType, Bundle eventArgs) {
}
}
@@ -656,6 +658,7 @@
* @throws IllegalArgumentException if any of the arguments is {@code null}.
* @hide
*/
+ @SystemApi
public void createSession(String inputId, final SessionCallback callback,
Handler handler) {
if (inputId == null) {
@@ -683,6 +686,7 @@
* The Session provides the per-session functionality of TV inputs.
* @hide
*/
+ @SystemApi
public static final class Session {
static final int DISPATCH_IN_PROGRESS = -1;
static final int DISPATCH_NOT_HANDLED = 0;
@@ -707,7 +711,6 @@
private InputChannel mChannel;
private List<TvTrackInfo> mTracks;
- /** @hide */
private Session(IBinder token, InputChannel channel, ITvInputManager service, int userId,
int seq, SparseArray<SessionCallbackRecord> sessionCallbackRecordMap) {
mToken = token;
@@ -739,7 +742,6 @@
* Sets the {@link android.view.Surface} for this session.
*
* @param surface A {@link android.view.Surface} used to render video.
- * @hide
*/
public void setSurface(Surface surface) {
if (mToken == null) {
@@ -763,6 +765,7 @@
* @param height The new height of the {@link Surface}.
* @hide
*/
+ @SystemApi
public void dispatchSurfaceChanged(int format, int width, int height) {
if (mToken == null) {
Log.w(TAG, "The session has been already released");
diff --git a/media/java/android/media/tv/TvView.java b/media/java/android/media/tv/TvView.java
index 0d36e84..35dd71e 100644
--- a/media/java/android/media/tv/TvView.java
+++ b/media/java/android/media/tv/TvView.java
@@ -230,6 +230,7 @@
* @see TvInputService.Session#dispatchContentBlocked(TvContentRating)
* @hide
*/
+ @SystemApi
public void requestUnblockContent(TvContentRating unblockedRating) {
if (mSession != null) {
mSession.requestUnblockContent(unblockedRating);
@@ -622,6 +623,7 @@
* @param eventArgs Optional arguments of the event.
* @hide
*/
+ @SystemApi
public void onEvent(String inputId, String eventType, Bundle eventArgs) {
}
}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index cf2f5d3..416cd54 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -191,7 +191,7 @@
<!-- Notification title displayed when we fail to take a screenshot. [CHAR LIMIT=50] -->
<string name="screenshot_failed_title">Couldn\'t capture screenshot.</string>
<!-- Notification text displayed when we fail to take a screenshot. [CHAR LIMIT=100] -->
- <string name="screenshot_failed_text">Couldn\'t save screenshot. Storage may be in use.</string>
+ <string name="screenshot_failed_text">Can\'t take screenshot due to limited storage space, or it isn\'t allowed by the app or your organization.</string>
<!-- Title for the USB function chooser in UsbPreferenceActivity. [CHAR LIMIT=30] -->
<string name="usb_preference_title">USB file transfer options</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index ffb4dc1..2f88e21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -99,6 +99,7 @@
private QSPanel mQSPanel;
private final Rect mClipBounds = new Rect();
+ private final StatusIconClipper mStatusIconClipper = new StatusIconClipper();
public StatusBarHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -231,6 +232,7 @@
updateAvatarScale();
updateClockLp();
updateBatteryLevelPaddingEnd();
+ mStatusIconClipper.update();
}
}
@@ -445,7 +447,7 @@
mSystemIconsContainer.addView(systemIcons);
mStatusIcons = systemIcons.findViewById(R.id.statusIcons);
mSignalCluster = systemIcons.findViewById(R.id.signal_cluster);
- mSignalCluster.addOnLayoutChangeListener(mSignalClusterChanged);
+ mSignalCluster.addOnLayoutChangeListener(mStatusIconClipper);
}
public void onSystemIconsDetached() {
@@ -453,7 +455,7 @@
mStatusIcons.setVisibility(View.VISIBLE);
}
if (mSignalCluster != null) {
- mSignalCluster.removeOnLayoutChangeListener(mSignalClusterChanged);
+ mSignalCluster.removeOnLayoutChangeListener(mStatusIconClipper);
mSignalCluster.setVisibility(View.VISIBLE);
}
mStatusIcons = null;
@@ -470,6 +472,7 @@
updateMultiUserSwitch();
updateClickTargets();
updateBatteryLevelPaddingEnd();
+ mStatusIconClipper.update();
}
public void setUserInfoController(UserInfoController userInfoController) {
@@ -536,7 +539,7 @@
// here.
}
- private final OnLayoutChangeListener mSignalClusterChanged = new OnLayoutChangeListener() {
+ private final class StatusIconClipper implements OnLayoutChangeListener {
private final Rect mClipBounds = new Rect();
@Override
@@ -546,9 +549,14 @@
// they are mirrored to the real status bar.
mClipBounds.set(left, 0, mSystemIconsContainer.getWidth(),
mSystemIconsContainer.getHeight());
- mSystemIconsContainer.setClipBounds(mClipBounds);
+ update();
}
- };
+
+ public void update() {
+ final boolean onKeyguardAndCollapsed = mKeyguardShowing && !mExpanded;
+ mSystemIconsContainer.setClipBounds(onKeyguardAndCollapsed ? null : mClipBounds);
+ }
+ }
private final QSPanel.Callback mQsPanelCallback = new QSPanel.Callback() {
@Override
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2d6d09b..dcb4f18 100755
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8060,8 +8060,8 @@
boolean checkedGrants = false;
if (checkUser) {
// Looking for cross-user grants before enforcing the typical cross-users permissions
- int tmpTargetUserId = unsafeConvertIncomingUser(UserHandle.getUserId(callingUid));
- if (tmpTargetUserId != userId) {
+ int tmpTargetUserId = unsafeConvertIncomingUser(userId);
+ if (tmpTargetUserId != UserHandle.getUserId(callingUid)) {
if (checkAuthorityGrants(callingUid, cpi, tmpTargetUserId, checkUser)) {
return null;
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ac19bde..3fdeb54 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -413,6 +413,20 @@
}
}
+ public void noteResetAudio() {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteResetAudioLocked();
+ }
+ }
+
+ public void noteResetVideo() {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteResetVideoLocked();
+ }
+ }
+
public void noteFlashlightOn() {
enforceCallingPermission();
synchronized (mStats) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 560ee84..95cd7ef 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1013,12 +1013,17 @@
}
@Override
- public void setRecordRequestListener(IHdmiRecordRequestListener listener) {
+ public void setOneTouchRecordRequestListener(IHdmiRecordRequestListener listener) {
// TODO: implement this.
}
@Override
- public void startRecord(int recorderAddress, byte[] recordSource) {
+ public void startOneTouchRecord(int recorderAddress, byte[] recordSource) {
+ // TODO: implement this.
+ }
+
+ @Override
+ public void startTimerRecording(int recorderAddress, byte[] recordSource) {
// TODO: implement this.
}
}
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index ede3dab..4304b59 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -31,6 +31,7 @@
import android.media.session.MediaSessionInfo;
import android.media.session.PlaybackState;
import android.media.session.ParcelableVolumeInfo;
+import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.Rating;
@@ -105,9 +106,9 @@
// End TransportPerformer fields
// Volume handling fields
+ private AudioAttributes mAudioAttrs;
private AudioManager mAudioManager;
private int mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL;
- private int mAudioStream = AudioManager.STREAM_MUSIC;
private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
private int mMaxVolume = 0;
private int mCurrentVolume = 0;
@@ -131,6 +132,7 @@
mService = service;
mHandler = new MessageHandler(handler.getLooper());
mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
+ mAudioAttrs = new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_MEDIA).build();
}
/**
@@ -219,7 +221,8 @@
direction = -1;
}
if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
- mAudioManager.adjustStreamVolume(mAudioStream, direction, flags);
+ int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
+ mAudioManager.adjustStreamVolume(stream, direction, flags);
} else {
if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
// Nothing to do, the volume cannot be changed
@@ -245,7 +248,8 @@
public void setVolumeTo(int value, int flags) {
if (mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL) {
- mAudioManager.setStreamVolume(mAudioStream, value, flags);
+ int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
+ mAudioManager.setStreamVolume(stream, value, flags);
} else {
if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
// Nothing to do. The volume can't be set directly.
@@ -315,8 +319,8 @@
*
* @return The audio stream the session is using.
*/
- public int getAudioStream() {
- return mAudioStream;
+ public AudioAttributes getAudioAttributes() {
+ return mAudioAttrs;
}
/**
@@ -613,36 +617,34 @@
}
@Override
- public void configureVolumeHandling(int type, int arg1, int arg2) throws RemoteException {
- boolean typeChanged = type != mVolumeType;
- switch(type) {
- case MediaSession.PLAYBACK_TYPE_LOCAL:
- mVolumeType = type;
- int audioStream = arg1;
- if (isValidStream(audioStream)) {
- mAudioStream = audioStream;
- } else {
- Log.e(TAG, "Cannot set stream to " + audioStream + ". Using music stream");
- mAudioStream = AudioManager.STREAM_MUSIC;
- }
- break;
- case MediaSession.PLAYBACK_TYPE_REMOTE:
- mVolumeType = type;
- mVolumeControlType = arg1;
- mMaxVolume = arg2;
- break;
- default:
- throw new IllegalArgumentException("Volume handling type " + type
- + " not recognized.");
+ public void setPlaybackToLocal(AudioAttributes attributes) {
+ boolean typeChanged;
+ synchronized (mLock) {
+ typeChanged = mVolumeType == MediaSession.PLAYBACK_TYPE_REMOTE;
+ mVolumeType = MediaSession.PLAYBACK_TYPE_LOCAL;
+ if (attributes != null) {
+ mAudioAttrs = attributes;
+ } else {
+ Log.e(TAG, "Received null audio attributes, using existing attributes");
+ }
}
if (typeChanged) {
mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this);
}
}
- private boolean isValidStream(int stream) {
- return stream >= AudioManager.STREAM_VOICE_CALL
- && stream <= AudioManager.STREAM_NOTIFICATION;
+ @Override
+ public void setPlaybackToRemote(int control, int max) {
+ boolean typeChanged;
+ synchronized (mLock) {
+ typeChanged = mVolumeType == MediaSession.PLAYBACK_TYPE_LOCAL;
+ mVolumeType = MediaSession.PLAYBACK_TYPE_REMOTE;
+ mVolumeControlType = control;
+ mMaxVolume = max;
+ }
+ if (typeChanged) {
+ mService.onSessionPlaybackTypeChanged(MediaSessionRecord.this);
+ }
}
}
@@ -822,11 +824,12 @@
current = mOptimisticVolume != -1 ? mOptimisticVolume
: mCurrentVolume;
} else {
+ int stream = AudioAttributes.toLegacyStreamType(mAudioAttrs);
type = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
- max = mAudioManager.getStreamMaxVolume(mAudioStream);
- current = mAudioManager.getStreamVolume(mAudioStream);
+ max = mAudioManager.getStreamMaxVolume(stream);
+ current = mAudioManager.getStreamVolume(stream);
}
- return new ParcelableVolumeInfo(mVolumeType, mAudioStream, type, max, current);
+ return new ParcelableVolumeInfo(mVolumeType, mAudioAttrs, type, max, current);
}
}
diff --git a/services/core/java/com/android/server/notification/GroupedNotificationComparator.java b/services/core/java/com/android/server/notification/GroupedNotificationComparator.java
new file mode 100644
index 0000000..608d55c
--- /dev/null
+++ b/services/core/java/com/android/server/notification/GroupedNotificationComparator.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * Sorts notifications, accounting for groups and sort keys.
+ */
+public class GroupedNotificationComparator extends NotificationComparator {
+ private static final String TAG = "GroupedNotificationComparator";
+
+ @Override
+ public int compare(NotificationRecord left, NotificationRecord right) {
+ // "recently intrusive" is an ad hoc group that temporarily claims noisy notifications
+ if (left.isRecentlyIntrusive() != right.isRecentlyIntrusive()) {
+ return left.isRecentlyIntrusive() ? -1 : 1;
+ }
+
+ final NotificationRecord leftProxy = left.getRankingProxy();
+ if (leftProxy == null) {
+ throw new RuntimeException("left proxy cannot be null: " + left.getKey());
+ }
+ final NotificationRecord rightProxy = right.getRankingProxy();
+ if (rightProxy == null) {
+ throw new RuntimeException("right proxy cannot be null: " + right.getKey());
+ }
+ final String leftSortKey = left.getNotification().getSortKey();
+ final String rightSortKey = right.getNotification().getSortKey();
+ if (leftProxy != rightProxy) {
+ // between groups, compare proxies
+ return Integer.compare(leftProxy.getAuthoritativeRank(),
+ rightProxy.getAuthoritativeRank());
+ } else if (TextUtils.isEmpty(leftSortKey) || TextUtils.isEmpty(rightSortKey)) {
+ // missing sort keys, use prior rank
+ return Integer.compare(left.getAuthoritativeRank(),
+ right.getAuthoritativeRank());
+ } else {
+ // use sort keys within group
+ return leftSortKey.compareTo(rightSortKey);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/notification/NotificationComparator.java b/services/core/java/com/android/server/notification/NotificationComparator.java
index 0546a55..ec81fd2 100644
--- a/services/core/java/com/android/server/notification/NotificationComparator.java
+++ b/services/core/java/com/android/server/notification/NotificationComparator.java
@@ -18,35 +18,35 @@
import java.util.Comparator;
/**
- * Sorts notificaitons into attention-relelvant order.
+ * Sorts notifications individually into attention-relelvant order.
*/
public class NotificationComparator
implements Comparator<NotificationRecord> {
@Override
- public int compare(NotificationRecord lhs, NotificationRecord rhs) {
- if (lhs.isRecentlyIntrusive() != rhs.isRecentlyIntrusive()) {
- return lhs.isRecentlyIntrusive() ? -1 : 1;
- }
- final int leftPackagePriority = lhs.getPackagePriority();
- final int rightPackagePriority = rhs.getPackagePriority();
+ public int compare(NotificationRecord left, NotificationRecord right) {
+ final int leftPackagePriority = left.getPackagePriority();
+ final int rightPackagePriority = right.getPackagePriority();
if (leftPackagePriority != rightPackagePriority) {
// by priority, high to low
return -1 * Integer.compare(leftPackagePriority, rightPackagePriority);
}
- final int leftScore = lhs.sbn.getScore();
- final int rightScore = rhs.sbn.getScore();
+
+ final int leftScore = left.sbn.getScore();
+ final int rightScore = right.sbn.getScore();
if (leftScore != rightScore) {
// by priority, high to low
return -1 * Integer.compare(leftScore, rightScore);
}
- final float leftPeple = lhs.getContactAffinity();
- final float rightPeople = rhs.getContactAffinity();
- if (leftPeple != rightPeople) {
+
+ final float leftPeople = left.getContactAffinity();
+ final float rightPeople = right.getContactAffinity();
+ if (leftPeople != rightPeople) {
// by contact proximity, close to far
- return -1 * Float.compare(leftPeple, rightPeople);
+ return -1 * Float.compare(leftPeople, rightPeople);
}
+
// then break ties by time, most recent first
- return -1 * Long.compare(lhs.getRankingTimeMs(), rhs.getRankingTimeMs());
+ return -1 * Long.compare(left.getRankingTimeMs(), right.getRankingTimeMs());
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 088b813..57f3e2d 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -21,6 +21,7 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.service.notification.StatusBarNotification;
+import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
import java.lang.reflect.Array;
@@ -60,7 +61,12 @@
public boolean isUpdate;
private int mPackagePriority;
- NotificationRecord(StatusBarNotification sbn, int score)
+ // The record that ranking should use for comparisons outside the group.
+ private NotificationRecord mRankingProxy;
+ private int mAuthoritativeRank;
+
+ @VisibleForTesting
+ public NotificationRecord(StatusBarNotification sbn, int score)
{
this.sbn = sbn;
this.score = score;
@@ -73,7 +79,9 @@
mRecentlyIntrusive = previous.mRecentlyIntrusive;
mPackagePriority = previous.mPackagePriority;
mIntercept = previous.mIntercept;
+ mRankingProxy = previous.mRankingProxy;
mRankingTimeMs = calculateRankingTimeMs(previous.getRankingTimeMs());
+ // Don't copy mGroupKey, recompute it, in case it has changed
}
public Notification getNotification() { return sbn.getNotification(); }
@@ -89,6 +97,7 @@
+ " / " + idDebugString(baseContext, sbn.getPackageName(), notification.icon));
pw.println(prefix + " pri=" + notification.priority + " score=" + sbn.getScore());
pw.println(prefix + " key=" + sbn.getKey());
+ pw.println(prefix + " groupKey=" + getGroupKey());
pw.println(prefix + " contentIntent=" + notification.contentIntent);
pw.println(prefix + " deleteIntent=" + notification.deleteIntent);
pw.println(prefix + " tickerText=" + notification.tickerText);
@@ -145,6 +154,7 @@
pw.println(prefix + " mRecentlyIntrusive=" + mRecentlyIntrusive);
pw.println(prefix + " mPackagePriority=" + mPackagePriority);
pw.println(prefix + " mIntercept=" + mIntercept);
+ pw.println(prefix + " mRankingProxy=" + getRankingProxy().getKey());
pw.println(prefix + " mRankingTimeMs=" + mRankingTimeMs);
}
@@ -241,4 +251,24 @@
}
return sbn.getPostTime();
}
+
+ public NotificationRecord getRankingProxy() {
+ return mRankingProxy;
+ }
+
+ public void setRankingProxy(NotificationRecord proxy) {
+ mRankingProxy = proxy;
+ }
+
+ public void setAuthoritativeRank(int authoritativeRank) {
+ mAuthoritativeRank = authoritativeRank;
+ }
+
+ public int getAuthoritativeRank() {
+ return mAuthoritativeRank;
+ }
+
+ public String getGroupKey() {
+ return sbn.getGroupKey();
+ }
}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index fc03c17..d59e17b 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -17,12 +17,12 @@
import android.app.Notification;
import android.content.Context;
+import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.ArrayMap;
-import android.util.Log;
import android.util.Slog;
import android.util.SparseIntArray;
import org.xmlpull.v1.XmlPullParser;
@@ -49,13 +49,13 @@
private static final String ATT_UID = "uid";
private static final String ATT_PRIORITY = "priority";
- private static final String VALUE_HIGH = "high";
-
private final NotificationSignalExtractor[] mSignalExtractors;
- private final NotificationComparator mRankingComparator = new NotificationComparator();
+ private final NotificationComparator mPreliminaryComparator = new NotificationComparator();
+ private final NotificationComparator mFinalComparator = new GroupedNotificationComparator();
// Package name to uid, to priority. Would be better as Table<String, Int, Int>
private final ArrayMap<String, SparseIntArray> mPackagePriorities;
+ private final ArrayMap<String, NotificationRecord> mProxyByGroupTmp;
private final Context mContext;
private final Handler mRankingHandler;
@@ -83,6 +83,7 @@
Slog.w(TAG, "Problem accessing extractor " + extractorNames[i] + ".", e);
}
}
+ mProxyByGroupTmp = new ArrayMap<String, NotificationRecord>();
}
public void extractSignals(NotificationRecord r) {
@@ -166,11 +167,39 @@
}
public void sort(ArrayList<NotificationRecord> notificationList) {
- Collections.sort(notificationList, mRankingComparator);
+ final int N = notificationList.size();
+ // clear group proxies
+ for (int i = N - 1; i >= 0; i--) {
+ notificationList.get(i).setRankingProxy(null);
+ }
+
+ // rank each record individually
+ Collections.sort(notificationList, mPreliminaryComparator);
+
+ // record inidivdual ranking result and nominate proxies for each group
+ for (int i = N - 1; i >= 0; i--) {
+ final NotificationRecord record = notificationList.get(i);
+ record.setAuthoritativeRank(i);
+ final String groupKey = record.getGroupKey();
+ boolean isGroupSummary = record.getNotification().getGroup() != null
+ && (record.getNotification().flags & Notification.FLAG_GROUP_SUMMARY) != 0;
+ if (isGroupSummary || mProxyByGroupTmp.get(groupKey) == null) {
+ mProxyByGroupTmp.put(groupKey, record);
+ }
+ }
+ // assign nominated proxies to each notification
+ for (int i = 0; i < N; i++) {
+ final NotificationRecord record = notificationList.get(i);
+ record.setRankingProxy(mProxyByGroupTmp.get(record.getGroupKey()));
+ }
+ // Do a second ranking pass, using group proxies
+ Collections.sort(notificationList, mFinalComparator);
+
+ mProxyByGroupTmp.clear();
}
public int indexOf(ArrayList<NotificationRecord> notificationList, NotificationRecord target) {
- return Collections.binarySearch(notificationList, target, mRankingComparator);
+ return Collections.binarySearch(notificationList, target, mFinalComparator);
}
private static int safeInt(XmlPullParser parser, String att, int defValue) {
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 01aaca0..d7ecd7a 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -993,7 +993,10 @@
synchronized (mLock) {
try {
getSessionLocked(sessionToken, callingUid, resolvedUserId).tune(channelUri);
-
+ if (TvContract.isChannelUriForPassthroughTvInput(channelUri)) {
+ // Do not log the watch history for passthrough inputs.
+ return;
+ }
long currentTime = System.currentTimeMillis();
long channelId = ContentUris.parseId(channelUri);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index aae2bb8..eac2819 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -19,6 +19,7 @@
import static android.view.WindowManager.LayoutParams.*;
import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import android.app.admin.DevicePolicyManager;
import android.app.AppOpsManager;
import android.util.ArraySet;
import android.util.TimeUtils;
@@ -425,6 +426,13 @@
*/
WindowState[] mRebuildTmp = new WindowState[20];
+ /**
+ * Stores for each user whether screencapture is disabled
+ * This array is essentially a cache for all userId for
+ * {@link android.app.admin.DevicePolicyManager#getScreenCaptureDisabled(null, userId)}
+ */
+ SparseArray<Boolean> mScreenCaptureDisabled = new SparseArray<Boolean>();
+
IInputMethodManager mInputMethodManager;
AccessibilityController mAccessibilityController;
@@ -2439,6 +2447,45 @@
return res;
}
+ /**
+ * Returns whether screen capture is disabled for all windows of a specific user.
+ */
+ boolean isScreenCaptureDisabledLocked(int userId) {
+ Boolean disabled = mScreenCaptureDisabled.get(userId);
+ if (disabled != null) {
+ return disabled;
+ }
+
+ // mScreenCaptureDisabled not set yet, try to update it.
+ updateScreenCaptureDisabledLocked(userId);
+ disabled = mScreenCaptureDisabled.get(userId);
+ if (disabled == null) {
+ // Not able to update, return false by default.
+ return false;
+ } else {
+ return disabled;
+ }
+ }
+
+ /**
+ * Update mScreenCaptureDisabled for specific user according to the device policy manager.
+ */
+ @Override
+ public void updateScreenCaptureDisabled(int userId) {
+ mH.sendMessage(mH.obtainMessage(H.UPDATE_SCRN_CAP, userId, 0 /* unused argument */));
+ }
+
+ void updateScreenCaptureDisabledLocked(int userId) {
+ DevicePolicyManager dpm = (DevicePolicyManager) mContext
+ .getSystemService(Context.DEVICE_POLICY_SERVICE);
+ if (dpm != null) {
+ boolean disabled = dpm.getScreenCaptureDisabled(null, userId);
+ mScreenCaptureDisabled.put(userId, disabled);
+ } else {
+ Slog.e(TAG, "Could not get DevicePolicyManager.");
+ }
+ }
+
public void removeWindow(Session session, IWindow client) {
synchronized(mWindowMap) {
WindowState win = windowForClientLocked(session, client, false);
@@ -7203,6 +7250,8 @@
public static final int NEW_ANIMATOR_SCALE = 34;
+ public static final int UPDATE_SCRN_CAP = 35;
+
@Override
public void handleMessage(Message msg) {
if (DEBUG_WINDOW_TRACE) {
@@ -7677,6 +7726,13 @@
}
}
break;
+
+ case UPDATE_SCRN_CAP: {
+ synchronized (mWindowMap) {
+ updateScreenCaptureDisabledLocked(msg.arg1);
+ }
+ }
+ break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG, "handleMessage: exit");
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 49d4ae9..f3afe82 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -39,6 +39,7 @@
import android.graphics.RectF;
import android.graphics.Region;
import android.os.Debug;
+import android.os.UserHandle;
import android.util.Slog;
import android.view.Display;
import android.view.DisplayInfo;
@@ -690,6 +691,10 @@
flags |= SurfaceControl.SECURE;
}
+ if (mService.isScreenCaptureDisabledLocked(UserHandle.getUserId(mWin.mOwnerUid))) {
+ flags |= SurfaceControl.SECURE;
+ }
+
int width;
int height;
if ((attrs.flags & LayoutParams.FLAG_SCALED) != 0) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5c661af..e0612eb 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -258,6 +258,7 @@
private static final String TAG_DISABLE_KEYGUARD_FEATURES = "disable-keyguard-features";
private static final String TAG_DISABLE_CAMERA = "disable-camera";
private static final String TAG_DISABLE_CALLER_ID = "disable-caller-id";
+ private static final String TAG_DISABLE_SCREEN_CAPTURE = "disable-screen-capture";
private static final String TAG_DISABLE_ACCOUNT_MANAGEMENT = "disable-account-management";
private static final String TAG_ACCOUNT_TYPE = "account-type";
private static final String TAG_ENCRYPTION_REQUESTED = "encryption-requested";
@@ -326,6 +327,8 @@
boolean encryptionRequested = false;
boolean disableCamera = false;
boolean disableCallerId = false;
+ boolean disableScreenCapture = false; // Can only be set by a device/profile owner.
+
Set<String> accountTypesWithManagementDisabled = new HashSet<String>();
// TODO: review implementation decisions with frameworks team
@@ -443,6 +446,11 @@
out.attribute(null, ATTR_VALUE, Boolean.toString(disableCallerId));
out.endTag(null, TAG_DISABLE_CALLER_ID);
}
+ if (disableScreenCapture) {
+ out.startTag(null, TAG_DISABLE_SCREEN_CAPTURE);
+ out.attribute(null, ATTR_VALUE, Boolean.toString(disableScreenCapture));
+ out.endTag(null, TAG_DISABLE_SCREEN_CAPTURE);
+ }
if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) {
out.startTag(null, TAG_DISABLE_KEYGUARD_FEATURES);
out.attribute(null, ATTR_VALUE, Integer.toString(disabledKeyguardFeatures));
@@ -528,6 +536,9 @@
} else if (TAG_DISABLE_CALLER_ID.equals(tag)) {
disableCallerId = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_VALUE));
+ } else if (TAG_DISABLE_SCREEN_CAPTURE.equals(tag)) {
+ disableScreenCapture = Boolean.parseBoolean(
+ parser.getAttributeValue(null, ATTR_VALUE));
} else if (TAG_DISABLE_KEYGUARD_FEATURES.equals(tag)) {
disabledKeyguardFeatures = Integer.parseInt(
parser.getAttributeValue(null, ATTR_VALUE));
@@ -606,6 +617,8 @@
pw.println(disableCamera);
pw.print(prefix); pw.print("disableCallerId=");
pw.println(disableCallerId);
+ pw.print(prefix); pw.print("disableScreenCapture=");
+ pw.println(disableScreenCapture);
pw.print(prefix); pw.print("disabledKeyguardFeatures=");
pw.println(disabledKeyguardFeatures);
}
@@ -2977,6 +2990,58 @@
private void setEncryptionRequested(boolean encrypt) {
}
+
+ /**
+ * Set whether the screen capture is disabled for the user managed by the specified admin.
+ */
+ public void setScreenCaptureDisabled(ComponentName who, int userHandle, boolean disabled) {
+ if (!mHasFeature) {
+ return;
+ }
+ synchronized (this) {
+ if (who == null) {
+ throw new NullPointerException("ComponentName is null");
+ }
+ ActiveAdmin ap = getActiveAdminForCallerLocked(who,
+ DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
+ if (ap.disableScreenCapture != disabled) {
+ ap.disableScreenCapture = disabled;
+ saveSettingsLocked(userHandle);
+ try {
+ getWindowManager().updateScreenCaptureDisabled(userHandle);
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Unable to notify WindowManager.", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns whether or not screen capture is disabled for a given admin, or disabled for any
+ * active admin (if given admin is null).
+ */
+ public boolean getScreenCaptureDisabled(ComponentName who, int userHandle) {
+ if (!mHasFeature) {
+ return false;
+ }
+ synchronized (this) {
+ if (who != null) {
+ ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
+ return (admin != null) ? admin.disableScreenCapture : false;
+ }
+
+ DevicePolicyData policy = getUserData(userHandle);
+ final int N = policy.mAdminList.size();
+ for (int i = 0; i < N; i++) {
+ ActiveAdmin admin = policy.mAdminList.get(i);
+ if (admin.disableScreenCapture) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
/**
* The system property used to share the state of the camera. The native camera service
* is expected to read this property and act accordingly.
diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
new file mode 100644
index 0000000..3cc04e8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.notification;
+
+import android.app.Notification;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.ArrayList;
+
+public class RankingHelperTest extends AndroidTestCase {
+
+ private Notification mNotiGroupGSortA;
+ private Notification mNotiGroupGSortB;
+ private Notification mNotiNoGroup;
+ private Notification mNotiNoGroup2;
+ private Notification mNotiNoGroupSortA;
+ private NotificationRecord mRecordGroupGSortA;
+ private NotificationRecord mRecordGroupGSortB;
+ private NotificationRecord mRecordNoGroup;
+ private NotificationRecord mRecordNoGroup2;
+ private NotificationRecord mRecordNoGroupSortA;
+ private RankingHelper mHelper;
+
+ @Override
+ public void setUp() {
+ UserHandle user = UserHandle.ALL;
+
+ mHelper = new RankingHelper(getContext(), null, new String[0]);
+
+ mNotiGroupGSortA = new Notification.Builder(getContext())
+ .setContentTitle("A")
+ .setGroup("G")
+ .setSortKey("A")
+ .setWhen(1205)
+ .build();
+ mRecordGroupGSortA = new NotificationRecord(new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortA, user), 0);
+
+ mNotiGroupGSortB = new Notification.Builder(getContext())
+ .setContentTitle("B")
+ .setGroup("G")
+ .setSortKey("B")
+ .setWhen(1200)
+ .build();
+ mRecordGroupGSortB = new NotificationRecord(new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortB, user), 0);
+
+ mNotiNoGroup = new Notification.Builder(getContext())
+ .setContentTitle("C")
+ .setWhen(1201)
+ .build();
+ mRecordNoGroup = new NotificationRecord(new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiNoGroup, user), 0);
+
+ mNotiNoGroup2 = new Notification.Builder(getContext())
+ .setContentTitle("D")
+ .setWhen(1202)
+ .build();
+ mRecordNoGroup2 = new NotificationRecord(new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiNoGroup2, user), 0);
+
+ mNotiNoGroupSortA = new Notification.Builder(getContext())
+ .setContentTitle("E")
+ .setWhen(1201)
+ .setSortKey("A")
+ .build();
+ mRecordNoGroupSortA = new NotificationRecord(new StatusBarNotification(
+ "package", "package", 1, null, 0, 0, 0, mNotiNoGroupSortA, user), 0);
+ }
+
+ @SmallTest
+ public void testFindAfterRankingWithASplitGroup() throws Exception {
+ ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3);
+ notificationList.add(mRecordGroupGSortA);
+ notificationList.add(mRecordGroupGSortB);
+ notificationList.add(mRecordNoGroup);
+ notificationList.add(mRecordNoGroupSortA);
+ mHelper.sort(notificationList);
+ assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortA) >= 0);
+ assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortB) >= 0);
+ assertTrue(mHelper.indexOf(notificationList, mRecordNoGroup) >= 0);
+ assertTrue(mHelper.indexOf(notificationList, mRecordNoGroupSortA) >= 0);
+ }
+
+ @SmallTest
+ public void testSortShouldNotThrowWithPlainNotifications() throws Exception {
+ ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2);
+ notificationList.add(mRecordNoGroup);
+ notificationList.add(mRecordNoGroup2);
+ mHelper.sort(notificationList);
+ }
+
+ @SmallTest
+ public void testSortShouldNotThrowOneSorted() throws Exception {
+ ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2);
+ notificationList.add(mRecordNoGroup);
+ notificationList.add(mRecordNoGroupSortA);
+ mHelper.sort(notificationList);
+ }
+
+ @SmallTest
+ public void testSortShouldNotThrowOneNotification() throws Exception {
+ ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1);
+ notificationList.add(mRecordNoGroup);
+ mHelper.sort(notificationList);
+ }
+
+ @SmallTest
+ public void testSortShouldNotThrowOneSortKey() throws Exception {
+ ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1);
+ notificationList.add(mRecordGroupGSortB);
+ mHelper.sort(notificationList);
+ }
+
+ @SmallTest
+ public void testSortShouldNotThrowOnEmptyList() throws Exception {
+ ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>();
+ mHelper.sort(notificationList);
+ }
+}
diff --git a/telecomm/java/android/telecomm/PhoneAccount.java b/telecomm/java/android/telecomm/PhoneAccount.java
index 5a7753c..1f9071e 100644
--- a/telecomm/java/android/telecomm/PhoneAccount.java
+++ b/telecomm/java/android/telecomm/PhoneAccount.java
@@ -71,8 +71,8 @@
private final String mSubscriptionNumber;
private final int mCapabilities;
private final int mIconResId;
- private final String mLabel;
- private final String mShortDescription;
+ private final CharSequence mLabel;
+ private final CharSequence mShortDescription;
private boolean mVideoCallingSupported;
public PhoneAccount(
@@ -81,8 +81,8 @@
String subscriptionNumber,
int capabilities,
int iconResId,
- String label,
- String shortDescription,
+ CharSequence label,
+ CharSequence shortDescription,
boolean supportsVideoCalling) {
mAccountHandle = account;
mHandle = handle;
@@ -136,11 +136,11 @@
}
/**
- * A short string label describing a {@code PhoneAccount}.
+ * A short label describing a {@code PhoneAccount}.
*
* @return A label for this {@code PhoneAccount}.
*/
- public String getLabel() {
+ public CharSequence getLabel() {
return mLabel;
}
@@ -149,7 +149,7 @@
*
* @return A description for this {@code PhoneAccount}.
*/
- public String getShortDescription() {
+ public CharSequence getShortDescription() {
return mShortDescription;
}
@@ -214,8 +214,8 @@
out.writeString(mSubscriptionNumber);
out.writeInt(mCapabilities);
out.writeInt(mIconResId);
- out.writeString(mLabel);
- out.writeString(mShortDescription);
+ out.writeCharSequence(mLabel);
+ out.writeCharSequence(mShortDescription);
out.writeInt(mVideoCallingSupported ? 1 : 0);
}
@@ -238,8 +238,8 @@
mSubscriptionNumber = in.readString();
mCapabilities = in.readInt();
mIconResId = in.readInt();
- mLabel = in.readString();
- mShortDescription = in.readString();
+ mLabel = in.readCharSequence();
+ mShortDescription = in.readCharSequence();
mVideoCallingSupported = in.readInt() == 1;
}
}
diff --git a/telecomm/java/android/telecomm/StatusHints.java b/telecomm/java/android/telecomm/StatusHints.java
index 5e64bff..50f525a 100644
--- a/telecomm/java/android/telecomm/StatusHints.java
+++ b/telecomm/java/android/telecomm/StatusHints.java
@@ -34,11 +34,11 @@
public final class StatusHints implements Parcelable {
private final ComponentName mComponentName;
- private final String mLabel;
+ private final CharSequence mLabel;
private final int mIconId;
private final Bundle mExtras;
- public StatusHints(ComponentName componentName, String label, int iconId, Bundle extras) {
+ public StatusHints(ComponentName componentName, CharSequence label, int iconId, Bundle extras) {
mComponentName = componentName;
mLabel = label;
mIconId = iconId;
@@ -55,7 +55,7 @@
/**
* @return The label displayed in the in-call UI.
*/
- public String getLabel() {
+ public CharSequence getLabel() {
return mLabel;
}
@@ -88,7 +88,7 @@
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeParcelable(mComponentName, flags);
- out.writeString(mLabel);
+ out.writeCharSequence(mLabel);
out.writeInt(mIconId);
out.writeParcelable(mExtras, 0);
}
@@ -106,7 +106,7 @@
private StatusHints(Parcel in) {
mComponentName = in.readParcelable(getClass().getClassLoader());
- mLabel = in.readString();
+ mLabel = in.readCharSequence();
mIconId = in.readInt();
mExtras = (Bundle) in.readParcelable(getClass().getClassLoader());
}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 6927b26..2ffe4a2 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -360,6 +360,11 @@
}
@Override
+ public void updateScreenCaptureDisabled(int userId) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
public void updateRotation(boolean arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
}