Merge "move framework metrics over to new proto"
diff --git a/api/current.txt b/api/current.txt
index f4e7c16..a10c510 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20083,6 +20083,7 @@
field public static final deprecated int COLOR_QCOM_FormatYUV420SemiPlanar = 2141391872; // 0x7fa30c00
field public static final deprecated int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100
field public static final java.lang.String FEATURE_AdaptivePlayback = "adaptive-playback";
+ field public static final java.lang.String FEATURE_IntraRefresh = "intra-refresh";
field public static final java.lang.String FEATURE_SecurePlayback = "secure-playback";
field public static final java.lang.String FEATURE_TunneledPlayback = "tunneled-playback";
field public int[] colorFormats;
@@ -20125,6 +20126,21 @@
field public static final int AVCProfileHigh422 = 32; // 0x20
field public static final int AVCProfileHigh444 = 64; // 0x40
field public static final int AVCProfileMain = 2; // 0x2
+ field public static final int DolbyVisionLevelFhd24 = 4; // 0x4
+ field public static final int DolbyVisionLevelFhd30 = 8; // 0x8
+ field public static final int DolbyVisionLevelFhd60 = 16; // 0x10
+ field public static final int DolbyVisionLevelHd24 = 1; // 0x1
+ field public static final int DolbyVisionLevelHd30 = 2; // 0x2
+ field public static final int DolbyVisionLevelUhd24 = 32; // 0x20
+ field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
+ field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
+ field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
+ field public static final int DolbyVisionProfileDvavDen = 2; // 0x2
+ field public static final int DolbyVisionProfileDvavDer = 1; // 0x1
+ field public static final int DolbyVisionProfileDvheDen = 4; // 0x4
+ field public static final int DolbyVisionProfileDvheDer = 3; // 0x3
+ field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5
+ field public static final int DolbyVisionProfileDvheStn = 6; // 0x6
field public static final int H263Level10 = 1; // 0x1
field public static final int H263Level20 = 2; // 0x2
field public static final int H263Level30 = 4; // 0x4
@@ -20456,6 +20472,7 @@
field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
field public static final java.lang.String KEY_FRAME_RATE = "frame-rate";
field public static final java.lang.String KEY_HEIGHT = "height";
+ field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
field public static final java.lang.String KEY_IS_ADTS = "is-adts";
field public static final java.lang.String KEY_IS_AUTOSELECT = "is-autoselect";
field public static final java.lang.String KEY_IS_DEFAULT = "is-default";
@@ -20777,9 +20794,11 @@
method public static final int getAudioSourceMax();
method public int getMaxAmplitude() throws java.lang.IllegalStateException;
method public android.view.Surface getSurface();
+ method public void pause() throws java.lang.IllegalStateException;
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
method public void release();
method public void reset();
+ method public void resume() throws java.lang.IllegalStateException;
method public void setAudioChannels(int);
method public void setAudioEncoder(int) throws java.lang.IllegalStateException;
method public void setAudioEncodingBitRate(int);
@@ -33918,6 +33937,7 @@
field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
+ field public static final int REASON_PACKAGE_SUSPENDED = 15; // 0xf
field public static final int REASON_TOPIC_BANNED = 14; // 0xe
field public static final int REASON_USER_STOPPED = 6; // 0x6
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
@@ -36000,6 +36020,8 @@
public final class CellIdentityGsm implements android.os.Parcelable {
method public int describeContents();
+ method public int getArfcn();
+ method public int getBsic();
method public int getCid();
method public int getLac();
method public int getMcc();
@@ -36012,6 +36034,7 @@
public final class CellIdentityLte implements android.os.Parcelable {
method public int describeContents();
method public int getCi();
+ method public int getEarfcn();
method public int getMcc();
method public int getMnc();
method public int getPci();
@@ -36027,6 +36050,7 @@
method public int getMcc();
method public int getMnc();
method public int getPsc();
+ method public int getUarfcn();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityWcdma> CREATOR;
}
@@ -49882,6 +49906,9 @@
field public static final java.lang.Class<java.lang.Float> TYPE;
}
+ public abstract class FunctionalInterface implements java.lang.annotation.Annotation {
+ }
+
public class IllegalAccessError extends java.lang.IncompatibleClassChangeError {
ctor public IllegalAccessError();
ctor public IllegalAccessError(java.lang.String);
diff --git a/api/system-current.txt b/api/system-current.txt
index 61a0595..8e247ad 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -21433,6 +21433,7 @@
field public static final deprecated int COLOR_QCOM_FormatYUV420SemiPlanar = 2141391872; // 0x7fa30c00
field public static final deprecated int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100
field public static final java.lang.String FEATURE_AdaptivePlayback = "adaptive-playback";
+ field public static final java.lang.String FEATURE_IntraRefresh = "intra-refresh";
field public static final java.lang.String FEATURE_SecurePlayback = "secure-playback";
field public static final java.lang.String FEATURE_TunneledPlayback = "tunneled-playback";
field public int[] colorFormats;
@@ -21475,6 +21476,21 @@
field public static final int AVCProfileHigh422 = 32; // 0x20
field public static final int AVCProfileHigh444 = 64; // 0x40
field public static final int AVCProfileMain = 2; // 0x2
+ field public static final int DolbyVisionLevelFhd24 = 4; // 0x4
+ field public static final int DolbyVisionLevelFhd30 = 8; // 0x8
+ field public static final int DolbyVisionLevelFhd60 = 16; // 0x10
+ field public static final int DolbyVisionLevelHd24 = 1; // 0x1
+ field public static final int DolbyVisionLevelHd30 = 2; // 0x2
+ field public static final int DolbyVisionLevelUhd24 = 32; // 0x20
+ field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
+ field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
+ field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
+ field public static final int DolbyVisionProfileDvavDen = 2; // 0x2
+ field public static final int DolbyVisionProfileDvavDer = 1; // 0x1
+ field public static final int DolbyVisionProfileDvheDen = 4; // 0x4
+ field public static final int DolbyVisionProfileDvheDer = 3; // 0x3
+ field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5
+ field public static final int DolbyVisionProfileDvheStn = 6; // 0x6
field public static final int H263Level10 = 1; // 0x1
field public static final int H263Level20 = 2; // 0x2
field public static final int H263Level30 = 4; // 0x4
@@ -21806,6 +21822,7 @@
field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
field public static final java.lang.String KEY_FRAME_RATE = "frame-rate";
field public static final java.lang.String KEY_HEIGHT = "height";
+ field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
field public static final java.lang.String KEY_IS_ADTS = "is-adts";
field public static final java.lang.String KEY_IS_AUTOSELECT = "is-autoselect";
field public static final java.lang.String KEY_IS_DEFAULT = "is-default";
@@ -22127,9 +22144,11 @@
method public static final int getAudioSourceMax();
method public int getMaxAmplitude() throws java.lang.IllegalStateException;
method public android.view.Surface getSurface();
+ method public void pause() throws java.lang.IllegalStateException;
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
method public void release();
method public void reset();
+ method public void resume() throws java.lang.IllegalStateException;
method public void setAudioChannels(int);
method public void setAudioEncoder(int) throws java.lang.IllegalStateException;
method public void setAudioEncodingBitRate(int);
@@ -36143,6 +36162,7 @@
field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
+ field public static final int REASON_PACKAGE_SUSPENDED = 15; // 0xf
field public static final int REASON_TOPIC_BANNED = 14; // 0xe
field public static final int REASON_USER_STOPPED = 6; // 0x6
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
@@ -38397,6 +38417,8 @@
public final class CellIdentityGsm implements android.os.Parcelable {
method public int describeContents();
+ method public int getArfcn();
+ method public int getBsic();
method public int getCid();
method public int getLac();
method public int getMcc();
@@ -38409,6 +38431,7 @@
public final class CellIdentityLte implements android.os.Parcelable {
method public int describeContents();
method public int getCi();
+ method public int getEarfcn();
method public int getMcc();
method public int getMnc();
method public int getPci();
@@ -38424,6 +38447,7 @@
method public int getMcc();
method public int getMnc();
method public int getPsc();
+ method public int getUarfcn();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityWcdma> CREATOR;
}
@@ -52679,6 +52703,9 @@
field public static final java.lang.Class<java.lang.Float> TYPE;
}
+ public abstract class FunctionalInterface implements java.lang.annotation.Annotation {
+ }
+
public class IllegalAccessError extends java.lang.IncompatibleClassChangeError {
ctor public IllegalAccessError();
ctor public IllegalAccessError(java.lang.String);
diff --git a/api/test-current.txt b/api/test-current.txt
index b5a7bca..25b72c0 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -20091,6 +20091,7 @@
field public static final deprecated int COLOR_QCOM_FormatYUV420SemiPlanar = 2141391872; // 0x7fa30c00
field public static final deprecated int COLOR_TI_FormatYUV420PackedSemiPlanar = 2130706688; // 0x7f000100
field public static final java.lang.String FEATURE_AdaptivePlayback = "adaptive-playback";
+ field public static final java.lang.String FEATURE_IntraRefresh = "intra-refresh";
field public static final java.lang.String FEATURE_SecurePlayback = "secure-playback";
field public static final java.lang.String FEATURE_TunneledPlayback = "tunneled-playback";
field public int[] colorFormats;
@@ -20133,6 +20134,21 @@
field public static final int AVCProfileHigh422 = 32; // 0x20
field public static final int AVCProfileHigh444 = 64; // 0x40
field public static final int AVCProfileMain = 2; // 0x2
+ field public static final int DolbyVisionLevelFhd24 = 4; // 0x4
+ field public static final int DolbyVisionLevelFhd30 = 8; // 0x8
+ field public static final int DolbyVisionLevelFhd60 = 16; // 0x10
+ field public static final int DolbyVisionLevelHd24 = 1; // 0x1
+ field public static final int DolbyVisionLevelHd30 = 2; // 0x2
+ field public static final int DolbyVisionLevelUhd24 = 32; // 0x20
+ field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
+ field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
+ field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
+ field public static final int DolbyVisionProfileDvavDen = 2; // 0x2
+ field public static final int DolbyVisionProfileDvavDer = 1; // 0x1
+ field public static final int DolbyVisionProfileDvheDen = 4; // 0x4
+ field public static final int DolbyVisionProfileDvheDer = 3; // 0x3
+ field public static final int DolbyVisionProfileDvheDtr = 5; // 0x5
+ field public static final int DolbyVisionProfileDvheStn = 6; // 0x6
field public static final int H263Level10 = 1; // 0x1
field public static final int H263Level20 = 2; // 0x2
field public static final int H263Level30 = 4; // 0x4
@@ -20464,6 +20480,7 @@
field public static final java.lang.String KEY_FLAC_COMPRESSION_LEVEL = "flac-compression-level";
field public static final java.lang.String KEY_FRAME_RATE = "frame-rate";
field public static final java.lang.String KEY_HEIGHT = "height";
+ field public static final java.lang.String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
field public static final java.lang.String KEY_IS_ADTS = "is-adts";
field public static final java.lang.String KEY_IS_AUTOSELECT = "is-autoselect";
field public static final java.lang.String KEY_IS_DEFAULT = "is-default";
@@ -20785,9 +20802,11 @@
method public static final int getAudioSourceMax();
method public int getMaxAmplitude() throws java.lang.IllegalStateException;
method public android.view.Surface getSurface();
+ method public void pause() throws java.lang.IllegalStateException;
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
method public void release();
method public void reset();
+ method public void resume() throws java.lang.IllegalStateException;
method public void setAudioChannels(int);
method public void setAudioEncoder(int) throws java.lang.IllegalStateException;
method public void setAudioEncodingBitRate(int);
@@ -33932,6 +33951,7 @@
field public static final int REASON_LISTENER_CANCEL_ALL = 11; // 0xb
field public static final int REASON_PACKAGE_BANNED = 7; // 0x7
field public static final int REASON_PACKAGE_CHANGED = 5; // 0x5
+ field public static final int REASON_PACKAGE_SUSPENDED = 15; // 0xf
field public static final int REASON_TOPIC_BANNED = 14; // 0xe
field public static final int REASON_USER_STOPPED = 6; // 0x6
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
@@ -36014,6 +36034,8 @@
public final class CellIdentityGsm implements android.os.Parcelable {
method public int describeContents();
+ method public int getArfcn();
+ method public int getBsic();
method public int getCid();
method public int getLac();
method public int getMcc();
@@ -36026,6 +36048,7 @@
public final class CellIdentityLte implements android.os.Parcelable {
method public int describeContents();
method public int getCi();
+ method public int getEarfcn();
method public int getMcc();
method public int getMnc();
method public int getPci();
@@ -36041,6 +36064,7 @@
method public int getMcc();
method public int getMnc();
method public int getPsc();
+ method public int getUarfcn();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityWcdma> CREATOR;
}
@@ -49898,6 +49922,9 @@
field public static final java.lang.Class<java.lang.Float> TYPE;
}
+ public abstract class FunctionalInterface implements java.lang.annotation.Annotation {
+ }
+
public class IllegalAccessError extends java.lang.IncompatibleClassChangeError {
ctor public IllegalAccessError();
ctor public IllegalAccessError(java.lang.String);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index e36a427..11154f2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2313,7 +2313,7 @@
* <p>In order to use a Toolbar within the Activity's window content the application
* must not request the window feature {@link Window#FEATURE_ACTION_BAR FEATURE_ACTION_BAR}.</p>
*
- * @param toolbar Toolbar to set as the Activity's action bar
+ * @param toolbar Toolbar to set as the Activity's action bar, or {@code null} to clear it
*/
public void setActionBar(@Nullable Toolbar toolbar) {
final ActionBar ab = getActionBar();
@@ -2332,10 +2332,17 @@
ab.onDestroy();
}
- ToolbarActionBar tbab = new ToolbarActionBar(toolbar, getTitle(), this);
- mActionBar = tbab;
- mWindow.setCallback(tbab.getWrappedWindowCallback());
- mActionBar.invalidateOptionsMenu();
+ if (toolbar != null) {
+ final ToolbarActionBar tbab = new ToolbarActionBar(toolbar, getTitle(), this);
+ mActionBar = tbab;
+ mWindow.setCallback(tbab.getWrappedWindowCallback());
+ } else {
+ mActionBar = null;
+ // Re-set the original window callback since we may have already set a Toolbar wrapper
+ mWindow.setCallback(this);
+ }
+
+ invalidateOptionsMenu();
}
/**
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index ba215bb..c776ef8 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -402,7 +402,7 @@
* type.
*/
public static String DIRECTORY_PODCASTS = "Podcasts";
-
+
/**
* Standard directory in which to place any audio files that should be
* in the list of ringtones that the user can select (not as regular
@@ -414,7 +414,7 @@
* type.
*/
public static String DIRECTORY_RINGTONES = "Ringtones";
-
+
/**
* Standard directory in which to place any audio files that should be
* in the list of alarms that the user can select (not as regular
@@ -426,7 +426,7 @@
* type.
*/
public static String DIRECTORY_ALARMS = "Alarms";
-
+
/**
* Standard directory in which to place any audio files that should be
* in the list of notifications that the user can select (not as regular
@@ -438,7 +438,7 @@
* type.
*/
public static String DIRECTORY_NOTIFICATIONS = "Notifications";
-
+
/**
* Standard directory in which to place pictures that are available to
* the user. Note that this is primarily a convention for the top-level
@@ -446,7 +446,7 @@
* in any directory.
*/
public static String DIRECTORY_PICTURES = "Pictures";
-
+
/**
* Standard directory in which to place movies that are available to
* the user. Note that this is primarily a convention for the top-level
@@ -454,7 +454,7 @@
* in any directory.
*/
public static String DIRECTORY_MOVIES = "Movies";
-
+
/**
* Standard directory in which to place files that have been downloaded by
* the user. Note that this is primarily a convention for the top-level
@@ -464,7 +464,7 @@
* backwards compatibility reasons.
*/
public static String DIRECTORY_DOWNLOADS = "Download";
-
+
/**
* The traditional location for pictures and videos when mounting the
* device as a camera. Note that this is primarily a convention for the
@@ -496,7 +496,7 @@
* </ul>
* @hide
*/
- public static final String[] STANDARD_DIRECTORIES = {
+ private static final String[] STANDARD_DIRECTORIES = {
DIRECTORY_MUSIC,
DIRECTORY_PODCASTS,
DIRECTORY_RINGTONES,
@@ -510,6 +510,18 @@
};
/**
+ * @hide
+ */
+ public static boolean isStandardDirectory(String dir) {
+ for (String valid : STANDARD_DIRECTORIES) {
+ if (valid.equals(dir)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Get a top-level shared/external storage directory for placing files of a
* particular type. This is where the user will typically place and manage
* their own files, so you should be careful about what you put here to
@@ -559,7 +571,7 @@
throwIfUserRequired();
return sCurrentUser.buildExternalStorageAppDataDirs(packageName);
}
-
+
/**
* Generates the raw path to an application's media
* @hide
@@ -568,7 +580,7 @@
throwIfUserRequired();
return sCurrentUser.buildExternalStorageAppMediaDirs(packageName);
}
-
+
/**
* Generates the raw path to an application's OBB files
* @hide
@@ -577,7 +589,7 @@
throwIfUserRequired();
return sCurrentUser.buildExternalStorageAppObbDirs(packageName);
}
-
+
/**
* Generates the path to an application's files.
* @hide
@@ -595,7 +607,7 @@
throwIfUserRequired();
return sCurrentUser.buildExternalStorageAppCacheDirs(packageName);
}
-
+
/**
* Return the download/cache content directory.
*/
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a9fd3c8..bc0d7d6 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5073,6 +5073,14 @@
public static final String TTS_DEFAULT_SYNTH = "tts_default_synth";
/**
+ * Whether text-to-speech higher speech rate is enabled.
+ * 0 = disabled.
+ * 1 = enabled.
+ * @hide
+ */
+ public static final String TTS_DEFAULT_HIGHER_SPEECH_RATE_ENABLED =
+ "tts_default_higher_speech_rate_enabled";
+ /**
* Default text-to-speech language.
*
* @deprecated this setting is no longer in use, as of the Ice Cream
@@ -5884,6 +5892,7 @@
ACCESSIBILITY_CAPTIONING_WINDOW_COLOR,
TTS_USE_DEFAULTS,
TTS_DEFAULT_RATE,
+ TTS_DEFAULT_HIGHER_SPEECH_RATE_ENABLED,
TTS_DEFAULT_PITCH,
TTS_DEFAULT_SYNTH,
TTS_DEFAULT_LANG,
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 035462d..bd41fb5 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -98,6 +98,9 @@
/** Notification was canceled by the user banning the topic. */
public static final int REASON_TOPIC_BANNED = 14;
+ /** Notification was canceled by the device administrator suspending the package. */
+ public static final int REASON_PACKAGE_SUSPENDED = 15;
+
public class Adjustment {
int mImportance;
CharSequence mExplanation;
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index b491710..ed91239c 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -165,6 +165,11 @@
| FROM_HTML_SEPARATOR_LINE_BREAK_DIV
| FROM_HTML_SEPARATOR_LINE_BREAK_BLOCKQUOTE;
+ /**
+ * The bit which indicates if lines delimited by '\n' will be grouped into <p> elements.
+ */
+ private static final int TO_HTML_PARAGRAPH_FLAG = 0x00000001;
+
private Html() { }
/**
@@ -255,7 +260,7 @@
*/
public static String toHtml(Spanned text, int option) {
StringBuilder out = new StringBuilder();
- withinHtml(out, text);
+ withinHtml(out, text, option);
return out.toString();
}
@@ -268,7 +273,16 @@
return out.toString();
}
- private static void withinHtml(StringBuilder out, Spanned text) {
+ private static void withinHtml(StringBuilder out, Spanned text, int option) {
+ if ((option & TO_HTML_PARAGRAPH_FLAG) == TO_HTML_PARAGRAPH_LINES_CONSECUTIVE) {
+ encodeTextAlignmentByDiv(out, text, option);
+ return;
+ }
+
+ withinDiv(out, text, 0, text.length(), option);
+ }
+
+ private static void encodeTextAlignmentByDiv(StringBuilder out, Spanned text, int option) {
int len = text.length();
int next;
@@ -296,7 +310,7 @@
out.append("<div ").append(elements).append(">");
}
- withinDiv(out, text, i, next);
+ withinDiv(out, text, i, next, option);
if (needDiv) {
out.append("</div>");
@@ -304,8 +318,8 @@
}
}
- private static void withinDiv(StringBuilder out, Spanned text,
- int start, int end) {
+ private static void withinDiv(StringBuilder out, Spanned text, int start, int end,
+ int option) {
int next;
for (int i = start; i < end; i = next) {
next = text.nextSpanTransition(i, end, QuoteSpan.class);
@@ -315,7 +329,7 @@
out.append("<blockquote>");
}
- withinBlockquote(out, text, i, next);
+ withinBlockquote(out, text, i, next, option);
for (QuoteSpan quote : quotes) {
out.append("</blockquote>\n");
@@ -323,7 +337,7 @@
}
}
- private static String getOpenParaTagWithDirection(Spanned text, int start, int end) {
+ private static String getTextDirection(Spanned text, int start, int end) {
final int len = end - start;
final byte[] levels = ArrayUtils.newUnpaddedByteArray(len);
final char[] buffer = TextUtils.obtain(len);
@@ -333,16 +347,101 @@
false /* no info */);
switch(paraDir) {
case Layout.DIR_RIGHT_TO_LEFT:
- return "<p dir=\"rtl\">";
+ return " dir=\"rtl\"";
case Layout.DIR_LEFT_TO_RIGHT:
default:
- return "<p dir=\"ltr\">";
+ return " dir=\"ltr\"";
}
}
- private static void withinBlockquote(StringBuilder out, Spanned text,
- int start, int end) {
- out.append(getOpenParaTagWithDirection(text, start, end));
+ private static String getTextStyles(Spanned text, int start, int end) {
+ final StringBuilder style = new StringBuilder(" style=\"margin-top:0; margin-bottom:0;");
+
+ final AlignmentSpan[] alignmentSpans = text.getSpans(start, end, AlignmentSpan.class);
+ final int len = alignmentSpans.length;
+ if (len > 0) {
+ final Layout.Alignment alignment = alignmentSpans[len - 1].getAlignment();
+ if (alignment == Layout.Alignment.ALIGN_NORMAL) {
+ style.append(" text-align:start;");
+ } else if (alignment == Layout.Alignment.ALIGN_CENTER) {
+ style.append(" text-align:center;");
+ } else if (alignment == Layout.Alignment.ALIGN_OPPOSITE) {
+ style.append(" text-align:end;");
+ }
+ }
+
+ style.append("\"");
+ return style.toString();
+ }
+
+ private static void withinBlockquote(StringBuilder out, Spanned text, int start, int end,
+ int option) {
+ if ((option & TO_HTML_PARAGRAPH_FLAG) == TO_HTML_PARAGRAPH_LINES_CONSECUTIVE) {
+ withinBlockquoteConsecutive(out, text, start, end);
+ } else {
+ withinBlockquoteIndividual(out, text, start, end);
+ }
+ }
+
+ private static void withinBlockquoteIndividual(StringBuilder out, Spanned text, int start,
+ int end) {
+ boolean isInList = false;
+ int next;
+ for (int i = start; i <= end; i = next) {
+ next = TextUtils.indexOf(text, '\n', i, end);
+ if (next < 0) {
+ next = end;
+ }
+
+ boolean isListItem = false;
+ ParagraphStyle[] paragraphStyles = text.getSpans(i, next, ParagraphStyle.class);
+ for (ParagraphStyle paragraphStyle : paragraphStyles) {
+ final int spanFlags = text.getSpanFlags(paragraphStyle);
+ if ((spanFlags & Spanned.SPAN_PARAGRAPH) == Spanned.SPAN_PARAGRAPH
+ && paragraphStyle instanceof BulletSpan) {
+ isListItem = true;
+ break;
+ }
+ }
+
+ if (isListItem && !isInList) {
+ // Current paragraph is the first item in a list
+ isInList = true;
+ out.append("<ul>\n");
+ }
+
+ if (isInList && !isListItem) {
+ // Current paragraph is no longer a list item; close the previously opened list
+ isInList = false;
+ out.append("</ul>\n");
+ }
+
+ String tagType = isListItem ? "li" : "p";
+ out.append("<").append(tagType).append(getTextDirection(text, start, next))
+ .append(getTextStyles(text, start, next)).append(">");
+
+ if (next - i == 0) {
+ out.append("<br>");
+ } else {
+ withinParagraph(out, text, i, next);
+ }
+
+ out.append("</");
+ out.append(tagType);
+ out.append(">\n");
+
+ if (next == end && isInList) {
+ isInList = false;
+ out.append("</ul>\n");
+ }
+
+ next++;
+ }
+ }
+
+ private static void withinBlockquoteConsecutive(StringBuilder out, Spanned text, int start,
+ int end) {
+ out.append("<p").append(getTextDirection(text, start, end)).append(">");
int next;
for (int i = start; i < end; i = next) {
@@ -358,19 +457,26 @@
next++;
}
- if (withinParagraph(out, text, i, next - nl, nl, next == end)) {
- /* Paragraph should be closed */
- out.append("</p>\n");
- out.append(getOpenParaTagWithDirection(text, next, end));
+ withinParagraph(out, text, i, next - nl);
+
+ if (nl == 1) {
+ out.append("<br>\n");
+ } else {
+ for (int j = 2; j < nl; j++) {
+ out.append("<br>");
+ }
+ if (next != end) {
+ /* Paragraph should be closed and reopened */
+ out.append("</p>\n");
+ out.append("<p").append(getTextDirection(text, start, end)).append(">");
+ }
}
}
out.append("</p>\n");
}
- /* Returns true if the caller should close and reopen the paragraph. */
- private static boolean withinParagraph(StringBuilder out, Spanned text, int start, int end,
- int nl, boolean last) {
+ private static void withinParagraph(StringBuilder out, Spanned text, int start, int end) {
int next;
for (int i = start; i < end; i = next) {
next = text.nextSpanTransition(i, end, CharacterStyle.class);
@@ -494,16 +600,6 @@
}
}
}
-
- if (nl == 1) {
- out.append("<br>\n");
- return false;
- } else {
- for (int i = 2; i < nl; i++) {
- out.append("<br>");
- }
- return !last;
- }
}
private static void withinStyle(StringBuilder out, CharSequence text,
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 9561f08..df84970 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1036,6 +1036,7 @@
mChoiceActionMode = startActionMode(mMultiChoiceModeCallback);
}
+ final boolean itemCheckChanged;
if (mChoiceMode == CHOICE_MODE_MULTIPLE || mChoiceMode == CHOICE_MODE_MULTIPLE_MODAL) {
boolean oldValue = mCheckStates.get(position);
mCheckStates.put(position, value);
@@ -1046,7 +1047,8 @@
mCheckedIdStates.delete(mAdapter.getItemId(position));
}
}
- if (oldValue != value) {
+ itemCheckChanged = oldValue != value;
+ if (itemCheckChanged) {
if (value) {
mCheckedItemCount++;
} else {
@@ -1062,6 +1064,7 @@
boolean updateIds = mCheckedIdStates != null && mAdapter.hasStableIds();
// Clear all values if we're checking something, or unchecking the currently
// selected item
+ itemCheckChanged = isItemChecked(position) != value;
if (value || isItemChecked(position)) {
mCheckStates.clear();
if (updateIds) {
@@ -1081,8 +1084,8 @@
}
}
- // Do not generate a data change while we are in the layout phase
- if (!mInLayout && !mBlockLayoutRequests) {
+ // Do not generate a data change while we are in the layout phase or data has not changed
+ if (!mInLayout && !mBlockLayoutRequests && itemCheckChanged) {
mDataChanged = true;
rememberSyncState();
requestLayout();
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 0fba992..331fde1 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -460,6 +460,11 @@
public static final String FEATURE_TunneledPlayback = "tunneled-playback";
/**
+ * <b>video encoder only</b>: codec supports intra refresh.
+ */
+ public static final String FEATURE_IntraRefresh = "intra-refresh";
+
+ /**
* Query codec feature capabilities.
* <p>
* These features are supported to be used by the codec. These
@@ -486,6 +491,10 @@
new Feature(FEATURE_TunneledPlayback, (1 << 2), false),
};
+ private static final Feature[] encoderFeatures = {
+ new Feature(FEATURE_IntraRefresh, (1 << 0), false),
+ };
+
/** @hide */
public String[] validFeatures() {
Feature[] features = getValidFeatures();
@@ -500,7 +509,7 @@
if (!isEncoder()) {
return decoderFeatures;
}
- return new Feature[] {};
+ return encoderFeatures;
}
private boolean checkFeature(String name, int flags) {
@@ -2563,6 +2572,25 @@
public static final int HEVCMainTierLevel62 = 0x1000000;
public static final int HEVCHighTierLevel62 = 0x2000000;
+ // from OMX_VIDEO_DOLBYVISIONPROFILETYPE
+ public static final int DolbyVisionProfileDvavDer = 0x1;
+ public static final int DolbyVisionProfileDvavDen = 0x2;
+ public static final int DolbyVisionProfileDvheDer = 0x3;
+ public static final int DolbyVisionProfileDvheDen = 0x4;
+ public static final int DolbyVisionProfileDvheDtr = 0x5;
+ public static final int DolbyVisionProfileDvheStn = 0x6;
+
+ // from OMX_VIDEO_DOLBYVISIONLEVELTYPE
+ public static final int DolbyVisionLevelHd24 = 0x1;
+ public static final int DolbyVisionLevelHd30 = 0x2;
+ public static final int DolbyVisionLevelFhd24 = 0x4;
+ public static final int DolbyVisionLevelFhd30 = 0x8;
+ public static final int DolbyVisionLevelFhd60 = 0x10;
+ public static final int DolbyVisionLevelUhd24 = 0x20;
+ public static final int DolbyVisionLevelUhd30 = 0x40;
+ public static final int DolbyVisionLevelUhd48 = 0x80;
+ public static final int DolbyVisionLevelUhd60 = 0x100;
+
/**
* Defined in the OpenMAX IL specs, depending on the type of media
* this can be OMX_VIDEO_AVCPROFILETYPE, OMX_VIDEO_H263PROFILETYPE,
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index abdf220..930d8b8 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -44,7 +44,8 @@
* for encoders, readable in the output format of decoders</b></td></tr>
* <tr><td>{@link #KEY_FRAME_RATE}</td><td>Integer or Float</td><td><b>encoder-only</b></td></tr>
* <tr><td>{@link #KEY_CAPTURE_RATE}</td><td>Integer</td><td></td></tr>
-* <tr><td>{@link #KEY_I_FRAME_INTERVAL}</td><td>Integer</td><td><b>encoder-only</b></td></tr>
+ * <tr><td>{@link #KEY_I_FRAME_INTERVAL}</td><td>Integer</td><td><b>encoder-only</b></td></tr>
+ * <tr><td>{@link #KEY_INTRA_REFRESH_PERIOD}</td><td>Integer</td><td><b>encoder-only</b>, optional</td></tr>
* <tr><td>{@link #KEY_MAX_WIDTH}</td><td>Integer</td><td><b>decoder-only</b>, optional, max-resolution width</td></tr>
* <tr><td>{@link #KEY_MAX_HEIGHT}</td><td>Integer</td><td><b>decoder-only</b>, optional, max-resolution height</td></tr>
* <tr><td>{@link #KEY_REPEAT_PREVIOUS_FRAME_AFTER}</td><td>Long</td><td><b>video encoder in surface-mode only</b></td></tr>
@@ -218,6 +219,20 @@
public static final String KEY_I_FRAME_INTERVAL = "i-frame-interval";
/**
+ * An optional key describing the period of intra refresh in frames. This is an
+ * optional parameter that applies only to video encoders. If encoder supports it
+ * ({@link MediaCodecInfo.CodecCapabilities#FEATURE_IntraRefresh}), the whole
+ * frame is completely refreshed after the specified period. Also for each frame,
+ * a fix subset of macroblocks must be intra coded which leads to more constant bitrate
+ * than inserting a key frame. This key is recommended for video streaming applications
+ * as it provides low-delay and good error-resilience. This key is ignored if the
+ * video encoder does not support the intra refresh feature. Use the output format to
+ * verify that this feature was enabled.
+ * The associated value is an integer.
+ */
+ public static final String KEY_INTRA_REFRESH_PERIOD = "intra-refresh-period";
+
+ /**
* 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
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 9517387..f09f654 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -822,7 +822,6 @@
*
* @throws IllegalStateException if it is called before start() or after
* stop()
- * {@hide}
*/
public native void pause() throws IllegalStateException;
@@ -833,7 +832,6 @@
* @throws IllegalStateException if it is called before start() or after
* stop()
* @see android.media.MediaRecorder#pause
- * {@hide}
*/
public native void resume() throws IllegalStateException;
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index a5d0303..5108eb5 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -80,8 +80,7 @@
}
bool GetExifFromRawImage(
- FileStream::FileStream* stream, const String8& filename,
- piex::PreviewImageData& image_data) {
+ FileStream* stream, const String8& filename, piex::PreviewImageData& image_data) {
memset(&image_data, 0, sizeof(image_data));
if (!stream->exists()) {
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index 6beef44..fa9ff01 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -54,8 +54,7 @@
android:name=".LauncherActivity"
android:theme="@android:style/Theme.NoDisplay"
android:icon="@drawable/ic_files_app"
- android:label="@string/files_label"
- android:enabled="@bool/productivity_device">
+ android:label="@string/files_label">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -78,6 +77,16 @@
</intent-filter>
</activity>
+ <activity
+ android:name=".OpenExternalDirectoryActivity"
+ android:theme="@android:style/Theme.Translucent.NoTitleBar">
+ <intent-filter>
+ <action android:name="android.intent.action.OPEN_EXTERNAL_DIRECTORY" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="file" />
+ </intent-filter>
+ </activity>
+
<provider
android:name=".RecentsProvider"
android:authorities="com.android.documentsui.recents"
diff --git a/packages/DocumentsUI/res/values/config.xml b/packages/DocumentsUI/res/values/config.xml
index ff28e15..e8d8c8e 100644
--- a/packages/DocumentsUI/res/values/config.xml
+++ b/packages/DocumentsUI/res/values/config.xml
@@ -18,7 +18,6 @@
<!-- Allow Advanced Devices default value to be customised -->
<bool name="config_defaultAdvancedDevices">false</bool>
- <bool name="productivity_device">true</bool>
<!-- Intentionally unset. Vendors should set this in an overlay. -->
<string name="trusted_quick_viewer_package"></string>
</resources>
diff --git a/packages/DocumentsUI/res/values/strings.xml b/packages/DocumentsUI/res/values/strings.xml
index 5a9bc16..ff7b7b9 100644
--- a/packages/DocumentsUI/res/values/strings.xml
+++ b/packages/DocumentsUI/res/values/strings.xml
@@ -196,4 +196,13 @@
<string name="menu_rename">Rename</string>
<!-- Toast shown when renaming document failed with an error [CHAR LIMIT=48] -->
<string name="rename_error">Failed to rename document</string>
+
+ <!-- DO NOT TRANSLATE - final phrase has not been decided yet (b/26750152) -->
+ <string name="open_external_dialog_request">Grant <xliff:g id="appName" example="System Settings"><b>^1</b></xliff:g>
+ access to <xliff:g id="directory" example="Pictures"><i>^2</i></xliff:g> folder on
+ <xliff:g id="storage" example="SD Card"><i>^3</i></xliff:g>?</string>
+ <!-- Text in the button asking user to allow access to a given directory. -->
+ <string name="allow">Allow</string>
+ <!-- Text in the button asking user to deny access to a given directory. -->
+ <string name="deny">Deny</string>
</resources>
diff --git a/packages/DocumentsUI/res/values/styles.xml b/packages/DocumentsUI/res/values/styles.xml
index d14631d..f4dfd73 100644
--- a/packages/DocumentsUI/res/values/styles.xml
+++ b/packages/DocumentsUI/res/values/styles.xml
@@ -45,4 +45,8 @@
<item name="android:maxHeight">3dp</item>
</style>
+ <!-- TODO: use the proper dialog and/or inline if not overriding -->
+ <style name="AlertDialogTheme" parent="@style/Theme.AppCompat.Light.Dialog.Alert">
+ </style>
+
</resources>
diff --git a/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java b/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
new file mode 100644
index 0000000..5dc4f57
--- /dev/null
+++ b/packages/DocumentsUI/src/com/android/documentsui/OpenExternalDirectoryActivity.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2016 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.documentsui;
+
+import static android.os.Environment.isStandardDirectory;
+import static com.android.documentsui.Shared.DEBUG;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.ContentProvider;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.storage.StorageManager;
+import android.os.storage.VolumeInfo;
+import android.provider.DocumentsContract;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * Activity responsible for handling {@link Intent#ACTION_OPEN_EXTERNAL_DOCUMENT}.
+ */
+public class OpenExternalDirectoryActivity extends Activity {
+ private static final String TAG = "OpenExternalDirectoryActivity";
+ private static final String FM_TAG = "open_external_directory";
+ private static final String EXTERNAL_STORAGE_AUTH = "com.android.externalstorage.documents";
+ private static final String EXTRA_FILE = "com.android.documentsui.FILE";
+ private static final String EXTRA_APP_LABEL = "com.android.documentsui.APP_LABEL";
+ private static final String EXTRA_VOLUME_LABEL = "com.android.documentsui.VOLUME_LABEL";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Intent intent = getIntent();
+ if (intent == null || intent.getData() == null) {
+ Log.d(TAG, "missing intent or intent data: " + intent);
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ final String path = intent.getData().getPath();
+ final int userId = UserHandle.myUserId();
+ if (!showFragment(this, userId, path)) {
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+ }
+
+ /**
+ * Validates the given {@code path} and display the appropriate dialog asking the user to grant
+ * access to it.
+ */
+ static boolean showFragment(Activity activity, int userId, String path) {
+ Log.d(TAG, "showFragment() for path " + path + " and user " + userId);
+ if (path == null) {
+ Log.e(TAG, "INTERNAL ERROR: showFragment() with null path");
+ return false;
+ }
+ File file;
+ try {
+ file = new File(new File(path).getCanonicalPath());
+ } catch (IOException e) {
+ Log.e(TAG, "Could not get canonical file from " + path);
+ return false;
+ }
+ final StorageManager sm =
+ (StorageManager) activity.getSystemService(Context.STORAGE_SERVICE);
+
+ final String root = file.getParent();
+ final String directory = file.getName();
+
+ // Verify directory is valid.
+ if (TextUtils.isEmpty(directory) || !isStandardDirectory(directory)) {
+ Log.d(TAG, "Directory '" + directory + "' is not standard (full path: '" + path + "')");
+ return false;
+ }
+
+ // Gets volume label and converted path
+ String volumeLabel = null;
+ final List<VolumeInfo> volumes = sm.getVolumes();
+ if (DEBUG) Log.d(TAG, "Number of volumes: " + volumes.size());
+ for (VolumeInfo volume : volumes) {
+ if (isRightVolume(volume, root, userId)) {
+ final File internalRoot = volume.getInternalPathForUser(userId);
+ // Must convert path before calling getDocIdForFileCreateNewDir()
+ if (DEBUG) Log.d(TAG, "Converting " + root + " to " + internalRoot);
+ file = new File(internalRoot, directory);
+ volumeLabel = sm.getBestVolumeDescription(volume);
+ break;
+ }
+ }
+ if (volumeLabel == null) {
+ Log.e(TAG, "Could not get volume for " + path);
+ return false;
+ }
+
+ // Gets the package label.
+ final String appLabel = getAppLabel(activity);
+ if (appLabel == null) {
+ return false;
+ }
+
+ // Sets args that will be retrieve on onCreate()
+ final Bundle args = new Bundle();
+ args.putString(EXTRA_FILE, file.getAbsolutePath());
+ args.putString(EXTRA_VOLUME_LABEL, volumeLabel);
+ args.putString(EXTRA_APP_LABEL, appLabel);
+
+ final FragmentManager fm = activity.getFragmentManager();
+ final FragmentTransaction ft = fm.beginTransaction();
+ final OpenExternalDirectoryDialogFragment fragment =
+ new OpenExternalDirectoryDialogFragment();
+ fragment.setArguments(args);
+ ft.add(fragment, FM_TAG);
+ ft.commitAllowingStateLoss();
+
+ return true;
+ }
+
+ private static String getAppLabel(Activity activity) {
+ final String packageName = activity.getCallingPackage();
+ final PackageManager pm = activity.getPackageManager();
+ try {
+ return pm.getApplicationLabel(pm.getApplicationInfo(packageName, 0)).toString();
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Could not get label for package " + packageName);
+ return null;
+ }
+ }
+
+ private static boolean isRightVolume(VolumeInfo volume, String root, int userId) {
+ final File userPath = volume.getPathForUser(userId);
+ final String path = userPath == null ? null : volume.getPathForUser(userId).getPath();
+ final boolean isVisible = volume.isVisibleForWrite(userId);
+ if (DEBUG) {
+ Log.d(TAG, "Volume: " + volume + " userId: " + userId + " root: " + root
+ + " volumePath: " + volume.getPath().getPath()
+ + " pathForUser: " + path
+ + " internalPathForUser: " + volume.getInternalPath()
+ + " isVisible: " + isVisible);
+ }
+ return volume.isVisibleForWrite(userId) && root.equals(path);
+ }
+
+ private static Intent createGrantedUriPermissionsIntent(ContentProviderClient provider,
+ File file) {
+ // Calls ExternalStorageProvider to get the doc id for the file
+ final Bundle bundle;
+ try {
+ bundle = provider.call("getDocIdForFileCreateNewDir", file.getPath(), null);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Did not get doc id from External Storage provider for " + file, e);
+ return null;
+ }
+ final String docId = bundle == null ? null : bundle.getString("DOC_ID");
+ if (docId == null) {
+ Log.e(TAG, "Did not get doc id from External Storage provider for " + file);
+ return null;
+ }
+ Log.d(TAG, "doc id for " + file + ": " + docId);
+
+ final Uri uri = DocumentsContract.buildTreeDocumentUri(EXTERNAL_STORAGE_AUTH, docId);
+ if (uri == null) {
+ Log.e(TAG, "Could not get URI for doc id " + docId);
+ return null;
+ }
+
+ if (DEBUG) Log.d(TAG, "URI for " + file + ": " + uri);
+ final Intent intent = new Intent();
+ intent.setData(uri);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+ return intent;
+ }
+
+ private static class OpenExternalDirectoryDialogFragment extends DialogFragment {
+
+ private File mFile;
+ private String mVolumeLabel;
+ private String mAppLabel;
+ private ContentProviderClient mExternalStorageClient;
+ private ContentResolver mResolver;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final Bundle args = getArguments();
+ if (args != null) {
+ mFile = new File(args.getString(EXTRA_FILE));
+ mVolumeLabel = args.getString(EXTRA_VOLUME_LABEL);
+ mAppLabel = args.getString(EXTRA_APP_LABEL);
+ mResolver = getContext().getContentResolver();
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (mExternalStorageClient != null) {
+ mExternalStorageClient.close();
+ }
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final String folder = mFile.getName();
+ final Activity activity = getActivity();
+ final OnClickListener listener = new OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ Intent intent = null;
+ if (which == DialogInterface.BUTTON_POSITIVE) {
+ intent = createGrantedUriPermissionsIntent(getExternalStorageClient(),
+ mFile);
+ }
+ if (which == DialogInterface.BUTTON_NEGATIVE || intent == null) {
+ activity.setResult(RESULT_CANCELED);
+ } else {
+ activity.setResult(RESULT_OK, intent);
+ }
+ activity.finish();
+ }
+ };
+
+ final CharSequence message = TextUtils
+ .expandTemplate(
+ getText(R.string.open_external_dialog_request), mAppLabel, folder,
+ mVolumeLabel);
+ return new AlertDialog.Builder(activity, R.style.AlertDialogTheme)
+ .setMessage(message)
+ .setPositiveButton(R.string.allow, listener)
+ .setNegativeButton(R.string.deny, listener)
+ .create();
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+ final Activity activity = getActivity();
+ activity.setResult(RESULT_CANCELED);
+ activity.finish();
+ }
+
+ private synchronized ContentProviderClient getExternalStorageClient() {
+ if (mExternalStorageClient == null) {
+ mExternalStorageClient =
+ mResolver.acquireContentProviderClient(EXTERNAL_STORAGE_AUTH);
+ }
+ return mExternalStorageClient;
+ }
+ }
+}
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 43527a2..1bfc19c 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -25,6 +25,7 @@
import android.database.MatrixCursor.RowBuilder;
import android.graphics.Point;
import android.net.Uri;
+import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.FileObserver;
import android.os.FileUtils;
@@ -234,7 +235,13 @@
return projection != null ? projection : DEFAULT_DOCUMENT_PROJECTION;
}
+
private String getDocIdForFile(File file) throws FileNotFoundException {
+ return getDocIdForFileMaybeCreate(file, false);
+ }
+
+ private String getDocIdForFileMaybeCreate(File file, boolean createNewDir)
+ throws FileNotFoundException {
String path = file.getAbsolutePath();
// Find the most-specific root path
@@ -266,6 +273,13 @@
path = path.substring(rootPath.length() + 1);
}
+ if (!file.exists() && createNewDir) {
+ Log.i(TAG, "Creating new directory " + file);
+ if (!file.mkdir()) {
+ Log.e(TAG, "Could not create directory " + file);
+ }
+ }
+
return mostSpecificId + ':' + path;
}
@@ -609,6 +623,34 @@
}
}
+ @Override
+ public Bundle call(String method, String arg, Bundle extras) {
+ Bundle bundle = super.call(method, arg, extras);
+ if (bundle == null && !TextUtils.isEmpty(method)) {
+ switch (method) {
+ case "getDocIdForFileCreateNewDir": {
+ getContext().enforceCallingPermission(
+ android.Manifest.permission.MANAGE_DOCUMENTS, null);
+ if (TextUtils.isEmpty(arg)) {
+ return null;
+ }
+ try {
+ final String docId = getDocIdForFileMaybeCreate(new File(arg), true);
+ bundle = new Bundle();
+ bundle.putString("DOC_ID", docId);
+ } catch (FileNotFoundException e) {
+ Log.w(TAG, "file '" + arg + "' not found");
+ return null;
+ }
+ break;
+ }
+ default:
+ Log.w(TAG, "unknown method passed to call(): " + method);
+ }
+ }
+ return bundle;
+ }
+
private static String getTypeForFile(File file) {
if (file.isDirectory()) {
return Document.MIME_TYPE_DIR;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7f783ec..b54efcc 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,6 +16,7 @@
package com.android.server.notification;
+import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
import static android.service.notification.NotificationAssistantService.REASON_APP_CANCEL;
import static android.service.notification.NotificationAssistantService.REASON_APP_CANCEL_ALL;
import static android.service.notification.NotificationAssistantService.REASON_DELEGATE_CANCEL;
@@ -28,6 +29,7 @@
import static android.service.notification.NotificationAssistantService.REASON_LISTENER_CANCEL_ALL;
import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_BANNED;
import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_CHANGED;
+import static android.service.notification.NotificationAssistantService.REASON_PACKAGE_SUSPENDED;
import static android.service.notification.NotificationAssistantService.REASON_TOPIC_BANNED;
import static android.service.notification.NotificationAssistantService.REASON_USER_STOPPED;
import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
@@ -456,7 +458,7 @@
/** Use this to check if a package can post a notification or toast. */
private boolean checkNotificationOp(String pkg, int uid) {
return mAppOps.checkOp(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
- == AppOpsManager.MODE_ALLOWED;
+ == AppOpsManager.MODE_ALLOWED && !isApplicationSuspended(pkg, uid);
}
private static final class ToastRecord
@@ -691,13 +693,15 @@
boolean queryRemove = false;
boolean packageChanged = false;
boolean cancelNotifications = true;
+ int reason = REASON_PACKAGE_CHANGED;
if (action.equals(Intent.ACTION_PACKAGE_ADDED)
|| (queryRemove=action.equals(Intent.ACTION_PACKAGE_REMOVED))
|| action.equals(Intent.ACTION_PACKAGE_RESTARTED)
|| (packageChanged=action.equals(Intent.ACTION_PACKAGE_CHANGED))
|| (queryRestart=action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART))
- || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
+ || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)
+ || action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
int changeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
UserHandle.USER_ALL);
String pkgList[] = null;
@@ -706,6 +710,9 @@
if (DBG) Slog.i(TAG, "action=" + action + " queryReplace=" + queryReplace);
if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ } else if (action.equals(Intent.ACTION_PACKAGES_SUSPENDED)) {
+ pkgList = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ reason = REASON_PACKAGE_SUSPENDED;
} else if (queryRestart) {
pkgList = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
} else {
@@ -745,7 +752,7 @@
for (String pkgName : pkgList) {
if (cancelNotifications) {
cancelAllNotificationsInt(MY_UID, MY_PID, pkgName, 0, 0, !queryRestart,
- changeUserId, REASON_PACKAGE_CHANGED, null, null);
+ changeUserId, reason, null, null);
}
}
}
@@ -989,6 +996,11 @@
getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, pkgFilter, null,
null);
+ IntentFilter suspendedPkgFilter = new IntentFilter();
+ suspendedPkgFilter.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
+ getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL,
+ suspendedPkgFilter, null, null);
+
IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
getContext().registerReceiverAsUser(mPackageIntentReceiver, UserHandle.ALL, sdFilter, null,
null);
@@ -1104,10 +1116,16 @@
}
final boolean isSystemToast = isCallerSystem() || ("android".equals(pkg));
+ final boolean isApplicationSuspended =
+ isApplicationSuspended(pkg, Binder.getCallingUid());
- if (ENABLE_BLOCKED_TOASTS && !noteNotificationOp(pkg, Binder.getCallingUid())) {
+ if (ENABLE_BLOCKED_TOASTS && (!noteNotificationOp(pkg, Binder.getCallingUid())
+ || isApplicationSuspended)) {
if (!isSystemToast) {
- Slog.e(TAG, "Suppressing toast from package " + pkg + " by user request.");
+ Slog.e(TAG, "Suppressing toast from package " + pkg
+ + (isApplicationSuspended
+ ? " due to package suspended by administrator."
+ : " by user request."));
return;
}
}
@@ -1232,7 +1250,7 @@
public boolean areNotificationsEnabledForPackage(String pkg, int uid) {
checkCallerIsSystem();
return (mAppOps.checkOpNoThrow(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg)
- == AppOpsManager.MODE_ALLOWED);
+ == AppOpsManager.MODE_ALLOWED) && !isApplicationSuspended(pkg, uid);
}
@Override
@@ -2325,14 +2343,20 @@
// why is this here?
savePolicyFile();
+ final boolean isApplicationSuspended = isApplicationSuspended(pkg, callingUid);
// blocked apps/topics
if (r.getImportance() == NotificationListenerService.Ranking.IMPORTANCE_NONE
- || !noteNotificationOp(pkg, callingUid)) {
+ || !noteNotificationOp(pkg, callingUid) || isApplicationSuspended) {
if (!isSystemNotification) {
- Slog.e(TAG, "Suppressing notification from package " + pkg
- + " by user request.");
- mUsageStats.registerBlocked(r);
+ if (isApplicationSuspended) {
+ Slog.e(TAG, "Suppressing notification from package due to package "
+ + "suspended by administrator.");
+ mUsageStats.registerSuspendedByAdmin(r);
+ } else {
+ Slog.e(TAG, "Suppressing notification from package by user request.");
+ mUsageStats.registerBlocked(r);
+ }
return;
}
}
@@ -3423,6 +3447,24 @@
return true;
}
+ private boolean isApplicationSuspended(String pkg, int uid) {
+ int userId = UserHandle.getUserId(uid);
+ ApplicationInfo ai;
+ try {
+ // TODO: it might be faster to return a boolean from package manager rather than the
+ // whole application info. Revisit and make the API change.
+ ai = AppGlobals.getPackageManager().getApplicationInfo(pkg, 0, userId);
+ if (ai == null) {
+ Slog.w(TAG, "No application info for package " + pkg + " and user " + userId);
+ return false;
+ }
+ } catch (RemoteException re) {
+ throw new SecurityException("Could not talk to package manager service");
+ }
+
+ return ((ai.flags & FLAG_SUSPENDED) != 0);
+ }
+
private class TrimCache {
StatusBarNotification heavy;
StatusBarNotification sbnClone;
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index e75324f..0272850 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -198,6 +198,14 @@
releaseAggregatedStatsLocked(aggregatedStatsArray);
}
+ public synchronized void registerSuspendedByAdmin(NotificationRecord notification) {
+ AggregatedStats[] aggregatedStatsArray = getAggregatedStatsLocked(notification);
+ for (AggregatedStats stats : aggregatedStatsArray) {
+ stats.numSuspendedByAdmin++;
+ }
+ releaseAggregatedStatsLocked(aggregatedStatsArray);
+ }
+
// Locked by this.
private AggregatedStats[] getAggregatedStatsLocked(NotificationRecord record) {
if (!ENABLE_AGGREGATED_IN_MEMORY_STATS) {
@@ -298,6 +306,7 @@
public int numWithStaredPeople;
public int numWithValidPeople;
public int numBlocked;
+ public int numSuspendedByAdmin;
public int numWithActions;
public int numPrivate;
public int numSecret;
@@ -414,6 +423,7 @@
maybeCount("people_cache_hit", (numPeopleCacheHit - mPrevious.numPeopleCacheHit));
maybeCount("people_cache_miss", (numPeopleCacheMiss - mPrevious.numPeopleCacheMiss));
maybeCount("note_blocked", (numBlocked - mPrevious.numBlocked));
+ maybeCount("note_suspended", (numSuspendedByAdmin - mPrevious.numSuspendedByAdmin));
maybeCount("note_with_actions", (numWithActions - mPrevious.numWithActions));
maybeCount("note_private", (numPrivate - mPrevious.numPrivate));
maybeCount("note_secret", (numSecret - mPrevious.numSecret));
@@ -442,6 +452,7 @@
mPrevious.numWithStaredPeople = numWithStaredPeople;
mPrevious.numWithValidPeople = numWithValidPeople;
mPrevious.numBlocked = numBlocked;
+ mPrevious.numSuspendedByAdmin = numSuspendedByAdmin;
mPrevious.numWithActions = numWithActions;
mPrevious.numPrivate = numPrivate;
mPrevious.numSecret = numSecret;
@@ -501,6 +512,8 @@
output.append(indentPlusTwo);
output.append("numBlocked=").append(numBlocked).append(",\n");
output.append(indentPlusTwo);
+ output.append("numSuspendedByAdmin=").append(numSuspendedByAdmin).append(",\n");
+ output.append(indentPlusTwo);
output.append("numWithActions=").append(numWithActions).append(",\n");
output.append(indentPlusTwo);
output.append("numPrivate=").append(numPrivate).append(",\n");
@@ -551,6 +564,7 @@
maybePut(dump, "numWithStaredPeople", numWithStaredPeople);
maybePut(dump, "numWithValidPeople", numWithValidPeople);
maybePut(dump, "numBlocked", numBlocked);
+ maybePut(dump, "numSuspendedByAdmin", numSuspendedByAdmin);
maybePut(dump, "numWithActions", numWithActions);
maybePut(dump, "numPrivate", numPrivate);
maybePut(dump, "numSecret", numSecret);
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 90d2aa0..74f1171 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -38,6 +38,10 @@
private final int mLac;
// 16-bit GSM Cell Identity described in TS 27.007, 0..65535
private final int mCid;
+ // 16-bit GSM Absolute RF Channel Number
+ private final int mArfcn;
+ // 6-bit Base Station Identity Code
+ private final int mBsic;
/**
* @hide
@@ -47,6 +51,8 @@
mMnc = Integer.MAX_VALUE;
mLac = Integer.MAX_VALUE;
mCid = Integer.MAX_VALUE;
+ mArfcn = Integer.MAX_VALUE;
+ mBsic = Integer.MAX_VALUE;
}
/**
* public constructor
@@ -58,10 +64,27 @@
* @hide
*/
public CellIdentityGsm (int mcc, int mnc, int lac, int cid) {
+ this(mcc, mnc, lac, cid, Integer.MAX_VALUE, Integer.MAX_VALUE);
+ }
+
+ /**
+ * public constructor
+ * @param mcc 3-digit Mobile Country Code, 0..999
+ * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+ * @param lac 16-bit Location Area Code, 0..65535
+ * @param cid 16-bit GSM Cell Identity or 28-bit UMTS Cell Identity
+ * @param arfcn 16-bit GSM Absolute RF Channel Number
+ * @param bsic 6-bit Base Station Identity Code
+ *
+ * @hide
+ */
+ public CellIdentityGsm (int mcc, int mnc, int lac, int cid, int arfcn, int bsic) {
mMcc = mcc;
mMnc = mnc;
mLac = lac;
mCid = cid;
+ mArfcn = arfcn;
+ mBsic = bsic;
}
private CellIdentityGsm(CellIdentityGsm cid) {
@@ -69,6 +92,8 @@
mMnc = cid.mMnc;
mLac = cid.mLac;
mCid = cid.mCid;
+ mArfcn = cid.mArfcn;
+ mBsic = cid.mBsic;
}
CellIdentityGsm copy() {
@@ -106,6 +131,21 @@
}
/**
+ * @return 16-bit GSM Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+ */
+ public int getArfcn() {
+ return mArfcn;
+ }
+
+ /**
+ * @return 6-bit Base Station Identity Code, Integer.MAX_VALUE if unknown
+ */
+ public int getBsic() {
+ return mBsic;
+ }
+
+
+ /**
* @return Integer.MAX_VALUE, undefined for GSM
*/
@Deprecated
@@ -132,7 +172,9 @@
return mMcc == o.mMcc &&
mMnc == o.mMnc &&
mLac == o.mLac &&
- mCid == o.mCid;
+ mCid == o.mCid &&
+ mArfcn == o.mArfcn &&
+ mBsic == o.mBsic;
}
@Override
@@ -142,6 +184,8 @@
sb.append(" mMnc=").append(mMnc);
sb.append(" mLac=").append(mLac);
sb.append(" mCid=").append(mCid);
+ sb.append(" mArfcn=").append(mArfcn);
+ sb.append(" mBsic=").append("0x").append(Integer.toHexString(mBsic));
sb.append("}");
return sb.toString();
@@ -161,6 +205,8 @@
dest.writeInt(mMnc);
dest.writeInt(mLac);
dest.writeInt(mCid);
+ dest.writeInt(mArfcn);
+ dest.writeInt(mBsic);
}
/** Construct from Parcel, type has already been processed */
@@ -169,6 +215,8 @@
mMnc = in.readInt();
mLac = in.readInt();
mCid = in.readInt();
+ mArfcn = in.readInt();
+ mBsic = in.readInt();
if (DBG) log("CellIdentityGsm(Parcel): " + toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index 1e7ac08..ce74383 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -40,6 +40,8 @@
private final int mPci;
// 16-bit tracking area code
private final int mTac;
+ // 18-bit Absolute RF Channel Number
+ private final int mEarfcn;
/**
* @hide
@@ -50,6 +52,7 @@
mCi = Integer.MAX_VALUE;
mPci = Integer.MAX_VALUE;
mTac = Integer.MAX_VALUE;
+ mEarfcn = Integer.MAX_VALUE;
}
/**
@@ -63,11 +66,27 @@
* @hide
*/
public CellIdentityLte (int mcc, int mnc, int ci, int pci, int tac) {
+ this(mcc, mnc, ci, pci, tac, Integer.MAX_VALUE);
+ }
+
+ /**
+ *
+ * @param mcc 3-digit Mobile Country Code, 0..999
+ * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+ * @param ci 28-bit Cell Identity
+ * @param pci Physical Cell Id 0..503
+ * @param tac 16-bit Tracking Area Code
+ * @param earfcn 18-bit LTE Absolute RF Channel Number
+ *
+ * @hide
+ */
+ public CellIdentityLte (int mcc, int mnc, int ci, int pci, int tac, int earfcn) {
mMcc = mcc;
mMnc = mnc;
mCi = ci;
mPci = pci;
mTac = tac;
+ mEarfcn = earfcn;
}
private CellIdentityLte(CellIdentityLte cid) {
@@ -76,6 +95,7 @@
mCi = cid.mCi;
mPci = cid.mPci;
mTac = cid.mTac;
+ mEarfcn = cid.mEarfcn;
}
CellIdentityLte copy() {
@@ -117,6 +137,13 @@
return mTac;
}
+ /**
+ * @return 18-bit Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+ */
+ public int getEarfcn() {
+ return mEarfcn;
+ }
+
@Override
public int hashCode() {
return Objects.hash(mMcc, mMnc, mCi, mPci, mTac);
@@ -137,7 +164,8 @@
mMnc == o.mMnc &&
mCi == o.mCi &&
mPci == o.mPci &&
- mTac == o.mTac;
+ mTac == o.mTac &&
+ mEarfcn == o.mEarfcn;
}
@Override
@@ -148,6 +176,7 @@
sb.append(" mCi="); sb.append(mCi);
sb.append(" mPci="); sb.append(mPci);
sb.append(" mTac="); sb.append(mTac);
+ sb.append(" mEarfcn="); sb.append(mEarfcn);
sb.append("}");
return sb.toString();
@@ -168,6 +197,7 @@
dest.writeInt(mCi);
dest.writeInt(mPci);
dest.writeInt(mTac);
+ dest.writeInt(mEarfcn);
}
/** Construct from Parcel, type has already been processed */
@@ -177,6 +207,7 @@
mCi = in.readInt();
mPci = in.readInt();
mTac = in.readInt();
+ mEarfcn = in.readInt();
if (DBG) log("CellIdentityLte(Parcel): " + toString());
}
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 56ee8c9..0d13efd 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -40,6 +40,8 @@
private final int mCid;
// 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511
private final int mPsc;
+ // 16-bit UMTS Absolute RF Channel Number
+ private final int mUarfcn;
/**
* @hide
@@ -50,6 +52,7 @@
mLac = Integer.MAX_VALUE;
mCid = Integer.MAX_VALUE;
mPsc = Integer.MAX_VALUE;
+ mUarfcn = Integer.MAX_VALUE;
}
/**
* public constructor
@@ -62,11 +65,27 @@
* @hide
*/
public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc) {
+ this(mcc, mnc, lac, cid, psc, Integer.MAX_VALUE);
+ }
+
+ /**
+ * public constructor
+ * @param mcc 3-digit Mobile Country Code, 0..999
+ * @param mnc 2 or 3-digit Mobile Network Code, 0..999
+ * @param lac 16-bit Location Area Code, 0..65535
+ * @param cid 28-bit UMTS Cell Identity
+ * @param psc 9-bit UMTS Primary Scrambling Code
+ * @param uarfcn 16-bit UMTS Absolute RF Channel Number
+ *
+ * @hide
+ */
+ public CellIdentityWcdma (int mcc, int mnc, int lac, int cid, int psc, int uarfcn) {
mMcc = mcc;
mMnc = mnc;
mLac = lac;
mCid = cid;
mPsc = psc;
+ mUarfcn = uarfcn;
}
private CellIdentityWcdma(CellIdentityWcdma cid) {
@@ -75,6 +94,7 @@
mLac = cid.mLac;
mCid = cid.mCid;
mPsc = cid.mPsc;
+ mUarfcn = cid.mUarfcn;
}
CellIdentityWcdma copy() {
@@ -123,6 +143,13 @@
return Objects.hash(mMcc, mMnc, mLac, mCid, mPsc);
}
+ /**
+ * @return 16-bit UMTS Absolute RF Channel Number, Integer.MAX_VALUE if unknown
+ */
+ public int getUarfcn() {
+ return mUarfcn;
+ }
+
@Override
public boolean equals(Object other) {
if (this == other) {
@@ -138,7 +165,8 @@
mMnc == o.mMnc &&
mLac == o.mLac &&
mCid == o.mCid &&
- mPsc == o.mPsc;
+ mPsc == o.mPsc &&
+ mUarfcn == o.mUarfcn;
}
@Override
@@ -149,6 +177,7 @@
sb.append(" mLac=").append(mLac);
sb.append(" mCid=").append(mCid);
sb.append(" mPsc=").append(mPsc);
+ sb.append(" mUarfcn=").append(mUarfcn);
sb.append("}");
return sb.toString();
@@ -169,6 +198,7 @@
dest.writeInt(mLac);
dest.writeInt(mCid);
dest.writeInt(mPsc);
+ dest.writeInt(mUarfcn);
}
/** Construct from Parcel, type has already been processed */
@@ -178,6 +208,7 @@
mLac = in.readInt();
mCid = in.readInt();
mPsc = in.readInt();
+ mUarfcn = in.readInt();
if (DBG) log("CellIdentityWcdma(Parcel): " + toString());
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index d27fcec..addf7ef 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -34,6 +34,7 @@
private int mSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
private int mBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
+ private int mTimingAdvance;
/**
* Empty constructor
@@ -75,6 +76,22 @@
public void initialize(int ss, int ber) {
mSignalStrength = ss;
mBitErrorRate = ber;
+ mTimingAdvance = Integer.MAX_VALUE;
+ }
+
+ /**
+ * Initialize all the values
+ *
+ * @param ss SignalStrength as ASU value
+ * @param ber is Bit Error Rate
+ * @param ta timing advance
+ *
+ * @hide
+ */
+ public void initialize(int ss, int ber, int ta) {
+ mSignalStrength = ss;
+ mBitErrorRate = ber;
+ mTimingAdvance = ta;
}
/**
@@ -83,6 +100,7 @@
protected void copyFrom(CellSignalStrengthGsm s) {
mSignalStrength = s.mSignalStrength;
mBitErrorRate = s.mBitErrorRate;
+ mTimingAdvance = s.mTimingAdvance;
}
/**
@@ -98,6 +116,7 @@
public void setDefaultValues() {
mSignalStrength = Integer.MAX_VALUE;
mBitErrorRate = Integer.MAX_VALUE;
+ mTimingAdvance = Integer.MAX_VALUE;
}
/**
@@ -174,7 +193,8 @@
return false;
}
- return mSignalStrength == s.mSignalStrength && mBitErrorRate == s.mBitErrorRate;
+ return mSignalStrength == s.mSignalStrength && mBitErrorRate == s.mBitErrorRate &&
+ s.mTimingAdvance == mTimingAdvance;
}
/**
@@ -184,7 +204,8 @@
public String toString() {
return "CellSignalStrengthGsm:"
+ " ss=" + mSignalStrength
- + " ber=" + mBitErrorRate;
+ + " ber=" + mBitErrorRate
+ + " mTa=" + mTimingAdvance;
}
/** Implement the Parcelable interface */
@@ -193,6 +214,7 @@
if (DBG) log("writeToParcel(Parcel, int): " + toString());
dest.writeInt(mSignalStrength);
dest.writeInt(mBitErrorRate);
+ dest.writeInt(mTimingAdvance);
}
/**
@@ -202,6 +224,7 @@
private CellSignalStrengthGsm(Parcel in) {
mSignalStrength = in.readInt();
mBitErrorRate = in.readInt();
+ mTimingAdvance = in.readInt();
if (DBG) log("CellSignalStrengthGsm(Parcel): " + toString());
}